LogicGoInfotechSpaces commited on
Commit
f1c1f42
·
1 Parent(s): 7471c96

update Postman collection

Browse files
Files changed (3) hide show
  1. app/colorize_model.py +13 -2
  2. app/main.py +16 -8
  3. test.py +152 -124
app/colorize_model.py CHANGED
@@ -33,11 +33,22 @@ class ColorizeModel:
33
  self.hf_token = os.getenv("HF_TOKEN") or None
34
 
35
  # Configure writable cache to avoid permission issues on Spaces
36
- hf_cache_dir = os.getenv("HF_HOME", "./hf_cache")
 
 
37
  os.environ.setdefault("HF_HOME", hf_cache_dir)
38
  os.environ.setdefault("HUGGINGFACE_HUB_CACHE", hf_cache_dir)
39
  os.environ.setdefault("TRANSFORMERS_CACHE", hf_cache_dir)
40
- os.makedirs(hf_cache_dir, exist_ok=True)
 
 
 
 
 
 
 
 
 
41
 
42
  # Avoid libgomp warning by setting a valid integer
43
  os.environ.setdefault("OMP_NUM_THREADS", "1")
 
33
  self.hf_token = os.getenv("HF_TOKEN") or None
34
 
35
  # Configure writable cache to avoid permission issues on Spaces
36
+ # Prefer user home cache: ~/.cache/huggingface
37
+ default_home_cache = os.path.join(os.path.expanduser("~"), ".cache", "huggingface")
38
+ hf_cache_dir = os.getenv("HF_HOME", default_home_cache)
39
  os.environ.setdefault("HF_HOME", hf_cache_dir)
40
  os.environ.setdefault("HUGGINGFACE_HUB_CACHE", hf_cache_dir)
41
  os.environ.setdefault("TRANSFORMERS_CACHE", hf_cache_dir)
42
+ try:
43
+ os.makedirs(hf_cache_dir, exist_ok=True)
44
+ except Exception:
45
+ # Fallback to a local data dir if home is not writable
46
+ hf_cache_dir = os.path.abspath(os.path.join(".", "data", "hf_cache"))
47
+ os.environ["HF_HOME"] = hf_cache_dir
48
+ os.environ["HUGGINGFACE_HUB_CACHE"] = hf_cache_dir
49
+ os.environ["TRANSFORMERS_CACHE"] = hf_cache_dir
50
+ os.makedirs(hf_cache_dir, exist_ok=True)
51
+ logger.info("HF cache directory: %s", hf_cache_dir)
52
 
53
  # Avoid libgomp warning by setting a valid integer
54
  os.environ.setdefault("OMP_NUM_THREADS", "1")
app/main.py CHANGED
@@ -61,15 +61,23 @@ else:
61
  except:
62
  pass
63
 
64
- # Create directories
65
- UPLOAD_DIR = Path("uploads")
66
- RESULT_DIR = Path("results")
67
- UPLOAD_DIR.mkdir(exist_ok=True)
68
- RESULT_DIR.mkdir(exist_ok=True)
 
 
69
 
70
- # Mount static files for serving results
71
- app.mount("/results", StaticFiles(directory="results"), name="results")
72
- app.mount("/uploads", StaticFiles(directory="uploads"), name="uploads")
 
 
 
 
 
 
73
 
74
  # Initialize ColorizeNet model
75
  colorize_model = None
 
61
  except:
62
  pass
63
 
64
+ # Create writable directories (prefer under user home unless overridden)
65
+ def _resolve_dir(path_str: str, default_subdir: str) -> Path:
66
+ p = Path(path_str).expanduser()
67
+ if not p.is_absolute():
68
+ # Place relative paths under ~/data to avoid permission issues
69
+ p = Path.home() / "data" / default_subdir
70
+ return p
71
 
