From f6e00ca300a2907a11066c1da766fc2345c6e8dd Mon Sep 17 00:00:00 2001 From: Kunal Vaishnavi Date: Thu, 8 Jan 2026 04:41:24 +0000 Subject: [PATCH 1/2] Add C# binding for Overlay API --- src/csharp/Config.cs | 5 +++++ src/csharp/NativeMethods.cs | 3 +++ 2 files changed, 8 insertions(+) diff --git a/src/csharp/Config.cs b/src/csharp/Config.cs index 5ff53fdac7..0bd349b7ea 100644 --- a/src/csharp/Config.cs +++ b/src/csharp/Config.cs @@ -46,6 +46,11 @@ public void RemoveModelData(string modelFilename) Result.VerifySuccess(NativeMethods.OgaConfigRemoveModelData(_configHandle, StringUtils.ToUtf8(modelFilename))); } + public void Overlay(string json) + { + Result.VerifySuccess(NativeMethods.OgaConfigOverlay(_configHandle, StringUtils.ToUtf8(json))); + } + public void SetDecoderProviderOptionsHardwareDeviceType(string provider, string hardware_device_type) { Result.VerifySuccess(NativeMethods.OgaConfigSetDecoderProviderOptionsHardwareDeviceType(_configHandle, StringUtils.ToUtf8(provider), StringUtils.ToUtf8(hardware_device_type))); diff --git a/src/csharp/NativeMethods.cs b/src/csharp/NativeMethods.cs index ce505b6ec1..09abc65bac 100644 --- a/src/csharp/NativeMethods.cs +++ b/src/csharp/NativeMethods.cs @@ -61,6 +61,9 @@ internal class NativeLib [DllImport(NativeLib.DllName, CallingConvention = CallingConvention.Winapi)] public static extern IntPtr /* OgaResult* */ OgaConfigRemoveModelData(IntPtr /* OgaConfig* */ config, byte[] /* const char* */ model_filename); + [DllImport(NativeLib.DllName, CallingConvention = CallingConvention.Winapi)] + public static extern IntPtr /* OgaResult* */ OgaConfigOverlay(IntPtr /* OgaConfig* */ config, byte[] /* const char* */ json); + [DllImport(NativeLib.DllName, CallingConvention = CallingConvention.Winapi)] public static extern IntPtr /* OgaResult* */ OgaConfigSetDecoderProviderOptionsHardwareDeviceType(IntPtr /* OgaConfig* */ config, byte[] /* const char* */ provider_name, byte[] /* const char* */ hardware_device_type); From 1db734061cf10724e57b76c6c3dd151fca31300b Mon Sep 17 00:00:00 2001 From: Kunal Vaishnavi Date: Fri, 16 Jan 2026 00:44:14 +0000 Subject: [PATCH 2/2] Add Java and Objective-C bindings for Config APIs --- .../java/ai/onnxruntime/genai/Config.java | 14 ++++ .../native/ai_onnxruntime_genai_Config.cpp | 8 +++ src/objectivec/include/ort_genai_objc.h | 65 +++++++++++++++++++ src/objectivec/oga_config.mm | 60 +++++++++++++++++ src/objectivec/oga_internal.h | 6 ++ src/objectivec/oga_model.mm | 12 ++++ src/objectivec/test/ort_genai_api_test.mm | 16 ++++- src/ort_genai_c.h | 4 +- 8 files changed, 182 insertions(+), 3 deletions(-) create mode 100644 src/objectivec/oga_config.mm diff --git a/src/java/src/main/java/ai/onnxruntime/genai/Config.java b/src/java/src/main/java/ai/onnxruntime/genai/Config.java index 8b11832f77..1b9aa83141 100644 --- a/src/java/src/main/java/ai/onnxruntime/genai/Config.java +++ b/src/java/src/main/java/ai/onnxruntime/genai/Config.java @@ -55,6 +55,18 @@ public void setProviderOption(String providerName, String optionKey, String opti setProviderOption(nativeHandle, providerName, optionKey, optionValue); } + /** + * Overlay JSON on top of the config file + * + * @param json The JSON string to overlay + */ + public void overlay(String json) { + if (nativeHandle == 0) { + throw new IllegalStateException("Instance has been freed and is invalid"); + } + overlay(nativeHandle, json); + } + @Override public void close() { if (nativeHandle != 0) { @@ -85,4 +97,6 @@ long nativeHandle() { private native void setProviderOption( long configHandle, String providerName, String optionKey, String optionValue); + + private native void overlay(long configHandle, String json); } diff --git a/src/java/src/main/native/ai_onnxruntime_genai_Config.cpp b/src/java/src/main/native/ai_onnxruntime_genai_Config.cpp index 6deb6f46f3..d3183f0bfb 100644 --- a/src/java/src/main/native/ai_onnxruntime_genai_Config.cpp +++ b/src/java/src/main/native/ai_onnxruntime_genai_Config.cpp @@ -50,3 +50,11 @@ Java_ai_onnxruntime_genai_Config_setProviderOption(JNIEnv* env, jobject thiz, jl ThrowIfError(env, OgaConfigSetProviderOption(config, c_provider_name, c_option_key, c_option_value)); } + +JNIEXPORT void JNICALL +Java_ai_onnxruntime_genai_Config_overlay(JNIEnv* env, jobject thiz, jlong native_handle, jstring json) { + CString c_json{env, json}; + OgaConfig* config = reinterpret_cast(native_handle); + + ThrowIfError(env, OgaConfigOverlay(config, c_json)); +} \ No newline at end of file diff --git a/src/objectivec/include/ort_genai_objc.h b/src/objectivec/include/ort_genai_objc.h index 9ff082ffc6..2e3d07c836 100644 --- a/src/objectivec/include/ort_genai_objc.h +++ b/src/objectivec/include/ort_genai_objc.h @@ -47,6 +47,63 @@ typedef NS_ENUM(NSInteger, OGAElementType) { OGAElementTypeUint64, // maps to c type uint64_t }; +/** + * An ORT GenAI config. + */ +@interface OGAConfig : NSObject + +- (instancetype)init NS_UNAVAILABLE; + +/** + * Creates a config. + * + * @param path The path to the ONNX GenAI model folder. + * @return The instance, or nil if an error occurs. + */ +- (nullable instancetype)initWithPath:(NSString*)path + error:(NSError**)error NS_DESIGNATED_INITIALIZER; + +/** + * Clear the list of providers in the given config + * + * @param error Optional error information set if an error occurs. + */ +- (BOOL)clearProvidersWithError:(NSError**)error; + +/** + * Add the provider at the end of the list of providers in the given config if it doesn't already exist. + * If it already exists, do nothing. + * + * @param provider The provider to set on the config. + * @param error Optional error information set if an error occurs. + */ +- (BOOL)appendProvider:(NSString*)provider + error:(NSError**)error; + +/** + * Set a provider option. + * + * @param provider The provider to set the option on + * @param key The key of the option to set + * @param value The value of the option to set + * @param error Optional error information set if an error occurs. + */ +- (BOOL)setProviderOption:(NSString*)provider + key:(NSString*)key + value:(NSString*)value + error:(NSError**)error; + +/** + * Overlay JSON on top of config file + * + * @param json The JSON to overlay on the config. + * @param error Optional error information set if an error occurs. + */ +- (BOOL)overlay:(NSString*)json + error:(NSError**)error; + +@end + /** * An ORT GenAI model. */ @@ -63,6 +120,14 @@ typedef NS_ENUM(NSInteger, OGAElementType) { - (nullable instancetype)initWithPath:(NSString*)path error:(NSError**)error NS_DESIGNATED_INITIALIZER; +/** + * Creates a model. + * + * @param config The OGAConfig object + * @return The instance, or nil if an error occurs. + */ +- (nullable instancetype)initWithConfig:(OGAConfig*)config + error:(NSError**)error NS_DESIGNATED_INITIALIZER; @end /** diff --git a/src/objectivec/oga_config.mm b/src/objectivec/oga_config.mm new file mode 100644 index 0000000000..69741d0ff9 --- /dev/null +++ b/src/objectivec/oga_config.mm @@ -0,0 +1,60 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +#import "error_utils.h" +#import "oga_internal.h" +#import "ort_genai_objc.h" + +@implementation OGAConfig { + std::unique_ptr _config; +} + +- (nullable instancetype)initWithPath:(NSString*)path error:(NSError**)error { + if ((self = [super init]) == nil) { + return nil; + } + + try { + _config = OgaConfig::Create([path UTF8String]); + return self; + } + OGA_OBJC_API_IMPL_CATCH_RETURNING_NULLABLE(error) +} + +- (BOOL)clearProvidersWithError:(NSError**)error { + try { + _config->ClearProviders(); + return YES; + } + OGA_OBJC_API_IMPL_CATCH_RETURNING_BOOL(error) +} + +- (BOOL)appendProvider:(NSString*)provider error:(NSError**)error { + try { + _config->AppendProvider([provider UTF8String]); + return YES; + } + OGA_OBJC_API_IMPL_CATCH_RETURNING_BOOL(error) +} + +- (BOOL)setProviderOption:(NSString*)provider key:(NSString*)key value:(NSString*)value error:(NSError**)error { + try { + _config->SetProviderOption([provider UTF8String], [key UTF8String], [value UTF8String]); + return YES; + } + OGA_OBJC_API_IMPL_CATCH_RETURNING_BOOL(error) +} + +- (BOOL)overlay:(NSString*)json error:(NSError**)error { + try { + _config->Overlay([json UTF8String]); + return YES; + } + OGA_OBJC_API_IMPL_CATCH_RETURNING_BOOL(error) +} + +- (const OgaConfig&)CXXAPIOgaConfig { + return *(_config.get()); +} + +@end diff --git a/src/objectivec/oga_internal.h b/src/objectivec/oga_internal.h index d199e0bcbd..c638be58a5 100644 --- a/src/objectivec/oga_internal.h +++ b/src/objectivec/oga_internal.h @@ -7,6 +7,12 @@ NS_ASSUME_NONNULL_BEGIN +@interface OGAConfig () + +- (OgaConfig&)CXXAPIOgaConfig; + +@end + @interface OGAModel () - (const OgaModel&)CXXAPIOgaModel; diff --git a/src/objectivec/oga_model.mm b/src/objectivec/oga_model.mm index a384140591..c4d9dcdaf5 100644 --- a/src/objectivec/oga_model.mm +++ b/src/objectivec/oga_model.mm @@ -21,6 +21,18 @@ - (nullable instancetype)initWithPath:(NSString*)path error:(NSError**)error { OGA_OBJC_API_IMPL_CATCH_RETURNING_NULLABLE(error) } +- (nullable instancetype)initWithConfig:(OGAConfig*)config error:(NSError**)error { + if ((self = [super init]) == nil) { + return nil; + } + + try { + _model = OgaModel::Create([config CXXAPIOgaConfig]); + return self; + } + OGA_OBJC_API_IMPL_CATCH_RETURNING_NULLABLE(error) +} + - (const OgaModel&)CXXAPIOgaModel { return *(_model.get()); } diff --git a/src/objectivec/test/ort_genai_api_test.mm b/src/objectivec/test/ort_genai_api_test.mm index f1e05d9a9d..3e8a5fc0e4 100644 --- a/src/objectivec/test/ort_genai_api_test.mm +++ b/src/objectivec/test/ort_genai_api_test.mm @@ -40,7 +40,20 @@ - (void)testTensor_And_AddExtraInput { NSError* error = nil; BOOL ret = NO; - OGAModel* model = [[OGAModel alloc] initWithPath:[ORTGenAIAPITest getModelPath] error:&error]; + + OGAConfig* config = [[OGAConfig alloc] initWithPath:[ORTGenAIAPITest getModelPath] error:&error]; + ORTAssertNullableResultSuccessful(config, error); + + ret = [config clearProvidersWithError:&error]; + ORTAssertBoolResultSuccessful(ret, error); + + ret = [config appendProvider:@"cpu" error:&error]; + ORTAssertBoolResultSuccessful(ret, error); + + ret = [config overlay:@"{'num_beams': 1}" error:&error]; + ORTAssertBoolResultSuccessful(ret, error); + + OGAModel* model = [[OGAModel alloc] initWithConfig:config error:&error]; ORTAssertNullableResultSuccessful(model, error); OGAGeneratorParams* params = [[OGAGeneratorParams alloc] initWithModel:model error:&error]; @@ -65,6 +78,7 @@ - (void)testGetOutput { NSError* error = nil; BOOL ret = NO; + OGAModel* model = [[OGAModel alloc] initWithPath:[ORTGenAIAPITest getModelPath] error:&error]; ORTAssertNullableResultSuccessful(model, error); diff --git a/src/ort_genai_c.h b/src/ort_genai_c.h index 2b6e4156ed..7b7c767587 100644 --- a/src/ort_genai_c.h +++ b/src/ort_genai_c.h @@ -245,8 +245,8 @@ OGA_EXPORT OgaResult* OGA_API_CALL OgaCreateConfig(const char* config_path, OgaC OGA_EXPORT OgaResult* OGA_API_CALL OgaConfigClearProviders(OgaConfig* config); /** - * \brief Add the provider at the end of the list of providers in the given config if it doesn't already exist - * if it already exists, does nothing. + * \brief Add the provider at the end of the list of providers in the given config if it doesn't already exist. + * If it already exists, do nothing. * \param[in] config The config to set the provider on. * \param[in] provider The provider to set on the config. * \return OgaResult containing the error message if the setting of the provider failed.