feat : test pizza with mcp
This commit is contained in:
parent
10837003b7
commit
ffcb1e3b52
11
BotSharp.sln
11
BotSharp.sln
|
|
@ -131,6 +131,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BotSharp.MCP", "src\Infrast
|
|||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BotSharp.PizzaBot.MCPServer", "tests\BotSharp.PizzaBot.MCPServer\BotSharp.PizzaBot.MCPServer.csproj", "{AD91B4ED-0623-4710-913E-6D7C893E76EF}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "mcpdotnet", "..\mcpdotnet\src\mcpdotnet\mcpdotnet.csproj", "{DF0DBE28-C0FE-4947-8E95-9FBC42B4D44A}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
|
|
@ -539,6 +541,14 @@ Global
|
|||
{AD91B4ED-0623-4710-913E-6D7C893E76EF}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{AD91B4ED-0623-4710-913E-6D7C893E76EF}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{AD91B4ED-0623-4710-913E-6D7C893E76EF}.Release|x64.Build.0 = Release|Any CPU
|
||||
{DF0DBE28-C0FE-4947-8E95-9FBC42B4D44A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{DF0DBE28-C0FE-4947-8E95-9FBC42B4D44A}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{DF0DBE28-C0FE-4947-8E95-9FBC42B4D44A}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{DF0DBE28-C0FE-4947-8E95-9FBC42B4D44A}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{DF0DBE28-C0FE-4947-8E95-9FBC42B4D44A}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{DF0DBE28-C0FE-4947-8E95-9FBC42B4D44A}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{DF0DBE28-C0FE-4947-8E95-9FBC42B4D44A}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{DF0DBE28-C0FE-4947-8E95-9FBC42B4D44A}.Release|x64.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
|
@ -602,6 +612,7 @@ Global
|
|||
{AF329442-B48E-4B48-A18A-1C869D1BA6F5} = {D5293208-2BEF-42FC-A64C-5954F61720BA}
|
||||
{8ED8EEF4-06DD-45F5-AA91-BD2395E901B5} = {E29DC6C4-5E57-48C5-BCB0-6B8F84782749}
|
||||
{AD91B4ED-0623-4710-913E-6D7C893E76EF} = {32FAFFFE-A4CB-4FEE-BF7C-84518BBC6DCC}
|
||||
{DF0DBE28-C0FE-4947-8E95-9FBC42B4D44A} = {E29DC6C4-5E57-48C5-BCB0-6B8F84782749}
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {A9969D89-C98B-40A5-A12B-FC87E55B3A19}
|
||||
|
|
|
|||
|
|
@ -72,7 +72,7 @@ public partial class AgentService
|
|||
hook.OnAgentUtilityLoaded(agent);
|
||||
}
|
||||
|
||||
if(agent.McpTools != null)
|
||||
if(agent.McpTools != null && agent.McpTools.Count >0)
|
||||
{
|
||||
hook.OnAgentMCPToolLoaded(agent);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,11 +8,8 @@
|
|||
<OutputPath>$(SolutionDir)packages</OutputPath>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="mcpdotnet" Version="1.0.1.1" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\..\..\mcpdotnet\src\mcpdotnet\mcpdotnet.csproj" />
|
||||
<ProjectReference Include="..\BotSharp.Abstraction\BotSharp.Abstraction.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
|
|
|||
|
|
@ -1,7 +1,11 @@
|
|||
using BotSharp.Abstraction.Agents;
|
||||
using BotSharp.Abstraction.Agents.Models;
|
||||
using BotSharp.Abstraction.Conversations.Models;
|
||||
using BotSharp.Abstraction.Functions;
|
||||
using McpDotNet.Client;
|
||||
using McpDotNet.Protocol.Types;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
|
@ -13,12 +17,14 @@ namespace BotSharp.Core.Mcp.Functions;
|
|||
public class McpToolFunction : IFunctionCallback
|
||||
{
|
||||
private readonly Tool _tool;
|
||||
private readonly IMcpClient _client;
|
||||
private readonly MCPClientManager _clientManager;
|
||||
private readonly IServiceProvider _serviceProvider;
|
||||
|
||||
public McpToolFunction(Tool tool, IMcpClient client)
|
||||
public McpToolFunction(IServiceProvider provider, Tool tool, MCPClientManager client)
|
||||
{
|
||||
_serviceProvider = provider ?? throw new ArgumentNullException(nameof(provider));
|
||||
_tool = tool ?? throw new ArgumentNullException(nameof(tool));
|
||||
_client = client ?? throw new ArgumentNullException(nameof(client));
|
||||
_clientManager = client ?? throw new ArgumentNullException(nameof(client));
|
||||
}
|
||||
|
||||
public string Name => _tool.Name;
|
||||
|
|
@ -27,9 +33,14 @@ public class McpToolFunction : IFunctionCallback
|
|||
{
|
||||
// Convert arguments to dictionary format expected by mcpdotnet
|
||||
Dictionary<string, object> argDict = JsonToDictionary(message.FunctionArgs);
|
||||
|
||||
var currentAgentId = message.CurrentAgentId;
|
||||
var agentService = _serviceProvider.GetRequiredService<IAgentService>();
|
||||
var agent = await agentService.LoadAgent(currentAgentId);
|
||||
var serverId = agent.McpTools.Where(t => t.Functions.Any(f => f.Name == Name)).FirstOrDefault().ServerId;
|
||||
|
||||
var client = await _clientManager.Factory.GetClientAsync(serverId);
|
||||
// Call the tool through mcpdotnet
|
||||
var result = await _client.CallToolAsync(
|
||||
var result = await client.CallToolAsync(
|
||||
_tool.Name,
|
||||
argDict.Count == 0 ? new() : argDict
|
||||
);
|
||||
|
|
|
|||
|
|
@ -26,14 +26,11 @@ public class MCPToolAgentHook : AgentHookBase
|
|||
{
|
||||
if (agent.Type == AgentType.Routing)
|
||||
return;
|
||||
|
||||
var conv = _services.GetRequiredService<IConversationService>();
|
||||
var isConvMode = conv.IsConversationMode();
|
||||
if (!isConvMode) return;
|
||||
|
||||
agent.SecondaryFunctions ??= [];
|
||||
agent.SecondaryInstructions ??= [];
|
||||
agent.McpTools ??= [];
|
||||
|
||||
var functions = GetMCPContent(agent);
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,11 @@
|
|||
using BotSharp.Abstraction.Agents;
|
||||
using BotSharp.Abstraction.Conversations;
|
||||
using BotSharp.Abstraction.Functions;
|
||||
using BotSharp.Abstraction.Plugins;
|
||||
using BotSharp.Core.Mcp.Functions;
|
||||
using BotSharp.Core.Mcp.Settings;
|
||||
using BotSharp.MCP.Hooks;
|
||||
using McpDotNet.Protocol.Types;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
|
@ -31,13 +35,16 @@ public class McpPlugin : IBotSharpPlugin
|
|||
var tools = client.ListToolsAsync().Result;
|
||||
|
||||
foreach (var tool in tools.Tools)
|
||||
{
|
||||
services.AddScoped<IFunctionCallback>(provider =>
|
||||
{
|
||||
var func = new McpToolFunction(tool, client);
|
||||
return func;
|
||||
});
|
||||
{
|
||||
services.AddScoped( provider => { return tool; });
|
||||
|
||||
services.AddScoped<IFunctionCallback>( provider => {
|
||||
var funcTool = new McpToolFunction( provider, tool, clientManager);
|
||||
return funcTool;
|
||||
});
|
||||
}
|
||||
}
|
||||
// Register hooks
|
||||
services.AddScoped<IAgentHook, MCPToolAgentHook>();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -50,10 +50,7 @@
|
|||
"Name": "gpt-35-turbo",
|
||||
"Version": "1106",
|
||||
"ApiKey": "",
|
||||
"Endpoint": "https://gpt-35-turbo-instruct.openai.azure.com/",
|
||||
"Type": "chat",
|
||||
"PromptCost": 0.0015,
|
||||
"CompletionCost": 0.002
|
||||
"Endpoint": "https://gpt-35-turbo-instruct.openai.azure.com/"
|
||||
},
|
||||
{
|
||||
"Name": "gpt-35-turbo-instruct",
|
||||
|
|
@ -170,7 +167,7 @@
|
|||
"HostAgentId": "01e2fc5c-2c89-4ec7-8470-7688608b496c",
|
||||
"EnableTranslator": false,
|
||||
"LlmConfig": {
|
||||
"Provider": "openai",
|
||||
"Provider": "azure-openai",
|
||||
"Model": "gpt-4o-mini"
|
||||
}
|
||||
},
|
||||
|
|
@ -187,7 +184,8 @@
|
|||
"Name": "PizzaServer",
|
||||
"TransportType": "stdio",
|
||||
"TransportOptions": {
|
||||
"Command": "BotSharp.PizzaBot.MCPServer.exe"
|
||||
"command": "BotSharp.PizzaBot.MCPServer.exe",
|
||||
"workingDirectory": "E:\\GitHub\\BotSharp\\src\\WebStarter\\bin\\Debug\\net8.0"
|
||||
}
|
||||
}
|
||||
]
|
||||
|
|
@ -409,6 +407,7 @@
|
|||
"BotSharp.Core.SideCar",
|
||||
"BotSharp.Core.Crontab",
|
||||
"BotSharp.Logger",
|
||||
"BotSharp.MCP",
|
||||
"BotSharp.Plugin.MongoStorage",
|
||||
"BotSharp.Plugin.Dashboard",
|
||||
"BotSharp.Plugin.OpenAI",
|
||||
|
|
|
|||
|
|
@ -54,6 +54,39 @@ namespace BotSharp.PizzaBot.MCPServer
|
|||
},
|
||||
Required = new List<string>() { "order_number", "total_amount" }
|
||||
},
|
||||
},
|
||||
new Tool()
|
||||
{
|
||||
Name = "get_pizza_prices",
|
||||
Description = "call this function to get pizza prices",
|
||||
InputSchema = new JsonSchema()
|
||||
{
|
||||
Type = "object",
|
||||
Properties = new Dictionary<string, JsonSchemaProperty>()
|
||||
{
|
||||
["pizza_type"] = new JsonSchemaProperty() { Type = "string", Description = "The pizza type." },
|
||||
["quantity"] = new JsonSchemaProperty() { Type = "string", Description = "quantity of pizza." },
|
||||
|
||||
},
|
||||
Required = new List<string>(){ "pizza_type", "quantity" }
|
||||
}
|
||||
},
|
||||
new Tool()
|
||||
{
|
||||
Name = "place_an_order",
|
||||
Description = "Place an order when user has confirmed the pizza type and quantity.",
|
||||
InputSchema = new JsonSchema()
|
||||
{
|
||||
Type = "object",
|
||||
Properties = new Dictionary<string, JsonSchemaProperty>()
|
||||
{
|
||||
["pizza_type"] = new JsonSchemaProperty() { Type = "string", Description = "The pizza type." },
|
||||
["quantity"] = new JsonSchemaProperty() { Type = "number", Description = "quantity of pizza." },
|
||||
["unit_price"] = new JsonSchemaProperty() { Type = "number", Description = "unit price" },
|
||||
|
||||
},
|
||||
Required = new List<string>(){"pizza_type", "quantity", "unit_price" }
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
||||
|
|
@ -72,9 +105,8 @@ namespace BotSharp.PizzaBot.MCPServer
|
|||
throw new McpServerException("Missing required argument 'total_amount'");
|
||||
}
|
||||
dynamic message = new ExpandoObject();
|
||||
message.pepperoni_unit_price = 3.2;
|
||||
message.cheese_unit_price = 3.5;
|
||||
message.margherita_unit_price = 3.8;
|
||||
message.Transaction = Guid.NewGuid().ToString();
|
||||
message.Status = "Success";
|
||||
|
||||
// Serialize the message to JSON
|
||||
var jso = new JsonSerializerOptions() { WriteIndented = true };
|
||||
|
|
@ -85,6 +117,53 @@ namespace BotSharp.PizzaBot.MCPServer
|
|||
Content = [new Content() { Text = jsonMessage , Type = "text" }]
|
||||
};
|
||||
}
|
||||
else if(request.Name == "get_pizza_prices")
|
||||
{
|
||||
if (request.Arguments is null || !request.Arguments.TryGetValue("pizza_type", out var pizza_type))
|
||||
{
|
||||
throw new McpServerException("Missing required argument 'pizza_type'");
|
||||
}
|
||||
if (request.Arguments is null || !request.Arguments.TryGetValue("quantity", out var quantity))
|
||||
{
|
||||
throw new McpServerException("Missing required argument 'quantity'");
|
||||
}
|
||||
dynamic message = new ExpandoObject();
|
||||
message.pepperoni_unit_price = 3.2;
|
||||
message.cheese_unit_price = 3.5;
|
||||
message.margherita_unit_price = 3.8;
|
||||
// Serialize the message to JSON
|
||||
var jso = new JsonSerializerOptions() { WriteIndented = true };
|
||||
var jsonMessage = JsonSerializer.Serialize(message, jso);
|
||||
return new CallToolResponse()
|
||||
{
|
||||
Content = [new Content() { Text = jsonMessage, Type = "text" }]
|
||||
};
|
||||
}
|
||||
else if (request.Name == "place_an_order")
|
||||
{
|
||||
if (request.Arguments is null || !request.Arguments.TryGetValue("pizza_type", out var pizza_type))
|
||||
{
|
||||
throw new McpServerException("Missing required argument 'pizza_type'");
|
||||
}
|
||||
if (request.Arguments is null || !request.Arguments.TryGetValue("quantity", out var quantity))
|
||||
{
|
||||
throw new McpServerException("Missing required argument 'quantity'");
|
||||
}
|
||||
if (request.Arguments is null || !request.Arguments.TryGetValue("unit_price", out var unit_price))
|
||||
{
|
||||
throw new McpServerException("Missing required argument 'unit_price'");
|
||||
}
|
||||
dynamic message = new ExpandoObject();
|
||||
message.order_number = "P123-01";
|
||||
message.Content = "The order number is P123-01";
|
||||
// Serialize the message to JSON
|
||||
var jso = new JsonSerializerOptions() { WriteIndented = true };
|
||||
var jsonMessage = JsonSerializer.Serialize(message, jso);
|
||||
return new CallToolResponse()
|
||||
{
|
||||
Content = [new Content() { Text = jsonMessage, Type = "text" }]
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new McpServerException($"Unknown tool: {request.Name}");
|
||||
|
|
|
|||
|
|
@ -1,21 +1,21 @@
|
|||
using BotSharp.Abstraction.Conversations.Models;
|
||||
using System.Text.Json;
|
||||
//using BotSharp.Abstraction.Conversations.Models;
|
||||
//using System.Text.Json;
|
||||
|
||||
namespace BotSharp.Plugin.PizzaBot.Functions;
|
||||
//namespace BotSharp.Plugin.PizzaBot.Functions;
|
||||
|
||||
public class GetPizzaPricesFn : IFunctionCallback
|
||||
{
|
||||
public string Name => "get_pizza_price";
|
||||
//public class GetPizzaPricesFn : IFunctionCallback
|
||||
//{
|
||||
// public string Name => "get_pizza_price";
|
||||
|
||||
public async Task<bool> Execute(RoleDialogModel message)
|
||||
{
|
||||
message.Data = new
|
||||
{
|
||||
pepperoni_unit_price = 3.2,
|
||||
cheese_unit_price = 3.5,
|
||||
margherita_unit_price = 3.8,
|
||||
};
|
||||
message.Content = JsonSerializer.Serialize(message.Data);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// public async Task<bool> Execute(RoleDialogModel message)
|
||||
// {
|
||||
// message.Data = new
|
||||
// {
|
||||
// pepperoni_unit_price = 3.2,
|
||||
// cheese_unit_price = 3.5,
|
||||
// margherita_unit_price = 3.8,
|
||||
// };
|
||||
// message.Content = JsonSerializer.Serialize(message.Data);
|
||||
// return true;
|
||||
// }
|
||||
//}
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@ public class GetPizzaTypesFn : IFunctionCallback
|
|||
}).ToArray()
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,24 +1,24 @@
|
|||
using BotSharp.Abstraction.Conversations;
|
||||
using BotSharp.Abstraction.Conversations.Models;
|
||||
//using BotSharp.Abstraction.Conversations;
|
||||
//using BotSharp.Abstraction.Conversations.Models;
|
||||
|
||||
namespace BotSharp.Plugin.PizzaBot.Functions;
|
||||
//namespace BotSharp.Plugin.PizzaBot.Functions;
|
||||
|
||||
public class PlaceOrderFn : IFunctionCallback
|
||||
{
|
||||
public string Name => "place_an_order";
|
||||
//public class PlaceOrderFn : IFunctionCallback
|
||||
//{
|
||||
// public string Name => "place_an_order";
|
||||
|
||||
private readonly IServiceProvider _service;
|
||||
public PlaceOrderFn(IServiceProvider service)
|
||||
{
|
||||
_service = service;
|
||||
}
|
||||
// private readonly IServiceProvider _service;
|
||||
// public PlaceOrderFn(IServiceProvider service)
|
||||
// {
|
||||
// _service = service;
|
||||
// }
|
||||
|
||||
public async Task<bool> Execute(RoleDialogModel message)
|
||||
{
|
||||
message.Content = "The order number is P123-01";
|
||||
var state = _service.GetRequiredService<IConversationStateService>();
|
||||
state.SetState("order_number", "P123-01");
|
||||
// public async Task<bool> Execute(RoleDialogModel message)
|
||||
// {
|
||||
// message.Content = "The order number is P123-01";
|
||||
// var state = _service.GetRequiredService<IConversationStateService>();
|
||||
// state.SetState("order_number", "P123-01");
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// return true;
|
||||
// }
|
||||
//}
|
||||
|
|
|
|||
|
|
@ -6,5 +6,19 @@
|
|||
"id": "c2b57a74-ae4e-4c81-b3ad-9ac5bff982bd",
|
||||
"disabled": false,
|
||||
"isPublic": true,
|
||||
"profiles": [ "pizza" ]
|
||||
"profiles": [ "pizza" ],
|
||||
"mcptools": [
|
||||
{
|
||||
"serverid": "PizzaServer",
|
||||
"disabled": false,
|
||||
"functions": [
|
||||
{
|
||||
"Name": "get_pizza_price"
|
||||
},
|
||||
{
|
||||
"Name": "place_an_order"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -1,18 +1,18 @@
|
|||
{
|
||||
"name": "get_pizza_price",
|
||||
"description": "call this function to get the pizza price",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"pizza_type": {
|
||||
"type": "string",
|
||||
"description": "The pizza type."
|
||||
},
|
||||
"quantity": {
|
||||
"type": "string",
|
||||
"description": "quantity of pizza."
|
||||
}
|
||||
},
|
||||
"required": [ "pizza_type", "quantity" ]
|
||||
}
|
||||
}
|
||||
//{
|
||||
// "name": "get_pizza_price",
|
||||
// "description": "call this function to get the pizza price",
|
||||
// "parameters": {
|
||||
// "type": "object",
|
||||
// "properties": {
|
||||
// "pizza_type": {
|
||||
// "type": "string",
|
||||
// "description": "The pizza type."
|
||||
// },
|
||||
// "quantity": {
|
||||
// "type": "string",
|
||||
// "description": "quantity of pizza."
|
||||
// }
|
||||
// },
|
||||
// "required": [ "pizza_type", "quantity" ]
|
||||
// }
|
||||
//}
|
||||
|
|
@ -1,22 +1,22 @@
|
|||
{
|
||||
"name": "place_an_order",
|
||||
"description": "Place an order when user has confirmed the pizza type and quantity.",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"pizza_type": {
|
||||
"type": "string",
|
||||
"description": "The pizza type."
|
||||
},
|
||||
"quantity": {
|
||||
"type": "number",
|
||||
"description": "quantity of pizza."
|
||||
},
|
||||
"unit_price": {
|
||||
"type": "number",
|
||||
"description": "unit price"
|
||||
}
|
||||
},
|
||||
"required": [ "pizza_type", "quantity", "unit_price" ]
|
||||
}
|
||||
}
|
||||
//{
|
||||
// "name": "place_an_order",
|
||||
// "description": "Place an order when user has confirmed the pizza type and quantity.",
|
||||
// "parameters": {
|
||||
// "type": "object",
|
||||
// "properties": {
|
||||
// "pizza_type": {
|
||||
// "type": "string",
|
||||
// "description": "The pizza type."
|
||||
// },
|
||||
// "quantity": {
|
||||
// "type": "number",
|
||||
// "description": "quantity of pizza."
|
||||
// },
|
||||
// "unit_price": {
|
||||
// "type": "number",
|
||||
// "description": "unit price"
|
||||
// }
|
||||
// },
|
||||
// "required": [ "pizza_type", "quantity", "unit_price" ]
|
||||
// }
|
||||
//}
|
||||
|
|
@ -7,15 +7,17 @@
|
|||
"disabled": false,
|
||||
"isPublic": true,
|
||||
"profiles": [ "pizza" ],
|
||||
"McpTools": {
|
||||
"ServerId": "b284db86-e9c2-4c25-a59e-4649797dd130",
|
||||
"Disabled": "false",
|
||||
"Functions": [
|
||||
{
|
||||
"Name": "make_payment"
|
||||
}
|
||||
]
|
||||
},
|
||||
"mcptools": [
|
||||
{
|
||||
"serverid": "PizzaServer",
|
||||
"disabled": false,
|
||||
"functions": [
|
||||
{
|
||||
"Name": "make_payment"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"routingRules": [
|
||||
{
|
||||
"field": "order_number",
|
||||
|
|
|
|||
Loading…
Reference in a new issue