From 33e88cbcf46bdc64490e8b9ac42426a6eb553267 Mon Sep 17 00:00:00 2001 From: Viet Le Date: Mon, 7 Sep 2026 15:57:15 +0100 Subject: [PATCH 1/2] Add option for structured JSON output --- OpenAI_Adapter/AdapterActions/Execute.cs | 36 +++++++++++++++++++---- OpenAI_oM/Config/PromptExecutionConfig.cs | 6 ++++ 2 files changed, 36 insertions(+), 6 deletions(-) diff --git a/OpenAI_Adapter/AdapterActions/Execute.cs b/OpenAI_Adapter/AdapterActions/Execute.cs index 636379a..b993084 100644 --- a/OpenAI_Adapter/AdapterActions/Execute.cs +++ b/OpenAI_Adapter/AdapterActions/Execute.cs @@ -110,13 +110,37 @@ private async Task PromptAsync(string system, IEnumerable user, messages.AddRange(user.Select(x => new { role = "user", content = x })); messages.AddRange(assistant.Select(x => new { role = "assistant", content = x })); - var requestBody = new + object requestBody; + if (!string.IsNullOrWhiteSpace(config.ResponseFormatJsonSchema)) { - messages = messages, - max_tokens = config.MaxTokens, - temperature = config.Temperature, - top_p = config.TopP - }; + requestBody = new + { + messages = messages, + max_tokens = config.MaxTokens, + temperature = config.Temperature, + top_p = config.TopP, + response_format = new + { + type = "json_schema", + json_schema = new + { + name = config.ResponseFormatName, + schema = JsonSerializer.Deserialize(config.ResponseFormatJsonSchema), + strict = true + } + } + }; + } + else + { + requestBody = new + { + messages = messages, + max_tokens = config.MaxTokens, + temperature = config.Temperature, + top_p = config.TopP + }; + } string json = JsonSerializer.Serialize(requestBody); StringContent content = new StringContent(json, Encoding.UTF8, "application/json"); diff --git a/OpenAI_oM/Config/PromptExecutionConfig.cs b/OpenAI_oM/Config/PromptExecutionConfig.cs index 274b2c7..049dc79 100644 --- a/OpenAI_oM/Config/PromptExecutionConfig.cs +++ b/OpenAI_oM/Config/PromptExecutionConfig.cs @@ -44,6 +44,12 @@ public class PromptExecutionConfig : ActionConfig [Description("Timeout in seconds for the prompt execution. This setting determines how long the system will wait for a response before timing out. A typical value is 30 seconds, but it can be adjusted based on the expected response time of the API.")] public virtual int TimeoutSeconds { get; set; } = 30; + [Description("Name of the JSON schema passed to the API when structured output is requested.")] + public virtual string ResponseFormatName { get; set; } = null; + + [Description("JSON Schema describing the expected response shape when structured output is requested. When null, the request is sent without a response_format constraint.")] + public virtual string ResponseFormatJsonSchema { get; set; } = null; + /***************************************************/ } } From 91cce9ffb950eb462775459d7f36254fc2afbef3 Mon Sep 17 00:00:00 2001 From: Viet Le Date: Wed, 9 Sep 2026 12:04:10 +0100 Subject: [PATCH 2/2] Use IOutputType for PromptExecutionConfig --- OpenAI_Adapter/AdapterActions/Execute.cs | 51 +++++++++++++++++------ OpenAI_oM/Config/PromptExecutionConfig.cs | 8 ++-- OpenAI_oM/Output/IOutputType.cs | 33 +++++++++++++++ OpenAI_oM/Output/JsonObject.cs | 32 ++++++++++++++ OpenAI_oM/Output/JsonSchema.cs | 42 +++++++++++++++++++ OpenAI_oM/Output/Text.cs | 32 ++++++++++++++ 6 files changed, 180 insertions(+), 18 deletions(-) create mode 100644 OpenAI_oM/Output/IOutputType.cs create mode 100644 OpenAI_oM/Output/JsonObject.cs create mode 100644 OpenAI_oM/Output/JsonSchema.cs create mode 100644 OpenAI_oM/Output/Text.cs diff --git a/OpenAI_Adapter/AdapterActions/Execute.cs b/OpenAI_Adapter/AdapterActions/Execute.cs index b993084..2697e15 100644 --- a/OpenAI_Adapter/AdapterActions/Execute.cs +++ b/OpenAI_Adapter/AdapterActions/Execute.cs @@ -24,6 +24,7 @@ using BH.oM.Adapter; using BH.oM.Adapters.OpenAI; using BH.oM.Adapters.OpenAI.Commands; +using BH.oM.Adapters.OpenAI.Output; using BH.oM.Base; using System; using System.Collections.Generic; @@ -110,25 +111,16 @@ private async Task PromptAsync(string system, IEnumerable user, messages.AddRange(user.Select(x => new { role = "user", content = x })); messages.AddRange(assistant.Select(x => new { role = "assistant", content = x })); + object responseFormat = BuildResponseFormat(config.OutputType); object requestBody; - if (!string.IsNullOrWhiteSpace(config.ResponseFormatJsonSchema)) + if (responseFormat == null) { requestBody = new { messages = messages, max_tokens = config.MaxTokens, temperature = config.Temperature, - top_p = config.TopP, - response_format = new - { - type = "json_schema", - json_schema = new - { - name = config.ResponseFormatName, - schema = JsonSerializer.Deserialize(config.ResponseFormatJsonSchema), - strict = true - } - } + top_p = config.TopP }; } else @@ -138,7 +130,8 @@ private async Task PromptAsync(string system, IEnumerable user, messages = messages, max_tokens = config.MaxTokens, temperature = config.Temperature, - top_p = config.TopP + top_p = config.TopP, + response_format = responseFormat }; } @@ -162,6 +155,38 @@ private async Task PromptAsync(string system, IEnumerable user, } /***************************************************/ + + private static object BuildResponseFormat(IOutputType outputType) + { + if (outputType == null || outputType is Text) + return null; + + if (outputType is JsonObject) + return new { type = "json_object" }; + + if (outputType is JsonSchema jsonSchema) + { + if (string.IsNullOrWhiteSpace(jsonSchema.Schema)) + return null; + + string schemaName = string.IsNullOrWhiteSpace(jsonSchema.Name) ? "structured_output" : jsonSchema.Name; + + return new + { + type = "json_schema", + json_schema = new + { + strict = true, + name = schemaName, + schema = JsonSerializer.Deserialize(jsonSchema.Schema), + } + }; + } + + return null; + } + + /***************************************************/ } } diff --git a/OpenAI_oM/Config/PromptExecutionConfig.cs b/OpenAI_oM/Config/PromptExecutionConfig.cs index 049dc79..b72f822 100644 --- a/OpenAI_oM/Config/PromptExecutionConfig.cs +++ b/OpenAI_oM/Config/PromptExecutionConfig.cs @@ -21,6 +21,7 @@ */ using BH.oM.Adapter; +using BH.oM.Adapters.OpenAI.Output; using System.ComponentModel; namespace BH.oM.Adapters.OpenAI @@ -44,11 +45,8 @@ public class PromptExecutionConfig : ActionConfig [Description("Timeout in seconds for the prompt execution. This setting determines how long the system will wait for a response before timing out. A typical value is 30 seconds, but it can be adjusted based on the expected response time of the API.")] public virtual int TimeoutSeconds { get; set; } = 30; - [Description("Name of the JSON schema passed to the API when structured output is requested.")] - public virtual string ResponseFormatName { get; set; } = null; - - [Description("JSON Schema describing the expected response shape when structured output is requested. When null, the request is sent without a response_format constraint.")] - public virtual string ResponseFormatJsonSchema { get; set; } = null; + [Description("How the model response should be formatted. Defaults to plain text with no response_format constraint.")] + public virtual IOutputType OutputType { get; set; } = new Text(); /***************************************************/ } diff --git a/OpenAI_oM/Output/IOutputType.cs b/OpenAI_oM/Output/IOutputType.cs new file mode 100644 index 0000000..d7addbd --- /dev/null +++ b/OpenAI_oM/Output/IOutputType.cs @@ -0,0 +1,33 @@ +/* + * This file is part of the Buildings and Habitats object Model (BHoM) + * Copyright (c) 2015 - 2026, the respective contributors. All rights reserved. + * + * Each contributor holds copyright over their respective contributions. + * The project versioning (Git) records all such contribution source information. + * + * + * The BHoM is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3.0 of the License, or + * (at your option) any later version. + * + * The BHoM is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this code. If not, see . + */ + +using BH.oM.Base; +using System.ComponentModel; + +namespace BH.oM.Adapters.OpenAI.Output +{ + [Description("Describes how the model response should be formatted when executing a prompt.")] + public interface IOutputType : IObject + { + + } +} diff --git a/OpenAI_oM/Output/JsonObject.cs b/OpenAI_oM/Output/JsonObject.cs new file mode 100644 index 0000000..029c539 --- /dev/null +++ b/OpenAI_oM/Output/JsonObject.cs @@ -0,0 +1,32 @@ +/* + * This file is part of the Buildings and Habitats object Model (BHoM) + * Copyright (c) 2015 - 2026, the respective contributors. All rights reserved. + * + * Each contributor holds copyright over their respective contributions. + * The project versioning (Git) records all such contribution source information. + * + * + * The BHoM is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3.0 of the License, or + * (at your option) any later version. + * + * The BHoM is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this code. If not, see . + */ + +using System.ComponentModel; + +namespace BH.oM.Adapters.OpenAI.Output +{ + [Description("JSON object output. The API is asked to return valid JSON, but no schema is enforced.")] + public class JsonObject : IOutputType + { + + } +} diff --git a/OpenAI_oM/Output/JsonSchema.cs b/OpenAI_oM/Output/JsonSchema.cs new file mode 100644 index 0000000..534589e --- /dev/null +++ b/OpenAI_oM/Output/JsonSchema.cs @@ -0,0 +1,42 @@ +/* + * This file is part of the Buildings and Habitats object Model (BHoM) + * Copyright (c) 2015 - 2026, the respective contributors. All rights reserved. + * + * Each contributor holds copyright over their respective contributions. + * The project versioning (Git) records all such contribution source information. + * + * + * The BHoM is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3.0 of the License, or + * (at your option) any later version. + * + * The BHoM is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this code. If not, see . + */ + +using System.ComponentModel; + +namespace BH.oM.Adapters.OpenAI.Output +{ + [Description("Structured JSON output with a strict JSON Schema. The API validates the response against Schema.")] + public class JsonSchema : IOutputType + { + /***************************************************/ + /**** Public properties ****/ + /***************************************************/ + + [Description("Identifier for the JSON Schema in the API request. Required by OpenAI when using json_schema mode. Use a descriptive name when several schemas exist in one integration; otherwise leave null and the adapter supplies a default.")] + public virtual string Name { get; set; } = null; + + [Description("JSON Schema describing the expected response shape, serialised as a JSON string.")] + public virtual string Schema { get; set; } = null; + + /***************************************************/ + } +} diff --git a/OpenAI_oM/Output/Text.cs b/OpenAI_oM/Output/Text.cs new file mode 100644 index 0000000..946d864 --- /dev/null +++ b/OpenAI_oM/Output/Text.cs @@ -0,0 +1,32 @@ +/* + * This file is part of the Buildings and Habitats object Model (BHoM) + * Copyright (c) 2015 - 2026, the respective contributors. All rights reserved. + * + * Each contributor holds copyright over their respective contributions. + * The project versioning (Git) records all such contribution source information. + * + * + * The BHoM is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3.0 of the License, or + * (at your option) any later version. + * + * The BHoM is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this code. If not, see . + */ + +using System.ComponentModel; + +namespace BH.oM.Adapters.OpenAI.Output +{ + [Description("Plain-text model output. No response_format constraint is sent to the API.")] + public class Text : IOutputType + { + + } +}