Adding Automated Email Alerts for Deprecated Groq Models in content‑automation
TL;DR: I added a watchdog that detects when a Groq model is deprecated, composes a concise email with the replacement list and a link to the required GitHub Secrets, and sends it through our existing SMTP helper. The change prevents silent failures in the content‑generation pipeline and gives the team a clear migration path.
The Problem
Our daily content‑automation job pulls the latest AI model from Groq’s public endpoint before generating newsletters, blog posts, and social‑media snippets. On 2026‑08‑03 the endpoint started returning a 404 for the previously‑used gpt‑oss‑20b model:
requests.exceptions.HTTPError: 404 Client Error: Not Found for url https://api.groq.com/v1/models/gpt-oss-20b
The exception bubbled up to the scheduler, causing the whole pipeline to abort. No alert was raised, so the team only noticed the issue when the next day’s newsletter was missing. The root cause was that we had no visibility when a model became unavailable.
What I Tried First
My first instinct was to wrap the request in a generic try/except and log the error to CloudWatch. I added:
try:
response = requests.get(model_url)
response.raise_for_status()
except Exception as e:
logger.error("Model fetch failed: %s", e)
That gave us a stack trace, but it didn’t solve the operational pain point: we still had to manually check logs, figure out which model was gone, and then edit the config file. I also tried a quick‑and‑dirty “fallback to the next model in the list” inside src/content_generator.py, but the fallback logic was brittle—different prompts required different model capabilities, and blindly switching could degrade output quality.
Result: the pipeline still failed silently for the team, and the fallback introduced subtle bugs in downstream content generation.
The Implementation
1. Centralising model metadata
I introduced a new private method _list_available_models (already present) that now returns a typed list of strings and catches any unexpected errors:
def _list_available_models(self) -> list[str]:
try:
# Pull the model catalog from Groq’s public endpoint
resp = requests.get("https://api.groq.com/v1/models")
resp.raise_for_status()
return [m["id"] for m in resp.json()["data"]]
except Exception:
return []
2. Detecting deprecation
The core change lives in src/content_generator.py. I added a new helper _detect_deprecated_model that compares the configured model against the live catalog:
def _detect_deprecated_model(self, current_model: str) -> bool:
"""Return True if `current_model` is no longer listed by Groq."""
available = self._list_available_models()
return current_model not in available
3. Building the alert email
When a deprecation is detected, we now compose an email that includes:
- The name of the deprecated model.
- A bullet list of all currently available models.
- A direct link to the repository’s GitHub Secrets page where the new model key must be stored.
def _compose_deprecation_email(self, deprecated: str, available: list[str]) -> str:
models_md = "\n".join(f"- {m}" for m in available)
return f"""\
The model you are using (`{deprecated}`) is no longer available on Groq.
Available alternatives:
{models_md}
Please update the `GROQ_MODEL` secret in your GitHub repository:
https://github.com/your-org/content-automation/settings/secrets/actions
"""
4. Sending the email
I reused the existing SMTPHelper class (defined in src/email_utils.py) to actually send the message:
def _notify_deprecation(self, deprecated: str) -> None:
available = self._list_available_models()
body = self._compose_deprecation_email(deprecated, available)
smtp = SMTPHelper()
smtp.send(
to=self.alert_recipient,
subject=f"Groq model `{deprecated}` deprecated",
body=body,
)
5. Hooking into the generation flow
The generation entry point (generate_content) now checks for deprecation right after loading the config:
def generate_content(self):
model = self.config["groq_model"]
if self._detect_deprecated_model(model):
self._notify_deprecation(model)
raise RuntimeError(
f"Model `{model}` is deprecated. Alert sent to {self.alert_recipient}."
)
# …rest of the generation pipeline…
6. Updating CI/CD
I added a small unit test in tests/test_model_deprecation.py to assert that the email body contains the expected GitHub Secrets URL. The CI pipeline now fails if the email composition logic regresses.
def test_compose_deprecation_email():
gen = ContentGenerator()
body = gen._compose_deprecation_email(
"gpt-oss-20b", ["gpt-oss-120b", "gpt-oss-40b"]
)
assert "https://github.com/your-org/content-automation/settings/secrets/actions" in body
All changes are grouped under the commit 60fe07fc with the message:
feat(alert): email alert when Groq model deprecated — lists available models + GitHub Secrets link
The diff added 114 new lines and removed 24, primarily around the new detection and notification helpers.
Key Takeaway
Never assume that external services will stay stable forever. By turning a passive failure (404 on a model endpoint) into an active alert that includes actionable remediation steps, you eliminate the “it broke somewhere” debugging cycle and keep the pipeline self‑healing. The pattern—detect → compose → notify → abort—can be reused for any third‑party dependency that may be retired or version‑locked.
What's Next
-
Automatic fallback – Once we have a reliable list of compatible models, I’ll add a configurable fallback map (e.g.,
gpt‑oss‑20b → gpt‑oss‑120b) so the pipeline can continue without manual intervention when a model is deprecated. - Dashboard integration – Push the deprecation event to our internal status board via a webhook, giving the whole org a real‑time view.
-
Secret rotation automation – Write a GitHub Action that updates the
GROQ_MODELsecret automatically after a successful migration, closing the loop.
Tags: #vibecoding #buildinpublic #python #ai #automation #email #devops
Part of my Build in Public series — sharing the real process of building SaaS projects from Playa del Carmen, México.
Repo: zaerohell/content-automation · 2026-08-04
#playadev #buildinpublic
Top comments (0)