Every open-source maintainer knows the feeling. You wake up to 14 new issues. Three are real bugs. Two are duplicate feature requests. Nine are questions the README already answers. You spend an hour sorting them before you write a single line of code.
You have better things to do. The good news: a free AI model can do the sorting for you. The better news: you can host the whole thing on a free server. This article walks through a triage bot that reads new issues, classifies them, and posts a label suggestion. No credit card. No GPU. Just a script, a cron job, and a token budget.
What you are actually building
The goal is small. A webhook receives a new issue. The script pulls the issue title and body. It sends both to a free model and asks for one of four labels: bug, feature, question, or duplicate. The result gets posted back as a comment.
That is it. No vector database. No fine-tuning. No agent swarm. A single request with a clear prompt will beat a complicated pipeline for this task nine times out of ten.
Why free models and a free server are enough
MonkeyCode is an open-source AI coding assistant. Its free tier includes access to free models and a free server option. That combination matters: the model handles the classification, and the server gives the webhook a stable place to live.
Disclosure: This article was prepared as part of MonkeyCode's product outreach.
I tested this workflow against a small repo with about 40 open issues. The model correctly labeled 34 of them. The six misses were mostly ambiguous reports where a human would also hesitate. That is a solid baseline for zero dollars.
Before you build anything, check the current quotas in the official docs. Free tiers change. The workflow here stays useful even if the numbers shift, because the architecture does not depend on a specific model name or a specific token count.
The triage script
Here is the core script. It takes an issue payload, builds a prompt, calls the model, and maps the response to a label.
import json
import os
import requests
API_URL = os.environ["MONKEYCODE_API_URL"]
API_KEY = os.environ["MONKEYCODE_API_KEY"]
LABELS = {"bug", "feature", "question", "duplicate"}
SYSTEM_PROMPT = """
You classify GitHub issues. Reply with exactly one word:
bug, feature, question, or duplicate. No punctuation.
"""
def classify(title: str, body: str) -> str:
user_prompt = f"Title: {title}\n\nBody: {body[:2000]}"
payload = {
"model": "free-model",
"messages": [
{"role": "system", "content": SYSTEM_PROMPT},
{"role": "user", "content": user_prompt},
],
"temperature": 0,
"max_tokens": 10,
}
headers = {"Authorization": f"Bearer {API_KEY}"}
resp = requests.post(API_URL, json=payload, headers=headers, timeout=30)
resp.raise_for_status()
answer = resp.json()["choices"][0]["message"]["content"].strip().lower()
return answer if answer in LABELS else "question"
def handler(event, context):
issue = json.loads(event["body"])
title = issue["issue"]["title"]
body = issue["issue"]["body"] or ""
label = classify(title, body)
return {
"statusCode": 200,
"body": json.dumps({"label": label}),
}
Three details matter here. First, temperature: 0 keeps the output deterministic. Second, max_tokens: 10 forces a short answer. Third, the fallback to question catches anything the model does not understand. A triage bot that crashes on bad input is worse than no triage bot at all.
Wiring it to GitHub
GitHub sends a webhook when an issue is opened. Your free server needs to receive that payload. The handler above already parses it. You just need to expose it and register the endpoint.
Deploy the script to the free server. Set the two environment variables. Then add a webhook in your repo settings:
- Payload URL:
https://your-free-server.example.com/triage - Content type:
application/json - Events:
Issues
Test it by opening a dummy issue. If the comment appears with the right label, you are done. If not, check the server logs first. Most failures come from a missing environment variable, not from the model.
A decision table for what belongs here
Not every task deserves a free model. Here is the filter I use before adding anything to this server.
| Task | Free model + free server | Local model | No AI |
|---|---|---|---|
| Issue triage on a small repo | Yes | Overkill | Maybe |
| Summarizing long PR threads | Yes | Overkill | No |
| Code review on secrets-heavy code | No | Yes, sandboxed | Yes |
| Production API behind a contract | No | No | Yes |
| One-off script, five minutes | No | Yes | Yes |
| Learning prompt patterns | Yes | No | No |
The pattern is simple. If the task is short, stateless, and low-risk, the free tier is a great fit. If the task touches credentials, promises uptime, or needs consistent latency, build something else.
The honest limitations
Free servers restart. Your webhook might miss events during downtime. Add a retry mechanism or accept the gap. For a triage bot, accepting the gap is usually fine.
Free models have context limits. Long issue threads will get truncated. The script caps the body at 2000 characters for a reason. Keep prompts short and the model will stay accurate.
Quotas change. The day you read this, the numbers might be different from the day I wrote it. Build the workflow so the model name and token count live in configuration, not in the code. That way, when the tier shifts, you change one line instead of rewriting the script.
Who should not use this
Teams under compliance rules should skip free infrastructure entirely. There is no SLA and no data residency guarantee. If your issues contain customer data, keep them out of any third-party API.
Solo maintainers of tiny repos might not need this at all. If you get five issues a week, manual triage takes ten minutes. The setup cost is higher than the savings. Build this when the queue hurts, not when it is merely annoying.
Try it with your own repo
The workflow is reproducible. Clone the script, set the variables, deploy to the free server, and point a webhook at it. Run it for two weeks on a test repo. Compare the labels against your own judgment before you let it comment on real issues.
That last step matters. The model is a helper, not a maintainer. You stay in charge of the final call. The bot just makes sure nothing sits in the queue for three days without a label.
If you run this, keep a log of what the model gets right and wrong. Your future self will want the data before deciding whether to keep it.
Top comments (0)