Spaces:
Running
Running
Cleaning
Browse files- .env_template +12 -0
- app/services/vertex_client.py +0 -77
- bin/generate-api.sh +0 -27
- docker-test.sh +0 -5
- generate-api.sh +0 -27
- openapi.yaml +0 -35
- package.json +1 -2
.env_template
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# GCP Project Configuration (for deployment)
|
| 2 |
+
PROJECT_ID=your-project-id
|
| 3 |
+
REGION=europe-west1
|
| 4 |
+
REPOSITORY=repo-name
|
| 5 |
+
SERVICE_NAME=app-name
|
| 6 |
+
|
| 7 |
+
# --- Models ---
|
| 8 |
+
# Mandatory for MedSigLIP model download
|
| 9 |
+
HF_TOKEN=take-from-model-download-site
|
| 10 |
+
# --- Hardware (Optional Overrides) ---
|
| 11 |
+
MEMORY=8Gi
|
| 12 |
+
CPU=4
|
app/services/vertex_client.py
DELETED
|
@@ -1,77 +0,0 @@
|
|
| 1 |
-
import os
|
| 2 |
-
import logging
|
| 3 |
-
try:
|
| 4 |
-
from google.cloud import aiplatform
|
| 5 |
-
from google.oauth2 import service_account
|
| 6 |
-
import google.auth
|
| 7 |
-
GOOGLE_CLOUD_AVAILABLE = True
|
| 8 |
-
except ImportError:
|
| 9 |
-
GOOGLE_CLOUD_AVAILABLE = False
|
| 10 |
-
aiplatform = None
|
| 11 |
-
service_account = None
|
| 12 |
-
google = None
|
| 13 |
-
|
| 14 |
-
logger = logging.getLogger(__name__)
|
| 15 |
-
|
| 16 |
-
class VertexClient:
|
| 17 |
-
def __init__(self):
|
| 18 |
-
self.project_id = os.environ.get("PROJECT_ID")
|
| 19 |
-
self.location = os.environ.get("LOCATION", "us-central1")
|
| 20 |
-
self.endpoint_id = os.environ.get("ENDPOINT_ID") # ID of the deployed MedGemma endpoint
|
| 21 |
-
self.credentials_path = os.environ.get("GOOGLE_APPLICATION_CREDENTIALS")
|
| 22 |
-
|
| 23 |
-
self.setup_complete = False
|
| 24 |
-
|
| 25 |
-
if not GOOGLE_CLOUD_AVAILABLE:
|
| 26 |
-
logger.warning("google-cloud-aiplatform not installed. Vertex AI client will be mocked.")
|
| 27 |
-
return
|
| 28 |
-
|
| 29 |
-
if self.project_id:
|
| 30 |
-
try:
|
| 31 |
-
# If credentials path is set, explicit load (dev), else default (cloud run)
|
| 32 |
-
if self.credentials_path and os.path.exists(self.credentials_path):
|
| 33 |
-
creds = service_account.Credentials.from_service_account_file(self.credentials_path)
|
| 34 |
-
else:
|
| 35 |
-
creds, _ = google.auth.default()
|
| 36 |
-
|
| 37 |
-
aiplatform.init(
|
| 38 |
-
project=self.project_id,
|
| 39 |
-
location=self.location,
|
| 40 |
-
credentials=creds
|
| 41 |
-
)
|
| 42 |
-
self.setup_complete = True
|
| 43 |
-
logger.info(f"Vertex AI initialized for project {self.project_id}")
|
| 44 |
-
except Exception as e:
|
| 45 |
-
logger.error(f"Failed to initialize Vertex AI: {e}")
|
| 46 |
-
|
| 47 |
-
async def predict(self, prompt: str, max_tokens: int = 256, temperature: float = 0.2) -> str:
|
| 48 |
-
if not self.setup_complete:
|
| 49 |
-
logger.info("Returning mock prediction because Vertex AI is not configured.")
|
| 50 |
-
return "Mock Response: Vertex AI is not configured. This is a dummy prediction."
|
| 51 |
-
|
| 52 |
-
if not self.endpoint_id:
|
| 53 |
-
return "Endpoint ID not configured."
|
| 54 |
-
|
| 55 |
-
try:
|
| 56 |
-
# Get Endpoint
|
| 57 |
-
endpoint = aiplatform.Endpoint(self.endpoint_id)
|
| 58 |
-
|
| 59 |
-
# Predict
|
| 60 |
-
# Structure depends on the model serving container.
|
| 61 |
-
# MedGemma usually expects instances=[{"prompt": ...}]
|
| 62 |
-
instances = [{"prompt": prompt, "max_tokens": max_tokens, "temperature": temperature}]
|
| 63 |
-
|
| 64 |
-
response = endpoint.predict(instances=instances)
|
| 65 |
-
|
| 66 |
-
# Parse prediction (assuming standard format, adjust based on actual model output)
|
| 67 |
-
# Typically response.predictions is a list
|
| 68 |
-
if response.predictions:
|
| 69 |
-
return str(response.predictions[0])
|
| 70 |
-
else:
|
| 71 |
-
return "No prediction returned."
|
| 72 |
-
|
| 73 |
-
except Exception as e:
|
| 74 |
-
logger.error(f"Prediction failed: {e}")
|
| 75 |
-
raise e
|
| 76 |
-
|
| 77 |
-
vertex_client = VertexClient()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
bin/generate-api.sh
DELETED
|
@@ -1,27 +0,0 @@
|
|
| 1 |
-
#!/bin/bash
|
| 2 |
-
|
| 3 |
-
# Exit on error
|
| 4 |
-
set -e
|
| 5 |
-
|
| 6 |
-
echo "Generating Python models..."
|
| 7 |
-
|
| 8 |
-
# Find datamodel-codegen in path or venv
|
| 9 |
-
if command -v datamodel-codegen >/dev/null 2>&1; then
|
| 10 |
-
CODEGEN_BIN="datamodel-codegen"
|
| 11 |
-
elif [ -f "./venv/bin/datamodel-codegen" ]; then
|
| 12 |
-
CODEGEN_BIN="./venv/bin/datamodel-codegen"
|
| 13 |
-
else
|
| 14 |
-
echo "datamodel-codegen not found. Attempting to install..."
|
| 15 |
-
pip install datamodel-code-generator || ./venv/bin/pip install datamodel-code-generator
|
| 16 |
-
CODEGEN_BIN="datamodel-codegen"
|
| 17 |
-
if ! command -v "$CODEGEN_BIN" >/dev/null 2>&1 && [ -f "./venv/bin/datamodel-codegen" ]; then
|
| 18 |
-
CODEGEN_BIN="./venv/bin/datamodel-codegen"
|
| 19 |
-
fi
|
| 20 |
-
fi
|
| 21 |
-
|
| 22 |
-
if ! "$CODEGEN_BIN" --input openapi.yaml --output app/models.py; then
|
| 23 |
-
echo "Error: Python model generation failed. Check openapi.yaml for syntax errors."
|
| 24 |
-
exit 1
|
| 25 |
-
fi
|
| 26 |
-
|
| 27 |
-
echo "API Generation for Python Complete!"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
docker-test.sh
DELETED
|
@@ -1,5 +0,0 @@
|
|
| 1 |
-
#!/bin/bash
|
| 2 |
-
# Helper script to run tests inside the Docker container
|
| 3 |
-
|
| 4 |
-
echo "Running tests in the 'app' container..."
|
| 5 |
-
docker compose exec app pytest "$@"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
generate-api.sh
DELETED
|
@@ -1,27 +0,0 @@
|
|
| 1 |
-
#!/bin/bash
|
| 2 |
-
|
| 3 |
-
# Exit on error
|
| 4 |
-
set -e
|
| 5 |
-
|
| 6 |
-
echo "Generating Python models..."
|
| 7 |
-
|
| 8 |
-
# Find datamodel-codegen in path or venv
|
| 9 |
-
if command -v datamodel-codegen >/dev/null 2>&1; then
|
| 10 |
-
CODEGEN_BIN="datamodel-codegen"
|
| 11 |
-
elif [ -f "./venv/bin/datamodel-codegen" ]; then
|
| 12 |
-
CODEGEN_BIN="./venv/bin/datamodel-codegen"
|
| 13 |
-
else
|
| 14 |
-
echo "datamodel-codegen not found. Attempting to install..."
|
| 15 |
-
pip install datamodel-code-generator || ./venv/bin/pip install datamodel-code-generator
|
| 16 |
-
CODEGEN_BIN="datamodel-codegen"
|
| 17 |
-
if ! command -v "$CODEGEN_BIN" >/dev/null 2>&1 && [ -f "./venv/bin/datamodel-codegen" ]; then
|
| 18 |
-
CODEGEN_BIN="./venv/bin/datamodel-codegen"
|
| 19 |
-
fi
|
| 20 |
-
fi
|
| 21 |
-
|
| 22 |
-
if ! "$CODEGEN_BIN" --input openapi.yaml --output app/models.py; then
|
| 23 |
-
echo "Error: Python model generation failed. Check openapi.yaml for syntax errors."
|
| 24 |
-
exit 1
|
| 25 |
-
fi
|
| 26 |
-
|
| 27 |
-
echo "API Generation for Python Complete!"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
openapi.yaml
DELETED
|
@@ -1,35 +0,0 @@
|
|
| 1 |
-
openapi: 3.0.3
|
| 2 |
-
info:
|
| 3 |
-
title: Dermatolog AI Scan
|
| 4 |
-
description: AI application for dermatology analysis
|
| 5 |
-
version: 1.0.0
|
| 6 |
-
paths:
|
| 7 |
-
/api/health:
|
| 8 |
-
get:
|
| 9 |
-
summary: Health check endpoint
|
| 10 |
-
operationId: health_check
|
| 11 |
-
responses:
|
| 12 |
-
'200':
|
| 13 |
-
description: Successful Response
|
| 14 |
-
content:
|
| 15 |
-
application/json:
|
| 16 |
-
schema:
|
| 17 |
-
$ref: '#/components/schemas/HealthCheckResponse'
|
| 18 |
-
components:
|
| 19 |
-
schemas:
|
| 20 |
-
HealthCheckResponse:
|
| 21 |
-
properties:
|
| 22 |
-
status:
|
| 23 |
-
type: string
|
| 24 |
-
title: Status
|
| 25 |
-
database:
|
| 26 |
-
type: string
|
| 27 |
-
title: Database
|
| 28 |
-
gcp_project:
|
| 29 |
-
type: string
|
| 30 |
-
title: Gcp Project
|
| 31 |
-
type: object
|
| 32 |
-
required:
|
| 33 |
-
- status
|
| 34 |
-
- database
|
| 35 |
-
title: HealthCheckResponse
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
package.json
CHANGED
|
@@ -9,8 +9,7 @@
|
|
| 9 |
"scripts": {
|
| 10 |
"test": "node --experimental-vm-modules node_modules/jest/bin/jest.js",
|
| 11 |
"test:watch": "node --experimental-vm-modules node_modules/jest/bin/jest.js --watch",
|
| 12 |
-
"test:coverage": "node --experimental-vm-modules node_modules/jest/bin/jest.js --coverage"
|
| 13 |
-
"generate-api": "bash bin/generate-api.sh"
|
| 14 |
},
|
| 15 |
"devDependencies": {
|
| 16 |
"@jest/globals": "^29.7.0",
|
|
|
|
| 9 |
"scripts": {
|
| 10 |
"test": "node --experimental-vm-modules node_modules/jest/bin/jest.js",
|
| 11 |
"test:watch": "node --experimental-vm-modules node_modules/jest/bin/jest.js --watch",
|
| 12 |
+
"test:coverage": "node --experimental-vm-modules node_modules/jest/bin/jest.js --coverage"
|
|
|
|
| 13 |
},
|
| 14 |
"devDependencies": {
|
| 15 |
"@jest/globals": "^29.7.0",
|