Skip to content

[lmi v29] Upgrade DJL and DJL-Serving to 0.38.0 - #3076

Merged
xyang16 merged 5 commits into
masterfrom
upgrade-djl-0.37.0
Sep 11, 2026
Merged

xyang16 merged 5 commits into
masterfrom
upgrade-djl-0.37.0

Conversation

@shiehbeth

@shiehbeth shiehbeth commented Sep 9, 2026

Copy link
Copy Markdown
Contributor

Description

Upgrade DJL and DJL-Serving from 0.36.0 to 0.38.0 for the LMI 29.0.0 release, following the
shape of #2981 ("[lmi v18] Upgrade DJL and DJL-Serving to 0.36.0").

This is the first DJL bump of the LMI line since v18 — LMI v19 through v28 all rode DJL 0.36.0,
so gradle/libs.versions.toml has been unchanged for ten releases.

This PR originally targeted 0.37.0 and was retargeted to 0.38.0 in e1ed465d at
@xyang16's request, so LMI 29 ships the current DJL release. v29 therefore skips 0.37.0
entirely, and a customer moving lmi28 → lmi29 inherits both releases' behavior changes.
The head branch is still named upgrade-djl-0.37.0: GitHub cannot rename a PR's head branch
without closing and reopening the PR, which would discard this review thread and the CI history,
so the stale branch name is deliberate.

File Change
gradle/libs.versions.toml djl and serving0.38.0
.github/workflows/optimization_integration.yml TEST_SERVING_VERSION fallback → 0.38.0
serving/src/test/java/ai/djl/serving/WorkflowTest.java opt in to dynamic Java compilation — see below
.github/workflows/continuous.yml set DJL_COMPILE_JAVA=true for the Gradle step — see below

The first two are the only places in the repo that carry the version literal (grep -rn '0\.36\.0'
over workflows, *.gradle*, *.toml, *.properties returns exactly those three lines).

WorkflowTest: opting in to dynamic Java compilation

The third file is not a version literal — it is a required adaptation to a DJL behavior change
introduced in 0.37.0 and carried into 0.38.0, and it is why this PR is not a pure two-file bump.

deepjavalibrary/djl#3875 makes compilation of bundled .java sources opt-in, gated on
DJL_COMPILE_JAVA / -Dai.djl.compile_java. ClassLoaderUtils.compileJavaClass now returns early
unless compilation is explicitly enabled.

WorkflowTest.testFunctions loads serving/src/test/resources/workflows/functions.json, which
declares the custom function oid backed by
workflows/libs/classes/.../OtherIdentityWF.java — a source file compiled at load time by
WorkflowDefinition. With compilation off by default the class is never produced,
findImplementation returns null, and the test fails with
BadWorkflowException: Could not load function oid. All three build (*) jobs failed on this
deterministically before this commit.

The fix enables the system property for the duration of that one test, mirroring what DJL applied to
its own CustomTranslatorTest in the same commit.

The test still runs — it is not skipped. The SkipException guard is
if (envOptIn != null && !Boolean.parseBoolean(envOptIn)), so it fires only when DJL_COMPILE_JAVA
is both set and parses false. On an unset environment — which is every job in this repo's CI —
neither condition holds, the property is set, and the test executes. The guard exists because the
environment variable takes precedence over the system property in DJL, so an environment that
explicitly pins compilation off would otherwise see this test fail for a reason unrelated to the
code under test. Evidence, from the run on 7fcf2696: WorkflowTest > testFunctions PASSED on all
three platforms; and locally against 0.38.0, the TestNG report records tests="6" skipped="0" failures="0" errors="0" with testFunctions taking 0.795s.

The fourth file adopts @xyang16's review suggestion to set DJL_COMPILE_JAVA=true for
continuous.yml's Gradle step, making the opt-in explicit at the CI level rather than relying solely
on the in-test system property. It is CI-only and does not change the shipped container, which keeps
DJL 0.38.0's compilation-off default. Self-contained on the semantics too: WorkflowTest is the only
consumer of that variable in this repo, no test asserts the compilation-off default, and
continuous.yml is the only workflow that runs the serving test suite (client-test.yml's
./gradlew build runs in tests/java-client).

