Skip to content
Volume G · The TensorCore.si Library

Groovy

Concise scripting for the JVM.

Groovy is a dynamic language for the Java Virtual Machine with a concise, Python-like feel and full access to Java libraries. It powers Gradle build scripts and Jenkins pipelines, so many developers use it daily for builds and CI/CD, including pipelines that train and deploy AI models.

Prologue

Why learn Groovy?

If you work with Jenkins or Gradle, you already write Groovy; learning it properly makes builds and pipelines far easier.

  • Less boilerplate: optional semicolons, types and parentheses.
  • Java compatible: use any Java library directly.
  • DevOps: the language of Jenkinsfiles and Gradle builds.
Prologue

Set up and run your first program

Install Java 17+ and Groovy (SDKMAN makes this easy).

terminal
curl -s "https://get.sdkman.io" | bash
sdk install groovy
groovy hello.groovy
hello.groovy
def name = "Ada"
println "Hello, AI! Welcome to Groovy, ${name}."
TipDouble-quoted strings support ${} interpolation (GStrings); single quotes do not.
Chapter 1

Variables and strings

Use def for dynamic variables or declare a type. GStrings interpolate values, and triple quotes allow multi-line strings.

basics.groovy
def layers = 32
String model = "llama3.1"
double params = 8.03e9
println "$model: $layers layers"
println "FP16 size: ${String.format('%.1f', params * 2 / 1e9)} GB"
def prompt = """
You are a helpful assistant.
Explain tensor cores.
"""
println prompt.trim()
Chapter 2

Lists, maps and closures

Lists [] and maps [:] have literal syntax. Closures { } work with methods like each, collect, findAll and inject.

collections.groovy
def gpus = ["H100", "H200", "B200"]
def vram = [H100: 80, H200: 141, "RTX 5090": 32]

gpus.each { println it }
println vram.findAll { k, v -> v >= 80 }.keySet()
println vram.values().sum()
println((1..5).collect { it * it })
println([1, 2, 3, 4].inject(0) { acc, x -> acc + x })
Chapter 3

Control flow

Groovy's switch matches values, ranges, lists and regexes. The safe-navigation ?. and Elvis ?: operators handle nulls neatly.

control.groovy
def precision(bits) {
    switch (bits) {
        case 32: return "FP32"
        case 16: return "FP16"
        case 1..8: return "low precision"
        default: return "other"
    }
}
println precision(8)

String user = null
println user?.toUpperCase() ?: "anonymous"
for (i in 1..3) println "epoch $i"
Chapter 4

Classes and traits

Classes get getters, setters and constructors automatically. Annotations like @ToString remove more boilerplate; traits share behaviour.

classes.groovy
import groovy.transform.ToString

trait Describable {
    String describe() { "${this.class.simpleName}: $name" }
}

@ToString
class Gpu implements Describable {
    String name
    int vram
    boolean fits(int gb) { gb <= vram }
}

def g = new Gpu(name: "RTX 5090", vram: 32)
println g
println g.describe()
println g.fits(16)
Chapter 5

JSON, files and HTTP

Groovy includes JsonSlurper and JsonOutput, and adds handy methods to files and URLs.

json.groovy
import groovy.json.*

def data = new JsonSlurper().parseText('{"model":"llama3.1","sizes":[8,70,405]}')
println data.sizes.max()

new File("out.json").text = JsonOutput.prettyPrint(JsonOutput.toJson([ok: true]))
new File("notes.txt").eachLine { line -> if (line.contains("GPU")) println line }

def repo = new JsonSlurper().parse(new URL("https://api.github.com/repos/pytorch/pytorch"))
println "Stars: ${repo.stargazers_count}"
Part II · AI with Groovy

AI with Groovy

Groovy's AI role is mostly in automation: Jenkins pipelines that train, evaluate and deploy models, Gradle builds for Java AI apps, and scripts that call LLM APIs. Because it runs on the JVM, it can also use Java AI libraries such as LangChain4j and Deep Java Library (DJL).

