n4nAI

Semantic Kernel setup tutorial: connecting to n4n.ai

Hands-on Python guide to a semantic kernel n4n.ai setup tutorial: connect Semantic Kernel to the OpenAI-compatible gateway with runnable code.

n4n Team3 min read629 words

Audio narration

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

This semantic kernel n4n.ai setup tutorial walks through connecting Microsoft’s Semantic Kernel to an OpenAI-compatible inference gateway. We’ll build a minimal Python project that points the kernel at the gateway endpoint, runs chat completions, defines a small plugin, and streams tokens—without writing any custom retry or routing logic.

Prerequisites

  • Python 3.10 or newer
  • pip and a virtual environment tool
  • An API key for the gateway (it exposes a single OpenAI-compatible endpoint)
  • Familiarity with async/await in Python

Install the SDK inside a clean environment:

python -m venv .venv
source .venv/bin/activate
pip install semantic-kernel python-dotenv

Load credentials from a .env file or export them directly. The gateway expects a bearer token; the base URL is fixed.

export N4N_API_KEY="sk-your-key-here"
export N4N_BASE_URL="https://api.n4n.ai/v1"

If you use python-dotenv, call load_dotenv() at the top of your script.

Step 1: Bootstrap the Kernel

Semantic Kernel decouples your code from a specific provider through service objects. For an OpenAI-compatible backend, use OpenAIChatCompletion and override base_url. The model ID is just a string the gateway maps to a downstream provider.

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

def build_kernel() -> Kernel:
    kernel = Kernel()
    chat_service = OpenAIChatCompletion(
        service_id="gw-chat",
        ai_model_id="openai/gpt-4o-mini",
        api_key=os.getenv("N4N_API_KEY"),
        base_url=os.getenv("N4N_BASE_URL", "https://api.n4n.ai/v1"),
    )
    kernel.add_chat_service(service_id="gw-chat", service=chat_service)
    return kernel

The ai_model_id can be any of the 240+ models the gateway addresses. You are not locked to OpenAI; prefix with the provider slug (e.g., anthropic/claude-3-haiku). The service_id is how you retrieve this service later—keep it stable.

Step 2: Run a single chat completion

Create a chat history and call the service directly. This validates your key and endpoint before layering on abstractions.

import asyncio
from semantic_kernel.contents import ChatHistory

async def main():
    kernel = build_kernel()
    chat_service = kernel.get_service("gw-chat")
    history = ChatHistory()
    history.add_user_message("What is the capital of Finland? Reply with one word.")
    try:
        response = await chat_service.get_chat_message_content(chat_history=history)
        print(response.content)
    except Exception as e:
        print("Request failed:", e)

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

Expected output:

Helsinki

A 401 means the env var is missing or wrong. A 404 suggests the base URL is incorrect—remember the gateway exposes a single /v1 endpoint. A 429 indicates rate limiting, which the gateway mitigates via automatic fallback to healthy providers.

Step 3: Semantic functions

The value of Semantic Kernel is composing prompts with code. Define a prompt template as a function and invoke it through the kernel.

from semantic_kernel.functions import KernelFunctionFromPrompt

summarize = KernelFunctionFromPrompt(
    function_name="summarize",
    prompt="Summarize the following text in 10 words or less:\n{{$input}}",
)

async def run_summary():
    kernel = build_kernel()
    text = (
        "Semantic Kernel is a lightweight SDK that lets you combine AI "
        "services with conventional code, planners, and plugins."
    )
    result = await kernel.invoke(summarize, input=text)
    print(result)

asyncio.run(run_summary())

Expected output (exact wording may vary):

Lightweight SDK combining AI services, code, planners, and plugins.

The {{$input}} placeholder is filled from the input argument. You can add more variables and pass them as keyword arguments.

Step 4: Native plugin

Mix deterministic logic with model calls. Decorate a Python method with @kernel_function and register the class.

from semantic_kernel.functions import kernel_function

class MathPlugin:
    @kernel_function(name="add", description="Add two integers")
    def add(self, a: int, b: int) -> int:
        return a + b

    @kernel_function(name="multiply", description="Multiply two integers")
    def multiply(self, a: int, b: int) -> int:
        return a * b

async def call_math():
    kernel = build_kernel()
    kernel.add_plugin(MathPlugin(), plugin_name="math")
    added = await kernel.invoke(kernel.plugins["math"]["add"], a=3, b=4)
    multiplied = await kernel.invoke(kernel.plugins["math"]["multiply"], a=3, b=4)
    print("add:", added, "multiply:", multiplied)

asyncio.run(call_math())

Output:

add: 7 multiply: 12

Plugins let you expose typed functions to planners or call them directly as shown.

Step 5: Streaming responses

For chat UIs, stream tokens. The service exposes an async generator that yields content chunks.

async def stream_chat():
    kernel = build_kernel()
    chat_service = kernel.get_service("gw-chat")
    history = ChatHistory()
    history.add_user_message("Count to 5 slowly.")
    full = ""
    async for chunk in chat_service.get_streaming_chat_message_content(chat_history=history):
        if chunk.content:
            print(chunk.content, end="", flush=True)
            full += chunk.content
    print("\n---")
    print("Assembled:", full)

asyncio.run(stream_chat())

You will see tokens printed incrementally rather than one blob. The assembled string matches what a non-streaming call would return.

Step 6: Routing and resilience

This semantic kernel n4n.ai setup tutorial deliberately avoids writing fallback logic. The gateway honors client routing directives via the model string and forwards provider cache-control hints, and it performs automatic fallback when a provider is rate-limited or degraded. Your code just requests anthropic/claude-3-haiku and gets a response or a structured error.

If you need explicit control, register multiple services with different ai_model_id values and select per call:

kernel.add_chat_service(
    service_id="haiku",
    service=OpenAIChatCompletion(
        service_id="haiku",
        ai_model_id="anthropic/claude-3-haiku",
        api_key=os.getenv("N4N_API_KEY"),
        base_url=os.getenv("N4N_BASE_URL"),
    ),
)

Then kernel.get_service("haiku"). The gateway also respects cache_control markers in the payload, so prompt caching works if the upstream provider supports it.

Step 7: Per-token metering

The gateway returns usage metadata on each response. Semantic Kernel exposes it via the metadata field on the completion object.

response = await chat_service.get_chat_message_content(chat_history=history)
print(response.metadata)

You can pipe this into your own cost tracker. The gateway handles per-token usage metering upstream, so the numbers match your bill.

Troubleshooting

  • SSL errors: ensure requests and aiohttp are up to date.
  • Model not found: the gateway maps slugs; check the model ID against the catalog.
  • Timeouts: the default SK timeout is 60s. Pass timeout=120 to OpenAIChatCompletion if you expect long generations.
  • Async context errors: every call must run inside an event loop; use asyncio.run or a framework that provides one.

Where to go next

You have a working kernel that talks to an OpenAI-compatible gateway, runs prompts, calls native code, and streams. From here, add a planner, or wire the kernel into a FastAPI app. The baseline is intentionally small—expand it with your own plugins and more complex prompt templates.

Tagssemantic-kerneln4n-aisetuptutorial

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 getting started with n4n.ai posts →