One adjustment to the suggested form, in 473e8a93. It was first applied literally, as a command
prefix (run: DJL_COMPILE_JAVA=true ./gradlew ...), which broke build (windows-latest): this
workflow's matrix is [ubuntu-latest, windows-latest, macos-latest] and it sets no
defaults.run.shell, so Windows runs the step under PowerShell, where NAME=value cmd is not a valid
assignment prefix. The step failed in 27s with every preceding step green, ubuntu and macos
unaffected. It is now a step-level env: block, which sets the variable identically on all three
runners and leaves the run: line byte-for-byte as it was:

      - name: Build with Gradle
        env:
          DJL_COMPILE_JAVA: "true"
        run: ./gradlew --refresh-dependencies build :jacoco:testCodeCoverageReport --stacktrace

The WorkflowTest change is deliberately test-only. It does not change the serving default, so
the arbitrary code execution path #3875 closed stays closed. Scope is repo-complete: there is exactly
one bundled .java in the repo (OtherIdentityWF.java) and exactly one compileJavaClass call site
in main code (WorkflowDefinition), so no other test or product path is affected.

Note for downstream consumers: users who ship custom translators or workflow functions as bundled
.java now need DJL_COMPILE_JAVA=true. LMI's Python (engine=Python) path is unaffected.

Why the other files from #2981 are not here

#2981 also touched serving/docs/lmcache_performance.md, tests/integration/llm/prepare.py,
tests/integration/tests.py, and tests/integration/lmcache_configs/djl_long_doc_qa_clean.py.
Those were incidental lmi18 content bundled into the same PR — a new LMCache doc plus yapf
reformatting — not part of the version upgrade, so they have no 0.38.0 analogue.

serving/docs/lmi/release_notes.md is deliberately deferred. That file currently tops out at
LMI V20; v21–v28 never added sections, so it is already ten releases stale, and the V29 entry
depends on benchmark numbers that have not been produced yet. Adding release notes here would
either fabricate figures or open a docs-backfill discussion inside a version-bump PR. Happy to
fold it in if reviewers would rather keep #2981's exact file set.

Type of change

  • Bug fix (non-breaking change which fixes an issue)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • New feature (non-breaking change which adds functionality)
  • This change requires a documentation update

Checklist:

Feature/Issue validation/testing

Verified locally against DJL 0.38.0 (Corretto 17, matching this repo's CI distribution and
java-version), running the same invocation the updated continuous.yml uses
(DJL_COMPILE_JAVA=true ./gradlew --refresh-dependencies ...):

  • --refresh-dependencies re-resolved every ai.djl* coordinate to 0.38.0-SNAPSHOT
    (:serving:dependencies --configuration runtimeClasspath), so this is a genuine resolution against
    the new line and not a cached 0.37.0.
  • :serving:compileJava and :serving:compileTestJava succeeded — no API break in a class
    djl-serving consumes.
  • WorkflowTest: 6 tests, 0 failures, 0 errors, 0 skipped, including testFunctions.

Upstream artifact availability, confirmed directly rather than assumed. Both the release and the
snapshot line are needed, because release-mode nightly builds ${DJL_VERSION} while nightly-mode
builds ${DJL_VERSION}-SNAPSHOT:

Artifact Result
ai.djl:bom:0.38.0 (Maven Central) 200
ai.djl:bom:0.38.0-SNAPSHOT (central.sonatype.com/repository/maven-snapshots) 200 — buildNumber 1, 20260910.191548
ai.djl:api:0.38.0-SNAPSHOT (same) 200
ai.djl.serving:serving:0.38.0 404 as expected — this repo produces it

The BOM snapshot matters specifically because tests/java-client/build.gradle.kts resolves
platform("ai.djl:bom:${libs.versions.djl.get()}-SNAPSHOT"), so that CI check would fail fast if
0.38.0-SNAPSHOT were not published. It is worth recording why that line is now available: djl master
carried 0.38.0 for only ~8h, so nightly_publish.yml's 0 10 * * * cron never fell inside that
window and the snapshot was initially absent. @xyang16 dispatched the publish workflow by hand, which
is what makes this retarget possible.

