xce009 commited on
Commit
feefeb8
·
1 Parent(s): c28ae12
Files changed (6) hide show
  1. app/api/server.py +0 -16
  2. app/config.py +1 -1
  3. app/core/banner.py +0 -20
  4. app/core/logger.py +10 -59
  5. requirements.txt +0 -2
  6. start.sh +1 -63
app/api/server.py CHANGED
@@ -1,24 +1,18 @@
1
  from __future__ import annotations
2
 
3
  import asyncio
4
- import os
5
- import time
6
  from contextlib import asynccontextmanager
7
- from datetime import datetime, timezone
8
 
9
  from fastapi import FastAPI
10
  from fastapi.middleware.cors import CORSMiddleware
11
  from fastapi.middleware.gzip import GZipMiddleware
12
 
13
  from app.config import get_settings
14
- from app.core.banner import print_banner
15
- from app.core.constants import SUPPORTED_EXTENSIONS
16
  from app.core.logger import get_logger
17
  from app.api.v1.router import api_v1_router
18
 
19
  _logger = get_logger(__name__)
20
  _settings = get_settings()
21
- _START_TIME = time.time()
22
 
23
 
24
  async def _self_ping():
@@ -39,15 +33,8 @@ async def _self_ping():
39
 
40
  @asynccontextmanager
41
  async def lifespan(app: FastAPI):
42
- print_banner()
43
- _logger.info("Server ready at http://%s:%s", _settings.host, _settings.port)
44
- _logger.info("Swagger UI : http://%s:%s/docs", _settings.host, _settings.port)
45
- _logger.info("ReDoc : http://%s:%s/redoc", _settings.host, _settings.port)
46
- _logger.info("Started at : %s", datetime.now(timezone.utc).strftime("%Y-%m-%d %H:%M:%S UTC"))
47
  asyncio.create_task(_self_ping())
48
- _logger.info("Self-ping task started (every hour)")
49
  yield
50
- _logger.info("Application shutting down gracefully")
51
 
52
 
53
  def create_application() -> FastAPI:
@@ -83,9 +70,6 @@ def create_application() -> FastAPI:
83
  async def ping():
84
  return {"message": f"{_settings.app_name} is running..."}
85
 
86
- _max_workers = min(32, (os.cpu_count() or 1) + 4)
87
- _logger.info("Initialized thread pool with %s workers", _max_workers)
88
-
89
  return app
90
 
91
 
 
1
  from __future__ import annotations
2
 
3
  import asyncio
 
 
4
  from contextlib import asynccontextmanager
 
5
 
6
  from fastapi import FastAPI
7
  from fastapi.middleware.cors import CORSMiddleware
8
  from fastapi.middleware.gzip import GZipMiddleware
9
 
10
  from app.config import get_settings
 
 
11
  from app.core.logger import get_logger
12
  from app.api.v1.router import api_v1_router
13
 
14
  _logger = get_logger(__name__)
15
  _settings = get_settings()
 
16
 
17
 
18
  async def _self_ping():
 
33
 
34
  @asynccontextmanager
35
  async def lifespan(app: FastAPI):
 
 
 
 
 
36
  asyncio.create_task(_self_ping())
 
37
  yield
 
38
 
39
 
40
  def create_application() -> FastAPI:
 
70
  async def ping():
71
  return {"message": f"{_settings.app_name} is running..."}
72
 
 
 
 
73
  return app
74
 
75
 
app/config.py CHANGED
@@ -13,7 +13,7 @@ class Settings(BaseSettings):
13
  extra="ignore",
14
  )
15
 
16
- app_name: str = "reconciliation-non-ai-extractor"
17
  app_version: str = "1.0.0"
18
  environment: str = "production"
19
  host: str = "0.0.0.0"
 
13
  extra="ignore",
14
  )
15
 
16
+ app_name: str = "MarkItDown API"
17
  app_version: str = "1.0.0"
18
  environment: str = "production"
19
  host: str = "0.0.0.0"
app/core/banner.py CHANGED
@@ -1,21 +1 @@
1
- from __future__ import annotations
2
 
