Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/sidmanale643/northstar/llms.txt

Use this file to discover all available pages before exploring further.

NorthStar prompt templates are plain strings with variable placeholders embedded directly in the content. When you compile a template, the SDK replaces every placeholder with the value you supply — and if any placeholder has no corresponding value, it raises a ValueError immediately rather than leaving a gap in the rendered output. The template engine supports two placeholder styles so you can use whichever feels natural for your team or mix them freely in a single template.

Supported template syntax

Both placeholder styles resolve to the same underlying mechanism — a regex scan followed by string substitution — and can be used together in the same template.
# Variables are wrapped in {{ }} with optional surrounding whitespace
template = "Summarize {{ doc }} in {{ max_words }} words."

# Renders to:
# "Summarize [doc text] in 120 words."
The single-brace {variable} pattern only matches identifiers — it will not accidentally consume Python format strings that use positional arguments like {0} or advanced format specs like {value:.2f}. Only plain alphabetic variable names ([a-zA-Z_][a-zA-Z0-9_]*) are treated as template variables.

Compilation API

northstar.compile_prompt(prompt_version, variables)

The module-level compile_prompt function (exported from northstar as an alias for prompts.compile) renders a PromptVersion with a dict of variable values and returns a CompiledPrompt.
from northstar import compile_prompt, PromptVersion

version = PromptVersion(
    id=...,
    prompt_id=...,
    version_number=3,
    content="Summarize {{ doc }} in {max_words} words.",
    content_hash="sha256:...",
)

compiled = compile_prompt(version, {"doc": "NorthStar traces agents.", "max_words": 50})

print(compiled.content)
# "Summarize NorthStar traces agents. in 50 words."

print(compiled.raw_content)
# "Summarize {{ doc }} in {max_words} words."

CompiledPrompt.bind(variables, span)

bind() is a context manager on CompiledPrompt that compiles the underlying template with the given variables and, when the block exits, records a prompt link on the target span. This is the preferred way to associate a prompt version with a model call span when you need explicit control over which span receives the link. client.pull_prompt() returns a CompiledPrompt pre-loaded with the resolved PromptVersion. Call bind() on that object to render and link it:
from northstar import Northstar

client = Northstar(api_key="ns_...", project_id="<project-ref>")
prompt = client.pull_prompt("summarizer", label="production")

with client.session() as session:
    with session.run("agent") as run:
        with run.span("chat") as span:
            with prompt.bind(
                variables={"doc": doc_text, "max_words": 120},
                span=span,
            ) as compiled:
                # compiled.content is the fully rendered string
                response = call_llm(model="gpt-4o", content=compiled.content)
When span is omitted, bind() automatically picks up the active model call span from the current context — so in most cases you do not need to pass it explicitly:
from northstar import Northstar, model_call

client = Northstar(api_key="ns_...", project_id="<project-ref>")
prompt = client.pull_prompt("greeting")

with client.session() as session:
    with session.run("agent") as run:
        with model_call("chat", model="gpt-4o-mini", run=run):
            with prompt.bind(variables={"name": "Ada"}) as compiled:
                # compiled.content == "Hello Ada."
                ...
bind() is both a sync and async context manager — async with prompt.bind(...) works identically in async agent code.

CompiledPrompt fields

prompt_id
UUID
required
The UUID of the parent Prompt this compiled result belongs to.
prompt_version_id
UUID
required
The UUID of the PromptVersion that was rendered. This is the value stored in the prompt link on the model call span.
content
str
required
The fully rendered template string with all variable placeholders replaced by their supplied values. Pass this directly to your LLM as the message content.
raw_content
str
required
The original unrendered template string (i.e. PromptVersion.content). Stored alongside content so you can always recover the template structure from the compiled result.
variables
dict[str, Any]
The dict of variable values that were substituted during compilation. Stored in the prompt link payload and visible in the dashboard trace view.
model
str | None
The model identifier hint inherited from the PromptVersion. Your code can read this instead of hardcoding the model name in the calling code.
temperature
float | None
The temperature hint inherited from the PromptVersion.
max_tokens
int | None
The maximum token limit hint inherited from the PromptVersion.
content_hash
str
required
The content_hash inherited from the PromptVersion, carried through to the prompt link so the dashboard can detect content drift between runs.

Missing variable behaviour

If any variable referenced in the template is absent from the variables dict passed to compile_prompt or bind(), the SDK raises a ValueError at compile time — before anything is sent to your LLM:
from northstar import compile_prompt, PromptVersion

version = PromptVersion(
    id=...,
    prompt_id=...,
    version_number=1,
    content="Hello {{ name }}.",
    content_hash="sha256:...",
)

# Raises: ValueError: Missing prompt variables: name
compiled = compile_prompt(version, {})
The error message lists every missing variable name in a comma-separated string so you can fix all omissions at once. No partial rendering occurs — the function is atomic.
Because validation happens at compile time (not at LLM call time), missing variable errors surface early in your request lifecycle, long before any tokens are consumed or latency is incurred.

Variable schema auto-extraction

When a PromptVersion is created — either on the server or locally in code — NorthStar scans the template content and builds a variables list automatically. The extraction logic lives in _prompt_template.py.

extract_variables(template)

Scans the template for both {{ jinja }} and {python} placeholder patterns and returns a sorted list of unique variable names:
from northstar._prompt_template import extract_variables

names = extract_variables("Hello {{ name }}, write {count} bullets for {{ topic }}.")
# ["count", "name", "topic"]  — sorted alphabetically, deduplicated

variables_schema(template)

Wraps extract_variables and returns the full list of variable descriptor dicts that is stored on PromptVersion.variables:
from northstar._prompt_template import variables_schema

schema = variables_schema("Hello {{ name }}, write {count} bullets for {{ topic }}.")
# [
#   {"name": "count",  "type": "string", "required": True, "default": None},
#   {"name": "name",   "type": "string", "required": True, "default": None},
#   {"name": "topic",  "type": "string", "required": True, "default": None},
# ]
All variables are typed as "string", marked required: True, and have default: None. If the server returns a PromptVersion with an empty variables list, the SDK re-runs variables_schema locally via a Pydantic model validator so the schema is always populated before you call compile_prompt.

render_template(template, variables)

The low-level render function used internally by compile_prompt. Calls extract_variables first to check for missing keys, then performs Jinja-style substitution followed by Python-style substitution on the result:
from northstar._prompt_template import render_template

result = render_template(
    "Summarize {{ doc }} in {max_words} words.",
    {"doc": "NorthStar traces agents.", "max_words": 50},
)
# "Summarize NorthStar traces agents. in 50 words."
You generally do not need to call render_template directly — use compile_prompt or bind() instead.

End-to-end example

The following shows the full pull → bind → record flow, reading the model hint directly from the compiled prompt:
from northstar import Northstar

client = Northstar(api_key="ns_...", project_id="<project-ref>")
prompt = client.pull_prompt("summarizer", label="production")

with client.session() as session:
    with session.run("agent") as run:
        with run.span("chat") as span:
            with prompt.bind(
                variables={"doc": doc_text, "max_words": 120},
                span=span,
            ) as compiled:
                # Read model hint from the version stored in the dashboard
                model = compiled.model or "gpt-4o"

                response = call_llm(
                    model=model,
                    messages=[
                        {"role": "system", "content": "You are a concise summariser."},
                        {"role": "user",   "content": compiled.content},
                    ],
                )
When this block exits, NorthStar flushes a prompt link containing the prompt_version_id, the content_hash, and the exact variable_values used — making the trace fully reproducible from the dashboard.

Build docs developers (and LLMs) love