Note that oss.sonatype.org no longer serves DJL snapshots — 200 only on
central.sonatype.com/repository/maven-snapshots.

0.39.0 is not a viable target today, for the record: 0.39.0-SNAPSHOT exists but the release
coordinate ai.djl:bom:0.39.0 is 404, and a release-mode nightly builds the release coordinate.

Compilation against the DJL 0.38.0 API surface is what this PR's CI validates — if 0.37.0 or 0.38.0
introduced a breaking change in a class djl-serving consumes, the Java build jobs surface it here
rather than during the release nightly. That is worth stating plainly: because master pins
serving = "0.36.0" and resolves ai.djl:bom:0.36.0-SNAPSHOT, whose last publish predates #3875,
master CI could not have caught this. The version-bump PR is where accumulated DJL changes first
become visible to this repo — and since v29 skips 0.37.0, this PR is the only integration test
covering two DJL releases at once. The green checks earned on 7fcf2696 were against 0.37.0 and
do not carry over; the build (*) jobs are re-running on 473e8a93.

Relationship to #3075

Independent of, and mergeable in either order with, #3075 (the vLLM 0.28.0 wheel bump for LMI
29.0.0). Both are required for the LMI 29.0.0 container: #3075 moves the Python/vLLM/torch layer,
this PR moves the Java/DJL layer. #3075 is unaffected by this retarget — it is Python/wheel only.
Note that until this PR merges, a nightly build produces images tagged 0.36.0-*, since the docker
tag is derived from serving in libs.versions.toml.

