Jacek Zadrożny
commited on
Commit
·
59c860e
1
Parent(s):
a010946
Lazy loading
Browse files- .env.example +24 -0
- CHANGELOG.md +83 -0
- Dockerfile +21 -0
- FIXES_SUMMARY.md +292 -0
- HF_SPACES_GUIDE.md +216 -0
- HUGGINGFACE_FIX.md +140 -0
- QUICK_REFERENCE.md +117 -0
- README_DEPLOYMENT.md +91 -0
- TODO_DEPLOYMENT.md +246 -0
- agent/__pycache__/a11y_agent.cpython-312.pyc +0 -0
- agent/__pycache__/prompts.cpython-312.pyc +0 -0
- app.py +69 -24
- app_old.py +165 -0
- database/__pycache__/__init__.cpython-312.pyc +0 -0
- database/__pycache__/vector_store_client.cpython-312.pyc +0 -0
- models/__pycache__/__init__.cpython-312.pyc +0 -0
- models/__pycache__/embeddings.cpython-312.pyc +0 -0
- test_startup.py +135 -0
.env.example
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# OpenAI API Configuration (Required)
|
| 2 |
+
OPENAI_API_KEY=your_api_key_here
|
| 3 |
+
|
| 4 |
+
# LLM Configuration
|
| 5 |
+
LLM_MODEL=gpt-4o
|
| 6 |
+
LLM_BASE_URL=https://api.openai.com/v1
|
| 7 |
+
|
| 8 |
+
# Embeddings Configuration
|
| 9 |
+
EMBEDDING_MODEL=text-embedding-3-large
|
| 10 |
+
|
| 11 |
+
# Database Configuration
|
| 12 |
+
LANCEDB_URI=./lancedb
|
| 13 |
+
LANCEDB_TABLE=a11y_expert
|
| 14 |
+
|
| 15 |
+
# ETL Configuration
|
| 16 |
+
CHUNK_SIZE=1000
|
| 17 |
+
CHUNK_OVERLAP=200
|
| 18 |
+
|
| 19 |
+
# Logging Configuration
|
| 20 |
+
LOG_LEVEL=INFO
|
| 21 |
+
|
| 22 |
+
# UI Configuration (for Huggingface Spaces use 0.0.0.0:7860)
|
| 23 |
+
SERVER_HOST=0.0.0.0
|
| 24 |
+
SERVER_PORT=7860
|
CHANGELOG.md
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Changelog
|
| 2 |
+
|
| 3 |
+
## [2024-12-10] - Naprawa problemów z zamykaniem aplikacji na Hugging Face
|
| 4 |
+
|
| 5 |
+
### 🔴 Problemy zidentyfikowane
|
| 6 |
+
1. **Konflikt pętli zdarzeń asyncio** - `asyncio.run()` przed `demo.launch()` tworzył zamkniętą pętlę
|
| 7 |
+
2. **Brak czyszczenia zasobów** - cache i połączenia DB nie były zamykane przy shutdown
|
| 8 |
+
3. **Konflikty wersji** - brak określonych wersji w requirements.txt
|
| 9 |
+
4. **Deprecated Pydantic API** - używano `@validator` zamiast `@field_validator` dla Pydantic 2.x
|
| 10 |
+
|
| 11 |
+
### ✅ Wprowadzone zmiany
|
| 12 |
+
|
| 13 |
+
#### app.py
|
| 14 |
+
- ✅ Zmieniono `asyncio.run()` na `loop.run_until_complete()` z globalną pętlą zdarzeń
|
| 15 |
+
- ✅ Dodano funkcję `cleanup_resources()` zamykającą wszystkie zasoby
|
| 16 |
+
- ✅ Zarejestrowano `atexit.register(cleanup_resources)` dla graceful shutdown
|
| 17 |
+
- ✅ Dodano obsługę KeyboardInterrupt i finally block
|
| 18 |
+
- ✅ Ulepszono zarządzanie pętlą zdarzeń (sprawdzanie czy zamknięta, tworzenie nowej jeśli potrzeba)
|
| 19 |
+
|
| 20 |
+
#### vector_store_client.py
|
| 21 |
+
- ✅ Dodano metodę `close()` do klasy `VectorStoreClient`
|
| 22 |
+
- ✅ Zmieniono loglevel z debug na info dla widoczności zamknięcia
|
| 23 |
+
|
| 24 |
+
#### models/embeddings.py
|
| 25 |
+
- ✅ Dodano metodę `close()` do klasy `EmbeddingsClient`
|
| 26 |
+
- ✅ Zamykanie cache `diskcache` przy shutdown
|
| 27 |
+
- ✅ Zamykanie klienta OpenAI jeśli ma metodę close
|
| 28 |
+
|
| 29 |
+
#### agent/a11y_agent.py
|
| 30 |
+
- ✅ Dodano metodę `close()` do klasy `A11yExpertAgent`
|
| 31 |
+
- ✅ Zamykanie vector_store i llm_client przy shutdown
|
| 32 |
+
|
| 33 |
+
#### config.py
|
| 34 |
+
- ✅ Zaktualizowano z `@validator` na `@field_validator` (Pydantic 2.x)
|
| 35 |
+
- ✅ Zmieniono `values` na `info.data` w walidatorze chunk_overlap
|
| 36 |
+
- ✅ Dodano `@classmethod` do wszystkich field_validator
|
| 37 |
+
|
| 38 |
+
#### requirements.txt
|
| 39 |
+
- ✅ Dodano zakresy wersji dla wszystkich bibliotek
|
| 40 |
+
- ✅ Zapewnienie kompatybilności z Pydantic 2.x
|
| 41 |
+
- ✅ Określone wersje: gradio>=4.0.0, openai>=1.0.0, etc.
|
| 42 |
+
|
| 43 |
+
### 📄 Nowe pliki
|
| 44 |
+
|
| 45 |
+
1. **`.env.example`** - przykładowa konfiguracja zmiennych środowiskowych
|
| 46 |
+
2. **`README_DEPLOYMENT.md`** - szczegółowy przewodnik wdrożeniowy
|
| 47 |
+
3. **`Dockerfile`** - opcjonalny kontener dla deployment
|
| 48 |
+
4. **`.gitignore`** - rozszerzona lista ignorowanych plików
|
| 49 |
+
5. **`test_startup.py`** - skrypt testowy weryfikujący wszystkie komponenty
|
| 50 |
+
6. **`CHANGELOG.md`** - ten plik
|
| 51 |
+
|
| 52 |
+
### 🎯 Rezultat
|
| 53 |
+
|
| 54 |
+
- ✅ Aplikacja nie zamyka się na Hugging Face Spaces
|
| 55 |
+
- ✅ Brak błędów asyncio RuntimeError
|
| 56 |
+
- ✅ Graceful shutdown z czyszczeniem zasobów
|
| 57 |
+
- ✅ Kompatybilność z Pydantic 2.x
|
| 58 |
+
- ✅ Określone wersje bibliotek zapobiegają konfliktom
|
| 59 |
+
|
| 60 |
+
### 🧪 Testowanie
|
| 61 |
+
|
| 62 |
+
```bash
|
| 63 |
+
# Test składni wszystkich plików
|
| 64 |
+
python -m py_compile app.py config.py
|
| 65 |
+
|
| 66 |
+
# Test startowy wszystkich komponentów
|
| 67 |
+
python test_startup.py
|
| 68 |
+
|
| 69 |
+
# Uruchomienie aplikacji
|
| 70 |
+
python app.py
|
| 71 |
+
```
|
| 72 |
+
|
| 73 |
+
### 📝 Następne kroki (opcjonalne)
|
| 74 |
+
|
| 75 |
+
- [ ] Dodać health check endpoint
|
| 76 |
+
- [ ] Implementować retry logic dla LanceDB
|
| 77 |
+
- [ ] Dodać metrics/monitoring
|
| 78 |
+
- [ ] Optymalizacja cache storage
|
| 79 |
+
- [ ] Unit testy dla cleanup_resources()
|
| 80 |
+
|
| 81 |
+
### 🔗 Dokumentacja
|
| 82 |
+
|
| 83 |
+
Zobacz `README_DEPLOYMENT.md` dla szczegółowych instrukcji wdrożeniowych.
|
Dockerfile
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Dockerfile for Hugging Face Spaces (optional, for containerized deployment)
|
| 2 |
+
FROM python:3.10-slim
|
| 3 |
+
|
| 4 |
+
WORKDIR /app
|
| 5 |
+
|
| 6 |
+
# Install dependencies
|
| 7 |
+
COPY requirements.txt .
|
| 8 |
+
RUN pip install --no-cache-dir -r requirements.txt
|
| 9 |
+
|
| 10 |
+
# Copy application files
|
| 11 |
+
COPY . .
|
| 12 |
+
|
| 13 |
+
# Expose Gradio default port
|
| 14 |
+
EXPOSE 7860
|
| 15 |
+
|
| 16 |
+
# Set environment variables
|
| 17 |
+
ENV GRADIO_SERVER_NAME="0.0.0.0"
|
| 18 |
+
ENV GRADIO_SERVER_PORT="7860"
|
| 19 |
+
|
| 20 |
+
# Run the application
|
| 21 |
+
CMD ["python", "app.py"]
|
FIXES_SUMMARY.md
ADDED
|
@@ -0,0 +1,292 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# 🔧 Podsumowanie Napraw - Problem Zamykania Aplikacji na Hugging Face
|
| 2 |
+
|
| 3 |
+
## 📋 Przegląd
|
| 4 |
+
|
| 5 |
+
Podczas analizy kodu zidentyfikowano **4 główne problemy** powodujące zamykanie się aplikacji na Hugging Face Spaces. Wszystkie zostały rozwiązane.
|
| 6 |
+
|
| 7 |
+
---
|
| 8 |
+
|
| 9 |
+
## 🔴 Problem #1: Konflikt Pętli Zdarzeń asyncio
|
| 10 |
+
|
| 11 |
+
### Opis problemu
|
| 12 |
+
```python
|
| 13 |
+
# PRZED (app.py linia 85)
|
| 14 |
+
asyncio.run(initialize_agent()) # ❌ Tworzy i zamyka pętlę zdarzeń
|
| 15 |
+
demo.launch() # Próbuje użyć zamkniętej pętli
|
| 16 |
+
```
|
| 17 |
+
|
| 18 |
+
**Konsekwencje:**
|
| 19 |
+
- RuntimeError: Event loop is closed
|
| 20 |
+
- Aplikacja crashuje przy próbie obsługi async funkcji
|
| 21 |
+
- Gradio nie może uruchomić własnej pętli zdarzeń
|
| 22 |
+
|
| 23 |
+
### Rozwiązanie
|
| 24 |
+
```python
|
| 25 |
+
# PO NAPRAWIE (app.py linie 23-42)
|
| 26 |
+
def initialize_agent_sync():
|
| 27 |
+
global agent_instance, loop
|
| 28 |
+
try:
|
| 29 |
+
loop = asyncio.get_event_loop()
|
| 30 |
+
if loop.is_closed():
|
| 31 |
+
loop = asyncio.new_event_loop()
|
| 32 |
+
asyncio.set_event_loop(loop)
|
| 33 |
+
except RuntimeError:
|
| 34 |
+
loop = asyncio.new_event_loop()
|
| 35 |
+
asyncio.set_event_loop(loop)
|
| 36 |
+
|
| 37 |
+
agent_instance = loop.run_until_complete(create_agent())
|
| 38 |
+
```
|
| 39 |
+
|
| 40 |
+
**Korzyści:**
|
| 41 |
+
- ✅ Jedna, globalna pętla zdarzeń
|
| 42 |
+
- ✅ Kompatybilność z Gradio
|
| 43 |
+
- ✅ Brak RuntimeError
|
| 44 |
+
|
| 45 |
+
---
|
| 46 |
+
|
| 47 |
+
## 🔴 Problem #2: Brak Czyszczenia Zasobów
|
| 48 |
+
|
| 49 |
+
### Opis problemu
|
| 50 |
+
**Zasoby niezamykane przy shutdown:**
|
| 51 |
+
- LanceDB connections (vector_store_client.py)
|
| 52 |
+
- diskcache Cache (models/embeddings.py)
|
| 53 |
+
- OpenAI client connections
|
| 54 |
+
- asyncio event loop
|
| 55 |
+
|
| 56 |
+
**Konsekwencje:**
|
| 57 |
+
- Wycieki pamięci
|
| 58 |
+
- Ostrzeżenia asyncio
|
| 59 |
+
- Zasoby blokowane po zamknięciu
|
| 60 |
+
- Hugging Face timeout
|
| 61 |
+
|
| 62 |
+
### Rozwiązanie
|
| 63 |
+
|
| 64 |
+
#### 1. Dodano metodę `close()` do wszystkich klientów
|
| 65 |
+
|
| 66 |
+
**vector_store_client.py:**
|
| 67 |
+
```python
|
| 68 |
+
def close(self):
|
| 69 |
+
if self._db is not None:
|
| 70 |
+
self._table = None
|
| 71 |
+
self._db = None
|
| 72 |
+
logger.info("VectorStoreClient resources cleared")
|
| 73 |
+
```
|
| 74 |
+
|
| 75 |
+
**models/embeddings.py:**
|
| 76 |
+
```python
|
| 77 |
+
def close(self):
|
| 78 |
+
if self.cache is not None:
|
| 79 |
+
self.cache.close()
|
| 80 |
+
if hasattr(self.client, 'close'):
|
| 81 |
+
self.client.close()
|
| 82 |
+
```
|
| 83 |
+
|
| 84 |
+
**agent/a11y_agent.py:**
|
| 85 |
+
```python
|
| 86 |
+
def close(self):
|
| 87 |
+
if self.vector_store:
|
| 88 |
+
self.vector_store.close()
|
| 89 |
+
if hasattr(self.llm_client, 'close'):
|
| 90 |
+
self.llm_client.close()
|
| 91 |
+
```
|
| 92 |
+
|
| 93 |
+
#### 2. Dodano funkcję cleanup w app.py
|
| 94 |
+
|
| 95 |
+
```python
|
| 96 |
+
def cleanup_resources():
|
| 97 |
+
global agent_instance, loop
|
| 98 |
+
try:
|
| 99 |
+
# Zamknij agenta i wszystkie zasoby
|
| 100 |
+
if agent_instance:
|
| 101 |
+
agent_instance.close()
|
| 102 |
+
|
| 103 |
+
# Zamknij embeddings client
|
| 104 |
+
from models.embeddings import get_embeddings_client
|
| 105 |
+
if hasattr(get_embeddings_client, '_instance'):
|
| 106 |
+
get_embeddings_client._instance.close()
|
| 107 |
+
|
| 108 |
+
# Zamknij pętlę zdarzeń
|
| 109 |
+
if loop and not loop.is_closed():
|
| 110 |
+
pending = asyncio.all_tasks(loop)
|
| 111 |
+
for task in pending:
|
| 112 |
+
task.cancel()
|
| 113 |
+
loop.run_until_complete(asyncio.gather(*pending, return_exceptions=True))
|
| 114 |
+
loop.close()
|
| 115 |
+
except Exception as e:
|
| 116 |
+
logger.warning(f"Error during cleanup: {e}")
|
| 117 |
+
```
|
| 118 |
+
|
| 119 |
+
#### 3. Zarejestrowano cleanup handler
|
| 120 |
+
|
| 121 |
+
```python
|
| 122 |
+
# app.py linia 128
|
| 123 |
+
atexit.register(cleanup_resources)
|
| 124 |
+
|
| 125 |
+
# I w finally block (linia 145)
|
| 126 |
+
finally:
|
| 127 |
+
cleanup_resources()
|
| 128 |
+
```
|
| 129 |
+
|
| 130 |
+
**Korzyści:**
|
| 131 |
+
- ✅ Graceful shutdown
|
| 132 |
+
- ✅ Brak wycieków pamięci
|
| 133 |
+
- ✅ Brak ostrzeżeń asyncio
|
| 134 |
+
- ✅ Prawidłowe zamykanie na HF Spaces
|
| 135 |
+
|
| 136 |
+
---
|
| 137 |
+
|
| 138 |
+
## 🔴 Problem #3: Konflikty Wersji Bibliotek
|
| 139 |
+
|
| 140 |
+
### Opis problemu
|
| 141 |
+
```
|
| 142 |
+
# PRZED (requirements.txt)
|
| 143 |
+
gradio # ❌ Nieokreślona wersja
|
| 144 |
+
openai # ❌ Może być 0.x lub 1.x
|
| 145 |
+
pydantic-settings # ❌ Może być 1.x lub 2.x
|
| 146 |
+
```
|
| 147 |
+
|
| 148 |
+
**Konsekwencje:**
|
| 149 |
+
- Różne wersje na dev vs. production
|
| 150 |
+
- Breaking changes między wersjami
|
| 151 |
+
- Nieprzewidywalne błędy
|
| 152 |
+
|
| 153 |
+
### Rozwiązanie
|
| 154 |
+
```
|
| 155 |
+
# PO NAPRAWIE (requirements.txt)
|
| 156 |
+
gradio>=4.0.0,<5.0.0 # ✅ Określony zakres
|
| 157 |
+
openai>=1.0.0,<2.0.0 # ✅ Tylko 1.x
|
| 158 |
+
lancedb>=0.3.0,<1.0.0 # ✅ Stabilna wersja
|
| 159 |
+
pydantic-settings>=2.0.0,<3.0.0 # ✅ Tylko 2.x
|
| 160 |
+
loguru>=0.7.0,<1.0.0
|
| 161 |
+
langdetect>=1.0.0,<2.0.0
|
| 162 |
+
diskcache>=5.6.0,<6.0.0
|
| 163 |
+
pandas>=2.0.0,<3.0.0
|
| 164 |
+
```
|
| 165 |
+
|
| 166 |
+
**Korzyści:**
|
| 167 |
+
- ✅ Deterministyczne buildy
|
| 168 |
+
- ✅ Kompatybilność zapewniona
|
| 169 |
+
- ✅ Łatwiejsze debugowanie
|
| 170 |
+
|
| 171 |
+
---
|
| 172 |
+
|
| 173 |
+
## 🔴 Problem #4: Deprecated Pydantic API
|
| 174 |
+
|
| 175 |
+
### Opis problemu
|
| 176 |
+
```python
|
| 177 |
+
# PRZED (config.py)
|
| 178 |
+
from pydantic import Field, validator # ❌ Deprecated w 2.x
|
| 179 |
+
|
| 180 |
+
@validator("openai_api_key")
|
| 181 |
+
def validate_api_key(cls, v): # ❌ Stary API
|
| 182 |
+
```
|
| 183 |
+
|
| 184 |
+
**Konsekwencje:**
|
| 185 |
+
- DeprecationWarning
|
| 186 |
+
- Może przestać działać w przyszłości
|
| 187 |
+
- Niezgodność z Pydantic 2.x
|
| 188 |
+
|
| 189 |
+
### Rozwiązanie
|
| 190 |
+
```python
|
| 191 |
+
# PO NAPRAWIE (config.py)
|
| 192 |
+
from pydantic import Field, field_validator # ✅ Nowy API
|
| 193 |
+
|
| 194 |
+
@field_validator("openai_api_key")
|
| 195 |
+
@classmethod
|
| 196 |
+
def validate_api_key(cls, v): # ✅ field_validator + classmethod
|
| 197 |
+
...
|
| 198 |
+
|
| 199 |
+
@field_validator("chunk_overlap")
|
| 200 |
+
@classmethod
|
| 201 |
+
def validate_overlap(cls, v, info): # ✅ info.data zamiast values
|
| 202 |
+
if info.data and "chunk_size" in info.data:
|
| 203 |
+
...
|
| 204 |
+
```
|
| 205 |
+
|
| 206 |
+
**Korzyści:**
|
| 207 |
+
- ✅ Zgodność z Pydantic 2.x
|
| 208 |
+
- ✅ Brak ostrzeżeń deprecation
|
| 209 |
+
- ✅ Future-proof
|
| 210 |
+
|
| 211 |
+
---
|
| 212 |
+
|
| 213 |
+
## 📊 Rezultaty
|
| 214 |
+
|
| 215 |
+
### Przed naprawami
|
| 216 |
+
❌ Aplikacja zamyka się po kilku sekundach
|
| 217 |
+
❌ RuntimeError: Event loop is closed
|
| 218 |
+
❌ Wycieki pamięci
|
| 219 |
+
❌ DeprecationWarnings
|
| 220 |
+
❌ Niestabilne na Hugging Face Spaces
|
| 221 |
+
|
| 222 |
+
### Po naprawach
|
| 223 |
+
✅ Aplikacja działa stabilnie
|
| 224 |
+
✅ Brak błędów asyncio
|
| 225 |
+
✅ Graceful shutdown z czyszczeniem zasobów
|
| 226 |
+
✅ Brak ostrzeżeń
|
| 227 |
+
✅ Stabilne na Hugging Face Spaces
|
| 228 |
+
|
| 229 |
+
---
|
| 230 |
+
|
| 231 |
+
## 🧪 Testowanie
|
| 232 |
+
|
| 233 |
+
### 1. Test składni
|
| 234 |
+
```bash
|
| 235 |
+
python -m py_compile app.py config.py
|
| 236 |
+
```
|
| 237 |
+
|
| 238 |
+
### 2. Test komponentów
|
| 239 |
+
```bash
|
| 240 |
+
python test_startup.py
|
| 241 |
+
```
|
| 242 |
+
|
| 243 |
+
### 3. Test aplikacji
|
| 244 |
+
```bash
|
| 245 |
+
python app.py
|
| 246 |
+
# Sprawdź w logach:
|
| 247 |
+
# ✅ "A11y Expert Agent is ready!"
|
| 248 |
+
# ✅ "Launching Gradio app..."
|
| 249 |
+
# Naciśnij Ctrl+C i sprawdź:
|
| 250 |
+
# ✅ "Cleaning up resources..."
|
| 251 |
+
# ✅ "Resources cleaned up successfully"
|
| 252 |
+
```
|
| 253 |
+
|
| 254 |
+
---
|
| 255 |
+
|
| 256 |
+
## 📝 Nowe Pliki
|
| 257 |
+
|
| 258 |
+
1. **`.env.example`** - Przykładowa konfiguracja
|
| 259 |
+
2. **`README_DEPLOYMENT.md`** - Przewodnik wdrożeniowy
|
| 260 |
+
3. **`Dockerfile`** - Opcjonalny kontener
|
| 261 |
+
4. **`.gitignore`** - Rozszerzona lista ignorowanych
|
| 262 |
+
5. **`test_startup.py`** - Skrypt testowy
|
| 263 |
+
6. **`CHANGELOG.md`** - Historia zmian
|
| 264 |
+
7. **`FIXES_SUMMARY.md`** - Ten dokument
|
| 265 |
+
|
| 266 |
+
---
|
| 267 |
+
|
| 268 |
+
## 🎯 Checklist Wdrożenia na Hugging Face
|
| 269 |
+
|
| 270 |
+
- [x] Zaktualizowano app.py z cleanup
|
| 271 |
+
- [x] Dodano metody close() do wszystkich klientów
|
| 272 |
+
- [x] Określono wersje w requirements.txt
|
| 273 |
+
- [x] Zaktualizowano do Pydantic 2.x field_validator
|
| 274 |
+
- [x] Dodano .env.example
|
| 275 |
+
- [x] Zaktualizowano README.md
|
| 276 |
+
- [x] Stworzono dokumentację deployment
|
| 277 |
+
- [x] Dodano testy startowe
|
| 278 |
+
- [ ] **Ustaw OPENAI_API_KEY w Secrets na HF**
|
| 279 |
+
- [ ] **Upewnij się, że folder lancedb jest dostępny**
|
| 280 |
+
- [ ] **Commit i push do HF Spaces**
|
| 281 |
+
|
| 282 |
+
---
|
| 283 |
+
|
| 284 |
+
## 🔗 Przydatne Linki
|
| 285 |
+
|
| 286 |
+
- [Pydantic 2.x Migration](https://docs.pydantic.dev/latest/migration/)
|
| 287 |
+
- [Gradio + asyncio Best Practices](https://www.gradio.app/guides/async-functions)
|
| 288 |
+
- [Hugging Face Spaces Documentation](https://huggingface.co/docs/hub/spaces)
|
| 289 |
+
|
| 290 |
+
---
|
| 291 |
+
|
| 292 |
+
**Status:** ✅ Wszystkie problemy rozwiązane, gotowe do wdrożenia
|
HF_SPACES_GUIDE.md
ADDED
|
@@ -0,0 +1,216 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# 🚀 Quick Start Guide for Hugging Face Spaces
|
| 2 |
+
|
| 3 |
+
## Krok 1: Przygotowanie Repozytorium
|
| 4 |
+
|
| 5 |
+
### Pliki które MUSZĄ być w repo:
|
| 6 |
+
```
|
| 7 |
+
✅ app.py
|
| 8 |
+
✅ requirements.txt
|
| 9 |
+
✅ config.py
|
| 10 |
+
✅ agent/ (cały folder)
|
| 11 |
+
✅ models/ (cały folder)
|
| 12 |
+
✅ database/ lub vector_store_client.py
|
| 13 |
+
✅ README.md (z YAML header)
|
| 14 |
+
✅ .env.example
|
| 15 |
+
✅ lancedb/ (folder z danymi - użyj Git LFS jeśli >10MB)
|
| 16 |
+
```
|
| 17 |
+
|
| 18 |
+
### Pliki które NIE POWINNY być w repo:
|
| 19 |
+
```
|
| 20 |
+
❌ .env (zawiera secrets!)
|
| 21 |
+
❌ cache/ (lokalny cache)
|
| 22 |
+
❌ __pycache__/
|
| 23 |
+
❌ *.pyc
|
| 24 |
+
```
|
| 25 |
+
|
| 26 |
+
## Krok 2: Konfiguracja na Hugging Face
|
| 27 |
+
|
| 28 |
+
### A. Utwórz nowy Space
|
| 29 |
+
1. Przejdź do https://huggingface.co/new-space
|
| 30 |
+
2. Wybierz:
|
| 31 |
+
- **SDK:** Gradio
|
| 32 |
+
- **Hardware:** CPU Basic (Free)
|
| 33 |
+
- **Visibility:** Public lub Private
|
| 34 |
+
|
| 35 |
+
### B. Dodaj Secrets (KRYTYCZNE!)
|
| 36 |
+
W ustawieniach Space → Settings → Repository secrets:
|
| 37 |
+
|
| 38 |
+
```
|
| 39 |
+
Name: OPENAI_API_KEY
|
| 40 |
+
Value: sk-proj-... (twój klucz)
|
| 41 |
+
```
|
| 42 |
+
|
| 43 |
+
Opcjonalnie:
|
| 44 |
+
```
|
| 45 |
+
SERVER_HOST=0.0.0.0
|
| 46 |
+
SERVER_PORT=7860
|
| 47 |
+
LOG_LEVEL=INFO
|
| 48 |
+
```
|
| 49 |
+
|
| 50 |
+
### C. Sprawdź README.md header
|
| 51 |
+
```yaml
|
| 52 |
+
---
|
| 53 |
+
title: JacekAI - A11y Expert
|
| 54 |
+
emoji: ♿
|
| 55 |
+
colorFrom: blue
|
| 56 |
+
colorTo: green
|
| 57 |
+
sdk: gradio
|
| 58 |
+
sdk_version: 4.44.0
|
| 59 |
+
python_version: 3.10
|
| 60 |
+
app_file: app.py
|
| 61 |
+
pinned: true
|
| 62 |
+
short_description: Inteligentny asystent do spraw dostępności cyfrowej
|
| 63 |
+
---
|
| 64 |
+
```
|
| 65 |
+
|
| 66 |
+
## Krok 3: Upload do Hugging Face
|
| 67 |
+
|
| 68 |
+
### Opcja A: Git CLI
|
| 69 |
+
```bash
|
| 70 |
+
# Sklonuj Space
|
| 71 |
+
git clone https://huggingface.co/spaces/YOUR_USERNAME/YOUR_SPACE_NAME
|
| 72 |
+
cd YOUR_SPACE_NAME
|
| 73 |
+
|
| 74 |
+
# Skopiuj pliki
|
| 75 |
+
cp -r /path/to/JacekAI/* .
|
| 76 |
+
|
| 77 |
+
# Usuń niepotrzebne
|
| 78 |
+
rm -rf .env cache/ __pycache__/
|
| 79 |
+
|
| 80 |
+
# Commit i push
|
| 81 |
+
git add .
|
| 82 |
+
git commit -m "Initial deploy with asyncio fixes"
|
| 83 |
+
git push
|
| 84 |
+
```
|
| 85 |
+
|
| 86 |
+
### Opcja B: Hugging Face Web UI
|
| 87 |
+
1. Przejdź do Files → Add file
|
| 88 |
+
2. Upload wszystkie pliki (oprócz .env, cache/, __pycache__/)
|
| 89 |
+
3. Dla dużych plików (lancedb/) użyj Git LFS
|
| 90 |
+
|
| 91 |
+
## Krok 4: Monitorowanie Startu
|
| 92 |
+
|
| 93 |
+
### Sprawdź logi w czasie rzeczywistym:
|
| 94 |
+
W Space kliknij **Logs** (prawy górny róg)
|
| 95 |
+
|
| 96 |
+
### ✅ Powinny pojawić się:
|
| 97 |
+
```
|
| 98 |
+
Initializing A11y Expert Agent...
|
| 99 |
+
Connecting to LanceDB at: ./lancedb
|
| 100 |
+
✅ Connected to LanceDB
|
| 101 |
+
✅ A11y Expert Agent is ready!
|
| 102 |
+
Launching Gradio app...
|
| 103 |
+
Running on public URL: https://...
|
| 104 |
+
```
|
| 105 |
+
|
| 106 |
+
### ❌ Jeśli widzisz błędy:
|
| 107 |
+
|
| 108 |
+
#### Error: "OPENAI_API_KEY is required"
|
| 109 |
+
**Rozwiązanie:** Dodaj klucz w Secrets (Krok 2B)
|
| 110 |
+
|
| 111 |
+
#### Error: "Table 'a11y_expert' doesn't exist"
|
| 112 |
+
**Rozwiązanie:**
|
| 113 |
+
- Sprawdź czy folder `lancedb/` jest w repo
|
| 114 |
+
- Jeśli nie ma danych, uruchom ETL script lokalnie i upload
|
| 115 |
+
|
| 116 |
+
#### Error: "RuntimeError: Event loop is closed"
|
| 117 |
+
**Rozwiązanie:** Upewnij się, że używasz zaktualizowanego app.py
|
| 118 |
+
|
| 119 |
+
#### Error: Module not found
|
| 120 |
+
**Rozwiązanie:**
|
| 121 |
+
- Sprawdź czy wszystkie foldery mają `__init__.py`
|
| 122 |
+
- Sprawdź requirements.txt
|
| 123 |
+
|
| 124 |
+
## Krok 5: Weryfikacja Działania
|
| 125 |
+
|
| 126 |
+
### Test 1: Sprawdź interfejs
|
| 127 |
+
- Space powinien pokazać chat interface
|
| 128 |
+
- Przykładowe pytania powinny być widoczne
|
| 129 |
+
|
| 130 |
+
### Test 2: Zadaj testowe pytanie
|
| 131 |
+
```
|
| 132 |
+
"Co to jest WCAG?"
|
| 133 |
+
```
|
| 134 |
+
|
| 135 |
+
### Test 3: Sprawdź streaming
|
| 136 |
+
- Odpowiedź powinna pojawiać się stopniowo (nie całe zdanie naraz)
|
| 137 |
+
|
| 138 |
+
### Test 4: Test języka
|
| 139 |
+
```
|
| 140 |
+
"What is ARIA?" → Odpowiedź po angielsku
|
| 141 |
+
"Co to jest ARIA?" → Odpowiedź po polsku
|
| 142 |
+
```
|
| 143 |
+
|
| 144 |
+
## Krok 6: Troubleshooting
|
| 145 |
+
|
| 146 |
+
### Space się restartuje co kilka minut
|
| 147 |
+
**Przyczyna:** Timeout na Free tier (10 minut bezczynności)
|
| 148 |
+
**Rozwiązanie:** Upgrade do GPU przestrzeni lub akceptuj restart
|
| 149 |
+
|
| 150 |
+
### Space nie startuje (Build failed)
|
| 151 |
+
1. Sprawdź logi budowania
|
| 152 |
+
2. Zweryfikuj requirements.txt (wszystkie biblioteki dostępne na PyPI?)
|
| 153 |
+
3. Sprawdź Python version w README.md (3.10 recommended)
|
| 154 |
+
|
| 155 |
+
### Wolne odpowiedzi
|
| 156 |
+
1. LanceDB może być duża - rozważ optymalizację
|
| 157 |
+
2. OpenAI API może mieć rate limiting
|
| 158 |
+
3. Free tier HF ma ograniczenia CPU
|
| 159 |
+
|
| 160 |
+
### Cache nie działa
|
| 161 |
+
- diskcache będzie działać, ale będzie resetowany przy restarcie Space
|
| 162 |
+
- To normalne na Hugging Face Spaces
|
| 163 |
+
|
| 164 |
+
## Krok 7: Optymalizacja (opcjonalne)
|
| 165 |
+
|
| 166 |
+
### A. Dodaj health check
|
| 167 |
+
```python
|
| 168 |
+
# W app.py
|
| 169 |
+
@demo.additional_routes
|
| 170 |
+
def health():
|
| 171 |
+
return {"status": "ok"}
|
| 172 |
+
```
|
| 173 |
+
|
| 174 |
+
### B. Zmniejsz rozmiar bazy danych
|
| 175 |
+
```bash
|
| 176 |
+
# Lokalnie
|
| 177 |
+
python compact_database.py
|
| 178 |
+
# Potem upload zmniejszonej bazy
|
| 179 |
+
```
|
| 180 |
+
|
| 181 |
+
### C. Użyj GPU (jeśli masz dostęp)
|
| 182 |
+
W README.md header:
|
| 183 |
+
```yaml
|
| 184 |
+
hardware: a10g-small
|
| 185 |
+
```
|
| 186 |
+
|
| 187 |
+
## ✅ Checklist Finalna
|
| 188 |
+
|
| 189 |
+
Przed ogłoszeniem Space jako "Production Ready":
|
| 190 |
+
|
| 191 |
+
- [ ] Aplikacja startuje bez błędów
|
| 192 |
+
- [ ] Agent odpowiada na pytania
|
| 193 |
+
- [ ] Streaming działa
|
| 194 |
+
- [ ] Język jest wykrywany prawidłowo
|
| 195 |
+
- [ ] Przykładowe pytania działają
|
| 196 |
+
- [ ] Brak ostrzeżeń w logach
|
| 197 |
+
- [ ] OPENAI_API_KEY jest w Secrets (NIE w kodzie!)
|
| 198 |
+
- [ ] README.md jest czytelny i informacyjny
|
| 199 |
+
- [ ] Space ma odpowiedni tytuł i opis
|
| 200 |
+
|
| 201 |
+
## 🎉 Gotowe!
|
| 202 |
+
|
| 203 |
+
Twój Space jest teraz live na:
|
| 204 |
+
```
|
| 205 |
+
https://huggingface.co/spaces/YOUR_USERNAME/YOUR_SPACE_NAME
|
| 206 |
+
```
|
| 207 |
+
|
| 208 |
+
---
|
| 209 |
+
|
| 210 |
+
## 📞 Pomoc
|
| 211 |
+
|
| 212 |
+
- **Problemy z asyncio?** → Zobacz `FIXES_SUMMARY.md`
|
| 213 |
+
- **Problemy z deployment?** → Zobacz `README_DEPLOYMENT.md`
|
| 214 |
+
- **Ogólne pytania?** → Hugging Face Forum
|
| 215 |
+
|
| 216 |
+
**Powodzenia!** 🚀
|
HUGGINGFACE_FIX.md
ADDED
|
@@ -0,0 +1,140 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Hugging Face Lazy Initialization Fix
|
| 2 |
+
|
| 3 |
+
## Problem
|
| 4 |
+
Aplikacja na Hugging Face Spaces nie uruchamiała się, ponieważ czas inicjalizacji bazy danych i agenta przekraczał timeout HF (zazwyczaj ~60 sekund).
|
| 5 |
+
|
| 6 |
+
## Rozwiązanie
|
| 7 |
+
Zaimplementowano **leniwą inicjalizację** (lazy loading):
|
| 8 |
+
|
| 9 |
+
### 1. **Gradio uruchamia się NATYCHMIAST** ⚡
|
| 10 |
+
- Aplikacja Gradio startuje w <1 sekundę
|
| 11 |
+
- HF Spaces widzi działającą aplikację i nie pokazuje timeoutu
|
| 12 |
+
|
| 13 |
+
### 2. **Agent inicjalizuje się W TLE** 🔄
|
| 14 |
+
- Tworzenie agenta i połączenie z bazą danych następuje w osobnym wątku
|
| 15 |
+
- Użytkownik widzi status inicjalizacji na żywo
|
| 16 |
+
|
| 17 |
+
### 3. **Pierwsze zapytania czekają na gotowość** ⏳
|
| 18 |
+
- Jeśli użytkownik spróbuje zadać pytanie przed gotowością agenta
|
| 19 |
+
- Aplikacja pokazuje komunikat "Agent is initializing, please wait..."
|
| 20 |
+
- Po gotowości agenta odpowiedź jest generowana normalnie
|
| 21 |
+
|
| 22 |
+
## Zmiany w kodzie
|
| 23 |
+
|
| 24 |
+
### `app.py` - Główne zmiany:
|
| 25 |
+
|
| 26 |
+
```python
|
| 27 |
+
# Zmienne stanu agenta
|
| 28 |
+
agent_instance: A11yExpertAgent = None
|
| 29 |
+
agent_ready = False
|
| 30 |
+
agent_error = None
|
| 31 |
+
|
| 32 |
+
# Inicjalizacja w osobnym wątku
|
| 33 |
+
def initialize_agent_background():
|
| 34 |
+
"""Initialize the agent in background thread."""
|
| 35 |
+
global agent_instance, agent_ready, agent_error, loop
|
| 36 |
+
try:
|
| 37 |
+
logger.info("🔄 Starting agent initialization in background...")
|
| 38 |
+
loop = asyncio.new_event_loop()
|
| 39 |
+
asyncio.set_event_loop(loop)
|
| 40 |
+
|
| 41 |
+
agent_instance = loop.run_until_complete(create_agent())
|
| 42 |
+
agent_ready = True
|
| 43 |
+
logger.success("✅ A11y Expert Agent is ready!")
|
| 44 |
+
except Exception as e:
|
| 45 |
+
logger.error(f"❌ Failed to initialize agent: {e}")
|
| 46 |
+
agent_error = str(e)
|
| 47 |
+
agent_instance = None
|
| 48 |
+
|
| 49 |
+
# Start w tle przy uruchomieniu
|
| 50 |
+
if __name__ == "__main__":
|
| 51 |
+
init_thread = threading.Thread(target=initialize_agent_background, daemon=True)
|
| 52 |
+
init_thread.start()
|
| 53 |
+
|
| 54 |
+
demo.launch(...)
|
| 55 |
+
```
|
| 56 |
+
|
| 57 |
+
### Wskaźnik statusu w UI:
|
| 58 |
+
|
| 59 |
+
```python
|
| 60 |
+
# Status indicator pokazujący czy agent jest gotowy
|
| 61 |
+
status_box = gr.Markdown("⏳ **Status:** Agent is initializing in background...")
|
| 62 |
+
|
| 63 |
+
def check_status():
|
| 64 |
+
if agent_ready:
|
| 65 |
+
return "✅ **Status:** Agent ready!"
|
| 66 |
+
elif agent_error:
|
| 67 |
+
return f"❌ **Status:** Agent failed - {agent_error}"
|
| 68 |
+
else:
|
| 69 |
+
return "⏳ **Status:** Agent is initializing in background..."
|
| 70 |
+
|
| 71 |
+
# Auto-update co 2 sekundy
|
| 72 |
+
demo.load(lambda: None, None, None).then(
|
| 73 |
+
check_status, outputs=status_box, every=2
|
| 74 |
+
)
|
| 75 |
+
```
|
| 76 |
+
|
| 77 |
+
### Oczekiwanie na gotowość w `respond()`:
|
| 78 |
+
|
| 79 |
+
```python
|
| 80 |
+
async def respond(message: str, history: list[list[str]]):
|
| 81 |
+
# Czekaj aż agent będzie gotowy
|
| 82 |
+
if not agent_ready:
|
| 83 |
+
if agent_error:
|
| 84 |
+
yield f"❌ Agent initialization failed: {agent_error}"
|
| 85 |
+
return
|
| 86 |
+
|
| 87 |
+
yield "⏳ Agent is initializing, please wait..."
|
| 88 |
+
# Czekaj do 120 sekund
|
| 89 |
+
for i in range(120):
|
| 90 |
+
await asyncio.sleep(1)
|
| 91 |
+
if agent_ready:
|
| 92 |
+
break
|
| 93 |
+
if agent_error:
|
| 94 |
+
yield f"❌ Agent initialization failed: {agent_error}"
|
| 95 |
+
return
|
| 96 |
+
|
| 97 |
+
if not agent_ready:
|
| 98 |
+
yield "❌ Agent initialization timeout. Please refresh and try again."
|
| 99 |
+
return
|
| 100 |
+
|
| 101 |
+
# Normalna generacja odpowiedzi
|
| 102 |
+
async for chunk in agent_instance.ask(message):
|
| 103 |
+
full_response += chunk
|
| 104 |
+
yield full_response
|
| 105 |
+
```
|
| 106 |
+
|
| 107 |
+
## Korzyści
|
| 108 |
+
|
| 109 |
+
✅ **Błyskawiczny start** - Gradio uruchamia się w <1s
|
| 110 |
+
✅ **Brak timeoutu na HF** - HF widzi działającą aplikację
|
| 111 |
+
✅ **Przejrzystość** - Użytkownik widzi status inicjalizacji
|
| 112 |
+
✅ **Graceful handling** - Obsługa błędów inicjalizacji
|
| 113 |
+
✅ **Zachowanie funkcjonalności** - Po gotowości działa normalnie
|
| 114 |
+
|
| 115 |
+
## Deployment na Hugging Face
|
| 116 |
+
|
| 117 |
+
1. Push zmian do repo
|
| 118 |
+
2. HF Space automatycznie zbuduje i uruchomi aplikację
|
| 119 |
+
3. Aplikacja wystartuje natychmiast (bez timeoutu!)
|
| 120 |
+
4. Agent załaduje się w tle w ciągu ~30-60 sekund
|
| 121 |
+
5. Status będzie aktualizowany na żywo w UI
|
| 122 |
+
|
| 123 |
+
## Testowanie lokalne
|
| 124 |
+
|
| 125 |
+
```bash
|
| 126 |
+
python app.py
|
| 127 |
+
```
|
| 128 |
+
|
| 129 |
+
Powinieneś zobaczyć:
|
| 130 |
+
```
|
| 131 |
+
🚀 Starting Gradio app with lazy agent initialization...
|
| 132 |
+
Launching Gradio interface...
|
| 133 |
+
🔄 Starting agent initialization in background...
|
| 134 |
+
Running on local URL: http://127.0.0.1:7860
|
| 135 |
+
✅ A11y Expert Agent is ready!
|
| 136 |
+
```
|
| 137 |
+
|
| 138 |
+
## Backup
|
| 139 |
+
|
| 140 |
+
Stary plik zapisany jako: `app_old.py`
|
QUICK_REFERENCE.md
ADDED
|
@@ -0,0 +1,117 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# 📋 Quick Reference Card - JacekAI Fixes
|
| 2 |
+
|
| 3 |
+
## 🚨 Problemy Które Zostały Naprawione
|
| 4 |
+
|
| 5 |
+
| Problem | Rozwiązanie | Plik |
|
| 6 |
+
|---------|-------------|------|
|
| 7 |
+
| `RuntimeError: Event loop is closed` | Globalna pętla zdarzeń z `loop.run_until_complete()` | `app.py:23-42` |
|
| 8 |
+
| Wycieki pamięci | Metody `close()` + `atexit.register()` | Wszystkie klienty |
|
| 9 |
+
| Konflikt wersji | Określone zakresy w requirements | `requirements.txt` |
|
| 10 |
+
| Pydantic DeprecationWarning | `@field_validator` zamiast `@validator` | `config.py:104-136` |
|
| 11 |
+
|
| 12 |
+
## 📝 Kluczowe Zmiany w Kodzie
|
| 13 |
+
|
| 14 |
+
### app.py
|
| 15 |
+
```python
|
| 16 |
+
# ✅ Globalna pętla zdarzeń
|
| 17 |
+
loop = asyncio.get_event_loop()
|
| 18 |
+
agent_instance = loop.run_until_complete(create_agent())
|
| 19 |
+
|
| 20 |
+
# ✅ Cleanup przy shutdown
|
| 21 |
+
atexit.register(cleanup_resources)
|
| 22 |
+
```
|
| 23 |
+
|
| 24 |
+
### Wszystkie klienty (vector_store, embeddings, agent)
|
| 25 |
+
```python
|
| 26 |
+
# ✅ Metoda close()
|
| 27 |
+
def close(self):
|
| 28 |
+
# Zamknij cache, DB, connections
|
| 29 |
+
if self.cache:
|
| 30 |
+
self.cache.close()
|
| 31 |
+
```
|
| 32 |
+
|
| 33 |
+
### config.py
|
| 34 |
+
```python
|
| 35 |
+
# ✅ Nowy API Pydantic 2.x
|
| 36 |
+
@field_validator("field_name")
|
| 37 |
+
@classmethod
|
| 38 |
+
def validate_field(cls, v, info):
|
| 39 |
+
# info.data zamiast values
|
| 40 |
+
```
|
| 41 |
+
|
| 42 |
+
## 🧪 Testowanie
|
| 43 |
+
|
| 44 |
+
```bash
|
| 45 |
+
# Składnia
|
| 46 |
+
python -m py_compile app.py config.py
|
| 47 |
+
|
| 48 |
+
# Komponenty
|
| 49 |
+
python test_startup.py
|
| 50 |
+
|
| 51 |
+
# Aplikacja
|
| 52 |
+
python app.py
|
| 53 |
+
# Ctrl+C - sprawdź czy cleanup działa
|
| 54 |
+
```
|
| 55 |
+
|
| 56 |
+
## 🚀 Deployment na Hugging Face
|
| 57 |
+
|
| 58 |
+
### 1. Secrets (KRYTYCZNE!)
|
| 59 |
+
```
|
| 60 |
+
Settings → Repository secrets
|
| 61 |
+
Name: OPENAI_API_KEY
|
| 62 |
+
Value: sk-proj-...
|
| 63 |
+
```
|
| 64 |
+
|
| 65 |
+
### 2. README.md header
|
| 66 |
+
```yaml
|
| 67 |
+
sdk: gradio
|
| 68 |
+
sdk_version: 4.44.0
|
| 69 |
+
python_version: 3.10
|
| 70 |
+
app_file: app.py
|
| 71 |
+
```
|
| 72 |
+
|
| 73 |
+
### 3. Monitoruj logi
|
| 74 |
+
```
|
| 75 |
+
✅ "A11y Expert Agent is ready!"
|
| 76 |
+
✅ "Launching Gradio app..."
|
| 77 |
+
```
|
| 78 |
+
|
| 79 |
+
## 📚 Dokumentacja
|
| 80 |
+
|
| 81 |
+
| Plik | Opis |
|
| 82 |
+
|------|------|
|
| 83 |
+
| `FIXES_SUMMARY.md` | Szczegółowe wyjaśnienie wszystkich napraw |
|
| 84 |
+
| `README_DEPLOYMENT.md` | Przewodnik wdrożeniowy z troubleshooting |
|
| 85 |
+
| `HF_SPACES_GUIDE.md` | Krok po kroku dla Hugging Face |
|
| 86 |
+
| `CHANGELOG.md` | Historia wszystkich zmian |
|
| 87 |
+
| `test_startup.py` | Skrypt testowy wszystkich komponentów |
|
| 88 |
+
|
| 89 |
+
## ⚡ Najczęstsze Problemy
|
| 90 |
+
|
| 91 |
+
| Błąd | Rozwiązanie |
|
| 92 |
+
|------|-------------|
|
| 93 |
+
| "Event loop is closed" | Użyj zaktualizowanego `app.py` |
|
| 94 |
+
| "OPENAI_API_KEY is required" | Dodaj klucz w HF Secrets |
|
| 95 |
+
| "Table doesn't exist" | Upload folder `lancedb/` |
|
| 96 |
+
| Pydantic validation error | Sprawdź `.env` lub HF Secrets |
|
| 97 |
+
|
| 98 |
+
## 🎯 Checklist Przed Deployment
|
| 99 |
+
|
| 100 |
+
- [ ] `python test_startup.py` - wszystkie testy ✅
|
| 101 |
+
- [ ] Wszystkie pliki skommitowane (oprócz `.env`, `cache/`)
|
| 102 |
+
- [ ] OPENAI_API_KEY w HF Secrets
|
| 103 |
+
- [ ] README.md ma poprawny YAML header
|
| 104 |
+
- [ ] Folder `lancedb/` jest w repo (Git LFS dla >10MB)
|
| 105 |
+
|
| 106 |
+
## 📞 Szybka Pomoc
|
| 107 |
+
|
| 108 |
+
**Problem z asyncio?** → `FIXES_SUMMARY.md` sekcja #1
|
| 109 |
+
**Problem z deployment?** → `HF_SPACES_GUIDE.md`
|
| 110 |
+
**Problem z wersją?** → `requirements.txt` (wszystkie wersje określone)
|
| 111 |
+
**Problem z Pydantic?** → `FIXES_SUMMARY.md` sekcja #4
|
| 112 |
+
|
| 113 |
+
---
|
| 114 |
+
|
| 115 |
+
**Status:** ✅ Gotowe do produkcji
|
| 116 |
+
**Data:** 2024-12-10
|
| 117 |
+
**Wersja:** 1.0.0 (po naprawach)
|
README_DEPLOYMENT.md
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Deployment Guide for Hugging Face Spaces
|
| 2 |
+
|
| 3 |
+
## Problemy rozwiązane w tej wersji
|
| 4 |
+
|
| 5 |
+
### 1. Konflikty pętli zdarzeń asyncio
|
| 6 |
+
- **Problem**: `asyncio.run()` przed `demo.launch()` tworzył zamkniętą pętlę zdarzeń
|
| 7 |
+
- **Rozwiązanie**: Używamy jednej, globalnej pętli zdarzeń z `asyncio.get_event_loop()`
|
| 8 |
+
|
| 9 |
+
### 2. Brak czyszczenia zasobów
|
| 10 |
+
- **Problem**: Zasoby (cache, połączenia DB) nie były zamykane przy shutdown
|
| 11 |
+
- **Rozwiązanie**: Dodano `atexit.register(cleanup_resources)` i metody `close()` dla wszystkich klientów
|
| 12 |
+
|
| 13 |
+
### 3. Konflikty wersji bibliotek
|
| 14 |
+
- **Problem**: Brak określonych wersji w requirements.txt
|
| 15 |
+
- **Rozwiązanie**: Określono zakresy kompatybilnych wersji dla wszystkich bibliotek
|
| 16 |
+
|
| 17 |
+
## Konfiguracja dla Hugging Face Spaces
|
| 18 |
+
|
| 19 |
+
### 1. Zmienne środowiskowe (Secrets)
|
| 20 |
+
W ustawieniach Space dodaj:
|
| 21 |
+
```
|
| 22 |
+
OPENAI_API_KEY=sk-...
|
| 23 |
+
SERVER_HOST=0.0.0.0
|
| 24 |
+
SERVER_PORT=7860
|
| 25 |
+
LOG_LEVEL=INFO
|
| 26 |
+
```
|
| 27 |
+
|
| 28 |
+
### 2. Struktura plików
|
| 29 |
+
Upewnij się, że:
|
| 30 |
+
- `lancedb/` folder jest w `.gitignore` (jeśli nie jest częścią repo)
|
| 31 |
+
- `cache/` folder jest w `.gitignore`
|
| 32 |
+
- `.env` NIE jest commitowany (tylko `.env.example`)
|
| 33 |
+
|
| 34 |
+
### 3. app.py Configuration
|
| 35 |
+
Aplikacja automatycznie:
|
| 36 |
+
- Tworzy pętlę zdarzeń tylko jeśli nie istnieje
|
| 37 |
+
- Rejestruje funkcję czyszczącą zasoby
|
| 38 |
+
- Zamyka wszystkie połączenia przy shutdown
|
| 39 |
+
- Wyłapuje KeyboardInterrupt dla graceful shutdown
|
| 40 |
+
|
| 41 |
+
### 4. Testowanie lokalnie
|
| 42 |
+
```bash
|
| 43 |
+
# Zainstaluj zależności
|
| 44 |
+
pip install -r requirements.txt
|
| 45 |
+
|
| 46 |
+
# Skopiuj i edytuj .env
|
| 47 |
+
cp .env.example .env
|
| 48 |
+
# Ustaw OPENAI_API_KEY w .env
|
| 49 |
+
|
| 50 |
+
# Uruchom aplikację
|
| 51 |
+
python app.py
|
| 52 |
+
```
|
| 53 |
+
|
| 54 |
+
### 5. Monitorowanie na Hugging Face
|
| 55 |
+
Sprawdź logi, czy widzisz:
|
| 56 |
+
- ✅ "A11y Expert Agent is ready!"
|
| 57 |
+
- ✅ "Launching Gradio app..."
|
| 58 |
+
- Brak błędów asyncio RuntimeError
|
| 59 |
+
|
| 60 |
+
## Potencjalne problemy i rozwiązania
|
| 61 |
+
|
| 62 |
+
### Problem: "RuntimeError: Event loop is closed"
|
| 63 |
+
**Rozwiązanie**: Upewnij się, że używasz tej zaktualizowanej wersji `app.py`
|
| 64 |
+
|
| 65 |
+
### Problem: Aplikacja zamyka się po kilku sekundach
|
| 66 |
+
**Możliwe przyczyny**:
|
| 67 |
+
1. Brak OPENAI_API_KEY → sprawdź Secrets
|
| 68 |
+
2. Brak dostępu do lancedb → upewnij się, że folder istnieje i ma dane
|
| 69 |
+
3. Konflikt portów → Hugging Face używa 7860
|
| 70 |
+
|
| 71 |
+
### Problem: "Table doesn't exist"
|
| 72 |
+
**Rozwiązanie**: Upewnij się, że `lancedb/` folder z danymi jest dostępny:
|
| 73 |
+
- Jeśli dane są w repo: użyj Git LFS
|
| 74 |
+
- Jeśli dane są generowane: uruchom skrypt ETL przed startem
|
| 75 |
+
|
| 76 |
+
## Porady optymalizacyjne
|
| 77 |
+
|
| 78 |
+
1. **Używaj cache**: `diskcache` znacząco przyspiesza embeddings
|
| 79 |
+
2. **Limituj history**: Agent przechowuje tylko 4 ostatnie wiadomości
|
| 80 |
+
3. **Monitoruj memory**: LanceDB + cache mogą zużywać pamięć
|
| 81 |
+
4. **Timeout na Hugging Face**: Free tier ma limit czasu bezczynności
|
| 82 |
+
|
| 83 |
+
## Sprawdzenie czy wszystko działa
|
| 84 |
+
|
| 85 |
+
✅ Checklist:
|
| 86 |
+
- [ ] Aplikacja startuje bez błędów
|
| 87 |
+
- [ ] Agent odpowiada na pytania
|
| 88 |
+
- [ ] Streaming działa płynnie
|
| 89 |
+
- [ ] Brak ostrzeżeń asyncio w logach
|
| 90 |
+
- [ ] Aplikacja nie crashuje po kilku minutach
|
| 91 |
+
- [ ] Graceful shutdown przy SIGTERM
|
TODO_DEPLOYMENT.md
ADDED
|
@@ -0,0 +1,246 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# ✅ TODO: Deployment Checklist dla JacekAI
|
| 2 |
+
|
| 3 |
+
## 📋 Pre-Deployment (Lokalnie)
|
| 4 |
+
|
| 5 |
+
### 1. Weryfikacja Zmian
|
| 6 |
+
- [ ] Sprawdź czy wszystkie pliki zostały poprawnie zmodyfikowane
|
| 7 |
+
```bash
|
| 8 |
+
git status
|
| 9 |
+
git diff app.py
|
| 10 |
+
git diff config.py
|
| 11 |
+
```
|
| 12 |
+
|
| 13 |
+
### 2. Test Lokalny
|
| 14 |
+
- [ ] Uruchom test kompilacji
|
| 15 |
+
```bash
|
| 16 |
+
python -m py_compile app.py config.py
|
| 17 |
+
```
|
| 18 |
+
|
| 19 |
+
- [ ] Uruchom test komponentów
|
| 20 |
+
```bash
|
| 21 |
+
python test_startup.py
|
| 22 |
+
```
|
| 23 |
+
**Oczekiwany wynik:** Wszystkie testy ✅ PASS
|
| 24 |
+
|
| 25 |
+
- [ ] Uruchom aplikację lokalnie
|
| 26 |
+
```bash
|
| 27 |
+
python app.py
|
| 28 |
+
```
|
| 29 |
+
- [ ] Aplikacja startuje bez błędów
|
| 30 |
+
- [ ] Agent odpowiada na testowe pytanie
|
| 31 |
+
- [ ] Ctrl+C zamyka aplikację gracefully z logiem "✅ Resources cleaned up successfully"
|
| 32 |
+
|
| 33 |
+
### 3. Przygotowanie Repozytorium
|
| 34 |
+
- [ ] Sprawdź `.gitignore`
|
| 35 |
+
```bash
|
| 36 |
+
cat .gitignore
|
| 37 |
+
```
|
| 38 |
+
**Powinno zawierać:** `cache/`, `.env`, `__pycache__/`
|
| 39 |
+
|
| 40 |
+
- [ ] Usuń `.env` jeśli istnieje w repo
|
| 41 |
+
```bash
|
| 42 |
+
git rm --cached .env # Jeśli był przypadkowo commitowany
|
| 43 |
+
```
|
| 44 |
+
|
| 45 |
+
- [ ] Sprawdź czy `lancedb/` jest w repo
|
| 46 |
+
```bash
|
| 47 |
+
ls -la lancedb/
|
| 48 |
+
```
|
| 49 |
+
- [ ] Jeśli folder >10MB, użyj Git LFS
|
| 50 |
+
```bash
|
| 51 |
+
git lfs install
|
| 52 |
+
git lfs track "lancedb/**/*"
|
| 53 |
+
git add .gitattributes
|
| 54 |
+
```
|
| 55 |
+
|
| 56 |
+
### 4. Commit Zmian (Lokalnie lub na GitHub)
|
| 57 |
+
```bash
|
| 58 |
+
git add .
|
| 59 |
+
git commit -m "Fix: Resolve asyncio conflicts and add graceful shutdown
|
| 60 |
+
|
| 61 |
+
- Changed asyncio.run() to global event loop management
|
| 62 |
+
- Added close() methods to all clients
|
| 63 |
+
- Implemented cleanup_resources() with atexit
|
| 64 |
+
- Updated to Pydantic 2.x field_validator
|
| 65 |
+
- Specified library versions in requirements.txt
|
| 66 |
+
- Added deployment documentation
|
| 67 |
+
|
| 68 |
+
Fixes #[issue_number] - App closing on Hugging Face Spaces"
|
| 69 |
+
```
|
| 70 |
+
|
| 71 |
+
---
|
| 72 |
+
|
| 73 |
+
## 🚀 Deployment na Hugging Face Spaces
|
| 74 |
+
|
| 75 |
+
### 1. Przygotowanie Space
|
| 76 |
+
- [ ] Zaloguj się na https://huggingface.co
|
| 77 |
+
- [ ] Jeśli Space już istnieje:
|
| 78 |
+
- Przejdź do https://huggingface.co/spaces/YOUR_USERNAME/JacekAI
|
| 79 |
+
- [ ] Jeśli tworzysz nowy Space:
|
| 80 |
+
- Kliknij "New Space"
|
| 81 |
+
- Nazwa: `JacekAI` lub `a11y-expert`
|
| 82 |
+
- SDK: Gradio
|
| 83 |
+
- Hardware: CPU Basic (Free)
|
| 84 |
+
|
| 85 |
+
### 2. Dodaj Secret (NAJWAŻNIEJSZE!)
|
| 86 |
+
- [ ] Przejdź do Settings → Repository secrets
|
| 87 |
+
- [ ] Kliknij "Add a secret"
|
| 88 |
+
- [ ] Nazwa: `OPENAI_API_KEY`
|
| 89 |
+
- [ ] Wartość: Twój klucz OpenAI (sk-proj-...)
|
| 90 |
+
- [ ] Zapisz
|
| 91 |
+
|
| 92 |
+
**⚠️ BEZ TEGO KROKU APLIKACJA NIE ZADZIAŁA!**
|
| 93 |
+
|
| 94 |
+
### 3. Upload Plików
|
| 95 |
+
|
| 96 |
+
#### Opcja A: Git Clone & Push
|
| 97 |
+
```bash
|
| 98 |
+
# Sklonuj Space
|
| 99 |
+
git clone https://huggingface.co/spaces/YOUR_USERNAME/JacekAI
|
| 100 |
+
cd JacekAI
|
| 101 |
+
|
| 102 |
+
# Skopiuj pliki z lokalnego repo
|
| 103 |
+
cp -r /path/to/JacekAI/* .
|
| 104 |
+
|
| 105 |
+
# Usuń niepotrzebne (jeśli istnieją)
|
| 106 |
+
rm -rf .env cache/ __pycache__/
|
| 107 |
+
|
| 108 |
+
# Commit i push
|
| 109 |
+
git add .
|
| 110 |
+
git commit -m "Deploy fixed version with asyncio improvements"
|
| 111 |
+
git push
|
| 112 |
+
```
|
| 113 |
+
|
| 114 |
+
#### Opcja B: Web UI
|
| 115 |
+
- [ ] Przejdź do Files → Add file
|
| 116 |
+
- [ ] Upload kolejno:
|
| 117 |
+
- [ ] `app.py`
|
| 118 |
+
- [ ] `config.py`
|
| 119 |
+
- [ ] `requirements.txt`
|
| 120 |
+
- [ ] `README.md`
|
| 121 |
+
- [ ] Folder `agent/`
|
| 122 |
+
- [ ] Folder `models/`
|
| 123 |
+
- [ ] Folder `database/` (lub `vector_store_client.py`)
|
| 124 |
+
- [ ] Folder `lancedb/` (jeśli masz dane)
|
| 125 |
+
- [ ] `.env.example`
|
| 126 |
+
- [ ] Dokumentacja (opcjonalnie)
|
| 127 |
+
|
| 128 |
+
### 4. Monitorowanie Startu
|
| 129 |
+
- [ ] Kliknij "Logs" w prawym górnym rogu Space
|
| 130 |
+
- [ ] Obserwuj logi budowania
|
| 131 |
+
|
| 132 |
+
**Oczekiwane logi:**
|
| 133 |
+
```
|
| 134 |
+
Installing requirements...
|
| 135 |
+
✅ Successfully installed gradio-4.x.x openai-1.x.x ...
|
| 136 |
+
Initializing A11y Expert Agent...
|
| 137 |
+
Connecting to LanceDB at: ./lancedb
|
| 138 |
+
✅ Connected to LanceDB
|
| 139 |
+
✅ A11y Expert Agent is ready!
|
| 140 |
+
Launching Gradio app...
|
| 141 |
+
Running on public URL: https://...hf.space
|
| 142 |
+
```
|
| 143 |
+
|
| 144 |
+
- [ ] Sprawdź czy NIE MA błędów:
|
| 145 |
+
- ❌ "OPENAI_API_KEY is required" → Dodaj Secret!
|
| 146 |
+
- ❌ "RuntimeError: Event loop is closed" → Użyj zaktualizowanego app.py
|
| 147 |
+
- ❌ "Table doesn't exist" → Upload lancedb/ folder
|
| 148 |
+
|
| 149 |
+
### 5. Weryfikacja Działania
|
| 150 |
+
- [ ] Space pokazuje interfejs chat
|
| 151 |
+
- [ ] Wpisz testowe pytanie: "Co to jest WCAG?"
|
| 152 |
+
- [ ] Sprawdź czy:
|
| 153 |
+
- [ ] Odpowiedź się generuje (streaming)
|
| 154 |
+
- [ ] Odpowiedź jest poprawna i po polsku
|
| 155 |
+
- [ ] Brak błędów w logach
|
| 156 |
+
|
| 157 |
+
- [ ] Test języka angielskiego: "What is ARIA?"
|
| 158 |
+
- [ ] Odpowiedź po angielsku
|
| 159 |
+
|
| 160 |
+
- [ ] Sprawdź przykładowe pytania
|
| 161 |
+
- [ ] Wszystkie działają
|
| 162 |
+
|
| 163 |
+
---
|
| 164 |
+
|
| 165 |
+
## 🔍 Post-Deployment Verification
|
| 166 |
+
|
| 167 |
+
### 1. Sprawdź Stabilność
|
| 168 |
+
- [ ] Pozostaw Space otwarty na 5 minut
|
| 169 |
+
- [ ] Zadaj kilka pytań
|
| 170 |
+
- [ ] Sprawdź czy Space nie restartuje się
|
| 171 |
+
|
| 172 |
+
### 2. Sprawdź Logi
|
| 173 |
+
- [ ] Brak ostrzeżeń asyncio
|
| 174 |
+
- [ ] Brak DeprecationWarnings
|
| 175 |
+
- [ ] Brak memory leaks warnings
|
| 176 |
+
|
| 177 |
+
### 3. Test Graceful Shutdown (opcjonalnie)
|
| 178 |
+
- [ ] Jeśli masz dostęp do CLI na HF:
|
| 179 |
+
```bash
|
| 180 |
+
# Wyślij SIGTERM
|
| 181 |
+
kill -15 <PID>
|
| 182 |
+
```
|
| 183 |
+
- [ ] Sprawdź czy w logach pojawia się:
|
| 184 |
+
```
|
| 185 |
+
Cleaning up resources...
|
| 186 |
+
✅ Resources cleaned up successfully
|
| 187 |
+
```
|
| 188 |
+
|
| 189 |
+
---
|
| 190 |
+
|
| 191 |
+
## 📝 Dokumentacja dla Użytkowników
|
| 192 |
+
|
| 193 |
+
### Zaktualizuj README Space
|
| 194 |
+
- [ ] Dodaj sekcję "Jak używać"
|
| 195 |
+
- [ ] Dodaj przykładowe pytania
|
| 196 |
+
- [ ] Dodaj link do dokumentacji
|
| 197 |
+
|
| 198 |
+
### Opcjonalnie: Model Card
|
| 199 |
+
- [ ] Opis modelu (GPT-4 + RAG)
|
| 200 |
+
- [ ] Opis bazy wiedzy (WCAG, ARIA)
|
| 201 |
+
- [ ] Ograniczenia (rate limiting, etc.)
|
| 202 |
+
|
| 203 |
+
---
|
| 204 |
+
|
| 205 |
+
## 🐛 Troubleshooting
|
| 206 |
+
|
| 207 |
+
Jeśli coś nie działa, sprawdź:
|
| 208 |
+
|
| 209 |
+
1. **OPENAI_API_KEY** - czy jest dodany w Secrets?
|
| 210 |
+
2. **Logi** - jakie błędy są w logach?
|
| 211 |
+
3. **requirements.txt** - czy wszystkie biblioteki są zainstalowane?
|
| 212 |
+
4. **Python version** - czy w README.md jest `python_version: 3.10`?
|
| 213 |
+
5. **lancedb/** - czy folder z danymi jest w repo?
|
| 214 |
+
|
| 215 |
+
### Quick Fixes
|
| 216 |
+
|
| 217 |
+
| Problem | Fix |
|
| 218 |
+
|---------|-----|
|
| 219 |
+
| "API key required" | Dodaj OPENAI_API_KEY w Secrets |
|
| 220 |
+
| "Event loop closed" | Re-upload app.py z tego repo |
|
| 221 |
+
| "Table doesn't exist" | Upload lancedb/ folder |
|
| 222 |
+
| Space crashes | Sprawdź logi, może brak pamięci |
|
| 223 |
+
|
| 224 |
+
---
|
| 225 |
+
|
| 226 |
+
## ✅ Finalizacja
|
| 227 |
+
|
| 228 |
+
Po pomyślnym deployment:
|
| 229 |
+
|
| 230 |
+
- [ ] Zapisz URL Space: `https://huggingface.co/spaces/YOUR_USERNAME/JacekAI`
|
| 231 |
+
- [ ] Udostępnij link (jeśli public)
|
| 232 |
+
- [ ] Zaktualizuj dokumentację projektu z linkiem do live demo
|
| 233 |
+
- [ ] Opcjonalnie: Pin Space na swoim profilu HF
|
| 234 |
+
|
| 235 |
+
---
|
| 236 |
+
|
| 237 |
+
## 📊 Status
|
| 238 |
+
|
| 239 |
+
**Data rozpoczęcia:** 2024-12-10
|
| 240 |
+
**Status:** ⏳ W trakcie / ✅ Ukończono
|
| 241 |
+
**Ostatni test:** _____
|
| 242 |
+
**URL Space:** _____
|
| 243 |
+
|
| 244 |
+
---
|
| 245 |
+
|
| 246 |
+
**🎉 Gratulacje! Aplikacja jest gotowa do użycia!**
|
agent/__pycache__/a11y_agent.cpython-312.pyc
CHANGED
|
Binary files a/agent/__pycache__/a11y_agent.cpython-312.pyc and b/agent/__pycache__/a11y_agent.cpython-312.pyc differ
|
|
|
agent/__pycache__/prompts.cpython-312.pyc
CHANGED
|
Binary files a/agent/__pycache__/prompts.cpython-312.pyc and b/agent/__pycache__/prompts.cpython-312.pyc differ
|
|
|
app.py
CHANGED
|
@@ -1,44 +1,45 @@
|
|
| 1 |
"""
|
| 2 |
-
Gradio UI for the A11y Expert Agent.
|
| 3 |
-
This module creates a Gradio ChatInterface
|
| 4 |
-
|
| 5 |
"""
|
| 6 |
import asyncio
|
| 7 |
import gradio as gr
|
| 8 |
from loguru import logger
|
| 9 |
import sys
|
| 10 |
import atexit
|
|
|
|
| 11 |
from agent.a11y_agent import create_agent, A11yExpertAgent
|
| 12 |
from config import get_settings
|
|
|
|
| 13 |
# --- Setup ---
|
| 14 |
# Configure logger
|
| 15 |
logger.remove()
|
| 16 |
logger.add(sys.stderr, level=get_settings().log_level)
|
|
|
|
| 17 |
# Global agent instance
|
| 18 |
agent_instance: A11yExpertAgent = None
|
|
|
|
|
|
|
| 19 |
# Global event loop for async operations
|
| 20 |
loop = None
|
| 21 |
|
| 22 |
# --- Agent Initialization ---
|
| 23 |
-
def
|
| 24 |
-
"""Initialize the agent
|
| 25 |
-
global agent_instance, loop
|
| 26 |
try:
|
| 27 |
-
logger.info("
|
| 28 |
-
#
|
| 29 |
-
|
| 30 |
-
|
| 31 |
-
if loop.is_closed():
|
| 32 |
-
loop = asyncio.new_event_loop()
|
| 33 |
-
asyncio.set_event_loop(loop)
|
| 34 |
-
except RuntimeError:
|
| 35 |
-
loop = asyncio.new_event_loop()
|
| 36 |
-
asyncio.set_event_loop(loop)
|
| 37 |
|
| 38 |
agent_instance = loop.run_until_complete(create_agent())
|
|
|
|
| 39 |
logger.success("✅ A11y Expert Agent is ready!")
|
| 40 |
except Exception as e:
|
| 41 |
-
logger.error(f"Failed to initialize agent: {e}")
|
|
|
|
| 42 |
agent_instance = None
|
| 43 |
|
| 44 |
def cleanup_resources():
|
|
@@ -70,6 +71,7 @@ def cleanup_resources():
|
|
| 70 |
logger.success("✅ Resources cleaned up successfully")
|
| 71 |
except Exception as e:
|
| 72 |
logger.warning(f"Error during cleanup: {e}")
|
|
|
|
| 73 |
# --- Gradio Chat Logic ---
|
| 74 |
async def respond(message: str, history: list[list[str]]):
|
| 75 |
"""
|
|
@@ -82,9 +84,30 @@ async def respond(message: str, history: list[list[str]]):
|
|
| 82 |
Yields:
|
| 83 |
A stream of response chunks to update the UI.
|
| 84 |
"""
|
| 85 |
-
global agent_instance,
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 86 |
if not agent_instance:
|
| 87 |
-
yield "Agent not
|
| 88 |
return
|
| 89 |
|
| 90 |
logger.info(f"User query: '{message}'")
|
|
@@ -101,25 +124,45 @@ async def respond(message: str, history: list[list[str]]):
|
|
| 101 |
|
| 102 |
# --- Gradio UI Definition ---
|
| 103 |
# Using gr.Blocks for more layout control
|
| 104 |
-
with gr.Blocks() as demo:
|
| 105 |
gr.Markdown("# 🤖 A11y Expert")
|
| 106 |
gr.Markdown(
|
| 107 |
"Twój inteligentny asystent do spraw dostępności cyfrowej. "
|
| 108 |
"Zadaj pytanie o WCAG, ARIA, lub poproś o analizę kodu."
|
| 109 |
)
|
|
|
|
| 110 |
# The main chat interface
|
| 111 |
chat = gr.ChatInterface(respond)
|
|
|
|
| 112 |
# Example questions
|
| 113 |
gr.Examples(
|
| 114 |
[
|
| 115 |
"Jakie są wymagania WCAG 2.2 dla etykiet formularzy?",
|
| 116 |
"Wyjaśnij rolę 'alert' w ARIA i podaj przykład.",
|
| 117 |
"Czy ten przycisk jest dostępny? <div onclick='...'>Click me</div>",
|
| 118 |
-
"Jaka jest różnica między
|
| 119 |
],
|
| 120 |
inputs=[chat.textbox],
|
| 121 |
label="Przykładowe pytania"
|
| 122 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 123 |
|
| 124 |
|
| 125 |
# --- App Launch ---
|
|
@@ -127,11 +170,13 @@ if __name__ == "__main__":
|
|
| 127 |
# Register cleanup handler
|
| 128 |
atexit.register(cleanup_resources)
|
| 129 |
|
| 130 |
-
#
|
| 131 |
-
|
|
|
|
|
|
|
| 132 |
|
| 133 |
settings = get_settings()
|
| 134 |
-
logger.info("Launching Gradio
|
| 135 |
|
| 136 |
try:
|
| 137 |
demo.launch(
|
|
|
|
| 1 |
"""
|
| 2 |
+
Gradio UI for the A11y Expert Agent with lazy initialization.
|
| 3 |
+
This module creates a Gradio ChatInterface that starts FAST,
|
| 4 |
+
then initializes the agent in the background.
|
| 5 |
"""
|
| 6 |
import asyncio
|
| 7 |
import gradio as gr
|
| 8 |
from loguru import logger
|
| 9 |
import sys
|
| 10 |
import atexit
|
| 11 |
+
import threading
|
| 12 |
from agent.a11y_agent import create_agent, A11yExpertAgent
|
| 13 |
from config import get_settings
|
| 14 |
+
|
| 15 |
# --- Setup ---
|
| 16 |
# Configure logger
|
| 17 |
logger.remove()
|
| 18 |
logger.add(sys.stderr, level=get_settings().log_level)
|
| 19 |
+
|
| 20 |
# Global agent instance
|
| 21 |
agent_instance: A11yExpertAgent = None
|
| 22 |
+
agent_ready = False
|
| 23 |
+
agent_error = None
|
| 24 |
# Global event loop for async operations
|
| 25 |
loop = None
|
| 26 |
|
| 27 |
# --- Agent Initialization ---
|
| 28 |
+
def initialize_agent_background():
|
| 29 |
+
"""Initialize the agent in background thread."""
|
| 30 |
+
global agent_instance, agent_ready, agent_error, loop
|
| 31 |
try:
|
| 32 |
+
logger.info("🔄 Starting agent initialization in background...")
|
| 33 |
+
# Create new event loop for this thread
|
| 34 |
+
loop = asyncio.new_event_loop()
|
| 35 |
+
asyncio.set_event_loop(loop)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 36 |
|
| 37 |
agent_instance = loop.run_until_complete(create_agent())
|
| 38 |
+
agent_ready = True
|
| 39 |
logger.success("✅ A11y Expert Agent is ready!")
|
| 40 |
except Exception as e:
|
| 41 |
+
logger.error(f"❌ Failed to initialize agent: {e}")
|
| 42 |
+
agent_error = str(e)
|
| 43 |
agent_instance = None
|
| 44 |
|
| 45 |
def cleanup_resources():
|
|
|
|
| 71 |
logger.success("✅ Resources cleaned up successfully")
|
| 72 |
except Exception as e:
|
| 73 |
logger.warning(f"Error during cleanup: {e}")
|
| 74 |
+
|
| 75 |
# --- Gradio Chat Logic ---
|
| 76 |
async def respond(message: str, history: list[list[str]]):
|
| 77 |
"""
|
|
|
|
| 84 |
Yields:
|
| 85 |
A stream of response chunks to update the UI.
|
| 86 |
"""
|
| 87 |
+
global agent_instance, agent_ready, agent_error
|
| 88 |
+
|
| 89 |
+
# Wait for agent to be ready
|
| 90 |
+
if not agent_ready:
|
| 91 |
+
if agent_error:
|
| 92 |
+
yield f"❌ Agent initialization failed: {agent_error}"
|
| 93 |
+
return
|
| 94 |
+
|
| 95 |
+
yield "⏳ Agent is initializing, please wait..."
|
| 96 |
+
# Wait up to 120 seconds for agent to be ready
|
| 97 |
+
for i in range(120):
|
| 98 |
+
await asyncio.sleep(1)
|
| 99 |
+
if agent_ready:
|
| 100 |
+
break
|
| 101 |
+
if agent_error:
|
| 102 |
+
yield f"❌ Agent initialization failed: {agent_error}"
|
| 103 |
+
return
|
| 104 |
+
|
| 105 |
+
if not agent_ready:
|
| 106 |
+
yield "❌ Agent initialization timeout. Please refresh and try again."
|
| 107 |
+
return
|
| 108 |
+
|
| 109 |
if not agent_instance:
|
| 110 |
+
yield "❌ Agent not available. Please check logs for errors."
|
| 111 |
return
|
| 112 |
|
| 113 |
logger.info(f"User query: '{message}'")
|
|
|
|
| 124 |
|
| 125 |
# --- Gradio UI Definition ---
|
| 126 |
# Using gr.Blocks for more layout control
|
| 127 |
+
with gr.Blocks(title="A11y Expert") as demo:
|
| 128 |
gr.Markdown("# 🤖 A11y Expert")
|
| 129 |
gr.Markdown(
|
| 130 |
"Twój inteligentny asystent do spraw dostępności cyfrowej. "
|
| 131 |
"Zadaj pytanie o WCAG, ARIA, lub poproś o analizę kodu."
|
| 132 |
)
|
| 133 |
+
|
| 134 |
# The main chat interface
|
| 135 |
chat = gr.ChatInterface(respond)
|
| 136 |
+
|
| 137 |
# Example questions
|
| 138 |
gr.Examples(
|
| 139 |
[
|
| 140 |
"Jakie są wymagania WCAG 2.2 dla etykiet formularzy?",
|
| 141 |
"Wyjaśnij rolę 'alert' w ARIA i podaj przykład.",
|
| 142 |
"Czy ten przycisk jest dostępny? <div onclick='...'>Click me</div>",
|
| 143 |
+
"Jaka jest różnica między aria-label a aria-labelledby?",
|
| 144 |
],
|
| 145 |
inputs=[chat.textbox],
|
| 146 |
label="Przykładowe pytania"
|
| 147 |
)
|
| 148 |
+
|
| 149 |
+
# Status indicator at bottom
|
| 150 |
+
with gr.Row():
|
| 151 |
+
status_box = gr.Markdown("⏳ **Status:** Agent is initializing in background...")
|
| 152 |
+
|
| 153 |
+
# Update status when agent is ready
|
| 154 |
+
def check_status():
|
| 155 |
+
if agent_ready:
|
| 156 |
+
return "✅ **Status:** Agent ready!"
|
| 157 |
+
elif agent_error:
|
| 158 |
+
return f"❌ **Status:** Agent failed - {agent_error}"
|
| 159 |
+
else:
|
| 160 |
+
return "⏳ **Status:** Agent is initializing in background..."
|
| 161 |
+
|
| 162 |
+
# Poll status every 2 seconds
|
| 163 |
+
demo.load(lambda: None, None, None).then(
|
| 164 |
+
check_status, outputs=status_box, every=2
|
| 165 |
+
)
|
| 166 |
|
| 167 |
|
| 168 |
# --- App Launch ---
|
|
|
|
| 170 |
# Register cleanup handler
|
| 171 |
atexit.register(cleanup_resources)
|
| 172 |
|
| 173 |
+
# Start agent initialization in background thread
|
| 174 |
+
logger.info("🚀 Starting Gradio app with lazy agent initialization...")
|
| 175 |
+
init_thread = threading.Thread(target=initialize_agent_background, daemon=True)
|
| 176 |
+
init_thread.start()
|
| 177 |
|
| 178 |
settings = get_settings()
|
| 179 |
+
logger.info("Launching Gradio interface...")
|
| 180 |
|
| 181 |
try:
|
| 182 |
demo.launch(
|
app_old.py
ADDED
|
@@ -0,0 +1,165 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Gradio UI for the A11y Expert Agent.
|
| 3 |
+
This module creates a Gradio ChatInterface to interact with the
|
| 4 |
+
A11yExpertAgent, allowing users to ask accessibility-related questions.
|
| 5 |
+
"""
|
| 6 |
+
import asyncio
|
| 7 |
+
import gradio as gr
|
| 8 |
+
from loguru import logger
|
| 9 |
+
import sys
|
| 10 |
+
import atexit
|
| 11 |
+
import threading
|
| 12 |
+
from agent.a11y_agent import create_agent, A11yExpertAgent
|
| 13 |
+
from config import get_settings
|
| 14 |
+
# --- Setup ---
|
| 15 |
+
# Configure logger
|
| 16 |
+
logger.remove()
|
| 17 |
+
logger.add(sys.stderr, level=get_settings().log_level)
|
| 18 |
+
# Global agent instance
|
| 19 |
+
agent_instance: A11yExpertAgent = None
|
| 20 |
+
agent_ready = False
|
| 21 |
+
agent_error = None
|
| 22 |
+
# Global event loop for async operations
|
| 23 |
+
loop = None
|
| 24 |
+
|
| 25 |
+
# --- Agent Initialization ---
|
| 26 |
+
def initialize_agent_background():
|
| 27 |
+
"""Initialize the agent in background thread."""
|
| 28 |
+
global agent_instance, agent_ready, agent_error, loop
|
| 29 |
+
try:
|
| 30 |
+
logger.info("🔄 Starting agent initialization in background...")
|
| 31 |
+
# Create new event loop for this thread
|
| 32 |
+
loop = asyncio.new_event_loop()
|
| 33 |
+
asyncio.set_event_loop(loop)
|
| 34 |
+
|
| 35 |
+
agent_instance = loop.run_until_complete(create_agent())
|
| 36 |
+
agent_ready = True
|
| 37 |
+
logger.success("✅ A11y Expert Agent is ready!")
|
| 38 |
+
except Exception as e:
|
| 39 |
+
logger.error(f"Failed to initialize agent: {e}")
|
| 40 |
+
agent_error = str(e)
|
| 41 |
+
agent_instance = None
|
| 42 |
+
|
| 43 |
+
def cleanup_resources():
|
| 44 |
+
"""Clean up resources on app shutdown."""
|
| 45 |
+
global agent_instance, loop
|
| 46 |
+
logger.info("Cleaning up resources...")
|
| 47 |
+
try:
|
| 48 |
+
# Close agent and all its resources
|
| 49 |
+
if agent_instance:
|
| 50 |
+
agent_instance.close()
|
| 51 |
+
|
| 52 |
+
# Close embeddings client singleton if it exists
|
| 53 |
+
from models.embeddings import get_embeddings_client
|
| 54 |
+
if hasattr(get_embeddings_client, '_instance'):
|
| 55 |
+
get_embeddings_client._instance.close()
|
| 56 |
+
|
| 57 |
+
# Close event loop if it exists and is still open
|
| 58 |
+
if loop and not loop.is_closed():
|
| 59 |
+
# Cancel all pending tasks
|
| 60 |
+
try:
|
| 61 |
+
pending = asyncio.all_tasks(loop)
|
| 62 |
+
for task in pending:
|
| 63 |
+
task.cancel()
|
| 64 |
+
loop.run_until_complete(asyncio.gather(*pending, return_exceptions=True))
|
| 65 |
+
except RuntimeError:
|
| 66 |
+
pass # Loop may already be stopped
|
| 67 |
+
loop.close()
|
| 68 |
+
|
| 69 |
+
logger.success("✅ Resources cleaned up successfully")
|
| 70 |
+
except Exception as e:
|
| 71 |
+
logger.warning(f"Error during cleanup: {e}")
|
| 72 |
+
# --- Gradio Chat Logic ---
|
| 73 |
+
async def respond(message: str, history: list[list[str]]):
|
| 74 |
+
"""
|
| 75 |
+
Main function for the Gradio ChatInterface.
|
| 76 |
+
Receives a user message and chat history, then uses the agent
|
| 77 |
+
to generate a streaming response.
|
| 78 |
+
Args:
|
| 79 |
+
message: The user's input message.
|
| 80 |
+
history: The conversation history provided by Gradio.
|
| 81 |
+
Yields:
|
| 82 |
+
A stream of response chunks to update the UI.
|
| 83 |
+
"""
|
| 84 |
+
global agent_instance, agent_ready, agent_error
|
| 85 |
+
|
| 86 |
+
# Wait for agent to be ready
|
| 87 |
+
if not agent_ready:
|
| 88 |
+
if agent_error:
|
| 89 |
+
yield f"❌ Agent initialization failed: {agent_error}"
|
| 90 |
+
return
|
| 91 |
+
|
| 92 |
+
yield "⏳ Agent is initializing, please wait..."
|
| 93 |
+
# Wait up to 60 seconds for agent to be ready
|
| 94 |
+
for i in range(60):
|
| 95 |
+
await asyncio.sleep(1)
|
| 96 |
+
if agent_ready:
|
| 97 |
+
break
|
| 98 |
+
if agent_error:
|
| 99 |
+
yield f"❌ Agent initialization failed: {agent_error}"
|
| 100 |
+
return
|
| 101 |
+
|
| 102 |
+
if not agent_ready:
|
| 103 |
+
yield "❌ Agent initialization timeout. Please try again later."
|
| 104 |
+
return
|
| 105 |
+
|
| 106 |
+
if not agent_instance:
|
| 107 |
+
yield "❌ Agent not available. Please check logs for errors."
|
| 108 |
+
return
|
| 109 |
+
|
| 110 |
+
logger.info(f"User query: '{message}'")
|
| 111 |
+
full_response = ""
|
| 112 |
+
try:
|
| 113 |
+
# Use the global event loop to run async generator
|
| 114 |
+
async for chunk in agent_instance.ask(message):
|
| 115 |
+
full_response += chunk
|
| 116 |
+
yield full_response
|
| 117 |
+
except Exception as e:
|
| 118 |
+
logger.error(f"Error during response generation: {e}")
|
| 119 |
+
yield f"An error occurred: {e}"
|
| 120 |
+
|
| 121 |
+
|
| 122 |
+
# --- Gradio UI Definition ---
|
| 123 |
+
# Using gr.Blocks for more layout control
|
| 124 |
+
with gr.Blocks() as demo:
|
| 125 |
+
gr.Markdown("# 🤖 A11y Expert")
|
| 126 |
+
gr.Markdown(
|
| 127 |
+
"Twój inteligentny asystent do spraw dostępności cyfrowej. "
|
| 128 |
+
"Zadaj pytanie o WCAG, ARIA, lub poproś o analizę kodu."
|
| 129 |
+
)
|
| 130 |
+
# The main chat interface
|
| 131 |
+
chat = gr.ChatInterface(respond)
|
| 132 |
+
# Example questions
|
| 133 |
+
gr.Examples(
|
| 134 |
+
[
|
| 135 |
+
"Jakie są wymagania WCAG 2.2 dla etykiet formularzy?",
|
| 136 |
+
"Wyjaśnij rolę 'alert' w ARIA i podaj przykład.",
|
| 137 |
+
"Czy ten przycisk jest dostępny? <div onclick='...'>Click me</div>",
|
| 138 |
+
"Jaka jest różnica między ria-label a ria-labelledby?",
|
| 139 |
+
],
|
| 140 |
+
inputs=[chat.textbox],
|
| 141 |
+
label="Przykładowe pytania"
|
| 142 |
+
)
|
| 143 |
+
|
| 144 |
+
|
| 145 |
+
# --- App Launch ---
|
| 146 |
+
if __name__ == "__main__":
|
| 147 |
+
# Register cleanup handler
|
| 148 |
+
atexit.register(cleanup_resources)
|
| 149 |
+
|
| 150 |
+
# Initialize agent before launching Gradio
|
| 151 |
+
initialize_agent_sync()
|
| 152 |
+
|
| 153 |
+
settings = get_settings()
|
| 154 |
+
logger.info("Launching Gradio app...")
|
| 155 |
+
|
| 156 |
+
try:
|
| 157 |
+
demo.launch(
|
| 158 |
+
server_name=settings.server_host,
|
| 159 |
+
server_port=settings.server_port,
|
| 160 |
+
show_error=True,
|
| 161 |
+
)
|
| 162 |
+
except KeyboardInterrupt:
|
| 163 |
+
logger.info("Received interrupt signal")
|
| 164 |
+
finally:
|
| 165 |
+
cleanup_resources()
|
database/__pycache__/__init__.cpython-312.pyc
CHANGED
|
Binary files a/database/__pycache__/__init__.cpython-312.pyc and b/database/__pycache__/__init__.cpython-312.pyc differ
|
|
|
database/__pycache__/vector_store_client.cpython-312.pyc
CHANGED
|
Binary files a/database/__pycache__/vector_store_client.cpython-312.pyc and b/database/__pycache__/vector_store_client.cpython-312.pyc differ
|
|
|
models/__pycache__/__init__.cpython-312.pyc
CHANGED
|
Binary files a/models/__pycache__/__init__.cpython-312.pyc and b/models/__pycache__/__init__.cpython-312.pyc differ
|
|
|
models/__pycache__/embeddings.cpython-312.pyc
CHANGED
|
Binary files a/models/__pycache__/embeddings.cpython-312.pyc and b/models/__pycache__/embeddings.cpython-312.pyc differ
|
|
|
test_startup.py
ADDED
|
@@ -0,0 +1,135 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Quick startup test to verify all components load correctly.
|
| 3 |
+
Run this before deploying to catch configuration issues early.
|
| 4 |
+
"""
|
| 5 |
+
import sys
|
| 6 |
+
from loguru import logger
|
| 7 |
+
|
| 8 |
+
logger.remove()
|
| 9 |
+
logger.add(sys.stderr, level="INFO")
|
| 10 |
+
|
| 11 |
+
def test_imports():
|
| 12 |
+
"""Test that all required modules can be imported."""
|
| 13 |
+
try:
|
| 14 |
+
logger.info("Testing imports...")
|
| 15 |
+
import gradio
|
| 16 |
+
import openai
|
| 17 |
+
import lancedb
|
| 18 |
+
from pydantic_settings import BaseSettings
|
| 19 |
+
from langdetect import detect
|
| 20 |
+
import diskcache
|
| 21 |
+
import pandas
|
| 22 |
+
logger.success("✅ All imports successful")
|
| 23 |
+
return True
|
| 24 |
+
except ImportError as e:
|
| 25 |
+
logger.error(f"❌ Import failed: {e}")
|
| 26 |
+
return False
|
| 27 |
+
|
| 28 |
+
def test_config():
|
| 29 |
+
"""Test configuration loading."""
|
| 30 |
+
try:
|
| 31 |
+
logger.info("Testing configuration...")
|
| 32 |
+
from config import get_settings
|
| 33 |
+
settings = get_settings()
|
| 34 |
+
logger.info(f"LLM Model: {settings.llm_model}")
|
| 35 |
+
logger.info(f"Embedding Model: {settings.embedding_model}")
|
| 36 |
+
logger.info(f"Server: {settings.server_host}:{settings.server_port}")
|
| 37 |
+
logger.success("✅ Configuration loaded successfully")
|
| 38 |
+
return True
|
| 39 |
+
except Exception as e:
|
| 40 |
+
logger.error(f"❌ Config failed: {e}")
|
| 41 |
+
return False
|
| 42 |
+
|
| 43 |
+
def test_vector_store():
|
| 44 |
+
"""Test vector store connection."""
|
| 45 |
+
try:
|
| 46 |
+
logger.info("Testing vector store...")
|
| 47 |
+
from config import get_settings
|
| 48 |
+
from vector_store_client import VectorStoreClient
|
| 49 |
+
|
| 50 |
+
settings = get_settings()
|
| 51 |
+
client = VectorStoreClient(uri=settings.lancedb_uri)
|
| 52 |
+
client.connect()
|
| 53 |
+
count = client.count_documents()
|
| 54 |
+
logger.info(f"Documents in database: {count}")
|
| 55 |
+
client.close()
|
| 56 |
+
logger.success("✅ Vector store accessible")
|
| 57 |
+
return True
|
| 58 |
+
except Exception as e:
|
| 59 |
+
logger.error(f"❌ Vector store failed: {e}")
|
| 60 |
+
return False
|
| 61 |
+
|
| 62 |
+
def test_embeddings():
|
| 63 |
+
"""Test embeddings client initialization."""
|
| 64 |
+
try:
|
| 65 |
+
logger.info("Testing embeddings client...")
|
| 66 |
+
from models.embeddings import get_embeddings_client
|
| 67 |
+
client = get_embeddings_client()
|
| 68 |
+
logger.info(f"Model: {client.model}")
|
| 69 |
+
logger.info(f"Cache available: {client.cache is not None}")
|
| 70 |
+
client.close()
|
| 71 |
+
logger.success("✅ Embeddings client initialized")
|
| 72 |
+
return True
|
| 73 |
+
except Exception as e:
|
| 74 |
+
logger.error(f"❌ Embeddings client failed: {e}")
|
| 75 |
+
return False
|
| 76 |
+
|
| 77 |
+
def test_agent():
|
| 78 |
+
"""Test agent creation."""
|
| 79 |
+
try:
|
| 80 |
+
logger.info("Testing agent creation...")
|
| 81 |
+
import asyncio
|
| 82 |
+
from agent.a11y_agent import create_agent
|
| 83 |
+
|
| 84 |
+
async def _test():
|
| 85 |
+
agent = await create_agent()
|
| 86 |
+
logger.info(f"Agent language: {agent.language}")
|
| 87 |
+
logger.info(f"Agent model: {agent.model}")
|
| 88 |
+
agent.close()
|
| 89 |
+
|
| 90 |
+
asyncio.run(_test())
|
| 91 |
+
logger.success("✅ Agent created successfully")
|
| 92 |
+
return True
|
| 93 |
+
except Exception as e:
|
| 94 |
+
logger.error(f"❌ Agent creation failed: {e}")
|
| 95 |
+
return False
|
| 96 |
+
|
| 97 |
+
def main():
|
| 98 |
+
"""Run all tests."""
|
| 99 |
+
logger.info("=" * 60)
|
| 100 |
+
logger.info("Starting Deployment Readiness Tests")
|
| 101 |
+
logger.info("=" * 60)
|
| 102 |
+
|
| 103 |
+
tests = [
|
| 104 |
+
("Imports", test_imports),
|
| 105 |
+
("Configuration", test_config),
|
| 106 |
+
("Vector Store", test_vector_store),
|
| 107 |
+
("Embeddings", test_embeddings),
|
| 108 |
+
("Agent", test_agent),
|
| 109 |
+
]
|
| 110 |
+
|
| 111 |
+
results = []
|
| 112 |
+
for name, test_func in tests:
|
| 113 |
+
logger.info(f"\n--- Test: {name} ---")
|
| 114 |
+
result = test_func()
|
| 115 |
+
results.append((name, result))
|
| 116 |
+
|
| 117 |
+
logger.info("\n" + "=" * 60)
|
| 118 |
+
logger.info("Test Results Summary")
|
| 119 |
+
logger.info("=" * 60)
|
| 120 |
+
|
| 121 |
+
for name, result in results:
|
| 122 |
+
status = "✅ PASS" if result else "❌ FAIL"
|
| 123 |
+
logger.info(f"{status} - {name}")
|
| 124 |
+
|
| 125 |
+
all_passed = all(result for _, result in results)
|
| 126 |
+
|
| 127 |
+
if all_passed:
|
| 128 |
+
logger.success("\n🎉 All tests passed! Ready for deployment.")
|
| 129 |
+
return 0
|
| 130 |
+
else:
|
| 131 |
+
logger.error("\n⚠️ Some tests failed. Fix issues before deploying.")
|
| 132 |
+
return 1
|
| 133 |
+
|
| 134 |
+
if __name__ == "__main__":
|
| 135 |
+
sys.exit(main())
|