DEV Community

Sam Li
Sam Li

Posted on

A Tiny CI Guard That Lets a Free AI Model Read Your Build Failures

Disclosure: This article was prepared as part of MonkeyCode's product outreach.

Every CI pipeline I have ever maintained eventually turns into a wall of unread logs. The build fails at 2 AM, the notification fires, and the first thing you do is scroll past forty lines of npm ERR! to find the one sentence that actually matters. I wanted a guard that would read those failures for me, tag them, and suggest a likely fix before I even opened the terminal. So I built a small script around MonkeyCode, an open-source coding gateway that also offers free model access and a free server option.

Before you ask: yes, the free tier was enough for this job. I set up a minimal Node.js service that watches a CI output file, extracts the error block, and sends it to MonkeyCode's OpenAI-compatible endpoint. The free server handled the request without any payment or provisioning. I only needed an endpoint and an API key from the dashboard. The entire cost was my time.

The core idea is simple: instead of logging raw stderr, route the error text through a classifier that returns a structured summary. Here is the part of the script that does the heavy lifting:

import { readFile } from 'node:fs/promises';

const MONKEYCODE_ENDPOINT = process.env.MONKEYCODE_ENDPOINT;
const MONKEYCODE_API_KEY = process.env.MONKEYCODE_API_KEY;

export async function classifyBuildError(rawLog, context) {
  const response = await fetch(`${MONKEYCODE_ENDPOINT}/v1/chat/completions`, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'Authorization': `Bearer ${MONKEYCODE_API_KEY}`
    },
    body: JSON.stringify({
      model: 'default',
      messages: [
        { role: 'system', content:
          'You are a CI log analyst. Return a JSON object with keys: ' +
          'errorType, confidence, suggestedFix. errorType must be one of: ' +
          'syntax, type, dependency, resource, permission, network, unknown.' },
        { role: 'user', content: `Context: ${context}\n\nRaw log:\n${rawLog}` }
      ],
      temperature: 0
    })
  });

  const data = await response.json();
  return JSON.parse(data.choices[0].message.content);
}
Enter fullscreen mode Exit fullscreen mode

That function is the heart of the guard. I feed it the last two hundred lines of the failing step plus the project name and the branch. The model returns a JSON object, which I then pipe into a small reporter that prints a single line like [type] confidence=0.83 fix=Add null check before calling .map. The whole pipeline runs in under five seconds, and the free model handled more than a hundred classification requests during my test week without hitting a limit.

To decide whether the suggestion was trustworthy, I used the confidence score to route the output. The decision table I settled on looks like this:

Confidence Action taken
>= 0.80 Show suggested fix and auto-open the relevant file
0.50 - 0.79 Show error type and ask the developer to confirm
< 0.50 Fall back to raw log excerpt, no AI suggestion

That table comes from experience. When the model was unsure, it often produced plausible-sounding advice that was wrong. Trusting only the high-confidence results kept the tool useful without turning it into yet another source of noise.

I also added a retry loop for malformed JSON responses. Sometimes the model would wrap the output in markdown fences or add extra commentary. A simple regex extracted the JSON before parsing, and only after three failed attempts did the script give up and print the raw log. That small resilience step made the tool feel much more reliable.

The real test came on a legacy repository where the build failed for three different reasons over the course of a week. The guard correctly identified a missing dependency in the first failure, a permission issue in the second, and a syntax error in the third. It did not fix them, but it cut my triage time from roughly ten minutes to two. That is the value I care about: not replacing the developer, but removing the boring part of failure investigation.

There are clear limitations. Free model access is a shared resource, so latency spikes under heavy load. The free server is not meant for production traffic, so I only ran the guard locally on demand rather than as a permanent CI service. The model can also misclassify errors when the log is truncated or when the relevant error is hidden deep in a minified stack trace. I handled the truncation case by adding a context argument with the last known build step, which improved accuracy noticeably.

You should not use this technique if your CI logs contain secrets, customer data, or other sensitive information. Sending logs to a third-party API means you are sharing that content with the model provider, and free tiers rarely promise enterprise-grade privacy. For public open-source builds, though, the trade-off is comfortable.

If you are tired of reading the same five error messages every day, the pattern here is worth stealing. Take your worst log, write a classifier around MonkeyCode's free model access, and let the free server run it a few times. You will quickly learn which errors need your brain and which ones just need a script.

Top comments (0)