Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
6ca27d7
Add Java target to the model generation pipeline
sethjuarez Aug 4, 2026
c921c15
Add the Java runtime core: load, render, parse, prepare
sethjuarez Aug 4, 2026
9929845
Add the OpenAI provider: wire, executor, processor
sethjuarez Aug 4, 2026
30f323c
Add the Anthropic provider: wire, executor, processor
sethjuarez Aug 4, 2026
bec8828
Add the turn engine: iterations, tool rounds, durability, resume
sethjuarez Aug 4, 2026
2040e4c
Keep nonce attributes verbatim so all-digit nonces still validate
sethjuarez Aug 4, 2026
5bffdce
Correct tool parameters in the shared agent vectors
sethjuarez Aug 4, 2026
6088aa3
Add the live turn adapter and the public turn API
sethjuarez Aug 4, 2026
48b2966
Add model discovery and capability enrichment
sethjuarez Aug 4, 2026
f78f09a
Add memory scoring and the reference turn harness
sethjuarez Aug 4, 2026
5a99dc9
Add the Foundry provider: Azure URL routing, auth, processing
sethjuarez Aug 4, 2026
0e298e0
Add live end-to-end tests against real provider APIs
sethjuarez Aug 4, 2026
ebadd99
Add Foundry sign-in and Azure resource discovery
sethjuarez Aug 4, 2026
35864d8
Add Foundry model listing
sethjuarez Aug 4, 2026
c4e7ab9
Fix repo hygiene on the Java runtime files
sethjuarez Aug 4, 2026
4594f56
Run the Java runtime in CI
sethjuarez Aug 4, 2026
f63b98b
Stop the role-marker pattern backtracking on malformed input
sethjuarez Aug 4, 2026
bb003f4
Cover the ARM paging loop by injecting its transport
sethjuarez Aug 4, 2026
cec791d
Pin @typra/emitter 0.4.3, minimize the Java shim, and fill the seams
sethjuarez Aug 4, 2026
c36d37b
Cover the extension seams with tests
sethjuarez Aug 4, 2026
bea126c
Test the generated-output cleaner and guard generation in CI
sethjuarez Aug 4, 2026
2355dbe
fix(java): require every nested key in strict OpenAI schemas
sethjuarez Aug 4, 2026
da5a3f6
test(java): prove nested strict schemas against the live API
sethjuarez Aug 4, 2026
3837178
test(java): accept an empty collection where a vector states null
sethjuarez Aug 4, 2026
676d092
fix(java): let a test state that an environment variable is absent
sethjuarez Aug 4, 2026
bb695da
docs(schema): record how the Java normalization shim is retired
sethjuarez Aug 4, 2026
666091e
test(java): grade structured output against the full strict-widening …
sethjuarez Aug 4, 2026
bda4c37
Accept every known Typra seam marker when cleaning Java output
sethjuarez Aug 4, 2026
31f4962
Grade both canonical named-collection contracts in the Java gate
sethjuarez Aug 4, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
5 changes: 5 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
*.rs text eol=lf
*.go text eol=lf
*.cs text eol=lf
*.java text eol=lf
*.tsp text eol=lf
*.astro text eol=lf
*.css text eol=lf
Expand All @@ -36,6 +37,9 @@
# --- Project files ---
*.csproj text eol=lf
*.sln text eol=lf
*.gradle text eol=lf
*.kts text eol=lf
*.properties text eol=lf
*.ps1 text eol=lf
*.bat text eol=lf

Expand All @@ -51,6 +55,7 @@ Dockerfile text eol=lf
# --- Truly binary files --- keep as-is
*.png binary
*.exe binary
*.jar binary
*.svg text eol=lf

# --- Lock files: text but don't diff ---
Expand Down
13 changes: 9 additions & 4 deletions .github/workflows/codeql.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,19 @@ jobs:
matrix:
include:
- language: actions
build-mode: none
- language: csharp
build-mode: autobuild
- language: go
build-mode: autobuild
- language: java-kotlin
build-mode: none
- language: javascript-typescript
build-mode: none
- language: python
build-mode: none
- language: rust
build-mode: none

steps:
- name: Checkout repository
Expand All @@ -42,10 +50,7 @@ jobs:
uses: github/codeql-action/init@v4
with:
languages: ${{ matrix.language }}