72
+ UPLOAD_DIR = _resolve_dir(os.getenv("UPLOAD_DIR", settings.UPLOAD_DIR), "uploads")
73
+ RESULT_DIR = _resolve_dir(os.getenv("RESULT_DIR", settings.RESULT_DIR), "results")
74
+ UPLOAD_DIR.mkdir(parents=True, exist_ok=True)
75
+ RESULT_DIR.mkdir(parents=True, exist_ok=True)
76
+ logger.info("Storage directories -> uploads: %s, results: %s", str(UPLOAD_DIR), str(RESULT_DIR))
77
+
78
+ # Mount static files for serving results from resolved directories
79
+ app.mount("/results", StaticFiles(directory=str(RESULT_DIR)), name="results")
80
+ app.mount("/uploads", StaticFiles(directory=str(UPLOAD_DIR)), name="uploads")
81
 
82
  # Initialize ColorizeNet model
83
  colorize_model = None
test.py CHANGED
@@ -1,134 +1,162 @@
1
- import os
2
- import sys
3
  import argparse
 
 
4
  import logging
5
- from pathlib import Path
 
 
 
6
 
7
  import requests
8
 
9
 
10
- logging.basicConfig(
11
- level=logging.INFO,
12
- format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
13
- )
14
- logger = logging.getLogger("colorize_test")
15
-
16
-
17
- def normalize_space_url(url: str) -> str:
18
- """
19
- Accept either a runtime URL (https://<owner>-<space>.hf.space)
20
- or a repo URL (https://huggingface.co/spaces/<Owner>/<Space>?logs=container)
21
- and return a runtime base URL suitable for API calls.
22
- """
23
- if not url:
24
- return url
25
- url = url.strip()
26
- # Strip query params
27
- if "?" in url:
28
- url = url.split("?", 1)[0]
29
- # Already runtime URL
30
- if ".hf.space" in url:
31
- return url.rstrip("/")
32
- # Convert repo URL to runtime URL
33
- marker = "/spaces/"
34
- if "huggingface.co" in url and marker in url:
35
- try:
36
- _, rest = url.split(marker, 1)
37
- owner, space = rest.split("/", 1)
38
- runtime = f"https://{owner.lower()}-{space.lower()}.hf.space"
39
- return runtime.rstrip("/")
40
- except Exception:
41
- return url.rstrip("/")
42
- return url.rstrip("/")
43
-
44
-
45
- def main() -> None:
46
- parser = argparse.ArgumentParser(description="Test Colorize API")
47
- parser.add_argument(
48
- "--image",
49
- "-i",
50
- type=str,
51
- required=False,
52
- default=r"C:\projects\colorize_text\pexels-andrey-grushnikov-223358-707676.jpg",
53
- help="Path to input image file",
54
- )
55
- parser.add_argument(
56
- "--timeout",
57
- type=int,
58
- default=180,
59
- help="Request timeout in seconds (default: 180)",
60
- )
61
- args = parser.parse_args()
62
-
63
- base_url = os.getenv(
64
- "BASE_URL",
65
- os.getenv(
66
- "SPACE_HOST",
67
- "http://localhost:7860",
68
- ),
69
- )
70
- base_url = normalize_space_url(base_url)
71
- app_check_token = os.getenv("APP_CHECK_TOKEN", "").strip()
72
-
73
- image_path = Path(args.image)
74
- if not image_path.exists():
75
- logger.error("Image file not found at: %s", str(image_path))
76
- sys.exit(1)
77
-
78
- # Health check
79
- try:
80
- logger.info("Using BASE_URL: %s", base_url)
81
- health = requests.get(f"{base_url}/health", timeout=15)
82
- logger.info("Health check %s: %s", health.status_code, health.text)
83
- except Exception as e:
84
- logger.warning("Health check failed: %s", str(e))
85
-
86
- headers = {}
87
- if app_check_token:
88
- headers["X-Firebase-AppCheck"] = app_check_token
89
-
90
- files = {"file": (image_path.name, open(image_path, "rb"), "application/octet-stream")}
91
-
92
- logger.info("Sending request to %s", f"{base_url}/colorize")
93
- try:
94
- resp = requests.post(
95
- f"{base_url}/colorize",
96
- headers=headers,
97
- files=files,
98
- timeout=args.timeout,
99
- )
100
- finally:
101
- files["file"][1].close()
102
-
103
- logger.info("Response status: %s", resp.status_code)
104
- if not resp.ok:
105
- logger.error("Error response: %s", resp.text)
106
- sys.exit(2)
107
-
108
- data = resp.json()
109
- download_url = data.get("download_url")
110
- api_download_url = data.get("api_download_url")
111
- filename = data.get("filename")
112
-
113
- logger.info("Colorization success: %s", data.get("success"))
114
- logger.info("Download URL: %s", download_url)
115
- logger.info("API Download URL: %s", api_download_url)
116
- logger.info("Filename: %s", filename)
117
-
118
- # Optionally download the result locally
119
- if download_url:
120
- try:
121
- out_path = Path("colorized_result.jpg")
122
- r = requests.get(download_url, timeout=60)
123
- if r.ok:
124
- out_path.write_bytes(r.content)
125
- logger.info("Saved colorized image to: %s", str(out_path.resolve()))
126
- else:
127
- logger.warning("Could not download from public URL, status: %s", r.status_code)
128
- except Exception as e:
129
- logger.warning("Download via public URL failed: %s", str(e))
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
130
 
