jens-l commited on
Commit
fc46e81
·
1 Parent(s): 13080f1

Add Docker support with nginx and supervisor configuration, implement .dockerignore, and enhance local development script.

Browse files
Files changed (7) hide show
  1. .dockerignore +15 -0
  2. Dockerfile +32 -0
  3. README.md +26 -14
  4. app.py +39 -9
  5. nginx.conf +50 -0
  6. run_local.py +25 -0
  7. supervisord.conf +27 -0
.dockerignore ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ .git
2
+ .gitignore
3
+ .venv
4
+ __pycache__
5
+ *.pyc
6
+ *.pyo
7
+ *.pyd
8
+ .Python
9
+ .pytest_cache
10
+ .coverage
11
+ .ruff_cache
12
+ .gradio
13
+ *.md
14
+ !README.md
15
+ run_local.py
Dockerfile ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ FROM python:3.12-slim
2
+
3
+ # Install nginx and other dependencies
4
+ RUN apt-get update && apt-get install -y \
5
+ nginx \
6
+ supervisor \
7
+ && rm -rf /var/lib/apt/lists/*
8
+
9
+ # Set working directory
10
+ WORKDIR /app
11
+
12
+ # Copy requirements and install Python dependencies
13
+ COPY requirements.txt .
14
+ RUN pip install --no-cache-dir -r requirements.txt
15
+
16
+ # Copy application code
17
+ COPY . .
18
+
19
+ # Copy nginx configuration
20
+ COPY nginx.conf /etc/nginx/nginx.conf
21
+
22
+ # Copy supervisor configuration
23
+ COPY supervisord.conf /etc/supervisor/conf.d/supervisord.conf
24
+
25
+ # Create necessary directories
26
+ RUN mkdir -p /var/log/supervisor
27
+
28
+ # Expose port 7860 (the only port HF Spaces allows)
29
+ EXPOSE 7860
30
+
31
+ # Start supervisor (which will start nginx, main app, and preview app)
32
+ CMD ["/usr/bin/supervisord", "-c", "/etc/supervisor/conf.d/supervisord.conf"]
README.md CHANGED
@@ -1,19 +1,31 @@
1
  ---
2
- title: Likable
3
  emoji: 💗
4
- colorFrom: blue
5
- colorTo: purple
6
- sdk: gradio
7
- sdk_version: 5.33.0
8
- app_file: app.py
9
- pinned: false
10
- license: mit
11
- short_description: 💗Likable - it's almost lovable
12
  ---
13
 
14
- # 💗 Likable
15
 
16
- # TODOs:
17
- - [ ] venv
18
- - [ ] Deploy to HF Spaces
19
- - [ ] Session Management
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  ---
2
+ title: Likable GitHub
3
  emoji: 💗
4
+ colorFrom: purple
5
+ colorTo: pink
6
+ sdk: docker
7
+ app_port: 7860
 
 
 
 
8
  ---
9
 
10
+ # 💗 Likable GitHub
11
 
12
+ A powerful AI coding assistant that can create and preview Gradio applications in real-time.
13
+
14
+ ## Features
15
+
16
+ - **Real-time Code Generation**: AI agent that can write and modify code
17
+ - **Live Preview**: See your applications running instantly in an iframe
18
+ - **Multiple AI Providers**: Support for Anthropic, OpenAI, Mistral, and more
19
+ - **File Management**: Edit and save files directly in the interface
20
+ - **API Key Management**: Secure configuration for different AI providers
21
+
22
+ ## Usage
23
+
24
+ 1. Configure your API keys in the Settings tab
25
+ 2. Ask the AI to create or modify applications
26
+ 3. View the live preview in the Preview tab
27
+ 4. Edit code directly in the Code tab
28
+
29
+ ## Architecture
30
+
31
+ This Space uses nginx as a reverse proxy to serve both the main application and preview applications on a single port, making it compatible with Hugging Face Spaces' single-port limitation.
app.py CHANGED
@@ -18,11 +18,13 @@ PREVIEW_PORT = 7861 # Different port from main app
18
 
19
  def get_preview_url():
20
  """Get the appropriate preview URL based on environment."""
 
 
 
21
  # Check if running in Docker
22
- if os.path.exists("/.dockerenv") or os.getenv("DOCKER_CONTAINER"):
23
- # In Docker, use the host's IP for iframe access
24
- host_ip = os.getenv("HOST_IP", "localhost")
25
- return f"http://{host_ip}:{PREVIEW_PORT}"
26
  else:
27
  # Local development
28
  return f"http://localhost:{PREVIEW_PORT}"
@@ -64,6 +66,10 @@ def save_file(path, new_text):
64
 
65
  def stop_preview_app():
66
  """Stop the preview app subprocess if it's running."""
 
 
 
 
67
  global preview_process
68
  if preview_process and preview_process.poll() is None:
69
  print("🛑 Stopping preview app...")
@@ -90,6 +96,11 @@ def stop_preview_app():
90
 
91
  def start_preview_app():
92
  """Start the preview app in a subprocess."""
 
 
 
 
 
93
  global preview_process
94
 
95
  # Stop any existing preview app
@@ -134,9 +145,12 @@ def start_preview_app():
134
  max_retries = 10
135
  for i in range(max_retries):
136
  try:
137
- response = requests.get(PREVIEW_URL, timeout=1)
138
  if response.status_code == 200:
139
- print(f"✅ Preview app started successfully on {PREVIEW_URL}")
 
 
 
140
  return True, "App started successfully"
141
  except requests.exceptions.RequestException:
142
  print(
@@ -145,7 +159,10 @@ def start_preview_app():
145
  pass
146
  time.sleep(0.5)
147
 
148
- print(f"⚠️ Preview app started but may not be fully ready. Check {PREVIEW_URL}")
 
 
 
149
  return True, "App started successfully"
150
 
151
  except Exception as e:
@@ -173,12 +190,16 @@ def create_iframe_preview():
173
 
174
  def is_preview_running():
175
  """Check if the preview app is running and accessible."""
 
 
 
 
176
  global preview_process
177
  if preview_process is None or preview_process.poll() is not None:
178
  return False
179
 
180
  try:
181
- response = requests.get(PREVIEW_URL, timeout=2)
182
  return response.status_code == 200
183
  except requests.exceptions.RequestException:
184
  return False
@@ -674,4 +695,13 @@ if __name__ == "__main__":
674
  from kiss_agent import KISSAgent
675
 
676
  agent = KISSAgent()
677
- GradioUI(agent).launch(share=False, server_name="0.0.0.0", server_port=7860)
 
 
 
 
 
 
 
 
 
 
18
 
19
  def get_preview_url():
20
  """Get the appropriate preview URL based on environment."""
21
+ # For Hugging Face Spaces with nginx proxy, use the /preview/ path
22
+ if os.getenv("SPACE_ID") or os.getenv("HF_SPACE"):
23
+ return "/preview/"
24
  # Check if running in Docker
25
+ elif os.path.exists("/.dockerenv") or os.getenv("DOCKER_CONTAINER"):
26
+ # In Docker with nginx proxy, use relative path
27
+ return "/preview/"
 
28
  else:
29
  # Local development
30
  return f"http://localhost:{PREVIEW_PORT}"
 
66
 
67
  def stop_preview_app():
68
  """Stop the preview app subprocess if it's running."""
69
+ # In HF Spaces, preview app runs as separate service, no need to manage subprocess
70
+ if os.getenv("SPACE_ID") or os.getenv("HF_SPACE"):
71
+ return
72
+
73
  global preview_process
74
  if preview_process and preview_process.poll() is None:
75
  print("🛑 Stopping preview app...")
 
96
 
97
  def start_preview_app():
98
  """Start the preview app in a subprocess."""
99
+ # In HF Spaces, preview app runs as separate service via supervisor
100
+ if os.getenv("SPACE_ID") or os.getenv("HF_SPACE"):
101
+ print("✅ Preview app managed by supervisor in HF Spaces")
102
+ return True, "Preview app running via supervisor"
103
+
104
  global preview_process
105
 
106
  # Stop any existing preview app
 
145
  max_retries = 10
146
  for i in range(max_retries):
147
  try:
148
+ response = requests.get(f"http://localhost:{PREVIEW_PORT}", timeout=1)
149
  if response.status_code == 200:
150
+ print(
151
+ f"✅ Preview app started successfully on \
152
+ localhost:{PREVIEW_PORT}"
153
+ )
154
  return True, "App started successfully"
155
  except requests.exceptions.RequestException:
156
  print(
 
159
  pass
160
  time.sleep(0.5)
161
 
162
+ print(
163
+ f"⚠️ Preview app started but may not be fully ready. \
164
+ Check localhost:{PREVIEW_PORT}"
165
+ )
166
  return True, "App started successfully"
167
 
168
  except Exception as e:
 
190
 
191
  def is_preview_running():
192
  """Check if the preview app is running and accessible."""
193
+ # In HF Spaces, assume preview app is always running via supervisor
194
+ if os.getenv("SPACE_ID") or os.getenv("HF_SPACE"):
195
+ return True
196
+
197
  global preview_process
198
  if preview_process is None or preview_process.poll() is not None:
199
  return False
200
 
201
  try:
202
+ response = requests.get(f"http://localhost:{PREVIEW_PORT}", timeout=2)
203
  return response.status_code == 200
204
  except requests.exceptions.RequestException:
205
  return False
 
695
  from kiss_agent import KISSAgent
696
 
697
  agent = KISSAgent()
698
+
699
+ # Determine port based on environment
700
+ if os.getenv("SPACE_ID") or os.getenv("HF_SPACE"):
701
+ # In HF Spaces, run on 7862 (nginx will proxy to 7860)
702
+ port = 7862
703
+ else:
704
+ # Local development
705
+ port = 7860
706
+
707
+ GradioUI(agent).launch(share=False, server_name="0.0.0.0", server_port=port)
nginx.conf ADDED
@@ -0,0 +1,50 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ events {
2
+ worker_connections 1024;
3
+ }
4
+
5
+ http {
6
+ upstream main_app {
7
+ server 127.0.0.1:7862;
8
+ }
9
+
10
+ upstream preview_app {
11
+ server 127.0.0.1:7861;
12
+ }
13
+
14
+ server {
15
+ listen 7860;
16
+ server_name localhost;
17
+
18
+ # Main app - all requests except /preview/*
19
+ location / {
20
+ proxy_pass http://main_app;
21
+ proxy_set_header Host $host;
22
+ proxy_set_header X-Real-IP $remote_addr;
23
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
24
+ proxy_set_header X-Forwarded-Proto $scheme;
25
+
26
+ # WebSocket support for Gradio
27
+ proxy_http_version 1.1;
28
+ proxy_set_header Upgrade $http_upgrade;
29
+ proxy_set_header Connection "upgrade";
30
+ proxy_read_timeout 86400;
31
+ }
32
+
33
+ # Preview app - all requests to /preview/*
34
+ location /preview/ {
35
+ # Remove /preview from the path when forwarding
36
+ rewrite ^/preview/(.*) /$1 break;
37
+ proxy_pass http://preview_app;
38
+ proxy_set_header Host $host;
39
+ proxy_set_header X-Real-IP $remote_addr;
40
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
41
+ proxy_set_header X-Forwarded-Proto $scheme;
42
+
43
+ # WebSocket support for Gradio
44
+ proxy_http_version 1.1;
45
+ proxy_set_header Upgrade $http_upgrade;
46
+ proxy_set_header Connection "upgrade";
47
+ proxy_read_timeout 86400;
48
+ }
49
+ }
50
+ }
run_local.py ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """
3
+ Local development runner for Likable GitHub.
4
+ This script runs the app in local mode without Docker/nginx setup.
5
+ """
6
+
7
+ import os
8
+
9
+ if __name__ == "__main__":
10
+ # Ensure we're not in HF Spaces mode for local development
11
+ if "SPACE_ID" in os.environ:
12
+ del os.environ["SPACE_ID"]
13
+ if "HF_SPACE" in os.environ:
14
+ del os.environ["HF_SPACE"]
15
+
16
+ # Run the main app
17
+ from app import GradioUI
18
+ from kiss_agent import KISSAgent
19
+
20
+ agent = KISSAgent()
21
+ print("🚀 Starting Likable GitHub in local development mode...")
22
+ print("📱 Main app will be available at: http://localhost:7860")
23
+ print("🔍 Preview apps will be available at: http://localhost:7861")
24
+
25
+ GradioUI(agent).launch(share=False, server_name="0.0.0.0", server_port=7860)
supervisord.conf ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ [supervisord]
2
+ nodaemon=true
3
+ user=root
4
+
5
+ [program:nginx]
6
+ command=nginx -g "daemon off;"
7
+ autostart=true
8
+ autorestart=true
9
+ stdout_logfile=/var/log/supervisor/nginx.log
10
+ stderr_logfile=/var/log/supervisor/nginx.log
11
+
12
+ [program:main_app]
13
+ command=python app.py
14
+ directory=/app
15
+ autostart=true
16
+ autorestart=true
17
+ stdout_logfile=/var/log/supervisor/main_app.log
18
+ stderr_logfile=/var/log/supervisor/main_app.log
19
+ environment=GRADIO_SERVER_PORT="7862",GRADIO_SERVER_NAME="0.0.0.0"
20
+
21
+ [program:preview_app]
22
+ command=python sandbox/app.py --server-port 7861 --server-name 0.0.0.0
23
+ directory=/app
24
+ autostart=true
25
+ autorestart=true
26
+ stdout_logfile=/var/log/supervisor/preview_app.log
27
+ stderr_logfile=/var/log/supervisor/preview_app.log