Service
OpenAI
Describe the bug
When send a request with tool calls, the request json format is malformed json, And the json schema doesn't follow OpenAI doc
Steps to reproduce
- Send request by the following code:
private static string GetCurrentLocation()
{
// Call the location API here.
return "San Francisco";
}
private static string GetCurrentWeather(string location, string unit = "celsius")
{
// Call the weather API here.
return $"31 {unit}";
}
private static readonly ChatTool getCurrentLocationTool = ChatTool.CreateFunctionTool(
functionName: nameof(GetCurrentLocation),
functionDescription: "Get the user's current location"
);
private static readonly ChatTool getCurrentWeatherTool = ChatTool.CreateFunctionTool(
functionName: nameof(GetCurrentWeather),
functionDescription: "Get the current weather in a given location",
functionParameters: BinaryData.FromBytes("""
{
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "The city and state, e.g. Boston, MA"
},
"unit": {
"type": "string",
"enum": [ "celsius", "fahrenheit" ],
"description": "The temperature unit to use. Infer this from the specified location."
}
},
"required": [ "location" ]
}
"""u8.ToArray())
);
static async Task Main(string[] args)
{
// Initialize logger factory with console and file output
// Configure Serilog
var serilogLogger = new LoggerConfiguration()
.MinimumLevel.Debug()
.WriteTo.Console()
.WriteTo.File("logs/api-calls.log", rollingInterval: RollingInterval.Day)
.CreateLogger();
// Create logger factory with Serilog
var loggerFactory = new SerilogLoggerFactory(serilogLogger);
// Create a logger for the main program
var logger = loggerFactory.CreateLogger("Program");
logger.LogInformation("Application started");
// Create a logger for HTTP client messages
var httpLogger = loggerFactory.CreateLogger("HttpClient.RawMessages");
var innerHandler = new HttpClientHandler();
var loggingHandler = new LoggingHttpMessageHandler(innerHandler, httpLogger, logHeaders: true, logContent: true);
var httpClient = new HttpClient(loggingHandler);
try
{
logger.LogDebug("Configuring OpenAI client");
var opt = new OpenAIClientOptions
{
Endpoint = new Uri(@"https://openrouter.ai/api/v1"),
Transport = new HttpClientPipelineTransport(httpClient, true, loggerFactory)
};
string? apiKey = @"Your key";
if (string.IsNullOrEmpty(apiKey))
{
logger.LogError("OPENAI_API_KEY environment variable is not set");
Console.WriteLine("Error: OPENAI_API_KEY environment variable is not set");
return;
}
logger.LogInformation("Creating ChatClient");
var openaic = new OpenAIClient(new ApiKeyCredential(apiKey), opt);
ChatClient chatClient = openaic.GetChatClient("gpt-4o");
// Create a list of messages for the conversation
List<ChatMessage> messages =
[
new UserChatMessage("What's the weather like today?"),
];
// Create options with tools
ChatCompletionOptions options = new()
{
ToolChoice= ChatToolChoice.CreateRequiredChoice(),
Tools = { getCurrentLocationTool, getCurrentWeatherTool },
};
logger.LogInformation("Starting chat completion with tool calls");
bool requiresAction;
do
{
requiresAction = false;
logger.LogInformation("Sending chat completion request with streaming");
// Using synchronous streaming approach
Console.WriteLine("\n[USING SYNCHRONOUS STREAMING]\n");
var completionUpdates = chatClient.CompleteChatStreamingAsync(messages, options);
Console.Write($"[ASSISTANT]: ");
string fullContent = "";
List<StreamingChatToolCallUpdate> toolCalls = new ();
ChatFinishReason finishReason = ChatFinishReason.Stop;
await foreach (var completionUpdate in completionUpdates)
{
// Process content updates
if (completionUpdate.ContentUpdate.Count > 0)
{
Console.Write(completionUpdate.ContentUpdate[0].Text);
fullContent += completionUpdate.ContentUpdate[0].Text;
}
// Process tool calls
if (completionUpdate.ToolCallUpdates!= null)
{
foreach (var toolCallUpdate in completionUpdate.ToolCallUpdates)
{
// Add or update tool calls as they come in
var existingToolCall = toolCalls.FirstOrDefault(tc => tc.ToolCallId == toolCallUpdate.ToolCallId);
if (existingToolCall == null)
{
toolCalls.Add(toolCallUpdate);
}
}
}
// Update finish reason if provided
if (completionUpdate.FinishReason.HasValue)
{
finishReason = completionUpdate.FinishReason.Value;
}
}
Console.WriteLine(); // Add a newline after streaming content
} while (requiresAction);
// Demonstrate the async streaming approach (without tool calls for simplicity)
Console.WriteLine("\n[USING ASYNCHRONOUS STREAMING]\n");
}
catch (Exception ex)
{
logger.LogError(ex, "An error occurred while processing the request");
Console.WriteLine($"Error: {ex.Message}");
}
logger.LogInformation("Application completed");
}
LoggingHttpMessageHandler is not included. Any LLM generated class will work.
/// <summary>
/// A delegating handler that logs HTTP requests and responses
/// </summary>
public class LoggingHttpMessageHandler : DelegatingHandler `
Replace apiKey
2. We got this log:
HTTP Request: ec5c888e-2e27-453e-8c21-23a70f315f7d
Method: POST
Uri: https://openrouter.ai/api/v1/chat/completions
Headers:
Accept: application/json
OpenAI-Beta: assistants=v2
User-Agent: OpenAI/2.1.0, (.NET 9.0.5; Microsoft Windows 10.0)
Authorization: Bearer key
Content-Type: application/json
Content:
{"messages":[{"role":"user","content":"What\u0027s the weather like today?"}],"model":"gpt-4o","stream":true,"stream_options":{"include_usage":true},"tools":[{"type":"function","function":{"description":"Get the user\u0027s current location","name":"GetCurrentLocation"}},{"type":"function","function":{"description":"Get the current weather in a given location","name":"GetCurrentWeather","parameters": {
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "The city and state, e.g. Boston, MA"
},
"unit": {
"type": "string",
"enum": [ "celsius", "fahrenheit" ],
"description": "The temperature unit to use. Infer this from the specified location."
}
},
"required": [ "location" ]
}}],"tool_choice":"required"}
- Formated Json:
{
"messages": [
{
"role": "user",
"content": "What\u0027s the weather like today?"
}
],
"model": "gpt-4o",
"stream": true,
"stream_options": {
"include_usage": true
},
"tools": [
{
"type": "function",
"function": {
"description": "Get the user\u0027s current location",
"name": "GetCurrentLocation"
}
},
{
"type": "function",
"function": {
"description": "Get the current weather in a given location",
"name": "GetCurrentWeather",
"parameters": {
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "The city and state, e.g. Boston, MA"
},
"unit": {
"type": "string",
"enum": [
"celsius",
"fahrenheit"
],
"description": "The temperature unit to use. Infer this from the specified location."
}
},
"required": [
"location"
]
}
}
],
"tool_choice": "required"
}
There is an extra "function": { in the description of functions, according to https://platform.openai.com/docs/api-reference/responses/create
And the json braces are uneven.
OS
Win 10
.NET version
9.0.5
Library version
2.1.0. But 2.2.0-beta.4 would have same issue
Service
OpenAI
Describe the bug
When send a request with tool calls, the request json format is malformed json, And the json schema doesn't follow OpenAI doc
Steps to reproduce
LoggingHttpMessageHandler is not included. Any LLM generated class will work.
Replace apiKey
2. We got this log:
There is an extra
"function": {in the description of functions, according to https://platform.openai.com/docs/api-reference/responses/createAnd the json braces are uneven.
OS
Win 10
.NET version
9.0.5
Library version
2.1.0. But 2.2.0-beta.4 would have same issue