131
 
132
  if __name__ == "__main__":
133
- main()
134
 
 
 
 
1
  import argparse
2
+ import io
3
+ import json
4
  import logging
5
+ import os
6
+ import sys
7
+ import time
8
+ from typing import Optional
9
 
10
  import requests
11
 
12
 
13
+ def configure_logging(verbose: bool) -> None:
14
+ log_level = logging.DEBUG if verbose else logging.INFO
15
+ logging.basicConfig(
16
+ level=log_level,
17
+ format="%(asctime)s - %(levelname)s - %(message)s"
18
+ )
19
+
20
+
21
+ def get_base_url(cli_base_url: Optional[str]) -> str:
22
+ if cli_base_url:
23
+ return cli_base_url.rstrip("/")
24
+ env_base = os.getenv("BASE_URL")
25
+ if env_base:
26
+ return env_base.rstrip("/")
27
+ # Fallback to HF style URL if provided via POSTMAN collection, else localhost
28
+ return "http://localhost:7860"
29
+
30
+
31
+ def wait_for_model(base_url: str, timeout_seconds: int = 300) -> None:
32
+ deadline = time.time() + timeout_seconds
33
+ health_url = f"{base_url}/health"
34
+ logging.info("Waiting for model to load at %s", health_url)
35
+ last_status = None
36
+ while time.time() < deadline:
37
+ try:
38
+ resp = requests.get(health_url, timeout=15)
39
+ if resp.ok:
40
+ data = resp.json()
41
+ last_status = data
42
+ if data.get("model_loaded"):
43
+ logging.info("Model loaded: %s", json.dumps(data))
44
+ return
45
+ logging.info("Health: %s", json.dumps(data))
46
+ else:
47
+ logging.warning("Health check HTTP %s", resp.status_code)
48
+ except Exception as e:
49
+ logging.warning("Health check error: %s", str(e))
50
+ time.sleep(3)
51
+ raise RuntimeError("Model did not load before timeout. Last health: %s" % (last_status,))
52
+
53
+
54
+ def upload_image(base_url: str, image_path: str, auth_bearer: Optional[str], app_check: Optional[str]) -> dict:
55
+ url = f"{base_url}/upload"
56
+ headers = {}
57
+ if auth_bearer:
58
+ headers["Authorization"] = f"Bearer {auth_bearer}"
59
+ if app_check:
60
+ headers["X-Firebase-AppCheck"] = app_check
61
+ with open(image_path, "rb") as f:
62
+ files = {"file": (os.path.basename(image_path), f, "image/jpeg")}
63
+ resp = requests.post(url, files=files, headers=headers, timeout=120)
64
+ if not resp.ok:
65
+ raise RuntimeError("Upload failed: HTTP %s %s" % (resp.status_code, resp.text))
66
+ data = resp.json()
67
+ logging.info("Upload response: %s", json.dumps(data))
68
+ return data
69
+
70
+
71
+ def colorize_image(base_url: str, image_path: str, auth_bearer: Optional[str], app_check: Optional[str]) -> dict:
72
+ url = f"{base_url}/colorize"
73
+ headers = {}
74
+ if auth_bearer:
75
+ headers["Authorization"] = f"Bearer {auth_bearer}"
76
+ if app_check:
77
+ headers["X-Firebase-AppCheck"] = app_check
78
+ with open(image_path, "rb") as f:
79
+ files = {"file": (os.path.basename(image_path), f, "image/jpeg")}
80
+ resp = requests.post(url, files=files, headers=headers, timeout=900)
81
+ if not resp.ok:
82
+ raise RuntimeError("Colorize failed: HTTP %s %s" % (resp.status_code, resp.text))
83
+ data = resp.json()
84
+ logging.info("Colorize response: %s", json.dumps(data))
85
+ return data
86
+
87
+
88
+ def download_result(base_url: str, result_id: str, output_path: str, auth_bearer: Optional[str], app_check: Optional[str]) -> None:
89
+ url = f"{base_url}/download/{result_id}"
90
+ headers = {}
91
+ if auth_bearer:
92
+ headers["Authorization"] = f"Bearer {auth_bearer}"
93
+ if app_check:
94
+ headers["X-Firebase-AppCheck"] = app_check
95
+ resp = requests.get(url, headers=headers, stream=True, timeout=300)
96
+ if not resp.ok:
97
+ raise RuntimeError("Download failed: HTTP %s %s" % (resp.status_code, resp.text))
98
+ with open(output_path, "wb") as out:
99
+ for chunk in resp.iter_content(chunk_size=8192):
100
+ if chunk:
101
+ out.write(chunk)
102
+ logging.info("Saved colorized image to: %s", output_path)
103
+
104
+
105
+ def main() -> int:
106
+ parser = argparse.ArgumentParser(description="End-to-end test for Colorize API")
107
+ parser.add_argument("--base-url", type=str, help="API base URL, e.g. https://<space>.hf.space")
108
+ parser.add_argument("--image", type=str, required=True, help="Path to input image")
109
+ parser.add_argument("--out", type=str, default="colorized_result.jpg", help="Path to save colorized image")
110
+ parser.add_argument("--auth", type=str, default=os.getenv("ID_TOKEN", ""), help="Optional Firebase id_token")
111
+ parser.add_argument("--app-check", type=str, default=os.getenv("APP_CHECK_TOKEN", ""), help="Optional App Check token")
112
+ parser.add_argument("--skip-wait", action="store_true", help="Skip waiting for model to load")
113
+ parser.add_argument("--verbose", action="store_true", help="Verbose logging")
114
+ args = parser.parse_args()
115
+
116
+ configure_logging(args.verbose)
117
+ base_url = get_base_url(args.base_url)
118
+ image_path = args.image
119
+
120
+ if not os.path.exists(image_path):
121
+ logging.error("Image not found: %s", image_path)
122
+ return 1
123
+
124
+ if not args.skip_wait:
125
+ try:
126
+ wait_for_model(base_url, timeout_seconds=600)
127
+ except Exception as e:
128
+ logging.warning("Continuing despite health wait failure: %s", str(e))
129
+
130
+ auth_bearer = args.auth.strip() or None
131
+ app_check = args.app_check.strip() or None
132
+
133
+ try:
134
+ upload_resp = upload_image(base_url, image_path, auth_bearer, app_check)
135
+ except Exception as e:
136
+ logging.error("Upload error: %s", str(e))
137
+ return 1
138
+
139
+ try:
140
+ colorize_resp = colorize_image(base_url, image_path, auth_bearer, app_check)
141
+ except Exception as e:
142
+ logging.error("Colorize error: %s", str(e))
143
+ return 1
144
+
145
+ result_id = colorize_resp.get("result_id")
146
+ if not result_id:
147
+ logging.error("No result_id in response: %s", json.dumps(colorize_resp))
148
+ return 1
149
+
150
+ try:
151
+ download_result(base_url, result_id, args.out, auth_bearer, app_check)
152
+ except Exception as e:
153
+ logging.error("Download error: %s", str(e))
154
+ return 1
155
+
156
+ logging.info("Test workflow completed successfully.")
157
+ return 0
158
 
159
 
160
  if __name__ == "__main__":
161
+ sys.exit(main())
162