The essential libraries

LangChain4jLLM apps and agents on the JVM.
DJLDeep learning in Java (PyTorch, ONNX engines).
JsonSlurperBuilt-in JSON parsing.
Jenkins PipelineCI/CD for ML training and deployment.

A Jenkins pipeline for a model

A declarative Jenkinsfile that trains, evaluates and deploys a model on a GPU agent.

Jenkinsfile
pipeline {
    agent { label 'gpu' }
    environment { RUN = "run-${env.BUILD_NUMBER}" }
    stages {
        stage('Setup')    { steps { sh 'pip install -r requirements.txt' } }
        stage('Train')    { steps { sh 'python train.py --out runs/$RUN' } }
        stage('Evaluate') {
            steps {
                script {
                    def acc = sh(script: "python eval.py runs/$RUN", returnStdout: true).trim() as double
                    if (acc < 0.90) error "Accuracy too low: $acc"
                }
            }
        }
        stage('Deploy') { when { branch 'main' } steps { sh './deploy.sh runs/$RUN' } }
    }
    post { always { archiveArtifacts artifacts: "runs/$RUN/**", allowEmptyArchive: true } }
}
Part III · Build an AI agent

Build an AI agent in Groovy

Java's HttpClient plus Groovy's JSON tools make a compact tool-calling loop. Tools are closures in a map.

Your goal→Model plans→Tool call→Your code runs the tool→Result back to model→Final answer
agent.groovy
import groovy.json.*
import java.net.http.*

def client = HttpClient.newHttpClient()
def url = URI.create("http://localhost:11434/v1/chat/completions")
def tools = [[type: "function", function: [name: "get_weather", description: "Weather for a city",
    parameters: [type: "object", properties: [city: [type: "string"]], required: ["city"]]]]]
def impls = [get_weather: { args -> "It is 24°C and sunny in ${args.city}." }]
def messages = [[role: "user", content: "Should I take an umbrella in Lahore today?"]]

for (round in 1..5) {
    def req = HttpRequest.newBuilder(url).header("Content-Type", "application/json")
        .POST(HttpRequest.BodyPublishers.ofString(JsonOutput.toJson([model: "llama3.1", messages: messages, tools: tools])))
        .build()
    def body = client.send(req, HttpResponse.BodyHandlers.ofString()).body()
    def msg = new JsonSlurper().parseText(body).choices[0].message
    messages << msg
    if (!msg.tool_calls) { println msg.content; break }
    msg.tool_calls.each { call ->
        def args = new JsonSlurper().parseText(call.function.arguments)
        messages << [role: "tool", tool_call_id: call.id, content: impls[call.function.name](args).toString()]
    }
}

Adding a tool means adding a schema to tools and a closure to impls.

Run it locallyInstall Ollama, run ollama pull llama3.1, and keep it running. The code talks to its OpenAI-compatible API at http://localhost:11434/v1, so the same program also works with vLLM, llama.cpp server or a cloud API by changing the URL, model name and adding an API key.
Part IV · Your path

Groovy learning roadmap

Tick items off as you go. Your progress is saved in this browser.

Beginner

Intermediate

Advanced

Part IV · Your path

Projects to build

Log summariserParse logs and output JSON stats.
Gradle pluginAutomate a custom build step.
ML pipelineA Jenkinsfile that trains and gates a model.
Reference

Groovy cheat sheet

TaskSyntax
Variabledef x = 5
GString"Hi ${name}"
List[1, 2, 3]
Map[a: 1, b: 2]
Closurelist.collect { it * 2 }
Safe navobj?.prop ?: "default"
Reference

Common mistakes

  • Single-quoted strings do not interpolate ${}.
  • == calls equals() in Groovy; use .is() for identity.
  • Dynamic typing hides typos until runtime; add @CompileStatic for critical code.
Reference

Where to go next

On TensorCore.si: Java volume, Kotlin volume.

Keyboard: [ previous language · ] next language · / search the library