3
- import pyfiglet
4
- from rich.console import Console
5
- from rich.rule import Rule
6
- from rich.text import Text
7
-
8
- from app.config import get_settings
9
-
10
- _console = Console()
11
- _settings = get_settings()
12
-
13
-
14
- def print_banner() -> None:
15
- art = pyfiglet.figlet_format("ValidOps", font="slant")
16
- _console.print(Text(art, style="bold cyan"))
17
- _console.print(f" [bold white]{'Service:':<14}[/bold white] [cyan]{_settings.app_name}[/cyan]")
18
- _console.print(f" [bold white]{'Version:':<14}[/bold white] [cyan]v{_settings.app_version}[/cyan]")
19
- _console.print(f" [bold white]{'Environment:':<14}[/bold white] [cyan]{_settings.environment}[/cyan]")
20
- _console.print(Rule(style="dim cyan"))
21
- _console.print()
 
 
1
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
app/core/logger.py CHANGED
@@ -2,65 +2,16 @@ from __future__ import annotations
2
 
3
  import logging
4
  import sys
5
- from pathlib import Path
6
- from typing import Optional
7
-
8
- from rich.console import Console
9
- from rich.logging import RichHandler
10
-
11
- from app.config import get_settings
12
-
13
- _settings = get_settings()
14
- _console = Console()
15
-
16
-
17
- class AppLogger:
18
- def __init__(self) -> None:
19
- self._loggers: dict[str, logging.Logger] = {}
20
-
21
- def get_logger(self, name: str) -> logging.Logger:
22
- if name in self._loggers:
23
- return self._loggers[name]
24
-
25
- logger = logging.getLogger(name)
26
- logger.setLevel(getattr(logging, _settings.log_level.upper(), logging.INFO))
27
- logger.handlers.clear()
28
- logger.propagate = False
29
-
30
- rich_handler = RichHandler(
31
- console=_console,
32
- rich_tracebacks=True,
33
- show_path=False,
34
- show_time=True,
35
- )
36
- rich_handler.setFormatter(logging.Formatter("%(message)s"))
37
- logger.addHandler(rich_handler)
38
-
39
- file_handler = self._build_file_handler()
40
- if file_handler:
41
- logger.addHandler(file_handler)
42
-
43
- self._loggers[name] = logger
44
- return logger
45
-
46
- def _build_file_handler(self) -> Optional[logging.FileHandler]:
47
- log_dir = Path("logs")
48
- log_dir.mkdir(parents=True, exist_ok=True)
49
- file_handler = logging.FileHandler(
50
- log_dir / f"{_settings.app_name}.log",
51
- encoding="utf-8",
52
- )
53
- file_handler.setFormatter(
54
- logging.Formatter(
55
- fmt="%(asctime)s | %(levelname)-8s | %(name)s | %(message)s",
56
- datefmt="%Y-%m-%d %H:%M:%S",
57
- )
58
- )
59
- return file_handler
60
-
61
-
62
- _logger_factory = AppLogger()
63
 
64
 
65
  def get_logger(name: str) -> logging.Logger:
66
- return _logger_factory.get_logger(name)
 
 
 
 
 
 
 
 
 
 
2
 
3
  import logging
4
  import sys
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5
 
6
 
7
  def get_logger(name: str) -> logging.Logger:
8
+ logger = logging.getLogger(name)
9
+ if not logger.handlers:
10
+ logger.setLevel(logging.INFO)
11
+ handler = logging.StreamHandler(sys.stdout)
12
+ handler.setFormatter(logging.Formatter(
13
+ fmt="%(asctime)s | %(levelname)-8s | %(name)s | %(message)s",
14
+ datefmt="%Y-%m-%d %H:%M:%S",
15
+ ))
16
+ logger.addHandler(handler)
17
+ return logger
requirements.txt CHANGED
@@ -5,8 +5,6 @@ pydantic>=2.7.0
5
  pydantic-settings>=2.0.0
6
  python-multipart>=0.0.9
7
  httpx>=0.27.0
