-
Notifications
You must be signed in to change notification settings - Fork 353
feat: add grammar-constrained structured output across SDKs #468
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
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -219,7 +219,8 @@ Java_com_runanywhere_sdk_llm_llamacpp_LlamaCPPBridge_nativeDestroy( | |||||||||||
| JNIEXPORT jstring JNICALL | ||||||||||||
| Java_com_runanywhere_sdk_llm_llamacpp_LlamaCPPBridge_nativeGenerate( | ||||||||||||
| JNIEnv* env, jclass clazz, | ||||||||||||
| jlong handle, jstring prompt, jint maxTokens, jfloat temperature) { | ||||||||||||
| jlong handle, jstring prompt, jint maxTokens, jfloat temperature, | ||||||||||||
| jstring grammar) { | ||||||||||||
| (void)clazz; | ||||||||||||
|
|
||||||||||||
| if (handle == 0) { | ||||||||||||
|
|
@@ -240,12 +241,24 @@ Java_com_runanywhere_sdk_llm_llamacpp_LlamaCPPBridge_nativeGenerate( | |||||||||||
| options.max_tokens = maxTokens; | ||||||||||||
| options.temperature = temperature; | ||||||||||||
|
|
||||||||||||
| // Wire grammar field for constrained decoding | ||||||||||||
| const char* grammarStr = nullptr; | ||||||||||||
| if (grammar != nullptr) { | ||||||||||||
| grammarStr = env->GetStringUTFChars(grammar, nullptr); | ||||||||||||
| if (grammarStr && grammarStr[0] != '\0') { | ||||||||||||
| options.grammar = grammarStr; | ||||||||||||
| } | ||||||||||||
| } | ||||||||||||
|
|
||||||||||||
| rac_llm_result_t result = {}; | ||||||||||||
| rac_result_t status = rac_llm_llamacpp_generate( | ||||||||||||
| reinterpret_cast<rac_handle_t>(handle), | ||||||||||||
| promptStr, &options, &result); | ||||||||||||
|
|
||||||||||||
| env->ReleaseStringUTFChars(prompt, promptStr); | ||||||||||||
| if (grammarStr) { | ||||||||||||
| env->ReleaseStringUTFChars(grammar, grammarStr); | ||||||||||||
| } | ||||||||||||
|
|
||||||||||||
| if (status != RAC_SUCCESS) { | ||||||||||||
| LOGe("nativeGenerate: Failed with status %d", status); | ||||||||||||
|
|
@@ -263,6 +276,43 @@ Java_com_runanywhere_sdk_llm_llamacpp_LlamaCPPBridge_nativeGenerate( | |||||||||||
| return output; | ||||||||||||
| } | ||||||||||||
|
|
||||||||||||
| /** | ||||||||||||
| * Convert JSON Schema to GBNF grammar string | ||||||||||||
| */ | ||||||||||||
| JNIEXPORT jstring JNICALL | ||||||||||||
| Java_com_runanywhere_sdk_llm_llamacpp_LlamaCPPBridge_nativeJsonSchemaToGrammar( | ||||||||||||
| JNIEnv* env, jclass clazz, | ||||||||||||
| jlong handle, jstring jsonSchema) { | ||||||||||||
| (void)clazz; | ||||||||||||
|
|
||||||||||||
| if (handle == 0) { | ||||||||||||
| LOGe("nativeJsonSchemaToGrammar: Invalid handle"); | ||||||||||||
| return nullptr; | ||||||||||||
| } | ||||||||||||
|
|
||||||||||||
| const char* schemaStr = env->GetStringUTFChars(jsonSchema, nullptr); | ||||||||||||
| if (!schemaStr) { | ||||||||||||
| LOGe("nativeJsonSchemaToGrammar: Failed to get schema"); | ||||||||||||
| return nullptr; | ||||||||||||
| } | ||||||||||||
|
|
||||||||||||
| char* grammarOut = nullptr; | ||||||||||||
| rac_result_t status = rac_llm_llamacpp_json_schema_to_grammar( | ||||||||||||
| reinterpret_cast<rac_handle_t>(handle), | ||||||||||||
| schemaStr, &grammarOut); | ||||||||||||
|
|
||||||||||||
| env->ReleaseStringUTFChars(jsonSchema, schemaStr); | ||||||||||||
|
|
||||||||||||
| if (status != RAC_SUCCESS || !grammarOut) { | ||||||||||||
| LOGe("nativeJsonSchemaToGrammar: Failed with status %d", status); | ||||||||||||
| return nullptr; | ||||||||||||
| } | ||||||||||||
|
|
||||||||||||
| jstring result = env->NewStringUTF(grammarOut); | ||||||||||||
| free(grammarOut); | ||||||||||||
|
Comment on lines
+311
to
+312
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Both
Suggested change
Prompt To Fix With AIThis is a comment left during a code review.
Path: sdk/runanywhere-commons/src/backends/llamacpp/jni/rac_backend_llamacpp_jni.cpp
Line: 311-312
Comment:
**`free` instead of `rac_free` violates API contract**
Both `rac_llm_llamacpp_json_schema_to_grammar` and `rac_llm_json_schema_to_grammar` document their output pointer as "caller must free with `rac_free()`". Here and in `llm_component.cpp` lines 822 and 878, the raw `free()` is called on memory owned by the RAC API. If the allocator strategy ever changes (e.g., a custom arena), this will silently corrupt memory.
```suggestion
jstring result = env->NewStringUTF(grammarOut);
rac_free(grammarOut);
return result;
```
How can I resolve this? If you propose a fix, please make it concise. |
||||||||||||
| return result; | ||||||||||||
| } | ||||||||||||
|
|
||||||||||||
| /** | ||||||||||||
| * Cancel ongoing generation | ||||||||||||
| */ | ||||||||||||
|
|
||||||||||||
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.
🧩 Analysis chain
🏁 Script executed:
Repository: RunanywhereAI/runanywhere-sdks
Length of output: 1146
Fail fast when
GetStringUTFCharsfor grammar returns null.At Line 247,
GetStringUTFCharscan fail and leave a pending Java exception. The current flow skips grammar assignment but continues native work; it should return immediately after releasingpromptStr, consistent with howmodelPath(line 174),prompt(line 231), andjsonSchema(line 293) are handled elsewhere in the file.🔧 Suggested fix
const char* grammarStr = nullptr; if (grammar != nullptr) { grammarStr = env->GetStringUTFChars(grammar, nullptr); + if (!grammarStr) { + env->ReleaseStringUTFChars(prompt, promptStr); + LOGe("nativeGenerate: Failed to get grammar"); + return nullptr; + } if (grammarStr[0] != '\0') { options.grammar = grammarStr; } }🤖 Prompt for AI Agents