-
Notifications
You must be signed in to change notification settings - Fork 120
[Kotlin-sdk] Maven Release #286
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
When creating release ZIPs for iOS XCFrameworks, find the .xcframework directory (which may be nested in dist/) and place it at the root of the ZIP. This fixes SPM binary target resolution which expects the framework at the archive root. Affected packages: RACommons, RABackendLLAMACPP, RABackendONNX
The JNI bridge library (librunanywhere_jni.so) was being built to dist/android/jni/ but not uploaded as an artifact or copied to jniLibs. This caused the 'dlopen failed: library librunanywhere_jni.so not found' crash.
Issues fixed: 1. Backend modules had wrong download URLs (core-v0.1.4) - Removed module-level downloadJniLibs tasks (main SDK handles all native libs) 2. Android compilation was skipped on JitPack - Added explicit assembleRelease step before publishToMavenLocal - Added android-36 platform and ANDROID_SDK_ROOT env var 3. Race condition with native lib downloads - JitPack now runs downloadJniLibs -> assembleRelease -> publishToMavenLocal - Backend modules are just Kotlin code (no native libs)
- Chain all commands with && so env vars persist - Use publishAllPublicationsToMavenLocal to include Android AAR - Add --info for better debugging
- publishAllPublicationsToMavenLocal doesn't exist - Split downloadJniLibs and assembleRelease+publish for clarity
- Configure signing and Maven Central repository for main SDK - Add publishing configuration to LlamaCPP and ONNX modules - Add GitHub Actions workflow for automated publishing - Use io.github.sanchitmonga22 namespace (verified) Artifacts will be published as: - io.github.sanchitmonga22:runanywhere-sdk - io.github.sanchitmonga22:runanywhere-llamacpp - io.github.sanchitmonga22:runanywhere-onnx
- Add publishLibraryVariants("release") to androidTarget blocks
- Add mavenPublication { artifactId = ... } for correct artifact naming
- Create KOTLIN_MAVEN_CENTRAL_PUBLISHING.md documentation
This enables publishing Android AARs that contain native .so libraries
(librunanywhere_jni.so, librac_backend_llamacpp.so, libonnxruntime.so, etc.)
to Maven Central alongside the JVM JARs.
Artifacts now published:
- runanywhere-sdk-android (AAR with native libs)
- runanywhere-llamacpp-android (AAR with native libs)
- runanywhere-onnx-android (AAR with native libs)
- Plus 6 JVM/KMP artifacts
…peline Examples - Added a "Quick Start" section to the Documentation.md for easier onboarding. - Updated the Table of Contents to include new sections. - Included detailed examples for initializing the SDK, registering models, and using the voice pipeline. - Expanded usage examples for LLM, STT, and TTS functionalities to match the starter app implementation. - Improved clarity and structure of voice session events and processing flow.
|
Note Other AI code review bot(s) detectedCodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review. 📝 WalkthroughWalkthroughAdds Maven Central Android AAR publishing configuration across Kotlin modules, introduces a new streaming voice-session API/implementation (streamVoiceSession) with STT→LLM→TTS orchestration, and expands Kotlin SDK documentation and publishing guides; also adds secrets template and .gitignore entries. Changes
Sequence Diagram(s)sequenceDiagram
participant Client as Audio Client
participant SDK as RunAnywhere SDK
participant STT as STT Service
participant LLM as LLM Service
participant TTS as TTS Service
participant Events as Event Stream
Client->>SDK: audioChunks: Flow<ByteArray>
SDK->>SDK: initialize components & load models
loop per audio chunk
Client->>SDK: emit chunk
SDK->>SDK: buffer + compute RMS (audioLevel)
Events->>Events: emit Listening(audioLevel)
SDK->>SDK: detect speech start/stop (threshold & silence)
alt speech segment complete
Events->>Events: emit Processing
SDK->>STT: transcribe buffered audio
STT-->>SDK: transcription
Events->>Events: emit Transcribed(transcription)
SDK->>LLM: generate response from transcription
LLM-->>SDK: response text
Events->>Events: emit Responded(response)
SDK->>TTS: synthesize response
TTS-->>SDK: audio bytes
Events->>Events: emit Speaking
Events->>Events: emit TurnCompleted(transcription, response, audio)
end
end
Client->>SDK: stream completes
SDK->>SDK: flush remaining buffer, finalize
Events->>Events: emit Stopped
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Suggested labels
Poem
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. 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. Comment |
Resolved conflicts in Maven Central publishing configuration: - Updated to new Sonatype Central Portal URLs - Enhanced GPG signing with system GPG fallback - Added GPG key import steps in CI workflow
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Caution
Changes requested ❌
Reviewed everything up to a8206ee in 2 minutes and 32 seconds. Click for details.
- Reviewed
1414lines of code in7files - Skipped
0files when reviewing. - Skipped posting
5draft comments. View those below. - Modify your settings and rules to customize what types of comments Ellipsis leaves. And don't forget to react with 👍 or 👎 to teach Ellipsis.
1. sdk/runanywhere-kotlin/src/jvmAndroidMain/kotlin/com/runanywhere/sdk/public/extensions/RunAnywhere+VoiceAgent.jvmAndroid.kt:304
- Draft comment:
For heavy CPU tasks like RMS calculation in high-frequency audio streams, consider offloading to a dedicated dispatcher to avoid blocking the caller. - Reason this comment was not posted:
Decided after close inspection that this draft comment was likely wrong and/or not actionable: usefulness confidence = 15% vs. threshold = 50% The comment suggests a performance optimization but uses the word "consider", making it a suggestion rather than identifying a definite issue. The rules state "Do NOT make speculative comments, such as 'If X, then Y is an issue'. Only comment if it is definitely an issue." This comment is speculative - it assumes RMS calculation will be heavy enough to cause blocking without evidence. Audio chunks are typically small (hundreds of bytes to a few KB), and the RMS calculation is relatively simple. The comment also doesn't provide clear, actionable guidance - "dedicated dispatcher" is vague. The code already useschannelFlowwhich has its own dispatcher management. This seems like a premature optimization suggestion without evidence of an actual problem. Could the RMS calculation actually be a performance bottleneck in practice? Audio processing often happens at high frequency (e.g., 50-100 chunks per second), and even simple calculations could add up. The author might have performance data or experience suggesting this is a real concern. While high-frequency audio processing could theoretically be intensive, the comment provides no evidence this is actually a problem. The calculation is simple (a loop over shorts with basic arithmetic), and typical audio chunks are small. The comment uses "consider" which makes it advisory/speculative rather than identifying a definite issue. Per the rules, speculative optimization suggestions without clear evidence should be removed. If this were a real performance issue, the comment should definitively state the problem and provide specific guidance. This comment should be deleted. It's a speculative performance optimization suggestion ("consider") without evidence of an actual problem. The rules explicitly state not to make speculative comments and to only comment if something is definitely an issue. The suggestion is also not clearly actionable.
2. sdk/runanywhere-kotlin/src/jvmAndroidMain/kotlin/com/runanywhere/sdk/public/extensions/RunAnywhere+VoiceAgent.jvmAndroid.kt:349
- Draft comment:
If no speech is detected (empty transcription), consider emitting a distinct event (e.g., NoSpeechDetected) to inform the UI rather than silently returning false. - Reason this comment was not posted:
Decided after close inspection that this draft comment was likely wrong and/or not actionable: usefulness confidence = 30% vs. threshold = 50% This comment is suggesting a code quality improvement - adding a new event type to inform the UI when no speech is detected. However, this is a suggestion for enhancement rather than pointing out a bug or required change. The current behavior (silently returning false) is not incorrect - it's a design choice. The comment is asking the author to consider adding a feature, which falls under "speculative" or "optional" feedback. According to the rules, I should not keep comments that are purely suggestions unless they point to a clear issue. The code works as-is, it just might not be the most informative to the UI. This is more of a "nice to have" rather than a "must fix". The comment could be valid if there's a clear UX issue where the UI needs to know about no-speech-detected cases. Without seeing the event types defined or understanding the full requirements, I can't be certain this is unnecessary. Perhaps the UI does need this information for proper user feedback. While the UI might benefit from this information, the comment is phrased as "consider" which makes it a suggestion rather than identifying a bug. The current implementation is functional - it logs the event and continues. If this were a critical issue, the comment would be more definitive. This is a code quality suggestion, not a required fix. This comment is a suggestion for enhancement rather than identifying a bug or required change. It's asking the author to "consider" adding a new event type, which is optional feedback. According to the rules, I should remove comments that are suggestions unless they point to clear issues.
3. sdk/runanywhere-kotlin/src/jvmAndroidMain/kotlin/com/runanywhere/sdk/public/extensions/RunAnywhere+VoiceAgent.jvmAndroid.kt:227
- Draft comment:
Ensure that cancellation of native bridge operations (CppBridgeSTT, CppBridgeLLM, CppBridgeTTS) in stopVoiceSession is robust. Logging their cancellation results, if possible, might aid diagnostics. - Reason this comment was not posted:
Comment was not on a location in the diff, so it can't be submitted as a review comment.
4. sdk/runanywhere-kotlin/src/jvmAndroidMain/kotlin/com/runanywhere/sdk/public/extensions/RunAnywhere+VoiceAgent.jvmAndroid.kt:320
- Draft comment:
The inner function processAudio is well structured but could benefit from additional inline comments clarifying its state transitions and step-by-step processing, especially around resetting the audio buffer and continuous mode logic. - Reason this comment was not posted:
Decided after close inspection that this draft comment was likely wrong and/or not actionable: usefulness confidence = 20% vs. threshold = 50% This comment is asking for more inline comments, which is a code quality suggestion. However, it's quite vague - it doesn't specify exactly what needs clarification. The function already has step-by-step comments for the main processing flow. The audio buffer reset is a simple operation (line 329) that doesn't really need more explanation. The continuous mode logic mentioned in the comment isn't even in this function - it's in the caller. This seems like a generic "add more comments" suggestion without identifying a specific gap in understanding. According to the rules, comments should be actionable and clear, and this one is neither - it's subjective whether more comments are needed here. The function does handle multiple state transitions (isProcessingTurn, audioBuffer reset) and the comment might be valid if these transitions are confusing to readers. The code is new (added in this PR), so perhaps the reviewer genuinely found it hard to follow. I might be too quick to dismiss this as unnecessary. While the state transitions exist, they're relatively straightforward: check if processing, set flag, get and clear buffer, validate, process, reset flag. The existing comments already mark the three main steps. Adding more comments about obvious state management (like resetting a flag) could actually make the code noisier. The continuous mode logic isn't even in this function, which suggests the reviewer may not have fully understood the code structure. This comment is too vague and subjective. It doesn't identify a specific gap in documentation, and the function already has reasonable step-by-step comments. The request for clarification about "continuous mode logic" is misplaced since that logic is in the caller, not in processAudio. This is not an actionable, specific code change request.
5. sdk/runanywhere-kotlin/docs/Documentation.md:6
- Draft comment:
There's a typographical error: "acces" should be "access" in the introductory sentence. - Reason this comment was not posted:
Comment was not on a location in the diff, so it can't be submitted as a review comment.
Workflow ID: wflow_4X2MJ1fp2x0PeKss
You can customize by changing your verbosity settings, reacting with 👍 or 👎, replying to comments, or adding code review rules.
| /** | ||
| * Normalize audio level for visualization (0.0 to 1.0) | ||
| */ | ||
| fun normalizeAudioLevel(rms: Float): Float = (rms * 3.0f).coerceIn(0f, 1f) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Consider extracting the magic constant (3.0f) used in normalizeAudioLevel into a named constant. This improves clarity and makes future adjustments easier.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 6
🤖 Fix all issues with AI agents
In `@sdk/runanywhere-kotlin/docs/Documentation.md`:
- Around line 270-345: The docs for AudioCaptureService are missing the required
Android permission information; update the documentation to state that
AudioRecord (used by AudioCaptureService, particularly startCapture/stopCapture)
requires the android.permission.RECORD_AUDIO manifest permission and that apps
targeting Android 6.0+ must request this permission at runtime (include guidance
to check ContextCompat.checkSelfPermission and call
ActivityCompat.requestPermissions with a REQUEST_RECORD_AUDIO_PERMISSION
constant).
In `@sdk/runanywhere-kotlin/docs/KOTLIN_MAVEN_CENTRAL_PUBLISHING.md`:
- Around line 93-99: Update the unlabeled fenced code block under the "Expected
output" section in KOTLIN_MAVEN_CENTRAL_PUBLISHING.md by adding a language tag
(e.g., text) to the opening triple backticks so the fence reads ```text; this
satisfies markdownlint MD040 and preserves the listed jni/arm64-v8a/... entries
(the expected-output block containing the libc++_shared.so, libomp.so,
librac_commons.so, librunanywhere_jni.so lines).
- Around line 127-131: Replace the bolded critical note "**Critical: Upload to
keys.openpgp.org with email verification**" with a proper heading to satisfy
MD036; locate the section titled "### 4. Upload GPG Key to Keyservers" and
change that bold line into a heading (e.g., a level-4 or appropriate heading
like "#### Critical: Upload to keys.openpgp.org with email verification") so the
note is rendered as a heading instead of bold text.
- Around line 191-193: Update the incorrect repository path used in the local
publish step: replace the string "cd sdks/sdk/runanywhere-kotlin" with the
correct path "cd runanywhere-sdks/sdk/runanywhere-kotlin" so the command points
to the current repo root and SDK location; edit the line in
KOTLIN_MAVEN_CENTRAL_PUBLISHING.md that contains "cd
sdks/sdk/runanywhere-kotlin" accordingly.
- Around line 334-339: The "## Key URLs" section contains bare URLs (e.g.,
"https://central.sonatype.com",
"https://central.sonatype.com/search?q=io.github.sanchitmonga22",
"https://keys.openpgp.org", "https://keyserver.ubuntu.com") that trigger MD034;
update the "## Key URLs" block in KOTLIN_MAVEN_CENTRAL_PUBLISHING.md so each
bare URL is converted to an autolink or Markdown link (for example wrap each URL
in angle brackets like <https://central.sonatype.com> or use [Central
Portal](https://central.sonatype.com)), and apply the same transformation to any
other bare URLs elsewhere in the document.
In
`@sdk/runanywhere-kotlin/src/jvmAndroidMain/kotlin/com/runanywhere/sdk/public/extensions/RunAnywhere`+VoiceAgent.jvmAndroid.kt:
- Around line 294-299: The ByteArrayOutputStream audioBuffer is allowed to grow
unbounded during long silence; change buffering so you either (A) only write
incoming audio chunks into audioBuffer after speech is detected (use
isSpeechActive) or (B) impose a fixed pre-roll cap (e.g., N bytes) and implement
a rolling/circular behavior when appending to audioBuffer so oldest bytes are
discarded once the cap is exceeded; update both the initial buffer usage around
audioBuffer/minAudioBytes/silenceDurationMs and the corresponding logic later
(the code paths referenced at lines ~400-404) to respect the same cap or
start-buffering-after-speech policy and ensure isSpeechActive and lastSpeechTime
are used to switch modes.
🧹 Nitpick comments (2)
sdk/runanywhere-kotlin/docs/Documentation.md (2)
25-52: Consider adding a note about version numbers and verifying JitPack dependencies.The installation example hard-codes version
0.16.1across all three artifacts. While this ensures consistency, consider adding a note that users should check for the latest version on Maven Central.Additionally, JitPack is mentioned for transitive dependencies. Consider documenting:
- Why JitPack is required (specific transitive dependencies)
- Whether there are plans to publish these dependencies to Maven Central for better supply-chain security
- Any security considerations users should be aware of
📝 Suggested documentation addition
Add after line 39:
> **Note**: Replace `0.16.1` with the latest version available on [Maven Central](https://central.sonatype.com/search?q=io.github.sanchitmonga22).Add after line 52:
> **Note**: JitPack is required for transitive dependencies (android-vad, PRDownloader) that are not yet published to Maven Central. Ensure your security policies allow JitPack dependencies.
357-397: Consider parsing sample rate from WAV header instead of hard-coding.The function hard-codes the sample rate to 22050 Hz based on "Piper TTS default," but this may not be correct for other TTS models or future versions. The WAV header contains the sample rate at bytes 24-27, which could be parsed for better compatibility.
♻️ Proposed enhancement to parse sample rate from WAV header
suspend fun playWavAudio(wavData: ByteArray) = withContext(Dispatchers.IO) { if (wavData.size < 44) return@withContext val headerSize = if (wavData.size > 44 && wavData[0] == 'R'.code.toByte() && wavData[1] == 'I'.code.toByte()) 44 else 0 val pcmData = wavData.copyOfRange(headerSize, wavData.size) - val sampleRate = 22050 // Piper TTS default sample rate + + // Parse sample rate from WAV header (bytes 24-27, little-endian) + val sampleRate = if (headerSize == 44) { + (wavData[24].toInt() and 0xFF) or + ((wavData[25].toInt() and 0xFF) shl 8) or + ((wavData[26].toInt() and 0xFF) shl 16) or + ((wavData[27].toInt() and 0xFF) shl 24) + } else { + 22050 // Default fallback + }
| #### Audio Capture Service (Required for Voice Pipeline) | ||
|
|
||
| ```kotlin | ||
| import android.media.AudioFormat | ||
| import android.media.AudioRecord | ||
| import android.media.MediaRecorder | ||
| import kotlinx.coroutines.* | ||
| import kotlinx.coroutines.channels.awaitClose | ||
| import kotlinx.coroutines.flow.* | ||
|
|
||
| class AudioCaptureService { | ||
| private var audioRecord: AudioRecord? = null | ||
|
|
||
| @Volatile | ||
| private var isCapturing = false | ||
|
|
||
| companion object { | ||
| const val SAMPLE_RATE = 16000 | ||
| const val CHUNK_SIZE_MS = 100 // Emit chunks every 100ms | ||
| } | ||
|
|
||
| fun startCapture(): Flow<ByteArray> = callbackFlow { | ||
| val bufferSize = AudioRecord.getMinBufferSize( | ||
| SAMPLE_RATE, | ||
| AudioFormat.CHANNEL_IN_MONO, | ||
| AudioFormat.ENCODING_PCM_16BIT | ||
| ) | ||
| val chunkSize = (SAMPLE_RATE * 2 * CHUNK_SIZE_MS) / 1000 | ||
|
|
||
| try { | ||
| audioRecord = AudioRecord( | ||
| MediaRecorder.AudioSource.MIC, | ||
| SAMPLE_RATE, | ||
| AudioFormat.CHANNEL_IN_MONO, | ||
| AudioFormat.ENCODING_PCM_16BIT, | ||
| maxOf(bufferSize, chunkSize * 2) | ||
| ) | ||
|
|
||
| if (audioRecord?.state != AudioRecord.STATE_INITIALIZED) { | ||
| close(IllegalStateException("AudioRecord initialization failed")) | ||
| return@callbackFlow | ||
| } | ||
|
|
||
| audioRecord?.startRecording() | ||
| isCapturing = true | ||
|
|
||
| val readJob = launch(Dispatchers.IO) { | ||
| val buffer = ByteArray(chunkSize) | ||
| while (isActive && isCapturing) { | ||
| val bytesRead = audioRecord?.read(buffer, 0, chunkSize) ?: -1 | ||
| if (bytesRead > 0) { | ||
| trySend(buffer.copyOf(bytesRead)) | ||
| } | ||
| } | ||
| } | ||
|
|
||
| awaitClose { | ||
| readJob.cancel() | ||
| stopCapture() | ||
| } | ||
| } catch (e: Exception) { | ||
| stopCapture() | ||
| close(e) | ||
| } | ||
| } | ||
|
|
||
| fun stopCapture() { | ||
| isCapturing = false | ||
| try { | ||
| audioRecord?.stop() | ||
| audioRecord?.release() | ||
| } catch (_: Exception) {} | ||
| audioRecord = null | ||
| } | ||
| } | ||
| ``` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Document required Android permissions for AudioCaptureService.
The AudioCaptureService uses AudioRecord which requires the RECORD_AUDIO permission. This is critical setup information that's missing from the documentation. Users will encounter a SecurityException at runtime without this context.
📋 Suggested documentation addition
Add before line 272:
**Required Permissions**
Add to your `AndroidManifest.xml`:
```xml
<uses-permission android:name="android.permission.RECORD_AUDIO" />For Android 6.0 (API 23) and above, you must also request runtime permission:
// Check and request permission
if (ContextCompat.checkSelfPermission(context, Manifest.permission.RECORD_AUDIO)
!= PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(
activity,
arrayOf(Manifest.permission.RECORD_AUDIO),
REQUEST_RECORD_AUDIO_PERMISSION
)
}🤖 Prompt for AI Agents
In `@sdk/runanywhere-kotlin/docs/Documentation.md` around lines 270 - 345, The
docs for AudioCaptureService are missing the required Android permission
information; update the documentation to state that AudioRecord (used by
AudioCaptureService, particularly startCapture/stopCapture) requires the
android.permission.RECORD_AUDIO manifest permission and that apps targeting
Android 6.0+ must request this permission at runtime (include guidance to check
ContextCompat.checkSelfPermission and call ActivityCompat.requestPermissions
with a REQUEST_RECORD_AUDIO_PERMISSION constant).
| ### 4. Upload GPG Key to Keyservers | ||
|
|
||
| **Critical: Upload to keys.openpgp.org with email verification** | ||
|
|
||
| ```bash |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use a heading instead of bold for the critical note
MD036 prefers headings over bold-as-heading.
✍️ Proposed fix
-**Critical: Upload to keys.openpgp.org with email verification**
+#### Critical: Upload to keys.openpgp.org with email verification📝 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.
| ### 4. Upload GPG Key to Keyservers | |
| **Critical: Upload to keys.openpgp.org with email verification** | |
| ```bash | |
| ### 4. Upload GPG Key to Keyservers | |
| #### Critical: Upload to keys.openpgp.org with email verification | |
🧰 Tools
🪛 markdownlint-cli2 (0.18.1)
129-129: Emphasis used instead of a heading
(MD036, no-emphasis-as-heading)
🤖 Prompt for AI Agents
In `@sdk/runanywhere-kotlin/docs/KOTLIN_MAVEN_CENTRAL_PUBLISHING.md` around lines
127 - 131, Replace the bolded critical note "**Critical: Upload to
keys.openpgp.org with email verification**" with a proper heading to satisfy
MD036; locate the section titled "### 4. Upload GPG Key to Keyservers" and
change that bold line into a heading (e.g., a level-4 or appropriate heading
like "#### Critical: Upload to keys.openpgp.org with email verification") so the
note is rendered as a heading instead of bold text.
| ```bash | ||
| cd sdks/sdk/runanywhere-kotlin | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix repo path in the local publish step
The command still uses the old sdks/ prefix; the SDK lives under sdk/runanywhere-kotlin in this repo, so this step is easy to miscopy.
🐛 Proposed fix
- cd sdks/sdk/runanywhere-kotlin
+ cd sdk/runanywhere-kotlin🤖 Prompt for AI Agents
In `@sdk/runanywhere-kotlin/docs/KOTLIN_MAVEN_CENTRAL_PUBLISHING.md` around lines
191 - 193, Update the incorrect repository path used in the local publish step:
replace the string "cd sdks/sdk/runanywhere-kotlin" with the correct path "cd
runanywhere-sdks/sdk/runanywhere-kotlin" so the command points to the current
repo root and SDK location; edit the line in KOTLIN_MAVEN_CENTRAL_PUBLISHING.md
that contains "cd sdks/sdk/runanywhere-kotlin" accordingly.
| ## Key URLs | ||
|
|
||
| - **Central Portal**: https://central.sonatype.com | ||
| - **Published Artifacts**: https://central.sonatype.com/search?q=io.github.sanchitmonga22 | ||
| - **keys.openpgp.org**: https://keys.openpgp.org | ||
| - **keyserver.ubuntu.com**: https://keyserver.ubuntu.com |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Convert bare URLs to links/autolinks
MD034 flags bare URLs; wrapping them as autolinks resolves it (apply the same pattern to other bare URLs earlier in the doc).
✍️ Proposed fix
-- **Central Portal**: https://central.sonatype.com
-- **Published Artifacts**: https://central.sonatype.com/search?q=io.github.sanchitmonga22
-- **keys.openpgp.org**: https://keys.openpgp.org
-- **keyserver.ubuntu.com**: https://keyserver.ubuntu.com
+- **Central Portal**: <https://central.sonatype.com>
+- **Published Artifacts**: <https://central.sonatype.com/search?q=io.github.sanchitmonga22>
+- **keys.openpgp.org**: <https://keys.openpgp.org>
+- **keyserver.ubuntu.com**: <https://keyserver.ubuntu.com>📝 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.
| ## Key URLs | |
| - **Central Portal**: https://central.sonatype.com | |
| - **Published Artifacts**: https://central.sonatype.com/search?q=io.github.sanchitmonga22 | |
| - **keys.openpgp.org**: https://keys.openpgp.org | |
| - **keyserver.ubuntu.com**: https://keyserver.ubuntu.com | |
| ## Key URLs | |
| - **Central Portal**: <https://central.sonatype.com> | |
| - **Published Artifacts**: <https://central.sonatype.com/search?q=io.github.sanchitmonga22> | |
| - **keys.openpgp.org**: <https://keys.openpgp.org> | |
| - **keyserver.ubuntu.com**: <https://keyserver.ubuntu.com> |
🧰 Tools
🪛 markdownlint-cli2 (0.18.1)
336-336: Bare URL used
(MD034, no-bare-urls)
337-337: Bare URL used
(MD034, no-bare-urls)
338-338: Bare URL used
(MD034, no-bare-urls)
339-339: Bare URL used
(MD034, no-bare-urls)
🤖 Prompt for AI Agents
In `@sdk/runanywhere-kotlin/docs/KOTLIN_MAVEN_CENTRAL_PUBLISHING.md` around lines
334 - 339, The "## Key URLs" section contains bare URLs (e.g.,
"https://central.sonatype.com",
"https://central.sonatype.com/search?q=io.github.sanchitmonga22",
"https://keys.openpgp.org", "https://keyserver.ubuntu.com") that trigger MD034;
update the "## Key URLs" block in KOTLIN_MAVEN_CENTRAL_PUBLISHING.md so each
bare URL is converted to an autolink or Markdown link (for example wrap each URL
in angle brackets like <https://central.sonatype.com> or use [Central
Portal](https://central.sonatype.com)), and apply the same transformation to any
other bare URLs elsewhere in the document.
| val audioBuffer = ByteArrayOutputStream() | ||
| var isSpeechActive = false | ||
| var lastSpeechTime = 0L | ||
| var isProcessingTurn = false | ||
| val minAudioBytes = 16000 // ~0.5s at 16kHz, 16-bit | ||
| val silenceDurationMs = (config.silenceDuration * 1000).toLong() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Prevent unbounded buffering during long silence
The buffer grows for every chunk even before speech detection; long silent streams can grow memory indefinitely. Consider bounding pre-roll or only buffering after speech starts.
🛠️ Suggested update
- val minAudioBytes = 16000 // ~0.5s at 16kHz, 16-bit
+ val minAudioBytes = 16000 // ~0.5s at 16kHz, 16-bit
+ val maxPreSpeechBytes = minAudioBytes * 4 // ~2s pre-roll cap
@@
- synchronized(audioBuffer) {
- audioBuffer.write(chunk)
- }
+ synchronized(audioBuffer) {
+ if (!isSpeechActive && audioBuffer.size() > maxPreSpeechBytes) {
+ audioBuffer.reset()
+ }
+ audioBuffer.write(chunk)
+ }Also applies to: 400-404
🤖 Prompt for AI Agents
In
`@sdk/runanywhere-kotlin/src/jvmAndroidMain/kotlin/com/runanywhere/sdk/public/extensions/RunAnywhere`+VoiceAgent.jvmAndroid.kt
around lines 294 - 299, The ByteArrayOutputStream audioBuffer is allowed to grow
unbounded during long silence; change buffering so you either (A) only write
incoming audio chunks into audioBuffer after speech is detected (use
isSpeechActive) or (B) impose a fixed pre-roll cap (e.g., N bytes) and implement
a rolling/circular behavior when appending to audioBuffer so oldest bytes are
discarded once the cap is exceeded; update both the initial buffer usage around
audioBuffer/minAudioBytes/silenceDurationMs and the corresponding logic later
(the code paths referenced at lines ~400-404) to respect the same cap or
start-buffering-after-speech policy and ensure isSpeechActive and lastSpeechTime
are used to switch modes.
- Simplified KOTLIN_MAVEN_CENTRAL_PUBLISHING.md for quick reference - Added secrets.template.properties for team credential sharing - Updated .gitignore to exclude secrets files
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Important
Looks good to me! 👍
Reviewed b068537 in 2 minutes and 16 seconds. Click for details.
- Reviewed
523lines of code in3files - Skipped
0files when reviewing. - Skipped posting
3draft comments. View those below. - Modify your settings and rules to customize what types of comments Ellipsis leaves. And don't forget to react with 👍 or 👎 to teach Ellipsis.
1. sdk/runanywhere-kotlin/.gitignore:45
- Draft comment:
Good addition of secret ignore rules. This ensures that credentials (secrets.properties files) are not accidentally committed. - Reason this comment was not posted:
Confidence changes required:0%<= threshold50%None
2. sdk/runanywhere-kotlin/docs/KOTLIN_MAVEN_CENTRAL_PUBLISHING.md:44
- Draft comment:
The directory path in the publish command ('cd sdks/sdk/runanywhere-kotlin') may be incorrect. Verify if it should be 'sdk/runanywhere-kotlin' to match the actual project structure. - Reason this comment was not posted:
Decided after close inspection that this draft comment was likely wrong and/or not actionable: usefulness confidence = 0% vs. threshold = 50% Looking at the diff carefully, the old documentation hadcd sdks/sdk/runanywhere-kotlinin the publishing steps section, and the new simplified version also hascd sdks/sdk/runanywhere-kotlinon line 44. This means the path was NOT changed in this diff - it existed before and continues to exist. The comment is about unchanged code. According to the rules, "If the comment is about unchanged code, this should be False, and the comment should be deleted." Even if the path might be incorrect, since it wasn't changed in this PR, the comment should not be kept. While the path might indeed be incorrect (the file location suggestssdk/runanywhere-kotlinis the correct path), this is pre-existing content that wasn't modified in this diff. The comment doesn't relate to any change made by the author. The rule is clear: comments must be about changes made in the diff. Since this path existed in the old version and was carried over unchanged to the new version, this comment is about unchanged code and should be deleted regardless of whether the path is actually correct or not. Delete this comment because it's about unchanged code. The pathcd sdks/sdk/runanywhere-kotlinexisted in the original documentation and was not modified in this diff.
3. sdk/runanywhere-kotlin/secrets.template.properties:1
- Draft comment:
The secrets template is comprehensive and clearly instructs on how to set up sensitive credentials. Ensure that this file is never committed with actual values. - Reason this comment was not posted:
Confidence changes required:0%<= threshold50%None
Workflow ID: wflow_RTUR9GfIzjz6SrBz
You can customize by changing your verbosity settings, reacting with 👍 or 👎, replying to comments, or adding code review rules.
Description
Brief description of the changes made.
Type of Change
Testing
Platform-Specific Testing (check all that apply)
Swift SDK / iOS Sample:
Kotlin SDK / Android Sample:
Flutter SDK / Flutter Sample:
React Native SDK / React Native Sample:
Labels
Please add the appropriate label(s):
SDKs:
Swift SDK- Changes to Swift SDK (sdk/runanywhere-swift)Kotlin SDK- Changes to Kotlin SDK (sdk/runanywhere-kotlin)Flutter SDK- Changes to Flutter SDK (sdk/runanywhere-flutter)React Native SDK- Changes to React Native SDK (sdk/runanywhere-react-native)Commons- Changes to shared native code (sdk/runanywhere-commons)Sample Apps:
iOS Sample- Changes to iOS example app (examples/ios)Android Sample- Changes to Android example app (examples/android)Flutter Sample- Changes to Flutter example app (examples/flutter)React Native Sample- Changes to React Native example app (examples/react-native)Checklist
Screenshots
Attach relevant UI screenshots for changes (if applicable):
Important
Enable Maven Central publishing for Kotlin Android SDK with new voice session API and updated documentation.
publishLibraryVariants("release")for Android AAR publishing inbuild.gradle.kts,runanywhere-core-llamacpp/build.gradle.kts, andrunanywhere-core-onnx/build.gradle.kts.runanywhere-sdk-android,runanywhere-llamacpp-android,runanywhere-onnx-android.KOTLIN_MAVEN_CENTRAL_PUBLISHING.mdfor publishing guide.Documentation.mdwith Quick Start guide and voice pipeline examples.streamVoiceSession()inRunAnywhere+VoiceAgent.jvmAndroid.ktfor automatic silence detection and voice pipeline orchestration.This description was created by
for b068537. You can customize this summary. It will automatically update as commits are pushed.
Summary by CodeRabbit
New Features
Documentation
✏️ Tip: You can customize this high-level summary in your review settings.
Greptile Summary
This PR enables Maven Central publishing for the Kotlin Android SDK by configuring AAR publication for all Android artifacts.
Key Changes:
publishLibraryVariants("release")for Android AAR publishing in 3 build filesrunanywhere-sdk-android,runanywhere-llamacpp-android,runanywhere-onnx-androidKOTLIN_MAVEN_CENTRAL_PUBLISHING.md)streamVoiceSession()API for simplified voice pipeline with automatic silence detectionMaven Publishing Configuration:
The critical addition of
publishLibraryVariants("release")ensures Android AARs (containing native.solibraries) are published to Maven Central, not just JVM JARs. This is essential for the SDK to function on Android devices.Documentation Improvements:
The documentation now includes production-ready code examples matching the starter app, complete Maven Central installation instructions, and detailed API usage for all components (LLM, STT, TTS, Voice Pipeline).
Voice Pipeline Enhancement:
The new
streamVoiceSession()API simplifies voice agent integration by handling silence detection, speech recognition, and pipeline orchestration automatically, reducing the implementation burden on developers.Confidence Score: 5/5
Important Files Changed
runanywhere-sdk-android)runanywhere-llamacpp-android)runanywhere-onnx-android)streamVoiceSession()with audio level calculation, speech detection, and pipeline orchestrationSequence Diagram
sequenceDiagram participant App as Application participant SDK as RunAnywhere SDK participant Audio as AudioCaptureService participant STT as Speech-to-Text participant LLM as Language Model participant TTS as Text-to-Speech App->>SDK: registerModel() for STT/LLM/TTS App->>SDK: downloadModel() SDK-->>App: Download progress App->>SDK: loadSTTModel() App->>SDK: loadLLMModel() App->>SDK: loadTTSVoice() App->>Audio: startCapture() Audio-->>App: Flow<ByteArray> App->>SDK: streamVoiceSession(audioChunks, config) SDK-->>App: VoiceSessionEvent.Started loop Audio streaming Audio->>SDK: Audio chunk (ByteArray) SDK->>SDK: calculateRMS(audioData) SDK->>SDK: normalizeAudioLevel(rms) SDK-->>App: VoiceSessionEvent.Listening(audioLevel) alt Audio level > threshold SDK->>SDK: Speech detected SDK-->>App: VoiceSessionEvent.SpeechStarted SDK->>SDK: Accumulate audio in buffer end alt Silence detected after speech SDK-->>App: VoiceSessionEvent.Processing SDK->>STT: transcribe(audioData) STT-->>SDK: transcriptionText alt Transcription not blank SDK-->>App: VoiceSessionEvent.Transcribed(text) SDK->>LLM: generate(prompt) LLM-->>SDK: responseText SDK-->>App: VoiceSessionEvent.Responded(text) SDK->>TTS: synthesize(responseText) TTS-->>SDK: audioData SDK-->>App: VoiceSessionEvent.TurnCompleted(transcript, response, audio) alt Continuous mode SDK->>SDK: Reset for next turn SDK-->>App: VoiceSessionEvent.Listening(0.0) end end end end App->>SDK: Cancel session SDK-->>App: VoiceSessionEvent.Stopped