Free model access has made code generation cheap enough that the real bottleneck is no longer model cost. It is deciding which generated answers are allowed to become code.
The default review loop is paste a prompt, read the response, merge if it looks right. That treats a free model as a junior colleague with good intentions. A safer rule is to treat every model answer as a hypothesis: an unverified patch that has not earned the right to touch main.
A binary pass/fail gate is also not enough. A model can produce code that compiles and still be wrong in expensive ways. Instead, I score each answer with an error budget. Each kind of failure adds a penalty, and the total penalty decides whether the answer gets to the next step. The budget makes the acceptance threshold explicit instead of leaving it to whoever is in a hurry.
I use this with free model endpoints, including MonkeyCode's advertised free model access. MonkeyCode also advertises a free server option, which matters for the runner part below. Disclosure: This article was prepared as part of MonkeyCode's product outreach. The harness itself is provider-neutral and does not depend on MonkeyCode specifics.
The six checks
- Extract exactly one code block. A response with no code or with multiple competing implementations is not a patch; it is a conversation. Penalty 5.
-
Parse the code. For Python,
ast.parse; for another language, call its compiler. Syntax that does not parse is an immediate reject. Penalty 5. -
Allowlist imports. A small helper that reads numbers should not be importing
os,subprocess,eval, or dynamic loaders. Penalty 3. - Check the observable behavior. Run the generated code in a fresh process with a timeout and compare stdout to the expected output. This is not a full test suite, just a smoke test. Penalty 2 per failing case.
- Spot nondeterminism. Run the same smoke test twice. A program that changes its output with the same input is not ready for review. Penalty 2.
- Check side effects later. The first five checks are cheap. Manual review still owns anything that writes files, touches the network, or mutates shared state.
The penalty values are not research metrics. They are starting numbers that make failures easy to rank and tune.
A runnable harness
The script below runs without an API key by using a stub client, so you can reproduce the scoring flow before plugging in any free model endpoint.
import ast
import re
import subprocess
import tempfile
from dataclasses import dataclass
from pathlib import Path
@dataclass
class Task:
name: str
prompt: str
allowed_imports: set
input_data: str
expected_output: str
snippet: str # model response with a Markdown code block
@dataclass
class Verdict:
penalties: dict
total: int
accepted: bool
BUDGET = 3
def extract_code(text: str) -> str:
match = re.search(r'```
(?:python)?\n(.*?)
```', text, re.S)
return match.group(1).strip() if match else ''
def parse_penalty(code: str) -> int:
try:
ast.parse(code)
return 0
except SyntaxError:
return 5
def import_penalty(code: str, allowed: set) -> int:
tree = ast.parse(code)
used = set()
for node in ast.walk(tree):
if isinstance(node, ast.Import):
used.update(alias.name.split('.')[0] for alias in node.names)
elif isinstance(node, ast.ImportFrom):
used.add(node.module.split('.')[0] if node.module else '')
return 3 if used - allowed else 0
def behavior_penalty(code: str, input_data: str, expected: str) -> int:
with tempfile.TemporaryDirectory() as d:
path = Path(d) / 'solution.py'
path.write_text(code)
try:
result = subprocess.run(
['python', str(path)],
input=input_data,
text=True,
capture_output=True,
timeout=2,
)
except subprocess.TimeoutExpired:
return 4
return 0 if result.stdout.strip() == expected.strip() else 2
def score(task: Task) -> Verdict:
code = extract_code(task.snippet)
penalties = {'extract': 5 if not code else 0}
if code:
penalties['parse'] = parse_penalty(code)
penalties['imports'] = import_penalty(code, task.allowed_imports)
penalties['behavior'] = behavior_penalty(
code, task.input_data, task.expected_output
)
total = sum(penalties.values())
return Verdict(penalties, total, total <= BUDGET)
if __name__ == '__main__':
demo = Task(
name='sum-two-numbers',
prompt='Write a Python program that reads two integers and prints their sum.',
allowed_imports={'sys'},
input_data='5\n7\n',
expected_output='12',
snippet='''```
python
import sys
a, b = map(int, sys.stdin.read().split())
print(a + b)
```''',
)
verdict = score(demo)
print(verdict)
Replace the demo snippet with the raw response from your model provider. In a real run, the behavior_penalty call should happen inside a separate container or a fresh child process, because subprocess.run is still running on the same machine. A clean runner prevents persisted files, cached environment variables, or a warm working directory from influencing the verdict.
That is where a free server option is useful. If the server can run a small container, put the judge there instead of on a developer laptop. The point is not cloud magic; it is that the acceptance check should run from the same clean state every time. MonkeyCode advertises both free model access and a free server option, so I would place the judge on the server side if that option supports a containerized runner. Do not assume it does; verify the server capability before relying on it.
Limitations
The harness checks observable smoke behavior, not correctness. It can accept code that has a subtle bug outside the smoke case, and it can reject good code that does not fit the narrow prompt format. The import allowlist and timeout values are policy choices, not universal truths. A budget of three is useful for a tutorial but needs tuning per project and per task.
It is also not a security boundary. Running unknown generated code in a shared environment is dangerous even with a timeout. Use a disposable sandbox, limit network access, and never give the process credentials or a writable production directory.
Do not use this when the task is subjective, when there is no deterministic expected output, or when you need a hard guarantee before execution. In those cases, use static analysis, a typed contract, or a human review before any model output runs.
The value is not that a free model becomes trustworthy. It is that the next merge decision stops being a feeling and becomes a scored, tunable check.
Top comments (0)