- name: Autobuild
if: matrix.language == 'csharp' || matrix.language == 'go' || matrix.language == 'rust'
uses: github/codeql-action/autobuild@v4
build-mode: ${{ matrix.build-mode }}

- name: Perform CodeQL analysis
uses: github/codeql-action/analyze@v4
Expand Down
111 changes: 111 additions & 0 deletions .github/workflows/prompty-java-check.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
name: prompty Java build and test
on:
pull_request:
paths:
- 'runtime/java/**'
- 'schema/scripts/clean-java-output*'
- 'schema/scripts/normalize-java-output*'
- '.github/workflows/prompty-java-check.yml'

workflow_call:

jobs:
generation-scripts:
name: test Java generation scripts
runs-on: ubuntu-latest
permissions:
contents: read
defaults:
run:
working-directory: schema
steps:
- uses: actions/checkout@v5

- name: Set up Node
uses: actions/setup-node@v6
with:
node-version: '22'

# Guards the generated-output cleaner. Both of its failure modes are
# silent: destroying the hand-written `@method` implementations kept in
# extension seams, or leaving stale generated files behind.
- name: Test generation scripts
run: npm run test:scripts

generation-drift:
name: verify Java generation is reproducible
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- uses: actions/checkout@v5

- name: Set up Node
uses: actions/setup-node@v6
with:
node-version: '22'

- name: Install schema toolchain
working-directory: schema
run: npm ci

# Regenerating exercises every guard in normalize-java-output.mjs. Those
# guards fail the build when a rewrite stops matching emitter output,
# which is how an emitter upgrade that silently changes Java codegen is
# caught. Running the emitter here is the only place they execute.
- name: Regenerate
working-directory: schema
run: npm run generate

# The committed Java tree must be exactly what the pinned emitter plus the
# normalization shim produce. A diff means either a hand edit to generated
# code or a stale checked-in tree.
- name: Verify committed Java output matches generated output
run: |
if ! git diff --quiet -- runtime/java; then
echo "::error::Committed Java output differs from freshly generated output."
git --no-pager diff --stat -- runtime/java
git --no-pager diff -- runtime/java | head -200
exit 1
fi
echo "Committed Java output matches generated output."

java-tests:
name: test Java on ${{ matrix.os }}
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, windows-latest]
permissions:
contents: read
defaults:
run:
working-directory: runtime/java
steps:
- uses: actions/checkout@v5

# The Gradle toolchain pins Java 21, so this is the version that actually
# compiles and runs the tests on every platform.
- name: Set up JDK 21
uses: actions/setup-java@v5
with:
distribution: temurin
java-version: '21'

- name: Set up Gradle
uses: gradle/actions/setup-gradle@v6

# Live provider tests are tagged "live" and are excluded unless
# -PliveTests is passed, so this is the offline suite. It needs no
# secrets and is therefore safe on pull requests from forks.
- name: Build and test
run: ./gradlew build --console=plain