Bethany Shieh and others added 2 commits September 9, 2026 20:50
First DJL bump of the LMI line since v18 (#2981) — v19 through v28 all
rode DJL 0.36.0, so libs.versions.toml has been unchanged for ten
releases.

These are the only three lines in the repo carrying the version literal:
libs.versions.toml's djl/serving pins, and the TEST_SERVING_VERSION
fallback in optimization_integration.yml.

ai.djl:api and ai.djl:bom confirmed published at 0.37.0 on Maven Central
and at 0.37.0-SNAPSHOT on central.sonatype.com/repository/maven-snapshots.
Both lines are required: release-mode nightly builds ${DJL_VERSION} while
nightly-mode builds ${DJL_VERSION}-SNAPSHOT, and tests/java-client
resolves the BOM snapshot.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
DJL 0.37.0 (deepjavalibrary/djl#3875) makes compilation of bundled .java
sources opt-in, gated on DJL_COMPILE_JAVA / -Dai.djl.compile_java.

WorkflowTest.testFunctions loads functions.json, which declares the custom
function "oid" backed by workflows/libs/classes/.../OtherIdentityWF.java, a
bundled source compiled at load time. With compilation now off by default the
class is never produced, findImplementation returns null, and the test fails
with BadWorkflowException: Could not load function oid.

Enable the system property for the duration of the test, mirroring the fix DJL
applied to its own CustomTranslatorTest in the same commit. The environment
variable takes precedence over the system property, so skip rather than fail
if an environment explicitly pins DJL_COMPILE_JAVA off.

Test-only: this does not change the serving default, so the arbitrary code
execution path #3875 closed stays closed.
// over the system property, so an environment that pins it off would make the property
// below a no-op and fail the test for an unrelated reason.
String envOptIn = Utils.getenv("DJL_COMPILE_JAVA");
if (envOptIn != null && !Boolean.parseBoolean(envOptIn)) {

@xyang16 xyang16 Sep 10, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change will make this test skipped.

Consider update https://github.com/deepjavalibrary/djl-serving/blob/v0.36.0/.github/workflows/continuous.yml#L48 to:

        run: DJL_COMPILE_JAVA=true ./gradlew --refresh-dependencies build :jacoco:testCodeCoverageReport --stacktrace

@xyang16 xyang16 Sep 10, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

        boolean envOptIn = Boolean.parseBoolean(Utils.getenv("DJL_COMPILE_JAVA", "false"));
        boolean propOptIn = Boolean.parseBoolean(System.getProperty("ai.djl.compile_java", "false"));
        if (!envOptIn && !propOptIn) {
            throw new SkipException("Java compilation is disabled");
        }

Revises 8fcc62e's 0.37.0 bump to 0.38.0 at xyang16's request, so LMI 29
ships the current DJL release. v29 therefore skips 0.37.0 entirely, and a
customer moving lmi28 -> lmi29 inherits both releases' behavior changes.

0.38.0 was initially passed over because the build resolves
platform("ai.djl:bom:${version}") and javaBase.gradle.kts appends
-SNAPSHOT unless -Pstaging, and ai.djl:bom:0.38.0-SNAPSHOT did not exist:
djl master carried 0.38.0 for only ~8h and nightly_publish.yml's 10:01Z
cron never fell inside that window. xyang16 has since dispatched that
workflow by hand, so both lines the build needs now resolve --
ai.djl:bom:0.38.0 on Maven Central, and ai.djl:bom:0.38.0-SNAPSHOT
(buildNumber 1, 20260910.191548) on the maven-snapshots host. Both are
required: a release-mode nightly builds ${DJL_VERSION} while nightly-mode
builds ${DJL_VERSION}-SNAPSHOT, and tests/java-client resolves the BOM
snapshot.

Also adopts the review suggestion to set DJL_COMPILE_JAVA=true on
continuous.yml's Gradle invocation, making the WorkflowTest opt-in
explicit at the CI level. This is CI-only and does not change the shipped
container, which keeps DJL 0.38.0's compilation-off default.

Verified locally against 0.38.0 with Corretto 17, matching CI's
distribution and java-version: --refresh-dependencies re-resolved every
ai.djl coordinate to 0.38.0-SNAPSHOT, :serving:compileJava and
compileTestJava succeeded, and WorkflowTest ran 6 tests / 0 skipped /
0 failures.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@shiehbeth shiehbeth changed the title [lmi v29] Upgrade DJL and DJL-Serving to 0.37.0 [lmi v29] Upgrade DJL and DJL-Serving to 0.38.0 Sep 10, 2026
Bethany Shieh and others added 2 commits September 10, 2026 21:47
e1ed465 applied the review suggestion literally, as a command prefix:

    run: DJL_COMPILE_JAVA=true ./gradlew ... build ...

That is bash syntax. continuous.yml's matrix includes windows-latest and
the workflow sets no `defaults.run.shell`, so Windows runs the step under
PowerShell, where `NAME=value cmd` is not a valid assignment-prefix form.
`build (windows-latest)` failed at the "Build with Gradle" step in 27s --
before Gradle started -- while every preceding step succeeded. ubuntu and
macos were unaffected.

Moving the variable to a step-level `env:` block sets it identically on all
three platforms and leaves the `run:` line exactly as it was. Same effect
that was asked for, shell-agnostic.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Adopts xyang16's suggested form. 7fcf269 had the test enable
ai.djl.compile_java itself for the duration of testFunctions; this makes the
test read the opt-in instead of setting it, and skip when neither
DJL_COMPILE_JAVA nor -Dai.djl.compile_java is on.

A test no longer flips a security-relevant global. Consequence: the bundled
.java compilation path is exercised only where the opt-in is set, which as of
473e8a9 is continuous.yml's Gradle step. A plain ./gradlew build now reports
testFunctions as skipped rather than running it, so the env var in
continuous.yml is load-bearing and must stay.

Verified with Corretto 17, deleting the compiled OtherIdentityWF.class before
each run since DJL writes it in place next to the bundled source:
DJL_COMPILE_JAVA=true gives 6 tests / 0 skipped / 0 failures, and no env var
gives 6 tests / 1 skipped / 0 failures with the build still green.
:serving:verifyJava is clean.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@xyang16
xyang16 merged commit 1e06931 into master Sep 11, 2026
14 of 17 checks passed
@xyang16
xyang16 deleted the upgrade-djl-0.37.0 branch September 11, 2026 03:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants