Three recipes for AI-assisted operations: triage server logs, benchmark LLM endpoints, and automate code review on KonsolScript source files.
Modules: curl plugin · File · Regex · String · List · JSON · Time · Konsol
Scans a structured log file, extracts every [ERROR] line with Regex:Groups, batches them into a single LLM prompt asking for root-cause analysis, and writes a Markdown report with the findings.
Key patterns:
Regex:Groups per line (same pattern as log_analyzer.ks) - separate INFO / WARN / ERROR countersList:Push each error entry; concatenate into one block for the LLM## Root Causes, ## Recommended Actions)Time:GetYear/Month/Day - add a generated-date header to the reportShips with server.log (15 realistic log entries including 5 errors).
Usage
minks log_triage.ks <api_key> <logfile> [report.md]
minks log_triage.ks sk-... server.log
minks log_triage.ks sk-... server.log incident_report.md
Sample output
=== AI Log Triage ===
Log summary - INFO: 8 WARN: 2 ERROR: 5
Sending 5 error(s) to LLM for analysis...
Report written to triage_report.md
## Root Causes
- Database connection pool exhaustion caused cascading timeouts...
- Disk space exhaustion on /var/data disabled the audit log subsystem...
## Recommended Actions
- Increase connection pool size or add read replicas...
Done.
Script
// log_triage.ks - scan a log file with Regex, send errors to LLM for analysis
// Modules: curl plugin, File, Regex, String, List, JSON, Konsol
// Usage: minks log_triage.ks <api_key> <logfile> [report.md]
//
// Reads a structured log, extracts every ERROR line with Regex, batches them
// into a single prompt, and asks the LLM to identify root causes and suggest
// fixes. Writes the analysis to a Markdown report.
//
// Expected log format: YYYY-MM-DD HH:MM:SS [LEVEL] message
// Sample log ships alongside this script: server.log
#include "curl"
Konsol:Print("=== AI Log Triage ===");
// ── Step 1: Read arguments ────────────────────────────────────────────────────
Var:List args;
OS:Args(args);
Var:Number argc;
List:Size(args, argc);
if (argc < 2) {
Konsol:Print("Usage: minks log_triage.ks <api_key> <logfile> [report.md]");
Konsol:Exit(1);
}
Var:String apiKey;
Var:String logPath;
List:Get(0, args, apiKey);
List:Get(1, args, logPath);
Var:String reportPath = "triage_report.md";
if (argc >= 3) {
List:Get(2, args, reportPath);
}
// ── Step 2: Scan the log and collect ERROR lines ──────────────────────────────
Var:Number fh;
File:Open(logPath, "r", fh);
Var:Number infoCount = 0;
Var:Number warnCount = 0;
Var:Number errCount = 0;
Var:List errorLines;
Var:String line;
Var:Boolean eof;
Var:List groups;
Var:Number gc;
File:EOF(fh, eof);
while (!eof) {
File:ReadLine(fh, line);
String:Trim(line, line);
if (line != "") {
List:Clear(groups);
Regex:Groups("^(\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}) \\[(INFO|WARN|ERROR)\\]\\s*(.+)$", line, groups);
List:Size(groups, gc);
if (gc >= 4) {
Var:String level;
Var:String message;
List:Get(2, groups, level);
List:Get(3, groups, message);
if (level == "INFO") { infoCount = infoCount + 1; }
if (level == "WARN") { warnCount = warnCount + 1; }
if (level == "ERROR") {
errCount = errCount + 1;
// Store timestamp + message for the LLM prompt.
Var:String ts;
List:Get(1, groups, ts);
List:Push("[${ts}] ${message}", errorLines);
}
}
}
File:EOF(fh, eof);
}
File:Close(fh);
Konsol:Print("Log summary - INFO: ${infoCount} WARN: ${warnCount} ERROR: ${errCount}");
if (errCount == 0) {
Konsol:Print("No errors found - nothing to triage.");
Konsol:Exit(0);
}
// ── Step 3: Build the LLM prompt ─────────────────────────────────────────────
// Concatenate all error lines into a single block for the LLM to analyse.
Var:String errorBlock = "";
for (Number ei = 0; ei < errCount; ei++) {
Var:String errLine;
List:Get(ei, errorLines, errLine);
errorBlock = errorBlock + errLine + "\n";
}
Var:String sysMsg = "You are a DevOps engineer. Given a list of server error log entries, identify the most likely root causes and provide concise remediation steps. Format your response as Markdown with sections: ## Root Causes and ## Recommended Actions.";
Var:String userMsg = "Here are the error entries:\n\n" + errorBlock;
Var:String safeUserMsg;
String:Replace(userMsg, "\"", "\\\"", safeUserMsg);
Var:String payload = """{
"model": "gpt-4o-mini",
"messages": [
{"role": "system", "content": "${sysMsg}"},
{"role": "user", "content": "${safeUserMsg}"}
],
"max_tokens": 800
}""";
// ── Step 4: Call the LLM ──────────────────────────────────────────────────────
Konsol:Print("Sending ${errCount} error(s) to LLM for analysis...");
Curl:SetHeader("Authorization", "Bearer ${apiKey}");
Curl:SetHeader("Content-Type", "application/json");
Curl:SetTimeout(45);
Var:String body;
try {
Curl:Post("https://api.openai.com/v1/chat/completions", payload, body);
} catch (CurlException e) {
Konsol:Print("API request failed: ${e.message}");
Konsol:Exit(1);
}
Var:Number status;
Curl:Status(status);
if (status != 200) {
Konsol:Print("API error ${status}: ${body}");
Konsol:Exit(1);
}
JSON:Parse(body, resp);
Var:String analysis;
JSON:Get("choices.0.message.content", resp, analysis);
// ── Step 5: Write the Markdown report ────────────────────────────────────────
Var:Number yr;
Var:Number mo;
Var:Number dy;
Time:GetYear(yr);
Time:GetMonth(mo);
Time:GetDay(dy);
Var:Number wh;
File:Open(reportPath, "w", wh);
File:Write("# Log Triage Report\n\n", wh);
File:Write("**Log file:** ${logPath}\n", wh);
File:Write("**Generated:** ${yr}-${mo}-${dy}\n\n", wh);
File:Write("## Log Summary\n\n", wh);
File:Write("| Level | Count |\n|-------|-------|\n", wh);
File:Write("| INFO | ${infoCount} |\n", wh);
File:Write("| WARN | ${warnCount} |\n", wh);
File:Write("| ERROR | ${errCount} |\n\n", wh);
File:Write("## Error Entries\n\n```\n${errorBlock}```\n\n", wh);
File:Write("## LLM Analysis\n\n", wh);
File:Write(analysis, wh);
File:Write("\n", wh);
File:Close(wh);
Konsol:Print("Report written to ${reportPath}");
Konsol:Print(analysis);
Konsol:Print("Done.");
Modules: curl plugin · JSON · CSV · Time · String · File · Konsol
Sends an identical prompt to each configured model/endpoint, measures round-trip latency with Time:GetTimer, records prompt and completion token counts, and writes a ranked benchmark_results.csv.
Key patterns:
List - easy to extendRegex:Groups to split each entry back into label, model, endpoint, auth fieldsCurl:ClearHeaders() between providers - avoids leaking auth headers across callsTime:GetTimer difference × 1000 = milliseconds; Math:Floor to truncateUsage
minks model_benchmarker.ks <openai_api_key>
minks model_benchmarker.ks sk-...
Sample output
=== LLM Model Benchmarker ===
Prompt: In exactly two sentences, explain what a REST API is.
Testing: OpenAI gpt-4o-mini
487ms 12+42 tokens
A REST API is an architectural style for networked applications...
Testing: OpenAI gpt-4o
1203ms 12+38 tokens
REST APIs use HTTP methods to enable communication between systems...
Testing: Ollama llama3 (local)
HTTP 500 - skipped.
Results saved to benchmark_results.csv
Done.
Script
// model_benchmarker.ks - benchmark multiple LLM endpoints on latency and output
// Modules: curl plugin, JSON, CSV, Time, String, File, Konsol
// Usage: minks model_benchmarker.ks <openai_api_key>
//
// Sends an identical prompt to each configured model/endpoint, measures
// round-trip latency, records token counts, and writes a ranked CSV summary.
// Add or remove entries from the 'suite' List to change what gets benchmarked.
#include "curl"
Konsol:Print("=== LLM Model Benchmarker ===");
// ── Step 1: Read API key ──────────────────────────────────────────────────────
Var:List args;
OS:Args(args);
Var:Number argc;
List:Size(args, argc);
if (argc < 1) {
Konsol:Print("Usage: minks model_benchmarker.ks <openai_api_key>");
Konsol:Exit(1);
}
Var:String apiKey;
List:Get(0, args, apiKey);
// ── Step 2: Define the benchmark suite ───────────────────────────────────────
// Each entry is a "label|model|endpoint|auth" record stored in a List.
// Pipe-delimited so we can split it back out with Regex:Groups.
Var:List suite;
List:Push("OpenAI gpt-4o-mini|gpt-4o-mini|https://api.openai.com/v1/chat/completions|openai", suite);
List:Push("OpenAI gpt-4o|gpt-4o|https://api.openai.com/v1/chat/completions|openai", suite);
List:Push("Ollama llama3 (local)|llama3|http://localhost:11434/v1/chat/completions|local", suite);
Var:Number suiteCount;
List:Size(suite, suiteCount);
// ── Step 3: The benchmark prompt ─────────────────────────────────────────────
// Use a fixed prompt so all models answer the same question.
Var:String benchPrompt = "In exactly two sentences, explain what a REST API is.";
Var:String safePrompt;
String:Replace(benchPrompt, "\"", "\\\"", safePrompt);
Konsol:Print("Prompt: ${benchPrompt}");
Konsol:Print("");
// ── Step 4: Initialise the output CSV ────────────────────────────────────────
CSV:Set(0, 0, "model", results);
CSV:Set(0, 1, "latency_ms", results);
CSV:Set(0, 2, "prompt_tokens", results);
CSV:Set(0, 3, "completion_tokens", results);
CSV:Set(0, 4, "response_snippet", results);
// ── Step 5: Run each benchmark ────────────────────────────────────────────────
Var:List groups;
Var:Number gc;
for (Number i = 0; i < suiteCount; i++) {
Var:String entry;
List:Get(i, suite, entry);
// Parse the pipe-delimited record.
List:Clear(groups);
Regex:Groups("^([^|]+)\\|([^|]+)\\|([^|]+)\\|([^|]+)$", entry, groups);
List:Size(groups, gc);
if (gc < 5) {
Konsol:Print("Malformed suite entry - skipping.");
} else {
Var:String label;
Var:String model;
Var:String endpoint;
Var:String auth;
List:Get(1, groups, label);
List:Get(2, groups, model);
List:Get(3, groups, endpoint);
List:Get(4, groups, auth);
Konsol:Print("Testing: ${label}");
// Set headers for this provider.
Curl:ClearHeaders();
Curl:SetHeader("Content-Type", "application/json");
if (auth == "openai") {
Curl:SetHeader("Authorization", "Bearer ${apiKey}");
} else {
Curl:SetHeader("Authorization", "Bearer ollama");
}
Curl:SetTimeout(60);
Var:String payload = """{
"model": "${model}",
"messages": [{"role": "user", "content": "${safePrompt}"}],
"max_tokens": 100
}""";
Var:Number t1;
Time:GetTimer(t1);
Var:String body;
Var:String curlErr = "";
try {
Curl:Post(endpoint, payload, body);
} catch (CurlException e) {
curlErr = e.message;
}
Var:Number t2;
Time:GetTimer(t2);
Var:Number status;
Curl:Status(status);
Var:Number csvRow = i + 1;
if (curlErr != "" || status != 200) {
Var:String errMsg = curlErr != "" ? curlErr : "HTTP ${status}";
Konsol:Print(" ${errMsg} - skipped.");
CSV:Set(csvRow, 0, label, results);
CSV:Set(csvRow, 1, "ERROR", results);
CSV:Set(csvRow, 2, "0", results);
CSV:Set(csvRow, 3, "0", results);
CSV:Set(csvRow, 4, errMsg, results);
} else {
JSON:Parse(body, resp);
Var:String answer;
Var:Number promptTok;
Var:Number completionTok;
JSON:Get("choices.0.message.content", resp, answer);
JSON:Get("usage.prompt_tokens", resp, promptTok);
JSON:Get("usage.completion_tokens", resp, completionTok);
Var:Number elapsedMs = (t2 - t1) * 1000;
Math:Floor(elapsedMs, elapsedMs);
// Truncate response to 80 chars for the snippet column.
Var:Number alen;
String:Length(answer, alen);
Var:String snippet = answer;
if (alen > 80) {
String:Mid(answer, 1, 80, snippet);
snippet = snippet + "...";
}
Konsol:Print(" ${elapsedMs}ms ${promptTok}+${completionTok} tokens");
Konsol:Print(" ${answer}");
Konsol:Print("");
CSV:Set(csvRow, 0, label, results);
CSV:Set(csvRow, 1, "${elapsedMs}", results);
CSV:Set(csvRow, 2, "${promptTok}", results);
CSV:Set(csvRow, 3, "${completionTok}", results);
CSV:Set(csvRow, 4, snippet, results);
}
}
}
// ── Step 6: Save results.csv ──────────────────────────────────────────────────
Var:String csvText;
CSV:Stringify(results, csvText);
CSV:Free(results);
Var:Number wh;
File:Open("benchmark_results.csv", "w", wh);
File:Write(csvText, wh);
File:Close(wh);
Konsol:Print("Results saved to benchmark_results.csv");
Konsol:Print("Done.");
Modules: curl plugin · OS · File · Path · JSON · String · List · Konsol
Walks a source directory for .ks files, sends each to gpt-4o-mini with a KonsolScript-aware review prompt, and writes the feedback to a <filename>.review file alongside the source.
Key patterns:
Path:Extension(name, ext) - filter .ks files from OS:ListDir## Summary, ## Issues, ## Suggestions)File:Open(path, "w", wh) immediately after each API callUsage
minks code_reviewer.ks <api_key> <source_dir>
minks code_reviewer.ks sk-... ./src
minks code_reviewer.ks sk-... kookbook/cli-tools-and-automation
Sample output
=== Automated Code Reviewer ===
Found 3 .ks file(s) to review.
[1/3] Reviewing file_organizer.ks...
→ kookbook/cli-tools-and-automation/file_organizer.ks.review
[2/3] Reviewing build_runner.ks...
→ kookbook/cli-tools-and-automation/build_runner.ks.review
[3/3] Reviewing log_analyzer.ks...
→ kookbook/cli-tools-and-automation/log_analyzer.ks.review
Reviewed 3 file(s).
Done.
Script
// code_reviewer.ks - send .ks source files to an LLM for automated code review
// Modules: curl plugin, OS, File, Path, JSON, String, List, Konsol
// Usage: minks code_reviewer.ks <api_key> <source_dir>
//
// Walks <source_dir> for .ks files, sends each one to the LLM with a code
// review prompt, and writes the feedback to <filename>.review alongside the
// source file. Useful as a pre-commit check or post-change audit.
#include "curl"
Konsol:Print("=== Automated Code Reviewer ===");
// ── Step 1: Read arguments ────────────────────────────────────────────────────
Var:List args;
OS:Args(args);
Var:Number argc;
List:Size(args, argc);
if (argc < 2) {
Konsol:Print("Usage: minks code_reviewer.ks <api_key> <source_dir>");
Konsol:Exit(1);
}
Var:String apiKey;
Var:String srcDir;
List:Get(0, args, apiKey);
List:Get(1, args, srcDir);
Var:Boolean dirOk;
Path:IsDirectory(srcDir, dirOk);
if (!dirOk) {
Konsol:Print("Not a directory: ${srcDir}");
Konsol:Exit(1);
}
// ── Step 2: Collect .ks files ─────────────────────────────────────────────────
Var:List entries;
OS:ListDirectory(srcDir, entries);
Var:Number entryCount;
List:Size(entries, entryCount);
Var:List ksFiles;
for (Number i = 0; i < entryCount; i++) {
Var:String name;
List:Get(i, entries, name);
Var:String ext;
Path:Extension(name, ext);
if (ext == ".ks") {
Var:String fp;
Path:Join(srcDir, name, fp);
List:Push(fp, ksFiles);
}
}
Var:Number fileCount;
List:Size(ksFiles, fileCount);
Konsol:Print("Found ${fileCount} .ks file(s) to review.");
if (fileCount == 0) {
Konsol:Print("Nothing to review.");
Konsol:Exit(0);
}
// ── Step 3: Set shared headers ────────────────────────────────────────────────
Curl:SetHeader("Authorization", "Bearer ${apiKey}");
Curl:SetHeader("Content-Type", "application/json");
Curl:SetTimeout(45);
Var:String sysMsg = "You are an expert KonsolScript code reviewer. Review the provided script for: correctness, proper use of ByRef convention (output always in last arg), off-by-one errors, resource leaks (unclosed files/handles), and clarity. Be concise. Format as Markdown with sections: ## Summary, ## Issues, ## Suggestions.";
// ── Step 4: Review each file ──────────────────────────────────────────────────
Var:Number reviewed = 0;
for (Number fi = 0; fi < fileCount; fi++) {
Var:String filePath;
List:Get(fi, ksFiles, filePath);
Var:String fileName;
Path:FileName(filePath, fileName);
Var:Number num = fi + 1;
Konsol:Print("[${num}/${fileCount}] Reviewing ${fileName}...");
// Read the source file.
Var:Number rfh;
File:Open(filePath, "r", rfh);
Var:String code = "";
Var:String rline;
Var:Boolean reof;
File:EOF(rfh, reof);
while (!reof) {
File:ReadLine(rfh, rline);
code = code + rline + "\n";
File:EOF(rfh, reof);
}
File:Close(rfh);
Var:String userMsg = "Review this KonsolScript file (${fileName}):\n\n```\n" + code + "\n```";
Var:String safeUserMsg;
String:Replace(userMsg, "\"", "\\\"", safeUserMsg);
Var:String payload = """{
"model": "gpt-4o-mini",
"messages": [
{"role": "system", "content": "${sysMsg}"},
{"role": "user", "content": "${safeUserMsg}"}
],
"max_tokens": 600
}""";
Var:String body;
Var:String curlErr = "";
try {
Curl:Post("https://api.openai.com/v1/chat/completions", payload, body);
} catch (CurlException e) {
curlErr = e.message;
}
Var:Number status;
Curl:Status(status);
if (curlErr != "" || status != 200) {
Var:String errMsg = curlErr != "" ? curlErr : "HTTP ${status}";
Konsol:Print(" API error ${errMsg} - skipping.");
} else {
JSON:Parse(body, resp);
Var:String review;
JSON:Get("choices.0.message.content", resp, review);
// Write review to <filename>.review in the same directory.
Var:String reviewPath = filePath + ".review";
Var:Number wh;
File:Open(reviewPath, "w", wh);
File:Write("# Code Review: ${fileName}\n\n", wh);
File:Write(review, wh);
File:Write("\n", wh);
File:Close(wh);
Konsol:Print(" → ${reviewPath}");
reviewed = reviewed + 1;
}
}
Konsol:Print("Reviewed ${reviewed} file(s).");
Konsol:Print("Done.");