Skip to content

Conversation

kpavlov
Copy link
Collaborator

@kpavlov kpavlov commented Sep 21, 2025

Adds shadow (shaded artifact) support and publishing across modules, updates CI to use the Gradle wrapper and adds a dependency-submission job, introduces a Maven sample with OpenAI integration tests, updates Makefile and .editorconfig formatting, and changes mokksy response APIs to accept numeric HTTP status codes with derived HttpStatusCode.

@kpavlov kpavlov added the enhancement New feature or request label Sep 21, 2025
Copy link
Contributor

coderabbitai bot commented Sep 21, 2025

Caution

Review failed

The pull request is closed.

📝 Walkthrough

Summary by CodeRabbit

  • New Features
    • Publish standalone (shaded) JARs for modules, simplifying adoption without extra dependencies.
    • Response and streaming definitions/builders now accept numeric HTTP status codes, automatically deriving the status.
  • Tests
    • Added Maven integration tests and a new OpenAI sample test.
  • Documentation
    • Updated release instructions; refreshed sample notebook to use the standalone artifact.
  • Chores
    • CI workflow improved (Gradle wrapper, dependency submission); build scripts rerun tasks and run integration tests.
  • Style
    • EditorConfig updated: tab-based indentation for Java/Kotlin/XML and formatting cleanups.

Walkthrough

Adds a shadow (shaded) publishing convention and applies it across modules, updates CI to use the Gradle wrapper, run Maven integration tests and submit dependency graphs, introduces a Maven sample + tests, adjusts EditorConfig, and changes response APIs to accept numeric HTTP status codes with derived HttpStatusCode.

Changes

