Skip to content

Instantly share code, notes, and snippets.

@artydev
Last active June 8, 2026 12:27
Show Gist options
  • Select an option

  • Save artydev/697965fe1d00e2725a505c2cdd941231 to your computer and use it in GitHub Desktop.

Select an option

Save artydev/697965fe1d00e2725a505c2cdd941231 to your computer and use it in GitHub Desktop.
lmf2-tools-aot.cs
using System.ClientModel;
using System.Text.Json;
using System.Text.Json.Nodes;
using System.Text.Json.Serialization;
using System.Text.RegularExpressions;
using OpenAI;
using OpenAI.Chat;
// create Model for lfm2 cf previous lmf2-tools
// ── Config ───────────────────────────────────────────────────────────────────
const string BaseUrl = "http://localhost:11434/v1";
const string Model = "lfm2.5-tools";
// ── Client ───────────────────────────────────────────────────────────────────
var client = new OpenAIClient(
credential: new ApiKeyCredential("ollama"),
options: new OpenAIClientOptions { Endpoint = new Uri(BaseUrl) }).GetChatClient(Model);
// ── Tool definitions ─────────────────────────────────────────────────────────
ChatTool[] tools = [
ChatTool.CreateFunctionTool("get_weather", "Get weather for a city.", BinaryData.FromString("""{"type": "object", "properties": {"location": {"type": "string"}, "unit": {"type": "string"}}, "required": ["location"]}""")),
ChatTool.CreateFunctionTool("calculate", "Evaluate math.", BinaryData.FromString("""{"type": "object", "properties": {"expression": {"type": "string"}}, "required": ["expression"]}""")),
ChatTool.CreateFunctionTool("search_contacts", "Search contacts.", BinaryData.FromString("""{"type": "object", "properties": {"name": {"type": "string"}}, "required": ["name"]}""")),
];
// ── Main Loop ────────────────────────────────────────────────────────────────
Console.WriteLine($"Model: {Model}\nURL: {BaseUrl}");
string[] testPrompts = ["What's the weather like in Paris?", "What is 10 + 20?", "Find Alice."];
foreach (var prompt in testPrompts)
{
try { await Run(prompt); }
catch (Exception ex) { Console.WriteLine($"Error: {ex.Message}"); }
}
async Task<string> Run(string userPrompt)
{
Console.WriteLine($"\nUSER: {userPrompt}");
var options = new ChatCompletionOptions();
foreach (var tool in tools) options.Tools.Add(tool);
var messages = new List<ChatMessage> { new UserChatMessage(userPrompt) };
for (int iter = 0; iter < 3; iter++)
{
ChatCompletion response = await client.CompleteChatAsync(messages, options);
if (response.FinishReason == ChatFinishReason.ToolCalls)
{
messages.Add(new AssistantChatMessage(response));
foreach (var tc in response.ToolCalls)
{
string result = DispatchTool(tc.FunctionName, tc.FunctionArguments.ToString());
messages.Add(new ToolChatMessage(tc.Id, result));
}
}
else
{
Console.WriteLine($"ASSISTANT: {response.Content[0].Text}");
return response.Content[0].Text;
}
}
return "";
}
// ── Logic Methods ────────────────────────────────────────────────────────────
string DispatchTool(string name, string argsJson)
{
var args = JsonNode.Parse(argsJson) as JsonObject ?? new JsonObject();
return name switch
{
"get_weather" => GetWeather(args["location"]?.ToString() ?? "", args["unit"]?.ToString() ?? "celsius"),
"calculate" => Calculate(args["expression"]?.ToString() ?? ""),
"search_contacts" => SearchContacts(args["name"]?.ToString() ?? ""),
_ => "Error"
};
}
string GetWeather(string location, string unit)
{
var db = new Dictionary<string, (int temp, string cond)>(StringComparer.OrdinalIgnoreCase) { ["paris"] = (18, "Cloudy") };
// Properly deconstruct the named tuple
var (temp, cond) = db.TryGetValue(location, out var val) ? val : (20, "Unknown");
return JsonSerializer.Serialize(new WeatherResult(temp, cond, location, unit), AppJsonContext.Default.WeatherResult);
}
string Calculate(string expression)
{
try { return JsonSerializer.Serialize(new CalcResult(expression, MathEval.Evaluate(expression)), AppJsonContext.Default.CalcResult); }
catch { return JsonSerializer.Serialize(new CalcError("Error"), AppJsonContext.Default.CalcError); }
}
string SearchContacts(string name)
{
var results = new Contact[] { new("Alice", "alice@example.com") }.Where(c => c.Name.Contains(name, StringComparison.OrdinalIgnoreCase)).ToArray();
return JsonSerializer.Serialize(new ContactResult(name, results, results.Length), AppJsonContext.Default.ContactResult);
}
// ── Data & AOT Context ───────────────────────────────────────────────────────
[JsonSerializable(typeof(WeatherResult))]
[JsonSerializable(typeof(CalcResult))]
[JsonSerializable(typeof(CalcError))]
[JsonSerializable(typeof(ContactResult))]
internal partial class AppJsonContext : JsonSerializerContext { }
internal record WeatherResult(int Temp, string Condition, string Location, string Unit);
internal record CalcResult(string Expression, double Result);
internal record CalcError(string Error);
internal record Contact(string Name, string Email);
internal record ContactResult(string Query, Contact[] Results, int Count);
internal static class MathEval
{
public static double Evaluate(string expr) => new Parser(expr).ParseExpr();
private class Parser(string s)
{
private readonly string _src = s; // Stores the parameter to satisfy CS9113
public double ParseExpr() => 10.0;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment