Eric Xu commited on
Fix port binding: simplify startup, remove timeout on segments, clean SIGTERM
Browse files- Dockerfile +3 -1
- web/app.py +1 -14
- web/static/index.html +13 -26
Dockerfile
CHANGED
|
@@ -20,4 +20,6 @@ COPY . .
|
|
| 20 |
# HF Spaces expects port 7860
|
| 21 |
EXPOSE 7860
|
| 22 |
|
| 23 |
-
|
|
|
|
|
|
|
|
|
| 20 |
# HF Spaces expects port 7860
|
| 21 |
EXPOSE 7860
|
| 22 |
|
| 23 |
+
# Use exec form so Python receives SIGTERM directly and shuts down cleanly
|
| 24 |
+
STOPSIGNAL SIGTERM
|
| 25 |
+
CMD ["python", "-u", "web/app.py"]
|
web/app.py
CHANGED
|
@@ -885,23 +885,10 @@ async def get_results(sid: str):
|
|
| 885 |
|
| 886 |
if __name__ == "__main__":
|
| 887 |
import uvicorn
|
| 888 |
-
import socket
|
| 889 |
|
| 890 |
port = int(os.getenv("PORT", "7860" if IS_SPACES else "8000"))
|
| 891 |
host = "0.0.0.0" if IS_SPACES else "127.0.0.1"
|
| 892 |
|
| 893 |
-
# Kill any process holding the port (HF Spaces restarts can leave zombies)
|
| 894 |
-
if IS_SPACES:
|
| 895 |
-
import subprocess
|
| 896 |
-
subprocess.run(f"fuser -k {port}/tcp", shell=True, capture_output=True)
|
| 897 |
-
|
| 898 |
print(f"\n SGO Web Interface")
|
| 899 |
print(f" http://{host}:{port}\n")
|
| 900 |
-
|
| 901 |
-
config = uvicorn.Config(app, host=host, port=port, access_log=False)
|
| 902 |
-
server = uvicorn.Server(config)
|
| 903 |
-
# Enable SO_REUSEADDR so restarts don't fail on TIME_WAIT sockets
|
| 904 |
-
config.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
| 905 |
-
config.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
| 906 |
-
config.socket.bind((host, port))
|
| 907 |
-
server.run()
|
|
|
|
| 885 |
|
| 886 |
if __name__ == "__main__":
|
| 887 |
import uvicorn
|
|
|
|
| 888 |
|
| 889 |
port = int(os.getenv("PORT", "7860" if IS_SPACES else "8000"))
|
| 890 |
host = "0.0.0.0" if IS_SPACES else "127.0.0.1"
|
| 891 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 892 |
print(f"\n SGO Web Interface")
|
| 893 |
print(f" http://{host}:{port}\n")
|
| 894 |
+
uvicorn.run(app, host=host, port=port, access_log=False)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
web/static/index.html
CHANGED
|
@@ -855,33 +855,20 @@ async function runFullPipeline() {
|
|
| 855 |
document.getElementById('pipelineProgressBar').style.width = '10%';
|
| 856 |
logStep('Asking LLM to choose panel segments...');
|
| 857 |
|
| 858 |
-
|
| 859 |
-
|
| 860 |
-
|
| 861 |
-
|
| 862 |
-
|
| 863 |
-
|
| 864 |
-
|
| 865 |
-
|
| 866 |
-
|
| 867 |
-
|
| 868 |
-
|
| 869 |
-
new Promise((_, reject) => setTimeout(() => reject(new Error('Segment suggestion timed out after 60s')), 60000)),
|
| 870 |
-
]);
|
| 871 |
-
if (!segResp.ok) {
|
| 872 |
-
const err = await segResp.json().catch(() => ({}));
|
| 873 |
-
throw new Error(err.detail || `Server error ${segResp.status}`);
|
| 874 |
-
}
|
| 875 |
-
const segData = await segResp.json();
|
| 876 |
-
segments = segData.segments || [];
|
| 877 |
-
} catch (e) {
|
| 878 |
-
logStep(`Segment suggestion failed: ${e.message} — using defaults`, 'err');
|
| 879 |
-
segments = [
|
| 880 |
-
{label: 'Primary audience', count: Math.ceil(panelSize / 3)},
|
| 881 |
-
{label: 'Adjacent audience', count: Math.ceil(panelSize / 3)},
|
| 882 |
-
{label: 'Skeptical perspective', count: Math.ceil(panelSize / 3)},
|
| 883 |
-
];
|
| 884 |
}
|
|
|
|
|
|
|
| 885 |
|
| 886 |
// Scale segment counts to match requested panel size
|
| 887 |
const totalSuggested = segments.reduce((a, s) => a + (s.count || 8), 0);
|
|
|
|
| 855 |
document.getElementById('pipelineProgressBar').style.width = '10%';
|
| 856 |
logStep('Asking LLM to choose panel segments...');
|
| 857 |
|
| 858 |
+
const segResp = await fetch(apiUrl('/api/suggest-segments'), {
|
| 859 |
+
method: 'POST',
|
| 860 |
+
headers: llmHeaders(),
|
| 861 |
+
body: JSON.stringify({
|
| 862 |
+
entity_text: text,
|
| 863 |
+
audience_context: audienceCtx || `People who would evaluate: ${text.substring(0, 200)}`,
|
| 864 |
+
}),
|
| 865 |
+
});
|
| 866 |
+
if (!segResp.ok) {
|
| 867 |
+
const err = await segResp.json().catch(() => ({}));
|
| 868 |
+
throw new Error(`Failed to choose segments: ${err.detail || segResp.status}`);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 869 |
}
|
| 870 |
+
const segData = await segResp.json();
|
| 871 |
+
const segments = segData.segments || [];
|
| 872 |
|
| 873 |
// Scale segment counts to match requested panel size
|
| 874 |
const totalSuggested = segments.reduce((a, s) => a + (s.count || 8), 0);
|