Cohort / File(s) Summary of changes
EditorConfig
\.editorconfig
Switched Kotlin/XML/Java sections to tab indentation, adjusted Kotlin import-on-demand spacing, and removed no-op trailing-space comments.
CI workflow
​.github/workflows/gradle.yml
Use ./gradlew --rerun-tasks --no-daemon ... publishToMavenLocal; add OpenAI Maven Integration Tests step (runs mvn --batch-mode test in ai-mocks-openai/samples/shadow) and a dependency-submission job.
Makefile
Makefile
Use Gradle wrapper with --rerun-tasks and publishToMavenLocal; clean local Maven repo and run Maven tests for the sample.
Shadow plugin & publishing (modules)
ai-mocks-*, mokksy
ai-mocks-a2a/build.gradle.kts, ai-mocks-anthropic/build.gradle.kts, ai-mocks-gemini/build.gradle.kts, ai-mocks-ollama/build.gradle.kts, ai-mocks-openai/build.gradle.kts, mokksy/build.gradle.kts
Apply shadow-convention and add publishing blocks creating a shadow MavenPublication that publishes a shaded jar (artifactId = project.name + "-standalone") and sources.
Sample Maven project & tests
ai-mocks-openai/samples/shadow/pom.xml, ai-mocks-openai/samples/shadow/src/test/java/com/example/OpenAITest.java
Add Maven POM (Java 17, deps, pluginManagement) and JUnit tests exercising MockOpenai success and error paths.
buildSrc: shadow & plugin
buildSrc/build.gradle.kts, buildSrc/src/main/kotlin/shadow-convention.gradle.kts
Add com.gradleup.shadow:shadow-gradle-plugin:9.1.0; new shadow-convention script configures shadowJar (minimize with Kotlin exclusions, relocations) and makes assemble depend on shadowJar.
buildSrc: publish/dokka tweaks
buildSrc/src/main/kotlin/publish-convention.gradle.kts, buildSrc/src/main/kotlin/dokka-convention.gradle.kts
Migrate POM DSL to direct property assignment, add issueManagement, update project URL to https://mokksy.dev, and remove extraneous blank line in Dokka script.
mokksy response API changes
mokksy/src/commonMain/kotlin/me/kpavlov/mokksy/response/*.kt
Constructors/builders now accept httpStatusCode: Int = 200 and derive httpStatus: HttpStatusCode = HttpStatusCode.fromValue(httpStatusCode); propagated via named supercalls and builders.
Mock server naming
ai-mocks-openai/src/commonMain/kotlin/.../MockOpenai.kt
Add name = "MockOpenai" to the ServerConfiguration for the mock server.
Notebook & samples
ai-mocks-openai/samples/OpenaiLc4jSample.ipynb
Swap to standalone artifact, adjust dependencies, add port=0 usage for MockOpenai, and update execution metadata.
Docs/release
RELEASE.md
Add --rerun-tasks to the release Gradle command snippet.

Sequence Diagram(s)

sequenceDiagram
    participant CI as GitHub Actions
    participant GW as ./gradlew
    participant ShadowConv as shadow-convention (buildSrc)
    participant Build as Project Build
    participant LocalMaven as publishToMavenLocal
    participant MavenSample as Maven (sample)
    participant DepJob as dependency-submission

    rect rgb(220,235,255)
      CI->>GW: run --rerun-tasks clean build publishToMavenLocal koverXmlReport
      GW->>ShadowConv: apply shadow-convention
      ShadowConv->>Build: configure shadowJar (minimize, relocate)
      Build->>LocalMaven: publish shadow artifacts
    end

    rect rgb(235,255,220)
      CI->>MavenSample: run mvn --batch-mode test (ai-mocks-openai/samples/shadow)
      MavenSample-->>CI: test results
    end

    rect rgb(255,245,215)
      CI->>DepJob: trigger dependency-submission job
      DepJob->>DepJob: generate & submit dependency graph
      DepJob-->>CI: submission result
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • A2A update #347 — Related changes to ai-mocks modules; likely overlaps with shadow publishing and module-level packaging changes.

Suggested labels

refactoring

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Title Check ✅ Passed The title "Introduce ShadowJar to avoid dependency conflicts" accurately captures the primary purpose of the changeset—the addition of ShadowJar/shadow-convention and publishing of shaded artifacts across modules—while other changes (CI, Makefile, editorconfig, and API tweaks) are secondary and do not need to be in the title.
Description Check ✅ Passed The description succinctly and correctly summarizes the main changes in the PR: adding shadow/shaded publishing, updating CI to use the Gradle wrapper and adding a dependency-submission job, introducing a Maven sample with integration tests, Makefile and .editorconfig updates, and the httpStatusCode API change, which aligns with the provided file-level summaries.
Docstring Coverage ✅ Passed No functions found in the changes. Docstring coverage check skipped.

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between dbce728 and 94c0f45.

📒 Files selected for processing (22)
  • .editorconfig (2 hunks)
  • .github/workflows/gradle.yml (2 hunks)
  • Makefile (2 hunks)
  • RELEASE.md (1 hunks)
  • ai-mocks-a2a/build.gradle.kts (2 hunks)
  • ai-mocks-anthropic/build.gradle.kts (2 hunks)
  • ai-mocks-gemini/build.gradle.kts (2 hunks)
  • ai-mocks-ollama/build.gradle.kts (2 hunks)
  • ai-mocks-openai/build.gradle.kts (2 hunks)
  • ai-mocks-openai/samples/OpenaiLc4jSample.ipynb (6 hunks)
  • ai-mocks-openai/samples/shadow/pom.xml (1 hunks)
  • ai-mocks-openai/samples/shadow/src/test/java/com/example/OpenAITest.java (1 hunks)
  • ai-mocks-openai/src/commonMain/kotlin/me/kpavlov/aimocks/openai/MockOpenai.kt (1 hunks)
  • buildSrc/build.gradle.kts (1 hunks)
  • buildSrc/src/main/kotlin/dokka-convention.gradle.kts (1 hunks)
  • buildSrc/src/main/kotlin/publish-convention.gradle.kts (2 hunks)
  • buildSrc/src/main/kotlin/shadow-convention.gradle.kts (1 hunks)
  • mokksy/build.gradle.kts (2 hunks)
  • mokksy/src/commonMain/kotlin/me/kpavlov/mokksy/response/AbstractResponseDefinition.kt (1 hunks)
  • mokksy/src/commonMain/kotlin/me/kpavlov/mokksy/response/ResponseDefinition.kt (2 hunks)
  • mokksy/src/commonMain/kotlin/me/kpavlov/mokksy/response/ResponseDefinitionBuilders.kt (3 hunks)
  • mokksy/src/commonMain/kotlin/me/kpavlov/mokksy/response/StreamResponseDefinition.kt (2 hunks)

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

codacy-production bot commented Sep 21, 2025

Coverage summary from Codacy

See diff coverage on Codacy

Coverage variation Diff coverage
Report missing for 3d2498e1 89.29%
Coverage variation details
Coverable lines Covered lines Coverage
Common ancestor commit (3d2498e) Report Missing Report Missing Report Missing
Head commit (94c0f45) 6720 4614 68.66%

Coverage variation is the difference between the coverage for the head and common ancestor commits of the pull request branch: <coverage of head commit> - <coverage of common ancestor commit>

Diff coverage details
Coverable lines Covered lines Diff coverage
Pull request (#375) 28 25 89.29%

Diff coverage is the percentage of lines that are covered by tests out of the coverable lines that the pull request added or modified: <covered lines added or modified>/<coverable lines added or modified> * 100%

See your quality gate settings    Change summary preferences

Footnotes

  1. Codacy didn't receive coverage data for the commit, or there was an error processing the received data. Check your integration for errors and validate that your coverage setup is correct.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 20

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (11)
.editorconfig (2)

42-45: Add indent_style = tab to the XML section

indent_size = tab without indent_style = tab is ambiguous; declare indent_style = tab under [*.xml].

File: .editorconfig (lines 42-45)

 [*.xml]
+indent_style = tab
 tab_width = 4
 indent_size = tab

Reformat a sample XML and ensure CI linters pass.


26-34: Kotlin: set indent_style=tab when using indent_size=tab (spec mismatch).

root sets indent_style = space (line 8) while [*.{kt,kts}] only sets indent_size = tab (line 27) in .editorconfig — make the Kotlin section explicit.

[*.{kt,kts}]
-indent_size = tab
+indent_style = tab
+indent_size = tab
+tab_width = 4

Run formatting checks (e.g., ./gradlew spotlessCheck or ./gradlew ktlintCheck) to confirm no new violations.

mokksy/src/commonMain/kotlin/me/kpavlov/mokksy/response/AbstractResponseDefinition.kt (1)

27-33: Enforce invariant: keep httpStatusCode and httpStatus in sync

Right now callers can pass mismatched values (e.g., 200 with HttpStatusCode.NotFound). Add a runtime check to prevent drift across the codebase.

Apply this diff:

 public abstract class AbstractResponseDefinition<T>(
     public val contentType: ContentType? = null,
     public val httpStatusCode: Int = 200,
     public val httpStatus: HttpStatusCode = HttpStatusCode.fromValue(httpStatusCode),
     public val headers: (ResponseHeaders.() -> Unit)? = null,
     public val headerList: List<Pair<String, String>> = emptyList(),
     public open val delay: Duration = Duration.ZERO,
     public var responseBody: T? = null,
 ) {
+    init {
+        require(httpStatus.value == httpStatusCode) {
+            "httpStatus ($httpStatus) must match httpStatusCode ($httpStatusCode)"
+        }
+    }
mokksy/src/commonMain/kotlin/me/kpavlov/mokksy/response/ResponseDefinitionBuilders.kt (4)

116-124: Streaming builder not migrated: add httpStatusCode and propagate to base

This still accepts only httpStatus, causing httpStatusCode to stick at 200. Mirror the non-streaming builder.

Apply this diff:

 @Suppress("LongParameterList")
 public open class StreamingResponseDefinitionBuilder<P : Any, T>(
     public val request: CapturedRequest<P>,
     public var flow: Flow<T>? = null,
     public var chunks: MutableList<T> = mutableListOf(),
     public var delayBetweenChunks: Duration = Duration.ZERO,
-    httpStatus: HttpStatusCode = HttpStatusCode.OK,
+    httpStatusCode: Int = 200,
+    httpStatus: HttpStatusCode = HttpStatusCode.fromValue(httpStatusCode),
     headers: MutableList<Pair<String, String>> = mutableListOf(),
-) : AbstractResponseDefinitionBuilder<P, T>(httpStatus = httpStatus, headers = headers) {
+) : AbstractResponseDefinitionBuilder<P, T>(
+        httpStatusCode = httpStatusCode,
+        httpStatus = httpStatus,
+        headers = headers,
+) {

136-145: Also pass httpStatusCode to StreamResponseDefinition.build()

Without this, StreamResponseDefinition uses its default (200) even when the builder changed status.

Apply this diff:

     public override fun build(): StreamResponseDefinition<P, T> =
         StreamResponseDefinition(
             chunkFlow = flow,
             chunks = chunks.toList(),
+            httpStatusCode = httpStatusCode,
             httpStatus = httpStatus,
             headers = headersLambda,
             headerList = Collections.unmodifiableList(headers),
             delayBetweenChunks = delayBetweenChunks,
             delay = delay,
         )

22-62: Optional: centralize sync by constraining the API

Consider removing httpStatus as a public var from builders and making it derived from httpStatusCode only. This eliminates the possibility of drift entirely.


1-147: Sync httpStatus and httpStatusCode in AbstractResponseDefinitionBuilder

Multiple call sites assign only httpStatus (HttpStatusCode) while httpStatusCode (Int) remains stale — this yields inconsistent ResponseDefinition/StreamResponseDefinition instances. Fix by keeping the two fields in sync (either update call sites to set both or add bidirectional setters that update the other).

Instances found (assign-only httpStatus):

  • mokksy/src/jvmTest/kotlin/me/kpavlov/mokksy/TypesafeMethodsIT.kt:221
  • mokksy/src/jvmTest/kotlin/me/kpavlov/mokksy/BodyMatchingIT.kt:50
  • mokksy/src/jvmTest/kotlin/me/kpavlov/mokksy/MokksySseIT.kt:80
  • mokksy/src/commonTest/kotlin/me/kpavlov/mokksy/BuildingStepTest.kt:60, 69, 77
  • ai-mocks-openai/src/jvmTest/kotlin/me/kpavlov/aimocks/openai/official/completions/ChatCompletionOpenaiTest.kt:90, 137

Suggested minimal change (AbstractResponseDefinitionBuilder): make the properties use private backing fields and update each other to avoid recursion, e.g.

private var _httpStatusCode: Int = 200
public var httpStatusCode: Int
get() = _httpStatusCode
set(value) {
_httpStatusCode = value
_httpStatus = HttpStatusCode.fromValue(value)
}

private var _httpStatus: HttpStatusCode = HttpStatusCode.fromValue(_httpStatusCode)
public var httpStatus: HttpStatusCode
get() = _httpStatus
set(value) {
_httpStatus = value
_httpStatusCode = value.value
}

Alternatively, remove the redundant field and use a single source-of-truth. Addressing this is required.

buildSrc/src/main/kotlin/dokka-convention.gradle.kts (1)

13-17: Fix Dokka sourceLink branch: master → main.

The repo’s default branch is “main”; pointing Dokka to “master” breaks source links.

Apply:

-            remoteUrl("https://github.com/mokksy/ai-mocks/tree/master")
+            remoteUrl("https://github.com/mokksy/ai-mocks/tree/main")
Makefile (1)

46-53: sourcesJar task likely unnecessary or missing at root.

Root “sourcesJar” often doesn’t exist in MPP; publishing already attaches sources if configured. This can fail the target.

Apply:

-	./gradlew clean build check sourcesJar publishToMavenLocal
+	./gradlew clean build check publishToMavenLocal

If you do need sources, enable withSourcesJar in the convention plugin instead of invoking a root task.

.github/workflows/gradle.yml (1)

31-36: Build JDK 24 may be too bleeding-edge.

Consider LTS (17/21) or a matrix; some tooling (Dokka, KSP, Kover) and upstream clients may lag 24 support.

Example:

-      - name: Set up JDK 24
+      - name: Set up JDK
         uses: actions/setup-java@v5
         with:
-          java-version: 24
+          java-version: 21
           distribution: 'temurin'

Or use a matrix over [17, 21].

ai-mocks-ollama/build.gradle.kts (1)

43-51: Duplicate dependency declaration.

implementation(libs.langchain4j.kotlin) appears twice (Line 43 and Line 49). Remove one.

-                implementation(libs.langchain4j.kotlin)
                 // Note: There's no specific Ollama client in langchain4j or spring-ai yet
                 // If/when they become available, they should be added here
                 implementation(project.dependencies.platform(libs.langchain4j.bom))
                 implementation(project.dependencies.platform(libs.spring.ai.bom))
                 implementation(project.dependencies.platform(libs.spring.bom))
-                implementation(libs.langchain4j.kotlin)
+                implementation(libs.langchain4j.kotlin)
                 implementation(libs.langchain4j.ollama)
📜 Review details

Configuration used: CodeRabbit UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3d2498e and f2abbaa.

📒 Files selected for processing (18)
  • .editorconfig (2 hunks)
  • .github/workflows/gradle.yml (2 hunks)
  • Makefile (1 hunks)
  • ai-mocks-a2a/build.gradle.kts (2 hunks)
  • ai-mocks-anthropic/build.gradle.kts (2 hunks)
  • ai-mocks-gemini/build.gradle.kts (2 hunks)
  • ai-mocks-ollama/build.gradle.kts (2 hunks)
  • ai-mocks-openai/build.gradle.kts (2 hunks)
  • ai-mocks-openai/samples/shadow/pom.xml (1 hunks)
  • ai-mocks-openai/samples/shadow/src/test/java/com/example/OpenAITest.java (1 hunks)
  • buildSrc/build.gradle.kts (1 hunks)
  • buildSrc/src/main/kotlin/dokka-convention.gradle.kts (1 hunks)
  • buildSrc/src/main/kotlin/shadow-convention.gradle.kts (1 hunks)
  • mokksy/build.gradle.kts (2 hunks)
  • mokksy/src/commonMain/kotlin/me/kpavlov/mokksy/response/AbstractResponseDefinition.kt (1 hunks)
  • mokksy/src/commonMain/kotlin/me/kpavlov/mokksy/response/ResponseDefinition.kt (2 hunks)
  • mokksy/src/commonMain/kotlin/me/kpavlov/mokksy/response/ResponseDefinitionBuilders.kt (3 hunks)
  • mokksy/src/commonMain/kotlin/me/kpavlov/mokksy/response/StreamResponseDefinition.kt (2 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
ai-mocks-openai/samples/shadow/src/test/java/com/example/OpenAITest.java (2)
ai-mocks-core/src/commonMain/kotlin/me/kpavlov/aimocks/core/ModelRequestSpecification.kt (1)
  • requestBodyContains (46-49)
ai-mocks-openai/src/commonMain/kotlin/me/kpavlov/aimocks/openai/model/chat/ChatCompletionRequestBuilder.kt (1)
  • maxCompletionTokens (135-138)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: Codacy Static Code Analysis
  • GitHub Check: build
  • GitHub Check: submit-gradle
🔇 Additional comments (12)
mokksy/src/commonMain/kotlin/me/kpavlov/mokksy/response/ResponseDefinition.kt (1)

37-43: Propagate consistent status pair to the superclass (after fixing defaults)

After adjusting defaults above, this call site is fine. Just ensure all builders/callers pass matching pairs or rely on the base-class require.

mokksy/src/commonMain/kotlin/me/kpavlov/mokksy/response/ResponseDefinitionBuilders.kt (1)

83-90: Good: ResponseDefinitionBuilder forwards both status fields to the base

Forwarding both httpStatusCode and httpStatus is correct; once the mutator is fixed, they’ll remain consistent.

mokksy/src/commonMain/kotlin/me/kpavlov/mokksy/response/StreamResponseDefinition.kt (1)

51-63: Fail fast on inconsistent httpStatus/httpStatusCode

Constructor accepts both httpStatusCode and httpStatus; ensure callers can’t pass inconsistent pairs — validate here or rely on the base-class invariant. If adding local validation:

 public open class StreamResponseDefinition<P, T>(
@@
     ) : AbstractResponseDefinition<T>(
@@
     ) {
+    init {
+        require(httpStatus.value == httpStatusCode) {
+            "httpStatus ($httpStatus) must match httpStatusCode ($httpStatusCode)"
+        }
+    }

Also check the Streaming builder in ResponseDefinitionBuilders.kt — it currently passes only httpStatus (httpStatusCode defaults to 200) and can desync.

buildSrc/src/main/kotlin/dokka-convention.gradle.kts (1)

23-23: No-op formatting change — OK to merge.

ai-mocks-openai/samples/shadow/src/test/java/com/example/OpenAITest.java (1)

91-93: Double-check expected exception type.

500 may map to a different exception depending on SDK version (e.g., OpenAIException/ServiceUnavailable). Ensure CI confirms this.

ai-mocks-anthropic/build.gradle.kts (1)

7-8: Shadow convention applied — LGTM.

Consistent with repo-wide shading approach.

ai-mocks-a2a/build.gradle.kts (1)

8-9: Shadow convention inclusion — OK.

ai-mocks-openai/samples/shadow/pom.xml (1)

22-25: Bump com.openai:openai-java-client-okhttp to 3.6.0.
ai-mocks-openai/samples/shadow/pom.xml (lines 22–25) currently pins com.openai:openai-java-client-okhttp:3.1.2; upstream latest stable is com.openai:openai-java-client-okhttp:3.6.0 (released 2025-09-19). Update the version or justify pinning.

buildSrc/build.gradle.kts (1)

16-16: Shadow plugin dependency: confirm version and compatibility with your Gradle wrapper.

9.1.0 is the GradleUp fork. Make sure your wrapper uses a compatible Gradle version; otherwise task types and DSL may differ.

Would you like me to add a small check that prints Gradle and Shadow versions during CI?

ai-mocks-gemini/build.gradle.kts (1)

7-8: Applying shadow-convention here is fine.

No issues spotted with plugin application.

mokksy/build.gradle.kts (1)

8-9: Shadow plugin applied — OK.

Matches the shared convention usage.

ai-mocks-ollama/build.gradle.kts (1)

7-8: Plugin application is consistent.

No issues here.

Comment on lines +59 to 66
- name: OpenAI Maven Integration Tests
working-directory: ai-mocks-openai/samples/shadow
run: mvn --batch-mode test

Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Align Maven test JDK with POM (Java 17).

The Maven step currently runs on JDK 24. Set up Java 17 before mvn test to match <maven.compiler.release>.

+      - name: Set up JDK 17 for Maven sample
+        uses: actions/setup-java@v5
+        with:
+          java-version: 17
+          distribution: 'temurin'
       - name: OpenAI Maven Integration Tests
         working-directory: ai-mocks-openai/samples/shadow
         run: mvn --batch-mode test
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
- name: OpenAI Maven Integration Tests
working-directory: ai-mocks-openai/samples/shadow
run: mvn --batch-mode test
- name: Set up JDK 17 for Maven sample
uses: actions/setup-java@v5
with:
java-version: 17
distribution: 'temurin'
- name: OpenAI Maven Integration Tests
working-directory: ai-mocks-openai/samples/shadow
run: mvn --batch-mode test
🤖 Prompt for AI Agents
.github/workflows/gradle.yml around lines 59 to 62: the Maven integration-test
step runs under JDK 24 while the project POM compiles for Java 17; add a setup
step immediately before the mvn --batch-mode test to install and configure Java
17 (for example using actions/setup-java with java-version: '17' and an
appropriate distribution like temurin) so the mvn test runs using Java 17 and
matches <maven.compiler.release>.

Comment on lines 8 to 10
`shadow-convention`
id("com.gradleup.shadow")
// id("org.openapi.generator") version "7.12.0"
}
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

🧩 Analysis chain

Avoid double-applying Shadow plugin.

If shadow-convention already applies com.gradleup.shadow, this explicit id is redundant and can cause confusion.

     `shadow-convention`
-    id("com.gradleup.shadow")

Verification script:


🏁 Script executed:

#!/bin/bash
# Check if shadow plugin is applied inside the convention
rg -n "id\\(\"com\\.gradleup\\.shadow\"\\)" buildSrc -C2 || true
# Check other modules for duplicate direct plugin application
rg -n "id\\(\"com\\.gradleup\\.shadow\"\\)" -g '!buildSrc/**' -C2 || true

Length of output: 538


Remove duplicate com.gradleup.shadow plugin

shadow-convention already applies com.gradleup.shadow (buildSrc/src/main/kotlin/shadow-convention.gradle.kts:6), so remove the explicit id("com.gradleup.shadow") from ai-mocks-openai/build.gradle.kts (lines 8–11).

     `shadow-convention`
-    id("com.gradleup.shadow")
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
`shadow-convention`
id("com.gradleup.shadow")
// id("org.openapi.generator") version "7.12.0"
}
`shadow-convention`
// id("org.openapi.generator") version "7.12.0"
}
🤖 Prompt for AI Agents
In ai-mocks-openai/build.gradle.kts around lines 8 to 11, remove the duplicate
explicit plugin application id("com.gradleup.shadow") because the
shadow-convention plugin
(buildSrc/src/main/kotlin/shadow-convention.gradle.kts:6) already applies it;
edit the file to delete the id("com.gradleup.shadow") line (and any
now-unnecessary adjacent blank/comment line if desired) and keep the
shadow-convention reference only.

Comment on lines +16 to +35
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j-open-ai</artifactId>
<version>1.5.0</version>
</dependency>
<dependency>
<groupId>com.openai</groupId>
<artifactId>openai-java-client-okhttp</artifactId>
<version>3.1.2</version>
</dependency>
Copy link
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick

Scope demo deps to test.

These are only used by tests; avoid leaking to compile/runtime.

-        <dependency>
+        <dependency>
             <groupId>dev.langchain4j</groupId>
             <artifactId>langchain4j-open-ai</artifactId>
             <version>1.5.0</version>
+            <scope>test</scope>
         </dependency>
         <dependency>
             <groupId>com.openai</groupId>
             <artifactId>openai-java-client-okhttp</artifactId>
             <version>3.1.2</version>
+            <scope>test</scope>
         </dependency>
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j-open-ai</artifactId>
<version>1.5.0</version>
</dependency>
<dependency>
<groupId>com.openai</groupId>
<artifactId>openai-java-client-okhttp</artifactId>
<version>3.1.2</version>
</dependency>
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j-open-ai</artifactId>
<version>1.5.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.openai</groupId>
<artifactId>openai-java-client-okhttp</artifactId>
<version>3.1.2</version>
<scope>test</scope>
</dependency>
🤖 Prompt for AI Agents
In ai-mocks-openai/samples/shadow/pom.xml around lines 16 to 25, the
langchain4j-open-ai and openai-java-client-okhttp dependencies are declared
without a scope and are only needed for tests; update each <dependency> block to
include a <scope>test</scope> element so they are not included on the
compile/runtime classpath and are only available to the test phase.

Comment on lines +40 to +60
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.13.4</version>
<scope>test</scope>
</dependency>
</dependencies>
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

🧩 Analysis chain

JUnit engine missing — tests may not run.

Use the aggregator “junit-jupiter” or add “junit-jupiter-engine”.

-        <dependency>
-            <groupId>org.junit.jupiter</groupId>
-            <artifactId>junit-jupiter-api</artifactId>
-            <version>5.13.4</version>
-            <scope>test</scope>
-        </dependency>
+        <dependency>
+            <groupId>org.junit.jupiter</groupId>
+            <artifactId>junit-jupiter</artifactId>
+            <version>5.13.4</version>
+            <scope>test</scope>
+        </dependency>

🌐 Web query:

Confirm current Maven coordinates for JUnit 5 aggregator “junit-jupiter” and whether it brings the engine transitively.

💡 Result:

  • Maven coordinates (artifact, without fixing a version):

    • groupId: org.junit.jupiter
    • artifactId: junit-jupiter. (docs.junit.org)
  • Does the junit-jupiter aggregator bring the engine transitively?

    • Yes — the junit-jupiter aggregator POM transitively pulls in junit-jupiter-api, junit-jupiter-params and junit-jupiter-engine (the engine is a runtime dependency). (docs.junit.org)

Tip: manage versions with the junit-bom (org.junit:junit-bom) to keep consistent versions. (docs.junit.org)

Citations:


JUnit engine missing — add the engine or use the junit-jupiter aggregator.

File: ai-mocks-openai/samples/shadow/pom.xml (lines 40-45)

junit-jupiter-api alone does not bring the test engine; use org.junit.jupiter:junit-jupiter (aggregator, which transitively includes junit-jupiter-engine) or add org.junit.jupiter:junit-jupiter-engine as a test dependency. Consider managing versions with org.junit:junit-bom.

-        <dependency>
-            <groupId>org.junit.jupiter</groupId>
-            <artifactId>junit-jupiter-api</artifactId>
-            <version>5.13.4</version>
-            <scope>test</scope>
-        </dependency>
+        <dependency>
+            <groupId>org.junit.jupiter</groupId>
+            <artifactId>junit-jupiter</artifactId>
+            <version>5.13.4</version>
+            <scope>test</scope>
+        </dependency>
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.13.4</version>
<scope>test</scope>
</dependency>
</dependencies>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>5.13.4</version>
<scope>test</scope>
</dependency>
</dependencies>
🤖 Prompt for AI Agents
In ai-mocks-openai/samples/shadow/pom.xml around lines 40-45, the pom currently
depends only on org.junit.jupiter:junit-jupiter-api which does not include a
test engine; update the dependencies to either replace or add an aggregator
(org.junit.jupiter:junit-jupiter) so the engine is transitively included, or
keep junit-jupiter-api and explicitly add org.junit.jupiter:junit-jupiter-engine
with scope test; optionally manage versions via org.junit:junit-bom in
dependencyManagement and reference its version for consistency.

Comment on lines 9 to 21
tasks.shadowJar {

minimize {
exclude(dependency("kotlin:.*:.*"))
}

dependencies {
relocate("io.ktor", "dev.mokksy.relocated.io.ktor")
relocate("kotlinx.serialization", "dev.mokksy.relocated.kotlinx.serialization")
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Safer ShadowJar configuration: correct Kotlin exclude, add service file merging, and avoid relocating public API libs.

  • exclude(dependency("kotlin:.:.")) won’t match Kotlin (group is org.jetbrains.kotlin) → risk of stripping stdlib during minimize.
  • Many libs (Ktor, serialization) use ServiceLoader resources; merge them to avoid runtime failures.
  • Relocating kotlinx.serialization (and possibly io.ktor) can break consumers if any of those types/annotations appear in your public API. Consider making relocations opt-in per-module or restricting to internals-only modules.
-tasks.shadowJar {
-
-    minimize {
-        exclude(dependency("kotlin:.*:.*"))
-    }
-
-    dependencies {
-        relocate("io.ktor", "dev.mokksy.relocated.io.ktor")
-        relocate("kotlinx.serialization", "dev.mokksy.relocated.kotlinx.serialization")
-    }
-}
+tasks.named<ShadowJar>("shadowJar") {
+    // Ensure reproducible, distinct artifact
+    archiveClassifier.set("all")
+
+    // Keep Kotlin stdlib/coroutines intact during minimize
+    minimize {
+        exclude(dependency("org.jetbrains.kotlin:.*:.*"))
+        exclude(dependency("org.jetbrains.kotlinx:kotlinx-coroutines-.*:.*"))
+    }
+
+    // Merge service files to preserve SPI wiring
+    mergeServiceFiles()
+
+    // Optional/safer: gate relocations behind a project property
+    val enableRelocation = providers.gradleProperty("shadow.relocate").orElse("false").map { it.toBoolean() }.get()
+    if (enableRelocation) {
+        // WARNING: Do not relocate libraries that leak into your public API
+        relocate("io.ktor", "dev.mokksy.relocated.io.ktor")
+        // Avoid relocating kotlinx.serialization unless it is strictly internal
+        // relocate("kotlinx.serialization", "dev.mokksy.relocated.kotlinx.serialization")
+    }
+}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
tasks.shadowJar {
minimize {
exclude(dependency("kotlin:.*:.*"))
}
dependencies {
relocate("io.ktor", "dev.mokksy.relocated.io.ktor")
relocate("kotlinx.serialization", "dev.mokksy.relocated.kotlinx.serialization")
}
}
tasks.named<com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar>("shadowJar") {
// Ensure reproducible, distinct artifact
archiveClassifier.set("all")
// Keep Kotlin stdlib/coroutines intact during minimize
minimize {
exclude(dependency("org.jetbrains.kotlin:.*:.*"))
exclude(dependency("org.jetbrains.kotlinx:kotlinx-coroutines-.*:.*"))
}
// Merge service files to preserve SPI wiring
mergeServiceFiles()
// Optional/safer: gate relocations behind a project property
val enableRelocation = providers.gradleProperty("shadow.relocate").orElse("false").map { it.toBoolean() }.get()
if (enableRelocation) {
// WARNING: Do not relocate libraries that leak into your public API
relocate("io.ktor", "dev.mokksy.relocated.io.ktor")
// Avoid relocating kotlinx.serialization unless it is strictly internal
// relocate("kotlinx.serialization", "dev.mokksy.relocated.kotlinx.serialization")
}
}
🤖 Prompt for AI Agents
In buildSrc/src/main/kotlin/shadow-convention.gradle.kts around lines 9 to 19,
the ShadowJar config is unsafe: the Kotlin minimize exclude uses the wrong group
(so stdlib may be stripped), service loader files aren’t merged (risking runtime
failures), and relocating public API libraries (kotlinx.serialization, io.ktor)
can break consumers; change the minimize exclude to match org.jetbrains.kotlin
artifacts (e.g. exclude(dependency("org.jetbrains.kotlin:.*:.*"))), configure
the ShadowJar to merge service files (e.g. use mergeServiceFiles() in the
transform/merge configuration), and remove or make relocations opt-in/restricted
to internal modules (do not relocate kotlinx.serialization and avoid relocating
io.ktor unless this jar is strictly internal or provide a flag to apply
relocations per-module).

Comment on lines 21 to 25
tasks.assemble {
dependsOn(tasks.shadowJar)
}
Copy link
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick

Assemble depending on shadowJar: consider making it opt-in.

For all modules, this slows builds and forces shading when not needed (e.g., during dev/test). Prefer a separate lifecycle task (e.g., shadowAll) or guard with a property.

-tasks.assemble {
-    dependsOn(tasks.shadowJar)
-}
+if (providers.gradleProperty("shadow.on.assemble").orElse("false").map { it.toBoolean() }.get()) {
+    tasks.named("assemble").configure { dependsOn(tasks.named("shadowJar")) }
+}

If CI depends on shaded jars, set -Pshadow.on.assemble=true in the workflow instead of enforcing it universally.

Makefile Outdated
Comment on lines 3 to 4
rm -rf ~/.m2/repository/me/kpavlov/aimocks ~/.m2/repository/me/kpavlov/mokksy
./gradlew clean build dokkaJavadocJar publishToMavenLocal koverHtmlReport
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Avoid nuking ~/.m2; isolate Maven Local per-project.

Deleting user-wide caches is risky and non‑portable. Prefer a project-scoped Maven local dir via Gradle.

Example:

-build:
-	rm -rf ~/.m2/repository/me/kpavlov/aimocks ~/.m2/repository/me/kpavlov/mokksy
-	./gradlew clean build dokkaJavadocJar publishToMavenLocal koverHtmlReport
+build:
+	# Use a project-local Maven repo to avoid wiping ~/.m2
+	./gradlew \
+	  -Dorg.gradle.maven.repo.local=.m2-local \
+	  clean build dokkaJavadocJar publishToMavenLocal koverHtmlReport

And similarly in “publish”.

Committable suggestion skipped: line range outside the PR's diff.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 11

📜 Review details

Configuration used: CodeRabbit UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f2abbaa and 86a8371.

📒 Files selected for processing (8)
  • ai-mocks-a2a/build.gradle.kts (2 hunks)
  • ai-mocks-anthropic/build.gradle.kts (2 hunks)
  • ai-mocks-gemini/build.gradle.kts (2 hunks)
  • ai-mocks-ollama/build.gradle.kts (2 hunks)
  • ai-mocks-openai/build.gradle.kts (2 hunks)
  • ai-mocks-openai/samples/shadow/pom.xml (1 hunks)
  • buildSrc/src/main/kotlin/publish-convention.gradle.kts (2 hunks)
  • mokksy/build.gradle.kts (2 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: Codacy Static Code Analysis
  • GitHub Check: dependency-submission
  • GitHub Check: build
  • GitHub Check: submit-gradle
🔇 Additional comments (7)
ai-mocks-openai/samples/shadow/pom.xml (3)

7-7: Typo in artifactId.

Use “shadowed”.

-    <artifactId>mokksy-openai-shadewed</artifactId>
+    <artifactId>mokksy-openai-shadowed</artifactId>

26-35: Scope demo deps to test.

These are only used in tests; don’t leak to compile/runtime.

         <dependency>
             <groupId>dev.langchain4j</groupId>
             <artifactId>langchain4j-open-ai</artifactId>
             <version>1.5.0</version>
+            <scope>test</scope>
         </dependency>
         <dependency>
             <groupId>com.openai</groupId>
             <artifactId>openai-java-client-okhttp</artifactId>
             <version>3.1.2</version>
+            <scope>test</scope>
         </dependency>

55-60: JUnit engine missing — switch to aggregator.

Replace junit-jupiter-api with junit-jupiter (brings the engine).

-        <dependency>
-            <groupId>org.junit.jupiter</groupId>
-            <artifactId>junit-jupiter-api</artifactId>
-            <version>5.13.4</version>
-            <scope>test</scope>
-        </dependency>
+        <dependency>
+            <groupId>org.junit.jupiter</groupId>
+            <artifactId>junit-jupiter</artifactId>
+            <version>5.13.4</version>
+            <scope>test</scope>
+        </dependency>
ai-mocks-openai/build.gradle.kts (1)

8-10: Remove duplicate Shadow plugin application.

shadow-convention already applies com.gradleup.shadow.

     `shadow-convention`
-    id("com.gradleup.shadow")
ai-mocks-gemini/build.gradle.kts (1)

66-77: Merge shaded jar and sources into one publication.

-publishing {
-    publications {
-        create<MavenPublication>("shadow") {
-            artifactId = project.name + "-all"
-            artifact(tasks["shadowJar"])
-        }
-        create<MavenPublication>("shadowSourcesJar") {
-            artifactId = project.name + "-all"
-            artifact(tasks["jvmSourcesJar"])
-        }
-    }
-}
+publishing {
+    publications {
+        create<MavenPublication>("shadowAll") {
+            artifactId = project.name + "-all"
+            artifact(tasks.named("shadowJar"))
+            artifact(tasks.named("jvmSourcesJar"))
+        }
+    }
+}
ai-mocks-ollama/build.gradle.kts (1)

72-83: Prevent repository conflicts: publish one GAV with multiple artifacts.

-publishing {
-    publications {
-        create<MavenPublication>("shadow") {
-            artifactId = project.name + "-all"
-            artifact(tasks["shadowJar"])
-        }
-        create<MavenPublication>("shadowSourcesJar") {
-            artifactId = project.name + "-all"
-            artifact(tasks["jvmSourcesJar"])
-        }
-    }
-}
+publishing {
+    publications {
+        create<MavenPublication>("shadowAll") {
+            artifactId = project.name + "-all"
+            artifact(tasks.named("shadowJar"))
+            artifact(tasks.named("jvmSourcesJar"))
+        }
+    }
+}
buildSrc/src/main/kotlin/publish-convention.gradle.kts (1)

68-71: SCM URLs: use canonical forms — manual verification required

Blocked: Gradle daemon failed to start and ~/.m2/repository is missing in the sandbox; run ./gradlew :ai-mocks-openai:publishToMavenLocal -x test locally and confirm the generated POM's scm entries.

File: buildSrc/src/main/kotlin/publish-convention.gradle.kts (lines 68–71)

-            connection = "scm:git:git://github.com/mokksy/ai-mocks.git"
-            developerConnection = "scm:git:ssh://github.com/mokksy/ai-mocks.git"
-            url = "https://github.com/mokksy/ai-mocks"
+            connection = "scm:git:https://github.com/mokksy/ai-mocks.git"
+            developerConnection = "scm:git:ssh://[email protected]/mokksy/ai-mocks.git"
+            url = "https://github.com/mokksy/ai-mocks"

Comment on lines 61 to 64
tasks.shadowJar {
archiveClassifier = ""
archiveExtension = "jar"
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Use .set(...) for archive properties in Kotlin DSL.

-tasks.shadowJar {
-    archiveClassifier = ""
-    archiveExtension = "jar"
-}
+tasks.shadowJar {
+    archiveClassifier.set("")
+    archiveExtension.set("jar")
+}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
tasks.shadowJar {
archiveClassifier = ""
archiveExtension = "jar"
}
tasks.shadowJar {
archiveClassifier.set("")
archiveExtension.set("jar")
}
🤖 Prompt for AI Agents
In ai-mocks-gemini/build.gradle.kts around lines 61 to 64, the Kotlin DSL
archive properties are being set via assignment which is incorrect; replace the
direct assignments with the property setters (use archiveClassifier.set("") and
archiveExtension.set("jar")) to correctly configure the shadowJar task in Kotlin
DSL.

Comment on lines 65 to 68
tasks.shadowJar {
archiveClassifier = ""
archiveExtension = "jar"
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Kotlin DSL fix: set archive props via .set(...).

-tasks.shadowJar {
-    archiveClassifier = ""
-    archiveExtension = "jar"
-}
+tasks.shadowJar {
+    archiveClassifier.set("")
+    archiveExtension.set("jar")
+}
🤖 Prompt for AI Agents
In ai-mocks-openai/build.gradle.kts around lines 65 to 68, the ShadowJar task is
setting archiveClassifier and archiveExtension using direct assignment which is
invalid in Kotlin DSL; replace those assignments with the property setters, e.g.
call archiveClassifier.set("") and archiveExtension.set("jar") on the task's
archive properties so the values are configured via the Gradle Property API.

Comment on lines 1 to 72
<?xml version="1.0" encoding="UTF-8"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"
xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>mokksy-openai-shadewed</artifactId>
<version>1.0.0-SNAPSHOT</version>

<properties>
<java.version>17</java.version>
<maven.compiler.release>${java.version}</maven.compiler.release>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-bom</artifactId>
<version>1.9.25</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j-open-ai</artifactId>
<version>1.5.0</version>
</dependency>
<dependency>
<groupId>com.openai</groupId>
<artifactId>openai-java-client-okhttp</artifactId>
<version>3.1.2</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<version>2.0.17</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<version>3.27.4</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>me.kpavlov.aimocks</groupId>
<artifactId>ai-mocks-openai-all</artifactId>
<version>0.5.0-SNAPSHOT</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.13.4</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Pin Surefire for JUnit 5.

Without an explicit surefire ≥2.22, Jupiter tests may not run.

 </dependencies>
 </project>
+<build>
+  <plugins>
+    <plugin>
+      <groupId>org.apache.maven.plugins</groupId>
+      <artifactId>maven-surefire-plugin</artifactId>
+      <version>3.5.0</version>
+    </plugin>
+  </plugins>
+  <pluginManagement/>
+</build>

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In ai-mocks-openai/samples/shadow/pom.xml around lines 1 to 61, the POM lacks an
explicit Maven Surefire Plugin declaration so JUnit Jupiter tests may not run;
add a build->plugins entry declaring maven-surefire-plugin with a version >=
2.22 (e.g., 2.22.2 or a newer stable release) to ensure Surefire runs JUnit 5
tests, or alternatively configure the Maven Surefire Provider for JUnit
Platform; place this plugin configuration inside a <build><plugins> section in
the pom.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 8

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
.editorconfig (1)

43-46: Also set indent_style=tab for XML.

Keep Kotlin/XML consistent; otherwise IDEs may mix tabs/spaces.

 [*.xml]
+indent_style = tab
 tab_width = 4
 indent_size = tab
ai-mocks-ollama/build.gradle.kts (1)

43-51: Duplicate dependency: langchain4j-kotlin listed twice.

Remove one to keep the graph clean.

                 implementation(libs.langchain4j.kotlin)
                 // Note: There's no specific Ollama client in langchain4j or spring-ai yet
                 // If/when they become available, they should be added here
                 implementation(project.dependencies.platform(libs.langchain4j.bom))
                 implementation(project.dependencies.platform(libs.spring.ai.bom))
                 implementation(project.dependencies.platform(libs.spring.bom))
-                implementation(libs.langchain4j.kotlin)
                 implementation(libs.langchain4j.ollama)
📜 Review details

Configuration used: CodeRabbit UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 86a8371 and 493bb62.

📒 Files selected for processing (12)
  • .editorconfig (2 hunks)
  • .github/workflows/gradle.yml (2 hunks)
  • Makefile (2 hunks)
  • RELEASE.md (1 hunks)
  • ai-mocks-a2a/build.gradle.kts (2 hunks)
  • ai-mocks-anthropic/build.gradle.kts (2 hunks)
  • ai-mocks-gemini/build.gradle.kts (2 hunks)
  • ai-mocks-ollama/build.gradle.kts (2 hunks)
  • ai-mocks-openai/build.gradle.kts (2 hunks)
  • ai-mocks-openai/samples/shadow/pom.xml (1 hunks)
  • ai-mocks-openai/src/commonMain/kotlin/me/kpavlov/aimocks/openai/MockOpenai.kt (1 hunks)
  • mokksy/build.gradle.kts (2 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Codacy Static Code Analysis
🔇 Additional comments (11)
ai-mocks-openai/samples/shadow/pom.xml (4)

7-7: Typo in artifactId.

-    <artifactId>mokksy-openai-shadewed</artifactId>
+    <artifactId>mokksy-openai-shadowed</artifactId>

26-35: Scope demo client deps to test.

These are only needed by tests.

         <dependency>
             <groupId>dev.langchain4j</groupId>
             <artifactId>langchain4j-open-ai</artifactId>
             <version>1.5.0</version>
+            <scope>test</scope>
         </dependency>
         <dependency>
             <groupId>com.openai</groupId>
             <artifactId>openai-java-client-okhttp</artifactId>
             <version>3.1.2</version>
+            <scope>test</scope>
         </dependency>

55-60: JUnit engine missing — use aggregator or add engine.

-        <dependency>
-            <groupId>org.junit.jupiter</groupId>
-            <artifactId>junit-jupiter-api</artifactId>
-            <version>5.13.4</version>
-            <scope>test</scope>
-        </dependency>
+        <dependency>
+            <groupId>org.junit.jupiter</groupId>
+            <artifactId>junit-jupiter</artifactId>
+            <version>5.13.4</version>
+            <scope>test</scope>
+        </dependency>

1-61: Pin Surefire to run JUnit 5.

Ensure a modern surefire runs Jupiter tests.

 </dependencies>
+</project>
+<build>
+  <plugins>
+    <plugin>
+      <groupId>org.apache.maven.plugins</groupId>
+      <artifactId>maven-surefire-plugin</artifactId>
+      <version>3.5.0</version>
+    </plugin>
+  </plugins>
+</build>
-</project>
ai-mocks-openai/build.gradle.kts (1)

8-9: Remove duplicate Shadow plugin application.

shadow-convention already applies com.gradleup.shadow.

-    `shadow-convention`
-    id("com.gradleup.shadow")
+    `shadow-convention`
Makefile (2)

3-5: Don’t delete ~/.m2; use a project-local Maven repo.

Safer and CI-friendly; avoids nuking user caches.

-	rm -rf ~/.m2/repository/me/kpavlov/aimocks ~/.m2/repository/me/kpavlov/mokksy && \
-	./gradlew --rerun-tasks clean build publishToMavenLocal koverHtmlReport && \
-	(cd ai-mocks-openai/samples/shadow && mvn test)
+	./gradlew --rerun-tasks \
+	  -Dorg.gradle.maven.repo.local=.m2-local \
+	  clean build publishToMavenLocal koverHtmlReport && \
+	(cd ai-mocks-openai/samples/shadow && mvn -Dmaven.repo.local=.m2-local test)

48-49: Same here: avoid rm -rf on ~/.m2 and publish to project-local repo.

-	./gradlew --rerun-tasks clean build check sourcesJar publishToMavenLocal
+	./gradlew --rerun-tasks \
+	  -Dorg.gradle.maven.repo.local=.m2-local \
+	  clean build check sourcesJar publishToMavenLocal
ai-mocks-anthropic/build.gradle.kts (1)

62-75: Use lazy task providers in publication.

Prevents eager configuration and speeds up configuration time.

-            artifact(tasks["shadowJar"]) {
+            artifact(tasks.named("shadowJar")) {
                 classifier = ""
                 extension = "jar"
             }
-            artifact(tasks["jvmSourcesJar"]) {
+            artifact(tasks.named("jvmSourcesJar")) {
                 classifier = "sources"
             }
ai-mocks-ollama/build.gradle.kts (1)

67-80: Use lazy providers for artifacts in publication.

-            artifact(tasks["shadowJar"]) {
+            artifact(tasks.named("shadowJar")) {
                 classifier = ""
                 extension = "jar"
             }
-            artifact(tasks["jvmSourcesJar"]) {
+            artifact(tasks.named("jvmSourcesJar")) {
                 classifier = "sources"
             }
mokksy/build.gradle.kts (1)

8-8: Confirm shadowJar task sets empty classifier at the task level.

If the empty classifier isn’t set on the ShadowJar task (via the convention), publishing with classifier = "" here won’t rename the produced file; it only affects POM metadata. Prefer configuring archiveClassifier.set("") on the task and keep the publication clean.

.github/workflows/gradle.yml (1)

59-61: Align Maven sample to JDK 17 before mvn test.

The Maven sample compiles for Java 17; switch toolchain before running tests.

Apply this diff:

+      - name: Set up JDK 17 for Maven sample
+        uses: actions/setup-java@v5
+        with:
+          java-version: 17
+          distribution: 'temurin'
       - name: OpenAI Maven Integration Tests
         working-directory: ai-mocks-openai/samples/shadow
         run: mvn --batch-mode test

Comment on lines 26 to 30
[*.{kt,kts}]
indent_size = 4
tab_width = 4
indent_size = tab
max_line_length = 100
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Set indent_style=tab for Kotlin to match indent_size=tab.

Without indent_style=tab, many tools will still insert spaces despite indent_size=tab.

Apply:

 [*.{kt,kts}]
+indent_style = tab
 tab_width = 4
 indent_size = tab
 max_line_length = 100
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
[*.{kt,kts}]
indent_size = 4
tab_width = 4
indent_size = tab
max_line_length = 100
[*.{kt,kts}]
indent_style = tab
tab_width = 4
indent_size = tab
max_line_length = 100
🤖 Prompt for AI Agents
In .editorconfig around lines 26 to 29, the Kotlin section lacks an indent_style
setting causing some tools to insert spaces; add "indent_style = tab" to the
[*.{kt,kts}] block so it explicitly uses tabs to match indent_size = tab and
prevent tools from defaulting to spaces.

- name: Build with Gradle
run: gradle clean build dokkaGenerate dokkaJavadocJar sourcesJar koverXmlReport
run: ./gradlew --rerun-tasks --no-daemon clean build publishToMavenLocal koverXmlReport
Copy link
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick

Minor: Add Maven cache to speed up the sample test.

Caching ~/.m2/repository cuts test time and avoids re-downloading the shaded artifact.

Apply this diff just before the Maven test step:

+      - name: Cache Maven repository
+        uses: actions/cache@v4
+        with:
+          path: ~/.m2/repository
+          key: maven-${{ runner.os }}-${{ hashFiles('**/pom.xml') }}
+          restore-keys: |
+            maven-${{ runner.os }}-
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
run: ./gradlew --rerun-tasks --no-daemon clean build publishToMavenLocal koverXmlReport
run: ./gradlew --rerun-tasks --no-daemon clean build publishToMavenLocal koverXmlReport
- name: Cache Maven repository
uses: actions/cache@v4
with:
path: ~/.m2/repository
key: maven-${{ runner.os }}-${{ hashFiles('**/pom.xml') }}
restore-keys: |
maven-${{ runner.os }}-
🤖 Prompt for AI Agents
.github/workflows/gradle.yml around line 57: the workflow currently reruns
Gradle tasks without caching Maven artifacts which slows tests and re-downloads
the shaded artifact; add an actions/cache step immediately before the
Maven/Gradle test step that caches ~/.m2/repository with an appropriate key
(e.g., runnerOS + hash of relevant build files like settings.gradle or
build.gradle(.kts) or pom.xml) and a restore-keys fallback so the cache is
restored on subsequent runs to speed up the sample test.

