Plugin registry
Register classes whose methods carry [LMFunction]. The caller discovers candidates by reflection.
Not every workload needs a full agent loop. Many real
interactions reduce to "the user said something; pick the right
method and call it." SingleFunctionCall is the
focused, low-overhead path for exactly that: a registry of C#
methods, a natural-language prompt, an LLM-driven selection
with typed argument extraction, and an optional auto-invoke.
No planning, no orchestration, no agent state.
Register classes whose methods carry [LMFunction]. The caller discovers candidates by reflection.
The model picks exactly one method per prompt. Returns the choice, the extracted parameters, and a confidence score.
Run the chosen method immediately, or hand the result back for the caller to invoke. Both flows supported.
Agent frameworks are powerful and heavy. Planning loops, tool
policies, memory, orchestration, observability all add up. Many
use cases do not need any of that. A search box that maps to a
method. A voice command that triggers an action. An admin form
that interprets a freeform instruction. SingleFunctionCall
serves those without paying the agent tax.
No planning loop, no tool-iteration loop, no orchestration round-trips. One inference pass, one selected method, one set of parameters.
Two concepts: a plugin (a class with annotated methods) and a call. Reads like a function-router, behaves like one.
[LMFunction] annotations work for both single function calling and agent tools. Investments in method metadata transfer when you graduate to full agents.
Every call returns a confidence score. Below your threshold, fall back to a clarifying question. Above it, run.
When you know the user must hit one of N methods, force selection: the model chooses among the registered set rather than refusing.
Outgrowing the lightweight path? Lift the same plugins into Agent.Tools. The annotations and signatures transfer; nothing wasted.
SingleFunctionCall
Constructed with an LM. Holds a Plugins collection. Exposes Invoke and InvokeAsync. Configurable for force-mode, force-method, and auto-invocation.
[LMFunction]
Marks a method as callable. Optional Name and Description fields. Method parameters become typed extraction targets; XML doc comments become parameter descriptions.
FunctionCallResult
Method (the selected MethodInfo), Parameters (extracted, typed), Result (the return value if auto-invoked), and Confidence (0 to 1).
One inference pass picks the right method on a plugin and extracts its parameters from the prompt.
using LMKit.FunctionCalling; using LMKit.Agents.Tools; // Annotate methods with [LMFunction]. The class is the plugin. public class BookPlugin { [LMFunction(Description = "Search the catalogue by author and genre")] public List<Book> SearchBooks(string author, string genre) => ... [LMFunction(Description = "Reserve a copy of a known book")] public Reservation Reserve(string isbn, string patronId) => ... } // One inference pass picks the right method and extracts the parameters. var caller = new SingleFunctionCall(model); caller.Plugins.Add(new BookPlugin()); FunctionCallResult r = await caller.InvokeAsync( "Find me a thriller written by an author whose surname starts with K."); Console.WriteLine(r.Method.Name); // SearchBooks Console.WriteLine(r.Confidence); // 0.92
Set AutoInvoke = true to actually run the chosen method and expose its return value as r.Result.
// Auto-invocation flow: the chosen method runs and Result holds the return value. var caller = new SingleFunctionCall(model) { AutoInvoke = true }; caller.Plugins.Add(new BookPlugin()); var r = await caller.InvokeAsync("Reserve ISBN 978-0-679-72313-1 for patron P0427."); // r.Result is the Reservation returned by Reserve(). var reservation = (Reservation)r.Result;
Force the model to pick one of the registered methods, ideal for command palettes and voice menus where free-form replies are not allowed.
// When you know the user must hit one of these methods, force selection. // Useful for command palettes, voice menus, controlled dialogues. var caller = new SingleFunctionCall(model) { ForceFunctionSelection = true // model must pick one of the registered methods }; caller.Plugins.Add(new VoiceCommandsPlugin()); var r = await caller.InvokeAsync(speechToText.LastTranscript); if (r.Confidence < 0.55) { speechSynthesizer.Speak("Sorry, I am not sure I understood. Try again?"); } else { r.Method.Invoke(commandHandler, r.Parameters); }
A power-user search input that maps freeform queries to one of a dozen actions. No agent needed; one method runs per query.
Speech-to-text feeds a registry of voice handlers. Force-mode plus a confidence threshold gives a clean "I did not understand" branch.
An ops engineer types a freeform request; the right management endpoint runs. No tool-call loop, no agent log noise.
Single message, single intent, single action. The bot replies with the result rather than continuing a conversation.
Paste a freeform request into a "describe what you want" field. The right form opens with the right fields populated.
Teams new to AI start here. Same [LMFunction] attributes, same C# methods, lower mental model. Graduate to Agent.Tools when the workflow demands it.
Hand-written keyword matching, regex, fuzzy search, plus a fallback when none match. Brittle. Breaks on phrasing the author did not anticipate. No typed parameters.
Powerful, but the planning loop, tool policies, memory, and orchestration are overkill when one method needs to run. Latency and complexity for capability you do not use.
One inference pass. One method picked. Typed parameters extracted. Confidence scored. Lower latency than agents, smarter than handwritten routers, attribute-compatible with the agent tooling you grow into.
When the workflow needs more than one tool call, graduate to the agent path. Same attributes, more orchestration.
Grammar-constrained generation backs the parameter extraction so the model cannot emit malformed arguments.
Compose dynamic prompts that flow into SingleFunctionCall. Templates plus single-call routing covers a surprising amount of UI surface.
Stateless conversations pair naturally with single function calling for parallel-safe, deterministic invocation.
Working console demos on GitHub, step-by-step how-to guides on the docs site, and the API reference for the classes used on this page.