- name: Upload test reports
if: always()
uses: actions/upload-artifact@v7
with:
name: java-test-reports-${{ matrix.os }}
path: runtime/java/*/build/reports/tests/test
if-no-files-found: ignore
21 changes: 21 additions & 0 deletions runtime/java/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# OpenAI
OPENAI_API_KEY=
OPENAI_MODEL=gpt-4o-mini
OPENAI_EMBEDDING_MODEL=text-embedding-3-small
OPENAI_IMAGE_MODEL=dall-e-2

# Azure OpenAI / Foundry
AZURE_OPENAI_API_KEY=
AZURE_OPENAI_ENDPOINT=https://your-resource.openai.azure.com
AZURE_OPENAI_CHAT_DEPLOYMENT=gpt-4o-mini
AZURE_OPENAI_EMBEDDING_DEPLOYMENT=text-embedding-3-small

# Azure AI Foundry project (agent / model discovery)
FOUNDRY_PROJECT_ENDPOINT=

# Azure Entra ID (for keyless auth tests)
AZURE_TENANT_ID=

# Anthropic
ANTHROPIC_API_KEY=
ANTHROPIC_MODEL=claude-sonnet-4-20250514
11 changes: 11 additions & 0 deletions runtime/java/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# Gradle
.gradle/
build/

# Local credentials for live provider tests — never commit.
.env

# IDE
.idea/
*.iml
.vscode/
78 changes: 78 additions & 0 deletions runtime/java/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
# Prompty for Java

The Java implementation of the Prompty runtime.

Prompty is a markdown-based asset format (`.prompty`) for LLM prompts: YAML
frontmatter describes the model, inputs, outputs, tools and template
configuration, and the markdown body becomes the prompt instructions. The
runtime loads, renders, parses, executes and processes those assets.

> **Status:** in development. The generated model layer and its example suites
> are complete; the loader, renderers, parser, pipeline and providers are being
> ported from the Rust reference implementation.

## Layout

| Module | Contents |
| ------------------- | --------------------------------------------------------------- |
| `prompty` | Canonical generated model + loader, renderers, parser, pipeline |
| `prompty-openai` | OpenAI provider (executor + processor) |
| `prompty-anthropic` | Anthropic provider (executor + processor) |
| `prompty-foundry` | Azure AI Foundry provider (executor + processor) |

## The model layer is generated — do not edit it

`prompty/src/main/java/com/microsoft/prompty/model/` and
`prompty/src/test/java/com/microsoft/prompty/model/` are emitted from the
TypeSpec definition in [`schema/`](../../schema) by the Typra emitter. They are
the single canonical model layer for this runtime; there is no hand-written
duplicate. Regenerate with:

```bash
cd schema
npm install # first time only
npm run generate
```

`npm run generate` runs `tsp compile` followed by
`schema/scripts/normalize-typra-output.mjs`, which applies a deterministic,
idempotent normalization pass to the emitted Java (see
`schema/scripts/normalize-java-output.mjs`). That shim exists only because the
Typra Java backend currently emits source that does not compile and that
diverges from the C#/Rust/Go/Python backends; every defect it works around is
documented at the top of the shim and has been reported upstream. The shim is
part of the generation pipeline, not a manual edit — running the pipeline twice
from a clean tree produces byte-identical output.

The emitter also produces example-driven suites next to the model. They are
package-private, so `GeneratedExamplesTest` discovers the compiled
`*GeneratedTest` classes by reflection and runs each as a named dynamic test,
which keeps the suite in step with the schema without a checked-in registry.
`ModelNormalizationTest` covers the behaviour the shim adds on top of the
emitter output, which the generated examples do not exercise.

## Building

The Gradle wrapper provisions Gradle itself; a JDK is required to run it. The
build pins a Java 21 toolchain, so a newer JDK works as long as Gradle can
locate or provision a 21 toolchain.

```bash
cd runtime/java
./gradlew build # compile + unit tests
./gradlew :prompty:test # unit tests for the core module only
```

## Live provider tests

Tests tagged `live` call real providers and are excluded from the default `test`
task. They read credentials from the process environment; the test support code
also reads `runtime/java/.env` when present so the file does not have to be
exported manually.

```bash
cp .env.example .env # then fill in credentials
./gradlew test -PliveTests
```

`.env` is git-ignored and must never be committed.
48 changes: 48 additions & 0 deletions runtime/java/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
plugins {
id("java-library")
}

allprojects {
group = "com.microsoft.prompty"
version = "2.0.0-beta.4"
}

subprojects {
apply(plugin = "java-library")

repositories {
mavenCentral()
}

extensions.configure<JavaPluginExtension> {
toolchain {
languageVersion.set(JavaLanguageVersion.of(21))
}
withSourcesJar()
}

tasks.withType<JavaCompile>().configureEach {
options.encoding = "UTF-8"
// Generated model sources are emitted by Typra and are not warning-clean by
// design; keep the build strict for hand-written code only.
options.compilerArgs.addAll(listOf("-Xlint:all", "-Xlint:-serial", "-Xlint:-this-escape"))
}

tasks.withType<Test>().configureEach {
// Live provider tests call real endpoints; they are opt-in via -PliveTests.
val liveTests = providers.gradleProperty("liveTests").isPresent
useJUnitPlatform {
if (!liveTests) {
excludeTags("live")
}
}
testLogging {
events("failed")
exceptionFormat = org.gradle.api.tasks.testing.logging.TestExceptionFormat.FULL
}
}

tasks.withType<Javadoc>().configureEach {
(options as StandardJavadocDocletOptions).addStringOption("Xdoclint:none", "-quiet")
}
}
Binary file added runtime/java/gradle/wrapper/gradle-wrapper.jar
Binary file not shown.
9 changes: 9 additions & 0 deletions runtime/java/gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-9.6.1-bin.zip
networkTimeout=10000
retries=0
retryBackOffMs=500
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
Loading
Loading