Comment on lines 68 to 81
publishing {
publications {
create<MavenPublication>("shadow") {
artifactId = project.name + "-standalone"
artifact(tasks["shadowJar"]) {
classifier = ""
extension = "jar"
}
artifact(tasks["jvmSourcesJar"]) {
classifier = "sources"
}
}
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick

Switch to lazy providers for publication artifacts.

-            artifact(tasks["shadowJar"]) {
+            artifact(tasks.named("shadowJar")) {
                 classifier = ""
                 extension = "jar"
             }
-            artifact(tasks["jvmSourcesJar"]) {
+            artifact(tasks.named("jvmSourcesJar")) {
                 classifier = "sources"
             }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
publishing {
publications {
create<MavenPublication>("shadow") {
artifactId = project.name + "-standalone"
artifact(tasks["shadowJar"]) {
classifier = ""
extension = "jar"
}
artifact(tasks["jvmSourcesJar"]) {
classifier = "sources"
}
}
}
}
publishing {
publications {
create<MavenPublication>("shadow") {
artifactId = project.name + "-standalone"
artifact(tasks.named("shadowJar")) {
classifier = ""
extension = "jar"
}
artifact(tasks.named("jvmSourcesJar")) {
classifier = "sources"
}
}
}
}
🤖 Prompt for AI Agents
In ai-mocks-a2a/build.gradle.kts around lines 68 to 81, switch direct task
accesses to lazy providers so publication artifact references are evaluated
lazily: replace tasks["shadowJar"] and tasks["jvmSourcesJar"] with
tasks.named("shadowJar") and tasks.named("jvmSourcesJar") (or the corresponding
TaskProvider variables) when calling artifact(...), and configure
classifier/extension inside the artifact block; this ensures the publication
uses TaskProvider-based lazy evaluation and avoids eager task realization during
configuration.

Comment on lines 65 to 75
publishing {
publications {
create<MavenPublication>("shadow") {
artifactId = project.name + "-standalone"
artifact(tasks["shadowJar"]) {
classifier = ""
extension = "jar"
}
artifact(tasks["jvmSourcesJar"]) {
classifier = "sources"
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick

Avoid eager task realization in publishing.

Use tasks.named(...) providers for publication artifacts.

-        create<MavenPublication>("shadow") {
+        create<MavenPublication>("shadow") {
             artifactId = project.name + "-standalone"
-            artifact(tasks["shadowJar"]) {
+            artifact(tasks.named("shadowJar")) {
                 classifier = ""
                 extension = "jar"
             }
-            artifact(tasks["jvmSourcesJar"]) {
+            artifact(tasks.named("jvmSourcesJar")) {
                 classifier = "sources"
             }
         }
🤖 Prompt for AI Agents
In ai-mocks-openai/build.gradle.kts around lines 65-76, the publication
currently realizes tasks eagerly via tasks["shadowJar"] and
tasks["jvmSourcesJar"]; replace those with task providers using
tasks.named("shadowJar") and tasks.named("jvmSourcesJar") so the publication
references task providers instead of forcing task realization. Update the
artifact calls to pass the TaskProvider for each artifact and keep the
classifier/extension configuration in the artifact block.

Comment on lines +34 to 36
name = "MockOpenai",
verbose = verbose,
) { config ->
Copy link
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick

🧩 Analysis chain

Named server improves observability; verify uniqueness expectations in mokksy.

Hard‑coding name = "MockOpenai" is fine and helpful for logs/tracing. If mokksy enforces unique names per running server, parallel tests or multiple mock instances could collide. Please confirm uniqueness semantics and whether we need a follow‑up to allow overriding the name in tests (without changing the current public API).

Run this to spot other ServerConfiguration( call sites lacking an explicit name:


🏁 Script executed:

#!/bin/bash
# Inspect nearby args for each ServerConfiguration usage
rg -nP --type=kt -C3 '\bServerConfiguration\s*\('

Length of output: 26


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "Searching repo for ServerConfiguration occurrences..."
rg -n -C3 'ServerConfiguration' || true

echo "----"
echo "Searching for name assignments (literal 'name = ' and 'name=')..."
rg -n -C1 'name =' || true
rg -n -C1 'name=' || true

echo "----"
echo "Searching for occurrences of MockOpenai..."
rg -n -C3 'MockOpenai' || true

echo "----"
FILE=ai-mocks-openai/src/commonMain/kotlin/me/kpavlov/aimocks/openai/MockOpenai.kt
if [ -f "$FILE" ]; then
  echo "Printing $FILE (first 200 lines):"
  sed -n '1,200p' "$FILE" || true
else
  echo "$FILE not found; listing Kotlin files under ai-mocks-openai:"
  rg -n --hidden --glob 'ai-mocks-openai/**' '\.kt' || true
fi

Length of output: 96903


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== Print MokksyServer.kt (first 400 lines) ==="
sed -n '1,400p' mokksy/src/commonMain/kotlin/me/kpavlov/mokksy/MokksyServer.kt || true

echo
echo "=== Search for configuration.name usages ==="
rg -n --hidden --no-ignore-vcs --type kotlin 'configuration\.name' || true

echo
echo "=== Search for accessing ServerConfiguration.name or comparing server names ==="
rg -n --hidden --no-ignore-vcs --type kotlin 'ServerConfiguration|configuration.name|configuration\\.name|\\.name\\s*==' || true

echo
echo "=== Search for global server registry/collection variables (servers, activeServers, instances) ==="
rg -n --hidden --no-ignore-vcs --type kotlin '\bservers\b|\bactiveServers\b|\binstances\b|\bregistered\b' || true

Length of output: 27099


Named server improves observability — Mokksy does not enforce unique server names; consider making the mock name overridable

ServerConfiguration defaults to name = "Mokksy" and mokksy has no global registry/enforcement of unique server names; the name is used only for logging/stub toString (ServerConfiguration.kt; AbstractMockLlm.kt; Stub.kt). MockOpenai sets name = "MockOpenai" (ai-mocks-openai/src/commonMain/kotlin/me/kpavlov/aimocks/openai/MockOpenai.kt:33–36) while many other mocks rely on the default. No runtime collision will be thrown by mokksy, but identical names can make logs ambiguous in parallel tests — consider adding an optional name parameter/overload on MockOpenai (and other mocks) to allow tests to override the server name for clearer observability.

🤖 Prompt for AI Agents
In ai-mocks-openai/src/commonMain/kotlin/me/kpavlov/aimocks/openai/MockOpenai.kt
around lines 34 to 36, MockOpenai hardcodes name = "MockOpenai", which can
create ambiguous logs in parallel tests; add an optional name parameter (with
default "MockOpenai") to the MockOpenai constructor or factory overload and pass
that value into the ServerConfiguration creation instead of the hardcoded
string; update callers/tests to accept the new optional parameter as needed and
mirror this pattern on other mock classes if desired for consistency.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 15

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (7)
ai-mocks-openai/src/commonMain/kotlin/me/kpavlov/aimocks/openai/MockOpenai.kt (1)

3-14: Fix missing import for ChatCompletionRequest (compile error)

ChatCompletionRequest::class is referenced but not imported; it’s in ...openai.model.chat. Add the import.

 import me.kpavlov.aimocks.openai.responses.OpenaiResponsesRequestSpecification
+import me.kpavlov.aimocks.openai.model.chat.ChatCompletionRequest
 import me.kpavlov.mokksy.ServerConfiguration

Also applies to: 56-59

ai-mocks-anthropic/build.gradle.kts (1)

1-9: Shading strategy: confirm relocations to prevent clashes (esp. ktor/kotlinx)

For “standalone” artifacts, verify relocation rules in shadow-convention don’t relocate Kotlin stdlib/ktor/kotlinx unless intentional; relocating these can break consumers.

ai-mocks-openai/samples/OpenaiLc4jSample.ipynb (1)

75-87: Replace unsupported DSL call with a supported overload.

The model.chat { … } builder isn’t available; call an existing overload.

-  val result =
-    model.chat {
-      parameters =
-        OpenAiChatRequestParameters
-          .builder()
-          .maxCompletionTokens(120)
-          .temperature(0.42)
-          .modelName("4o")
-          .seed(100500)
-          .build()
-      messages += userMessage("Say Hello")
-    }
+  val result = model.chat(userMessage("Say Hello"))
.github/workflows/gradle.yml (1)

63-71: Include Maven Surefire reports in the JUnit summary.

Current glob only catches Gradle reports; add Surefire to see Maven test results.

       - name: Publish Test Report
         uses: mikepenz/action-junit-report@v5
         if: success() || failure() # always run even if the previous step fails
         with:
-          report_paths: '**/test-results/**/TEST-*.xml'
+          report_paths: |
+            **/test-results/**/TEST-*.xml
+            **/surefire-reports/TEST-*.xml
           annotate_only: true
           detailed_summary: true
           flaky_summary: true
           include_empty_in_summary: false
           skip_success_summary: true
mokksy/src/commonMain/kotlin/me/kpavlov/mokksy/response/ResponseDefinitionBuilders.kt (3)

52-54: Builder mutator desyncs status fields

httpStatus(status: Int) updates only httpStatus, leaving httpStatusCode stale. Add an overload and set both.

-    public fun httpStatus(status: Int) {
-        this.httpStatus = HttpStatusCode.fromValue(status)
-    }
+    public fun httpStatus(status: Int) {
+        this.httpStatusCode = status
+        this.httpStatus = HttpStatusCode.fromValue(status)
+    }
+
+    public fun httpStatus(status: HttpStatusCode) {
+        this.httpStatusCode = status.value
+        this.httpStatus = status
+    }

116-124: Streaming builder passes only httpStatus; httpStatusCode remains 200

Forward both to the base builder to keep invariants and avoid 200/XXX mismatches.

 ) : AbstractResponseDefinitionBuilder<P, T>(
-        httpStatus = httpStatus,
-        headers = headers
-    ) {
+        httpStatusCode = httpStatus.value,
+        httpStatus = httpStatus,
+        headers = headers
+    ) {

136-145: StreamResponseDefinition build omits httpStatusCode

Pass httpStatusCode to the product to avoid defaulting to 200.

     public override fun build(): StreamResponseDefinition<P, T> =
         StreamResponseDefinition(
             chunkFlow = flow,
             chunks = chunks.toList(),
+            httpStatusCode = httpStatusCode,
             httpStatus = httpStatus,
             headers = headersLambda,
             headerList = Collections.unmodifiableList(headers),
             delayBetweenChunks = delayBetweenChunks,
             delay = delay,
         )
📜 Review details

Configuration used: CodeRabbit UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 493bb62 and dbce728.

📒 Files selected for processing (22)
  • .editorconfig (2 hunks)
  • .github/workflows/gradle.yml (2 hunks)
  • Makefile (2 hunks)
  • RELEASE.md (1 hunks)
  • ai-mocks-a2a/build.gradle.kts (2 hunks)
  • ai-mocks-anthropic/build.gradle.kts (2 hunks)
  • ai-mocks-gemini/build.gradle.kts (2 hunks)
  • ai-mocks-ollama/build.gradle.kts (2 hunks)
  • ai-mocks-openai/build.gradle.kts (2 hunks)
  • ai-mocks-openai/samples/OpenaiLc4jSample.ipynb (6 hunks)
  • ai-mocks-openai/samples/shadow/pom.xml (1 hunks)
  • ai-mocks-openai/samples/shadow/src/test/java/com/example/OpenAITest.java (1 hunks)
  • ai-mocks-openai/src/commonMain/kotlin/me/kpavlov/aimocks/openai/MockOpenai.kt (1 hunks)
  • buildSrc/build.gradle.kts (1 hunks)
  • buildSrc/src/main/kotlin/dokka-convention.gradle.kts (1 hunks)
  • buildSrc/src/main/kotlin/publish-convention.gradle.kts (2 hunks)
  • buildSrc/src/main/kotlin/shadow-convention.gradle.kts (1 hunks)
  • mokksy/build.gradle.kts (2 hunks)
  • mokksy/src/commonMain/kotlin/me/kpavlov/mokksy/response/AbstractResponseDefinition.kt (1 hunks)
  • mokksy/src/commonMain/kotlin/me/kpavlov/mokksy/response/ResponseDefinition.kt (2 hunks)
  • mokksy/src/commonMain/kotlin/me/kpavlov/mokksy/response/ResponseDefinitionBuilders.kt (3 hunks)
  • mokksy/src/commonMain/kotlin/me/kpavlov/mokksy/response/StreamResponseDefinition.kt (2 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
ai-mocks-openai/samples/shadow/src/test/java/com/example/OpenAITest.java (4)
ai-mocks-openai/src/commonMain/kotlin/me/kpavlov/aimocks/openai/MockOpenai.kt (3)
  • baseUrl (133-133)
  • completion (45-49)
  • completion (51-92)
mokksy/src/commonMain/kotlin/me/kpavlov/mokksy/response/ResponseDefinitionBuilders.kt (4)
  • build (61-61)
  • build (91-100)
  • build (136-145)
  • delayMillis (48-50)
ai-mocks-core/src/commonMain/kotlin/me/kpavlov/aimocks/core/ModelRequestSpecification.kt (1)
  • requestBodyContains (46-49)
ai-mocks-openai/src/commonMain/kotlin/me/kpavlov/aimocks/openai/model/chat/ChatCompletionRequestBuilder.kt (1)
  • maxCompletionTokens (135-138)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Codacy Static Code Analysis
  • GitHub Check: build
🔇 Additional comments (26)
.editorconfig (3)

40-40: LGTM: build-scope import-on-demand override is clear.

Scoped formatting for build Kotlin files is explicit and consistent.


26-29: Set indent_style=tab for Kotlin to actually use tabs.

Currently inherits indent_style=space from [*]; many tools will keep inserting spaces even with indent_size=tab.

 [*.{kt,kts}]
 tab_width = 4
 indent_size = tab
+indent_style = tab
 max_line_length = 100

43-45: Don't add indent_style = tab — repository XML files are space‑indented.

Set [*.xml] to indent_style = space and indent_size = 4 in .editorconfig (lines ~43–45), or convert the XML files (e.g. ai-mocks-openai/samples/shadow/pom.xml) to tabs before adding indent_style = tab.

Likely an incorrect or invalid review comment.

buildSrc/src/main/kotlin/dokka-convention.gradle.kts (1)

23-27: LGTM — formatting‑only change

No behavioral impact.

ai-mocks-openai/src/commonMain/kotlin/me/kpavlov/aimocks/openai/MockOpenai.kt (1)

27-36: Allow overriding server name to avoid ambiguous logs in parallel runs

Make the name configurable with a default.

-public open class MockOpenai(
-
-    port: Int = 0,
-    verbose: Boolean = true,
-) : AbstractMockLlm(
+public open class MockOpenai(
+    port: Int = 0,
+    verbose: Boolean = true,
+    name: String = "MockOpenai",
+) : AbstractMockLlm(
         port = port,
         configuration =
             ServerConfiguration(
-                name = "MockOpenai",
+                name = name,
                 verbose = verbose,
             ) { config ->
RELEASE.md (1)

16-16: Make --rerun-tasks opt‑in; don’t slow releases by default

It bypasses up‑to‑date checks and remote cache without clear benefit for publish flows.

-    --stacktrace --rerun-tasks --warning-mode=all \
+    --stacktrace --warning-mode=all \

Consider adding a short note below the snippet: “If you suspect stale outputs or changed publishing metadata, append --rerun-tasks.”

Makefile (2)

3-5: Do not delete user‑wide ~/.m2; use a project‑local Maven repo and drop --rerun-tasks

Safer for contributors/CI and faster via caching.

-build:
-	rm -rf ~/.m2/repository/me/kpavlov/aimocks ~/.m2/repository/me/kpavlov/mokksy && \
-	./gradlew --rerun-tasks clean build publishToMavenLocal koverHtmlReport && \
-	(cd ai-mocks-openai/samples/shadow && mvn test)
+build:
+	./gradlew -Dorg.gradle.maven.repo.local=.m2-local \
+	  clean build publishToMavenLocal koverHtmlReport && \
+	(cd ai-mocks-openai/samples/shadow && mvn -Dmaven.repo.local="$(PWD)/.m2-local" test)

47-49: Same here: avoid nuking ~/.m2 and make reruns opt‑in

Keep builds isolated and cache‑friendly.

-publish:
-	rm -rf ~/.m2/repository/me/kpavlov/aimocks  ~/.m2/repository/me/kpavlov/mokksy
-	./gradlew --rerun-tasks clean build check sourcesJar publishToMavenLocal
+publish:
+	./gradlew -Dorg.gradle.maven.repo.local=.m2-local \
+	  clean build check sourcesJar publishToMavenLocal
ai-mocks-openai/samples/shadow/src/test/java/com/example/OpenAITest.java (2)

32-39: Make tests deterministic

Randomized params can cause flakiness; use fixed values and no‑op setup.

-    private double temperature;
-    private long maxTokens;
+    private double temperature = 0.42;
+    private long maxTokens = 256;
@@
-    void beforeEach() {
-        temperature = RANDOM.nextDouble(0.0, 1.0);
-        maxTokens = RANDOM.nextLong(100, 500);
-    }
+    void beforeEach() { /* deterministic */ }

59-61: Remove redundant role override for user message

ofUser(...) already implies role=user.

-                    ChatCompletionUserMessageParam.builder()
-                        .role(JsonValue.from("user"))
-                        .content("Just say 'Hey!'").build())))
+                    ChatCompletionUserMessageParam.builder()
+                        .content("Just say 'Hey!'").build())))
ai-mocks-a2a/build.gradle.kts (1)

68-81: Switch to lazy providers for artifacts

Align with other modules and best practices.

-        create<MavenPublication>("shadow") {
+        create<MavenPublication>("shadow") {
             artifactId = project.name + "-standalone"
-            artifact(tasks["shadowJar"]) {
+            artifact(tasks.named("shadowJar")) {
                 classifier = ""
                 extension = "jar"
             }
-            artifact(tasks["jvmSourcesJar"]) {
+            artifact(tasks.named("jvmSourcesJar")) {
                 classifier = "sources"
             }
         }
buildSrc/build.gradle.kts (1)

16-16: No action needed — Shadow 9.1.0 is compatible with the Gradle wrapper (9.1.0).

gradle/wrapper/gradle-wrapper.properties shows Gradle 9.1.0; buildSrc/build.gradle.kts:16 pins com.gradleup.shadow:shadow-gradle-plugin:9.1.0. Shadow 9.1.0 requires Gradle ≥8.11 (compatible with Gradle 9) and Java 11+ — ensure CI/dev JDK is ≥11.

ai-mocks-openai/samples/shadow/pom.xml (3)

7-7: Typo in artifactId.

Use “shadowed”.

-    <artifactId>mokksy-openai-shadewed</artifactId>
+    <artifactId>mokksy-openai-shadowed</artifactId>

26-35: Scope demo client deps to test.

Prevent leaking to compile/runtime.

         <dependency>
             <groupId>dev.langchain4j</groupId>
             <artifactId>langchain4j-open-ai</artifactId>
             <version>1.5.0</version>
+            <scope>test</scope>
         </dependency>
         <dependency>
             <groupId>com.openai</groupId>
             <artifactId>openai-java-client-okhttp</artifactId>
             <version>3.1.2</version>
+            <scope>test</scope>
         </dependency>

55-60: JUnit engine missing; pin Surefire.

Use the aggregator that brings the engine and ensure Surefire ≥2.22.

-        <dependency>
-            <groupId>org.junit.jupiter</groupId>
-            <artifactId>junit-jupiter-api</artifactId>
-            <version>5.13.4</version>
-            <scope>test</scope>
-        </dependency>
+        <dependency>
+            <groupId>org.junit.jupiter</groupId>
+            <artifactId>junit-jupiter</artifactId>
+            <version>5.13.4</version>
+            <scope>test</scope>
+        </dependency>

Also add (near end of POM):

 </dependencies>
+<build>
+  <plugins>
+    <plugin>
+      <groupId>org.apache.maven.plugins</groupId>
+      <artifactId>maven-surefire-plugin</artifactId>
+      <version>3.5.0</version>
+    </plugin>
+  </plugins>
+</build>
 </project>
ai-mocks-openai/build.gradle.kts (1)

8-9: Remove duplicate Shadow plugin.

shadow-convention already applies com.gradleup.shadow.

     `shadow-convention`
-    id("com.gradleup.shadow")
ai-mocks-openai/samples/OpenaiLc4jSample.ipynb (1)

99-125: Notebook execution currently fails — verify stub DSL names.

Errors show unresolved identifiers (assistantContent, finishReason). Confirm the actual DSL property names exposed by MockOpenai completion/responds builders and update the cell accordingly.

buildSrc/src/main/kotlin/publish-convention.gradle.kts (3)

46-50: POM metadata assignments look good.


53-55: License block is correct.


73-76: Issue management block looks good.

buildSrc/src/main/kotlin/shadow-convention.gradle.kts (4)

1-1: Fix ShadowJar import for GradleUp fork.

Use the GradleUp task class; drop the Companion accessor.

-import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar.Companion.shadowJar
+import com.gradleup.shadow.tasks.ShadowJar

3-7: Use core base plugin id.

org.gradle.base is not a valid id. Use the base plugin.

 plugins {
     `maven-publish`
-    id("org.gradle.base")
+    base
     id("com.gradleup.shadow") // https://gradleup.com/shadow
 }

9-20: Harden ShadowJar config: correct minimize excludes, merge SPI files, avoid relocating Kotlin/public API, and use typed task access.

  • Wrong exclude pattern risks stripping Kotlin stdlib.
  • Missing mergeServiceFiles() can break ServiceLoader at runtime.
  • Relocating kotlin (and often kotlinx.serialization) is dangerous; avoid unless strictly internal.
  • Prefer tasks.named<ShadowJar>("shadowJar").
-tasks.shadowJar {
-
-    minimize {
-        exclude(dependency("kotlin:.*:.*"))
-    }
-
-    dependencies {
-        relocate("io.ktor", "dev.mokksy.relocated.io.ktor")
-        relocate("kotlinx.serialization", "dev.mokksy.relocated.kotlinx.serialization")
-        relocate("kotlin", "dev.mokksy.relocated.kotlin")
-    }
-}
+tasks.named<ShadowJar>("shadowJar") {
+    archiveClassifier.set("all")
+
+    minimize {
+        // Keep Kotlin + Coroutines
+        exclude(dependency("org.jetbrains.kotlin:.*:.*"))
+        exclude(dependency("org.jetbrains.kotlinx:kotlinx-coroutines-.*:.*"))
+    }
+
+    // Preserve SPI wiring
+    mergeServiceFiles()
+
+    // Optional: enable relocations with -Pshadow.relocate=true
+    val enableRelocation = providers.gradleProperty("shadow.relocate")
+        .map(String::toBoolean).getOrElse(false)
+    if (enableRelocation) {
+        // Avoid relocating libraries that leak into public API
+        relocate("io.ktor", "dev.mokksy.relocated.io.ktor")
+        // Consider NOT relocating kotlinx.serialization unless fully internal
+        // relocate("kotlinx.serialization", "dev.mokksy.relocated.kotlinx.serialization")
+    }
+}

22-24: Don’t force shading on every assemble; gate behind a property.

Keeps local/dev builds fast; enable in CI with -Pshadow.on.assemble=true.

-tasks.assemble {
-    dependsOn(tasks.shadowJar)
-}
+if (providers.gradleProperty("shadow.on.assemble").map(String::toBoolean).getOrElse(false)) {
+    tasks.named("assemble").configure {
+        dependsOn(tasks.named("shadowJar"))
+    }
+}
.github/workflows/gradle.yml (1)

57-61: Set JDK 17 for Maven sample and cache ~/.m2.

The sample POM targets Java 17; run Maven tests on JDK 17 and cache the Maven repo.

       - name: Build with Gradle
         run: ./gradlew --rerun-tasks --no-daemon clean build publishToMavenLocal koverXmlReport
 
+      - name: Set up JDK 17 for Maven sample
+        uses: actions/setup-java@v5
+        with:
+          java-version: 17
+          distribution: temurin
+
+      - name: Cache Maven repository
+        uses: actions/cache@v4
+        with:
+          path: ~/.m2/repository
+          key: maven-${{ runner.os }}-${{ hashFiles('**/pom.xml') }}
+          restore-keys: |
+            maven-${{ runner.os }}-
+
       - name: OpenAI Maven Integration Tests
         working-directory: ai-mocks-openai/samples/shadow
         run: mvn --batch-mode test
mokksy/src/commonMain/kotlin/me/kpavlov/mokksy/response/ResponseDefinition.kt (1)

31-33: Compilation error: default parameter references a later parameter

httpStatusCode: Int = httpStatus.value is illegal in Kotlin. Fix the defaults.

-    httpStatusCode: Int = httpStatus.value,
-    httpStatus: HttpStatusCode = HttpStatusCode.fromValue(httpStatusCode),
+    httpStatusCode: Int = 200,
+    httpStatus: HttpStatusCode = HttpStatusCode.fromValue(httpStatusCode),

- Introduced `shadow-convention` in build scripts for multiple modules.
- Enabled `shadow` publication in Maven configuration.
- Added sample OpenAI `pom.xml` and example tests utilizing MockOpenai.
- Added `httpStatusCode` with default value `200` in response classes and builders.
@kpavlov kpavlov marked this pull request as ready for review September 21, 2025 12:50
@kpavlov kpavlov merged commit 84f1ad5 into main Sep 21, 2025
7 of 8 checks passed
@kpavlov kpavlov deleted the shadow branch September 21, 2025 12:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant