Spaces:
Running
Running
Commit ·
715b529
1
Parent(s): 7d51cb2
fix: satisfy openenv multi-mode validation
Browse filesAdd root pyproject.toml and uv.lock with server script + openenv-core dependency, and expose server.app:main entrypoint for validator checks.
Also harden frontend health status handling to avoid false offline state when health responses vary by status text/content-type.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- frontend/src/api/client.ts +12 -2
- frontend/src/components/Dashboard.tsx +10 -2
- frontend/src/components/Settings.tsx +11 -3
- pyproject.toml +19 -0
- server/app.py +14 -0
- uv.lock +0 -0
frontend/src/api/client.ts
CHANGED
|
@@ -273,11 +273,21 @@ export const apiClient = {
|
|
| 273 |
|
| 274 |
// Health Check
|
| 275 |
async healthCheck(): Promise<{ status: string; version: string }> {
|
| 276 |
-
const response = await fetch(`${API_BASE}/health`);
|
| 277 |
if (!response.ok) {
|
| 278 |
throw new APIError('Health check failed', response.status);
|
| 279 |
}
|
| 280 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 281 |
},
|
| 282 |
|
| 283 |
// Scraping with streaming
|
|
|
|
| 273 |
|
| 274 |
// Health Check
|
| 275 |
async healthCheck(): Promise<{ status: string; version: string }> {
|
| 276 |
+
const response = await fetch(`${API_BASE}/health`, { cache: 'no-store' });
|
| 277 |
if (!response.ok) {
|
| 278 |
throw new APIError('Health check failed', response.status);
|
| 279 |
}
|
| 280 |
+
const contentType = response.headers.get('content-type') || '';
|
| 281 |
+
if (contentType.includes('application/json')) {
|
| 282 |
+
return response.json();
|
| 283 |
+
}
|
| 284 |
+
|
| 285 |
+
const text = await response.text();
|
| 286 |
+
try {
|
| 287 |
+
return JSON.parse(text) as { status: string; version: string };
|
| 288 |
+
} catch {
|
| 289 |
+
return { status: 'healthy', version: 'unknown' };
|
| 290 |
+
}
|
| 291 |
},
|
| 292 |
|
| 293 |
// Scraping with streaming
|
frontend/src/components/Dashboard.tsx
CHANGED
|
@@ -490,7 +490,7 @@ export const Dashboard: React.FC = () => {
|
|
| 490 |
const [stats, setStats] = useState({ episodes: 0, steps: 0, totalReward: 0, avgReward: 0 });
|
| 491 |
|
| 492 |
// API Queries
|
| 493 |
-
const { data: health } = useQuery({
|
| 494 |
queryKey: ['health'],
|
| 495 |
queryFn: () => apiClient.healthCheck(),
|
| 496 |
refetchInterval: 5000,
|
|
@@ -863,7 +863,15 @@ export const Dashboard: React.FC = () => {
|
|
| 863 |
};
|
| 864 |
|
| 865 |
// Check system status
|
| 866 |
-
const
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 867 |
|
| 868 |
// Show info popup
|
| 869 |
const showInfo = (title: string, description: string, details?: Record<string, string>) => {
|
|
|
|
| 490 |
const [stats, setStats] = useState({ episodes: 0, steps: 0, totalReward: 0, avgReward: 0 });
|
| 491 |
|
| 492 |
// API Queries
|
| 493 |
+
const { data: health, isError: healthError } = useQuery({
|
| 494 |
queryKey: ['health'],
|
| 495 |
queryFn: () => apiClient.healthCheck(),
|
| 496 |
refetchInterval: 5000,
|
|
|
|
| 863 |
};
|
| 864 |
|
| 865 |
// Check system status
|
| 866 |
+
const normalizedHealthStatus = typeof health?.status === 'string'
|
| 867 |
+
? health.status.toLowerCase()
|
| 868 |
+
: null;
|
| 869 |
+
const isSystemOnline = !healthError && (
|
| 870 |
+
normalizedHealthStatus === null
|
| 871 |
+
|| normalizedHealthStatus === 'healthy'
|
| 872 |
+
|| normalizedHealthStatus === 'ok'
|
| 873 |
+
|| normalizedHealthStatus === 'ready'
|
| 874 |
+
);
|
| 875 |
|
| 876 |
// Show info popup
|
| 877 |
const showInfo = (title: string, description: string, details?: Record<string, string>) => {
|
frontend/src/components/Settings.tsx
CHANGED
|
@@ -101,6 +101,14 @@ export const Settings: React.FC<SettingsProps> = ({ className }) => {
|
|
| 101 |
refetchInterval: 10000,
|
| 102 |
});
|
| 103 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 104 |
// Mutation to update API key
|
| 105 |
const updateApiKeyMutation = useMutation({
|
| 106 |
mutationFn: async ({ provider, api_key }: { provider: string; api_key: string }) => {
|
|
@@ -223,7 +231,7 @@ export const Settings: React.FC<SettingsProps> = ({ className }) => {
|
|
| 223 |
<span className="text-xs text-gray-400">Backend</span>
|
| 224 |
</div>
|
| 225 |
<p className="text-sm font-medium text-white mt-1">
|
| 226 |
-
{
|
| 227 |
</p>
|
| 228 |
</div>
|
| 229 |
<div className="p-3 bg-gray-900/50 rounded-lg">
|
|
@@ -588,8 +596,8 @@ export const Settings: React.FC<SettingsProps> = ({ className }) => {
|
|
| 588 |
{/* Status Footer */}
|
| 589 |
<div className="p-4 border-t border-gray-700/50">
|
| 590 |
<div className="flex items-center gap-2">
|
| 591 |
-
<div className={classNames('w-2 h-2 rounded-full',
|
| 592 |
-
<span className="text-xs text-gray-400">{
|
| 593 |
</div>
|
| 594 |
</div>
|
| 595 |
</div>
|
|
|
|
| 101 |
refetchInterval: 10000,
|
| 102 |
});
|
| 103 |
|
| 104 |
+
const normalizedHealthStatus = typeof health?.status === 'string'
|
| 105 |
+
? health.status.toLowerCase()
|
| 106 |
+
: '';
|
| 107 |
+
const isBackendOnline =
|
| 108 |
+
normalizedHealthStatus === 'healthy'
|
| 109 |
+
|| normalizedHealthStatus === 'ok'
|
| 110 |
+
|| normalizedHealthStatus === 'ready';
|
| 111 |
+
|
| 112 |
// Mutation to update API key
|
| 113 |
const updateApiKeyMutation = useMutation({
|
| 114 |
mutationFn: async ({ provider, api_key }: { provider: string; api_key: string }) => {
|
|
|
|
| 231 |
<span className="text-xs text-gray-400">Backend</span>
|
| 232 |
</div>
|
| 233 |
<p className="text-sm font-medium text-white mt-1">
|
| 234 |
+
{isBackendOnline ? 'Connected' : 'Disconnected'}
|
| 235 |
</p>
|
| 236 |
</div>
|
| 237 |
<div className="p-3 bg-gray-900/50 rounded-lg">
|
|
|
|
| 596 |
{/* Status Footer */}
|
| 597 |
<div className="p-4 border-t border-gray-700/50">
|
| 598 |
<div className="flex items-center gap-2">
|
| 599 |
+
<div className={classNames('w-2 h-2 rounded-full', isBackendOnline ? 'bg-emerald-400' : 'bg-red-400')} />
|
| 600 |
+
<span className="text-xs text-gray-400">{isBackendOnline ? 'System Online' : 'System Offline'}</span>
|
| 601 |
</div>
|
| 602 |
</div>
|
| 603 |
</div>
|
pyproject.toml
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
[build-system]
|
| 2 |
+
requires = ["hatchling"]
|
| 3 |
+
build-backend = "hatchling.build"
|
| 4 |
+
|
| 5 |
+
[project]
|
| 6 |
+
name = "scraperl-openenv"
|
| 7 |
+
version = "0.1.0"
|
| 8 |
+
description = "ScrapeRL OpenEnv-compatible environment package"
|
| 9 |
+
readme = "README.md"
|
| 10 |
+
requires-python = ">=3.11"
|
| 11 |
+
dependencies = [
|
| 12 |
+
"openenv-core>=0.2.0",
|
| 13 |
+
"fastapi>=0.109.0",
|
| 14 |
+
"uvicorn[standard]>=0.27.0",
|
| 15 |
+
]
|
| 16 |
+
|
| 17 |
+
[project.scripts]
|
| 18 |
+
server = "server.app:main"
|
| 19 |
+
|
server/app.py
CHANGED
|
@@ -2,6 +2,7 @@
|
|
| 2 |
|
| 3 |
from __future__ import annotations
|
| 4 |
|
|
|
|
| 5 |
import sys
|
| 6 |
from pathlib import Path
|
| 7 |
|
|
@@ -11,3 +12,16 @@ if str(BACKEND_DIR) not in sys.path:
|
|
| 11 |
|
| 12 |
from app.main import app # noqa: E402,F401
|
| 13 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2 |
|
| 3 |
from __future__ import annotations
|
| 4 |
|
| 5 |
+
import os
|
| 6 |
import sys
|
| 7 |
from pathlib import Path
|
| 8 |
|
|
|
|
| 12 |
|
| 13 |
from app.main import app # noqa: E402,F401
|
| 14 |
|
| 15 |
+
|
| 16 |
+
def main() -> None:
|
| 17 |
+
"""Run the FastAPI app for OpenEnv multi-mode validation."""
|
| 18 |
+
import uvicorn
|
| 19 |
+
|
| 20 |
+
host = os.getenv("HOST", "0.0.0.0")
|
| 21 |
+
port = int(os.getenv("PORT", "7860"))
|
| 22 |
+
uvicorn.run("server.app:app", host=host, port=port)
|
| 23 |
+
|
| 24 |
+
|
| 25 |
+
if __name__ == "__main__":
|
| 26 |
+
main()
|
| 27 |
+
|
uv.lock
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|