8
- pyfiglet>=1.0.0
9
- rich>=13.7.0
10
  numpy>=1.26.0
11
  rapidocr-onnxruntime>=1.4.4
12
  onnxruntime>=1.18.0
 
5
  pydantic-settings>=2.0.0
6
  python-multipart>=0.0.9
7
  httpx>=0.27.0
 
 
8
  numpy>=1.26.0
9
  rapidocr-onnxruntime>=1.4.4
10
  onnxruntime>=1.18.0
start.sh CHANGED
@@ -1,75 +1,13 @@
1
  #!/usr/bin/env bash
2
  set -euo pipefail
3
 
4
- LOG_DIR="${LOG_DIR:-/app/logs}"
5
- mkdir -p "$LOG_DIR"
6
- LOG_FILE="$LOG_DIR/startup.log"
7
-
8
- log() {
9
- local level="$1"
10
- shift
11
- local ts
12
- ts="$(date -u '+%Y-%m-%dT%H:%M:%SZ')"
13
- local msg="[$ts] [$level] $*"
14
- echo "$msg"
15
- echo "$msg" >> "$LOG_FILE"
16
- }
17
-
18
- info() { log "INFO " "$@"; }
19
- ok() { log "OK " "$@"; }
20
- warn() { log "WARN " "$@"; }
21
- err() { log "ERROR" "$@"; }
22
- step() { echo ""; log "STEP " "──── $* ────"; }
23
-
24
- info "========================================================"
25
- info " Reconciliation Non-AI Extractor v1.0.0 — Production Startup"
26
- info " $(date -u)"
27
- info "========================================================"
28
-
29
- step "1/2 Python dependencies"
30
-
31
- if python -c "import markitdown, fastapi, httpx" 2>/dev/null; then
32
- ok "Core dependencies verified."
33
- else
34
- err "Missing core dependencies. Check requirements.txt installation."
35
- exit 1
36
- fi
37
-
38
- step "2/2 Core functionality check"
39
-
40
- python - << 'PYEOF' >> "$LOG_FILE" 2>&1
41
- import sys
42
- print("[functionality-test] Testing core imports...")
43
- try:
44
- import markitdown
45
- import fastapi
46
- import httpx
47
- import pandas
48
- print("[functionality-test] All core dependencies imported successfully.")
49
- except ImportError as e:
50
- print(f"[functionality-test] Import error: {e}")
51
- sys.exit(1)
52
- print("[functionality-test] Core functionality check completed.")
53
- PYEOF
54
-
55
- if [ $? -eq 0 ]; then
56
- ok "Core functionality check completed."
57
- else
58
- warn "Core functionality check failed. Continuing startup..."
59
- fi
60
-
61
- step "Starting FastAPI server"
62
 
63
  HOST="${HOST:-0.0.0.0}"
64
  PORT="${PORT:-7860}"
65
  WORKERS="${WORKERS:-1}"
66
  LOG_LEVEL="${LOG_LEVEL:-info}"
67
 
68
- info "Starting uvicorn on $HOST:$PORT (workers=$WORKERS, log_level=$LOG_LEVEL) ..."
69
- info "Swagger UI : http://$HOST:$PORT/docs"
70
- info "ReDoc : http://$HOST:$PORT/redoc"
71
- info "========================================================"
72
-
73
  exec python -m uvicorn app.api.server:app \
74
  --host "$HOST" \
75
  --port "$PORT" \
 
1
  #!/usr/bin/env bash
2
  set -euo pipefail
3
 
4
+ python -c "import markitdown, fastapi, httpx, pandas" 2>/dev/null || { echo "Missing dependencies"; exit 1; }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5
 
6
  HOST="${HOST:-0.0.0.0}"
7
  PORT="${PORT:-7860}"
8
  WORKERS="${WORKERS:-1}"
9
  LOG_LEVEL="${LOG_LEVEL:-info}"
10
 
 
 
 
 
 
11
  exec python -m uvicorn app.api.server:app \
12
  --host "$HOST" \
13
  --port "$PORT" \