Soutrik commited on
Commit
1b0bd15
1 Parent(s): 6053557

added: client server on docker compose cpu tested

Browse files
Dockerfile CHANGED
@@ -29,6 +29,10 @@ RUN --mount=type=cache,target=/tmp/poetry_cache poetry install --only main --no-
29
  # Stage 2: Runtime environment
30
  FROM python:3.10.15-slim as runner
31
 
 
 
 
 
32
  # Copy application source code and necessary files
33
  COPY src /app/src
34
  COPY configs /app/configs
@@ -38,6 +42,9 @@ COPY main.py /app/main.py
38
  # Copy virtual environment from the builder stage
39
  COPY --from=builder /app/.venv /app/.venv
40
 
 
 
 
41
  # Set the working directory to /app
42
  WORKDIR /app
43
 
 
29
  # Stage 2: Runtime environment
30
  FROM python:3.10.15-slim as runner
31
 
32
+ # Install curl for health check script
33
+ RUN apt-get update && apt-get install -y --no-install-recommends curl && \
34
+ apt-get clean && rm -rf /var/lib/apt/lists/*
35
+
36
  # Copy application source code and necessary files
37
  COPY src /app/src
38
  COPY configs /app/configs
 
42
  # Copy virtual environment from the builder stage
43
  COPY --from=builder /app/.venv /app/.venv
44
 
45
+ # Copy the client files
46
+ COPY run_client.sh /app/run_client.sh
47
+
48
  # Set the working directory to /app
49
  WORKDIR /app
50
 
artifacts/image_prediction.png DELETED
Binary file (390 kB)
 
docker-compose-old.yaml ADDED
@@ -0,0 +1,74 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ version: '3.8'
2
+
3
+ services:
4
+ train:
5
+ build:
6
+ context: .
7
+ command: |
8
+ python -m src.train_optuna_callbacks experiment=catdog_experiment ++task_name=train ++train=True ++test=False && \
9
+ python -m src.create_artifacts && \
10
+ touch ./checkpoints/train_done.flag
11
+ volumes:
12
+ - ./data:/app/data
13
+ - ./checkpoints:/app/checkpoints
14
+ - ./artifacts:/app/artifacts
15
+ - ./logs:/app/logs
16
+ environment:
17
+ - PYTHONUNBUFFERED=1
18
+ - PYTHONPATH=/app
19
+ shm_size: '4g'
20
+ networks:
21
+ - default
22
+ env_file:
23
+ - .env
24
+
25
+ eval:
26
+ build:
27
+ context: .
28
+ command: |
29
+ sh -c 'while [ ! -f /app/checkpoints/train_done.flag ]; do sleep 10; done && python -m src.train_optuna_callbacks experiment=catdog_experiment ++task_name=test ++train=False ++test=True'
30
+ volumes:
31
+ - ./data:/app/data
32
+ - ./checkpoints:/app/checkpoints
33
+ - ./artifacts:/app/artifacts
34
+ - ./logs:/app/logs
35
+ environment:
36
+ - PYTHONUNBUFFERED=1
37
+ - PYTHONPATH=/app
38
+ shm_size: '4g'
39
+ networks:
40
+ - default
41
+ env_file:
42
+ - .env
43
+ depends_on:
44
+ - train
45
+
46
+ inference:
47
+ build:
48
+ context: .
49
+ command: |
50
+ sh -c 'while [ ! -f /app/checkpoints/train_done.flag ]; do sleep 10; done && python -m src.infer experiment=catdog_experiment'
51
+ volumes:
52
+ - ./data:/app/data
53
+ - ./checkpoints:/app/checkpoints
54
+ - ./artifacts:/app/artifacts
55
+ - ./logs:/app/logs
56
+ environment:
57
+ - PYTHONUNBUFFERED=1
58
+ - PYTHONPATH=/app
59
+ shm_size: '4g'
60
+ networks:
61
+ - default
62
+ env_file:
63
+ - .env
64
+ depends_on:
65
+ - train
66
+
67
+ volumes:
68
+ data:
69
+ checkpoints:
70
+ artifacts:
71
+ logs:
72
+
73
+ networks:
74
+ default:
docker-compose.yaml CHANGED
@@ -1,5 +1,3 @@
1
- version: '3.8'
2
-
3
  services:
4
  train:
5
  build:
@@ -21,7 +19,7 @@ services:
21
  - default
22
  env_file:
23
  - .env
24
-
25
  eval:
26
  build:
27
  context: .
@@ -40,14 +38,13 @@ services:
40
  - default
41
  env_file:
42
  - .env
43
- depends_on:
44
- - train
45
 
46
- inference:
47
  build:
48
  context: .
49
  command: |
50
- sh -c 'while [ ! -f /app/checkpoints/train_done.flag ]; do sleep 10; done && python -m src.infer experiment=catdog_experiment'
51
  volumes:
52
  - ./data:/app/data
53
  - ./checkpoints:/app/checkpoints
@@ -56,14 +53,36 @@ services:
56
  environment:
57
  - PYTHONUNBUFFERED=1
58
  - PYTHONPATH=/app
 
59
  shm_size: '4g'
60
  networks:
61
  - default
62
  env_file:
63
- - .env
64
- depends_on:
65
- - train
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
66
 
 
67
  volumes:
68
  data:
69
  checkpoints:
 
 
 
1
  services:
2
  train:
3
  build:
 
19
  - default
20
  env_file:
21
  - .env
22
+
23
  eval:
24
  build:
25
  context: .
 
38
  - default
39
  env_file:
40
  - .env
41
+
 
42
 
43
+ server:
44
  build:
45
  context: .
46
  command: |
47
+ sh -c 'while [ ! -f /app/checkpoints/train_done.flag ]; do sleep 10; done && python -m src.server'
48
  volumes:
49
  - ./data:/app/data
50
  - ./checkpoints:/app/checkpoints
 
53
  environment:
54
  - PYTHONUNBUFFERED=1
55
  - PYTHONPATH=/app
56
+ - SERVER_URL=http://localhost:8080
57
  shm_size: '4g'
58
  networks:
59
  - default
60
  env_file:
61
+ - .env
62
+ ports:
63
+ - "8080:8080"
64
+
65
+ client:
66
+ build:
67
+ context: .
68
+ command: |
69
+ sh -c 'while [ ! -f /app/checkpoints/train_done.flag ]; do sleep 10; done && ./run_client.sh'
70
+ volumes:
71
+ - ./data:/app/data
72
+ - ./checkpoints:/app/checkpoints
73
+ - ./artifacts:/app/artifacts
74
+ - ./logs:/app/logs
75
+ environment:
76
+ - PYTHONUNBUFFERED=1
77
+ - PYTHONPATH=/app
78
+ - SERVER_URL=http://server:8080
79
+ shm_size: '4g'
80
+ networks:
81
+ - default
82
+ env_file:
83
+ - .env
84
 
85
+
86
  volumes:
87
  data:
88
  checkpoints:
run_client.sh ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ #!/bin/bash
2
+
3
+ # Run the client script
4
+ echo "Running the client script..."
5
+ python -m src.client
src/client.py CHANGED
@@ -1,3 +1,4 @@
 
1
  import requests
2
  from urllib.request import urlopen
3
  import base64
@@ -8,45 +9,52 @@ def fetch_image(url):
8
  """
9
  Fetch image data from a URL.
10
  """
11
- return urlopen(url).read()
 
 
 
 
12
 
13
 
14
  def encode_image_to_base64(img_data):
15
  """
16
  Encode image bytes to a base64 string.
17
  """
18
- return base64.b64encode(img_data).decode("utf-8")
19
-
20
-
21
- def send_prediction_request(base64_image, server_url):
22
- """
23
- Send a single base64 image to the prediction API and retrieve predictions.
24
- """
25
  try:
26
- response = requests.post(f"{server_url}/predict", json={"image": base64_image})
27
- return response
28
- except requests.exceptions.RequestException as e:
29
- print(f"Error connecting to the server: {e}")
30
- return None
31
 
32
 
33
- def send_batch_prediction_request(base64_images, server_url):
34
  """
35
- Send a batch of base64 images to the prediction API and retrieve predictions.
 
36
  """
37
- try:
38
- response = requests.post(
39
- f"{server_url}/predict", json=[{"image": img} for img in base64_images]
40
- )
41
- return response
42
- except requests.exceptions.RequestException as e:
43
- print(f"Error connecting to the server: {e}")
44
- return None
 
 
 
 
 
 
 
 
 
45
 
46
 
47
  def main():
48
- # Server URL (default or from environment)
49
- server_url = os.getenv("SERVER_URL", "http://localhost:8080")
 
50
 
51
  # Example URLs for testing
52
  image_urls = [
@@ -55,30 +63,32 @@ def main():
55
 
56
  # Fetch and encode images
57
  try:
58
- print("Fetching and encoding images...")
59
  base64_images = [encode_image_to_base64(fetch_image(url)) for url in image_urls]
60
- print("Images fetched and encoded successfully.")
61
  except Exception as e:
62
- print(f"Error fetching or encoding images: {e}")
63
  return
64
 
65
  # Test single image prediction
66
  try:
67
- print("\n--- Single Image Prediction ---")
68
- single_response = send_prediction_request(base64_images[0], server_url)
69
  if single_response and single_response.status_code == 200:
70
  predictions = single_response.json().get("predictions", [])
71
  if predictions:
72
- print("Top 5 Predictions:")
73
  for pred in predictions:
74
- print(f"{pred['label']}: {pred['probability']:.2%}")
75
  else:
76
- print("No predictions returned.")
77
  elif single_response:
78
- print(f"Error: {single_response.status_code}")
79
- print(single_response.text)
 
 
80
  except Exception as e:
81
- print(f"Error sending single prediction request: {e}")
82
 
83
 
84
  if __name__ == "__main__":
 
1
+ from loguru import logger
2
  import requests
3
  from urllib.request import urlopen
4
  import base64
 
9
  """
10
  Fetch image data from a URL.
11
  """
12
+ try:
13
+ return urlopen(url).read()
14
+ except Exception as e:
15
+ logger.error(f"Failed to fetch image from {url}: {e}")
16
+ raise
17
 
18
 
19
  def encode_image_to_base64(img_data):
20
  """
21
  Encode image bytes to a base64 string.
22
  """
 
 
 
 
 
 
 
23
  try:
24
+ return base64.b64encode(img_data).decode("utf-8")
25
+ except Exception as e:
26
+ logger.error(f"Failed to encode image to base64: {e}")
27
+ raise
 
28
 
29
 
30
+ def send_prediction_request(base64_image, server_urls):
31
  """
32
+ Send a single base64 image to the prediction API and retrieve predictions.
33
+ Tries multiple server URLs in order.
34
  """
35
+ for server_url in server_urls:
36
+ try:
37
+ logger.info(f"Attempting to send prediction request to {server_url}...")
38
+ response = requests.post(
39
+ f"{server_url}/predict", json={"image": base64_image}
40
+ )
41
+ if response.status_code == 200:
42
+ logger.info(f"Successfully connected to {server_url}")
43
+ return response
44
+ else:
45
+ logger.warning(
46
+ f"Server at {server_url} returned status code {response.status_code}"
47
+ )
48
+ except requests.exceptions.RequestException as e:
49
+ logger.error(f"Error connecting to the server at {server_url}: {e}")
50
+ logger.error("Failed to connect to any server.")
51
+ return None
52
 
53
 
54
  def main():
55
+ # Server URLs to try
56
+ server_url_env = os.getenv("SERVER_URL", "http://localhost:8080")
57
+ server_urls = [server_url_env]
58
 
59
  # Example URLs for testing
60
  image_urls = [
 
63
 
64
  # Fetch and encode images
65
  try:
66
+ logger.info("Fetching and encoding images...")
67
  base64_images = [encode_image_to_base64(fetch_image(url)) for url in image_urls]
68
+ logger.info("Images fetched and encoded successfully.")
69
  except Exception as e:
70
+ logger.error(f"Error fetching or encoding images: {e}")
71
  return
72
 
73
  # Test single image prediction
74
  try:
75
+ logger.info("--- Single Image Prediction ---")
76
+ single_response = send_prediction_request(base64_images[0], server_urls)
77
  if single_response and single_response.status_code == 200:
78
  predictions = single_response.json().get("predictions", [])
79
  if predictions:
80
+ logger.info("Top Predictions:")
81
  for pred in predictions:
82
+ logger.info(f"{pred['label']}: {pred['probability']:.2%}")
83
  else:
84
+ logger.warning("No predictions returned.")
85
  elif single_response:
86
+ logger.error(f"Error: {single_response.status_code}")
87
+ logger.error(single_response.text)
88
+ else:
89
+ logger.error("Failed to get a response from any server.")
90
  except Exception as e:
91
+ logger.error(f"Error sending single prediction request: {e}")
92
 
93
 
94
  if __name__ == "__main__":
src/server.py CHANGED
@@ -82,7 +82,7 @@ class ImageClassifierAPI(lit.LitAPI):
82
 
83
  def decode_request(self, request):
84
  """Handle both single and batch inputs."""
85
- logger.info(f"decode_request received: {request}")
86
  if not isinstance(request, dict) or "image" not in request:
87
  logger.error(
88
  "Invalid request format. Expected a dictionary with key 'image'."
@@ -94,7 +94,7 @@ class ImageClassifierAPI(lit.LitAPI):
94
 
95
  def batch(self, inputs):
96
  """Batch process images."""
97
- logger.info(f"batch received inputs: {inputs}")
98
  if not isinstance(inputs, list):
99
  raise ValueError("Input to batch must be a list.")
100
 
 
82
 
83
  def decode_request(self, request):
84
  """Handle both single and batch inputs."""
85
+ # logger.info(f"decode_request received: {request}")
86
  if not isinstance(request, dict) or "image" not in request:
87
  logger.error(
88
  "Invalid request format. Expected a dictionary with key 'image'."
 
94
 
95
  def batch(self, inputs):
96
  """Batch process images."""
97
+ # logger.info(f"batch received inputs: {inputs}")
98
  if not isinstance(inputs, list):
99
  raise ValueError("Input to batch must be a list.")
100