forked from LiveCarta/CommentAutomation
Added settings module
This commit is contained in:
@@ -9,6 +9,7 @@ dependencies = [
|
||||
"fastapi[standard]>=0.116.0",
|
||||
"httpx>=0.28.1",
|
||||
"pydantic>=2.11.0",
|
||||
"pydantic-settings>=2.6.1",
|
||||
"uplink>=0.9.7",
|
||||
]
|
||||
|
||||
|
||||
@@ -1,11 +1,16 @@
|
||||
import os
|
||||
|
||||
from celery import Celery
|
||||
|
||||
from comment_automation.logging_config import configure_logging, get_celery_logger
|
||||
from comment_automation.settings import get_settings
|
||||
|
||||
configure_logging()
|
||||
logger = get_celery_logger()
|
||||
settings = get_settings()
|
||||
|
||||
celery_app = Celery(
|
||||
"comment_automation",
|
||||
broker=os.getenv("CELERY_BROKER_URL", "redis://localhost:6379/0"),
|
||||
backend=os.getenv("CELERY_RESULT_BACKEND", "redis://localhost:6379/0"),
|
||||
broker=settings.celery_broker_url,
|
||||
backend=settings.celery_result_backend,
|
||||
include=["comment_automation.webhooks.tasks"],
|
||||
)
|
||||
|
||||
@@ -16,3 +21,5 @@ celery_app.conf.update(
|
||||
timezone="UTC",
|
||||
enable_utc=True,
|
||||
)
|
||||
|
||||
logger.info("Celery app configured")
|
||||
|
||||
46
src/comment_automation/logging_config.py
Normal file
46
src/comment_automation/logging_config.py
Normal file
@@ -0,0 +1,46 @@
|
||||
import logging
|
||||
from logging.config import dictConfig
|
||||
|
||||
from comment_automation.settings import get_settings
|
||||
|
||||
|
||||
def configure_logging() -> None:
|
||||
settings = get_settings()
|
||||
|
||||
dictConfig(
|
||||
{
|
||||
"version": 1,
|
||||
"disable_existing_loggers": False,
|
||||
"formatters": {
|
||||
"standard": {
|
||||
"format": "%(asctime)s | %(levelname)s | %(name)s | %(message)s"
|
||||
}
|
||||
},
|
||||
"handlers": {
|
||||
"console": {
|
||||
"class": "logging.StreamHandler",
|
||||
"formatter": "standard",
|
||||
}
|
||||
},
|
||||
"loggers": {
|
||||
"comment_automation.http": {
|
||||
"handlers": ["console"],
|
||||
"level": settings.http_log_level,
|
||||
"propagate": False,
|
||||
},
|
||||
"comment_automation.celery": {
|
||||
"handlers": ["console"],
|
||||
"level": settings.celery_log_level,
|
||||
"propagate": False,
|
||||
},
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
def get_http_logger() -> logging.Logger:
|
||||
return logging.getLogger("comment_automation.http")
|
||||
|
||||
|
||||
def get_celery_logger() -> logging.Logger:
|
||||
return logging.getLogger("comment_automation.celery")
|
||||
@@ -1,8 +1,12 @@
|
||||
from fastapi import FastAPI
|
||||
from pydantic import BaseModel
|
||||
|
||||
from comment_automation.logging_config import configure_logging, get_http_logger
|
||||
from comment_automation.webhooks.app import webhooks_app
|
||||
|
||||
configure_logging()
|
||||
logger = get_http_logger()
|
||||
|
||||
app = FastAPI(title="CommentAutomation")
|
||||
app.mount("/webhooks", webhooks_app)
|
||||
|
||||
@@ -13,4 +17,5 @@ class HealthResponse(BaseModel):
|
||||
|
||||
@app.get("/health", response_model=HealthResponse)
|
||||
def health() -> HealthResponse:
|
||||
logger.debug("Health check requested")
|
||||
return HealthResponse(status="ok")
|
||||
|
||||
29
src/comment_automation/settings.py
Normal file
29
src/comment_automation/settings.py
Normal file
@@ -0,0 +1,29 @@
|
||||
from pydantic import Field
|
||||
from pydantic_settings import BaseSettings, SettingsConfigDict
|
||||
|
||||
|
||||
class AppSettings(BaseSettings):
|
||||
model_config = SettingsConfigDict(extra="ignore")
|
||||
|
||||
celery_broker_url: str = Field(
|
||||
default="redis://localhost:6379/0", alias="CELERY_BROKER_URL"
|
||||
)
|
||||
celery_result_backend: str = Field(
|
||||
default="redis://localhost:6379/0",
|
||||
alias="CELERY_RESULT_BACKEND",
|
||||
)
|
||||
|
||||
llm_endpoint_url: str | None = Field(default=None, alias="LLM_ENDPOINT_URL")
|
||||
llm_endpoint_api_key: str | None = Field(default=None, alias="LLM_ENDPOINT_API_KEY")
|
||||
llm_endpoint_timeout_seconds: float = Field(
|
||||
default=10.0,
|
||||
alias="LLM_ENDPOINT_TIMEOUT_SECONDS",
|
||||
gt=0,
|
||||
)
|
||||
|
||||
http_log_level: str = Field(default="INFO", alias="HTTP_LOG_LEVEL")
|
||||
celery_log_level: str = Field(default="INFO", alias="CELERY_LOG_LEVEL")
|
||||
|
||||
|
||||
def get_settings() -> AppSettings:
|
||||
return AppSettings()
|
||||
@@ -1,9 +1,11 @@
|
||||
import os
|
||||
from typing import Any
|
||||
|
||||
import httpx
|
||||
from celery import shared_task
|
||||
|
||||
from comment_automation.logging_config import get_celery_logger
|
||||
from comment_automation.settings import get_settings
|
||||
|
||||
|
||||
class LLMEndpointTemporaryError(Exception):
|
||||
"""Raised when the LLM endpoint fails with a transient/retryable error."""
|
||||
@@ -13,6 +15,9 @@ class LLMEndpointConfigurationError(Exception):
|
||||
"""Raised when required LLM endpoint configuration is missing."""
|
||||
|
||||
|
||||
logger = get_celery_logger()
|
||||
|
||||
|
||||
@shared_task(
|
||||
bind=True,
|
||||
name="comment_automation.tasks.send_instagram_comment_to_llm",
|
||||
@@ -33,18 +38,21 @@ def send_instagram_comment_to_llm(self, payload: dict[str, Any]) -> None:
|
||||
|
||||
def _forward_payload_to_llm(payload: dict[str, Any]) -> None:
|
||||
"""Send payload to LLM endpoint, mapping transient failures to retries."""
|
||||
endpoint_url = os.getenv("LLM_ENDPOINT_URL")
|
||||
settings = get_settings()
|
||||
|
||||
endpoint_url = settings.llm_endpoint_url
|
||||
if not endpoint_url:
|
||||
raise LLMEndpointConfigurationError("LLM_ENDPOINT_URL is not configured")
|
||||
|
||||
api_key = os.getenv("LLM_ENDPOINT_API_KEY")
|
||||
timeout = float(os.getenv("LLM_ENDPOINT_TIMEOUT_SECONDS", "10"))
|
||||
api_key = settings.llm_endpoint_api_key
|
||||
timeout = settings.llm_endpoint_timeout_seconds
|
||||
|
||||
headers = {"Content-Type": "application/json"}
|
||||
if api_key:
|
||||
headers["Authorization"] = f"Bearer {api_key}"
|
||||
|
||||
try:
|
||||
logger.debug("Sending webhook payload to LLM endpoint")
|
||||
response = httpx.post(
|
||||
endpoint_url,
|
||||
json=payload,
|
||||
@@ -52,11 +60,13 @@ def _forward_payload_to_llm(payload: dict[str, Any]) -> None:
|
||||
timeout=timeout,
|
||||
)
|
||||
except (httpx.TimeoutException, httpx.NetworkError) as exc:
|
||||
logger.warning("Transient network failure while calling LLM endpoint")
|
||||
raise LLMEndpointTemporaryError(
|
||||
"Temporary network issue while calling LLM endpoint"
|
||||
) from exc
|
||||
|
||||
if response.status_code in {429, 500, 502, 503, 504}:
|
||||
logger.warning("Retryable response from LLM endpoint: %s", response.status_code)
|
||||
raise LLMEndpointTemporaryError(
|
||||
f"Retryable LLM endpoint response status: {response.status_code}"
|
||||
)
|
||||
|
||||
2
uv.lock
generated
2
uv.lock
generated
@@ -305,6 +305,7 @@ dependencies = [
|
||||
{ name = "fastapi", extra = ["standard"] },
|
||||
{ name = "httpx" },
|
||||
{ name = "pydantic" },
|
||||
{ name = "pydantic-settings" },
|
||||
{ name = "uplink" },
|
||||
]
|
||||
|
||||
@@ -326,6 +327,7 @@ requires-dist = [
|
||||
{ name = "fastapi", extras = ["standard"], specifier = ">=0.116.0" },
|
||||
{ name = "httpx", specifier = ">=0.28.1" },
|
||||
{ name = "pydantic", specifier = ">=2.11.0" },
|
||||
{ name = "pydantic-settings", specifier = ">=2.6.1" },
|
||||
{ name = "uplink", specifier = ">=0.9.7" },
|
||||
]
|
||||
|
||||
|
||||
Reference in New Issue
Block a user