n4nAI

Semantic Kernel plugins tutorial: n4n.ai function calling

Build semantic kernel plugins function calling n4n.ai: a hands-on Python tutorial to wire native functions to an OpenAI-compatible gateway with runnable code.

n4n Team3 min read557 words

Audio narration

Coming soon — every post will get a voice note here.

This tutorial walks through semantic kernel plugins function calling n4n.ai to connect native Python functions to an LLM orchestration loop. You’ll define a weather plugin, point Semantic Kernel at an OpenAI-compatible gateway, and let the model invoke your code automatically.

Prerequisites

  • Python 3.10 or newer
  • semantic-kernel Python package (1.3.0+)
  • An API key for the n4n.ai OpenAI-compatible endpoint (or any similar gateway)
  • Comfort with async/await and type hints
pip install semantic-kernel==1.3.0

Step 1: Define a native plugin

Semantic Kernel exposes Python methods to the model through the @kernel_function decorator. The LLM sees the function name, description, and typed parameters—not your source code.

from semantic_kernel.functions import kernel_function

class WeatherPlugin:
    @kernel_function(
        name="get_current_weather",
        description="Get the current weather for a city. Returns temperature in Celsius.",
    )
    def get_current_weather(self, city: str) -> str:
        # Mock implementation; swap for a real HTTP call.
        return f"It is 22°C and sunny in {city}."

The description is mandatory. The model uses it to decide whether to call the function. A missing or vague description silently drops the tool from consideration.

Step 2: Configure the kernel with the gateway

Semantic Kernel’s OpenAI connector accepts a base_url. Point it at the n4n.ai OpenAI-compatible endpoint to route 240+ models through one URL. The gateway forwards tool schemas unchanged and handles provider fallback when a backend is degraded.

import asyncio
from semantic_kernel import Kernel
from semantic_kernel.connectors.ai.open_ai import OpenAIChatCompletion

async def main():
    kernel = Kernel()
    chat_service = OpenAIChatCompletion(
        ai_model_id="gpt-4o-mini",
        api_key="YOUR_N4N_API_KEY",
        base_url="https://api.n4n.ai/v1",
    )
    kernel.add_service(chat_service)

    kernel.add_plugin(WeatherPlugin(), plugin_name="weather")

    settings = OpenAIChatPromptExecutionSettings(
        function_choice_behavior=FunctionChoiceBehavior.Auto()
    )

    result = await kernel.invoke_prompt(
        "What's the weather in Berlin?", settings=settings
    )
    print(result)

if __name__ == "__main__":
    asyncio.run(main())

Run the script. Expected output:

It is 22°C and sunny in Berlin.

The model emitted a tool call, the kernel executed get_current_weather("Berlin"), and the returned string was folded back into the final answer.

Step 3: Inspect the invocation and token usage

The semantic kernel plugins function calling n4n.ai flow gives you full visibility into what the model did. After the call, the kernel history contains the assistant’s tool-call message and the tool’s response.

    for msg in kernel.history:
        if msg.role == "tool":
            print("Tool payload:", msg.content)

    usage = chat_service.get_last_usage()
    if usage:
        print(f"Prompt tokens: {usage.prompt_tokens}")
        print(f"Completion tokens: {usage.completion_tokens}")

Sample output:

Tool payload: It is 22°C and sunny in Berlin.
Prompt tokens: 84
Completion tokens: 11

Per-token metering arrives straight from the gateway, so you can attribute cost without custom instrumentation.

Step 4: Add a second plugin to show composition

Real agents rarely have one tool. Add a time plugin and ask a compound question.

from datetime import datetime, timezone

class TimePlugin:
    @kernel_function(
        name="get_utc_time",
        description="Return current UTC time as HH:MM string.",
    )
    def get_utc_time(self) -> str:
        return datetime.now(timezone.utc).strftime("%H:%M")

# inside main(), after weather plugin:
kernel.add_plugin(TimePlugin(), plugin_name="clock")

result = await kernel.invoke_prompt(
    "What time is it in UTC, and is it hot in Paris?",
    settings=settings,
)
print(result)

The model may call both get_utc_time and get_current_weather in a single turn. The kernel runs them, collects results, and returns a merged answer.

Step 5: Return structured data instead of strings

String returns are fine for prototypes. For production, return typed objects so the model gets clean JSON.

from dataclasses import dataclass

@dataclass
class WeatherReport:
    city: str
    temp_c: float
    conditions: str

class WeatherPlugin:
    @kernel_function(name="get_weather", description="Get structured weather report.")
    def get_weather(self, city: str) -> WeatherReport:
        return WeatherReport(city=city, temp_c=22.0, conditions="sunny")

Semantic Kernel serializes the dataclass to JSON when sending the tool result back to the model. You can verify by printing the tool message content—it will be a JSON object, not a formatted sentence.

Step 6: Test plugins without the model

Before wiring the LLM, call the function directly to validate logic.

plugin = WeatherPlugin()
report = plugin.get_weather("London")
print(report.temp_c, report.conditions)

This isolates bugs in your native code from prompt or routing issues.

Routing directives and cache control

The gateway honors client routing directives and forwards provider cache-control hints. If you need to pin a provider, set a header on the underlying client:

chat_service.client.default_headers["x-n4n-provider"] = "openai"

Automatic fallback already covers rate limits, so only set this when you have a specific compliance or latency requirement.

Common pitfalls

  • Trailing slash on base_url: The OpenAI SDK appends /chat/completions. Use https://api.n4n.ai/v1 with no slash.
  • Sync functions doing I/O: Mark them async if they call external services, or they’ll block the event loop.
  • Undocumented parameters: Every argument the model can pass must be a typed parameter on the decorated method. Python *args won’t appear in the schema.
  • Overlapping descriptions: If two plugins sound identical, the model guesses. Make descriptions distinct and action-oriented.

Wrapping up

You now have a working native function loop using semantic kernel plugins function calling n4n.ai. The plugin code is identical regardless of which backend model the gateway selects—change ai_model_id and the same weather tool runs against a different provider with zero refactoring.

Tagssemantic-kernelpluginsfunction-callingn4n-ai

Written by

n4n Team

The team building n4n — a single OpenAI-compatible API in front of 240+ models, with automatic fallback, load balancing and pay-per-token metering.

More from n4n Team →

All semantic kernel plugins & native functions posts →