Models and settings¶
Choose any model from any provider, and set it for the whole program, a block, a function, or one copy.
import functai
functai.configure(lm="gpt-4.1-mini", temperature=0) # the model behind every output on this page
from functai import ai, _ai
An AI function says what you want. Which model answers it, and how, is a setting, and settings can change without touching the function.
If you set nothing¶
functai looks at what this machine can use (API keys in the environment,
saved keys, subscription logins) and picks a small, capable model,
preferring API keys, in this order: gpt-4.1-mini (OpenAI),
claude-haiku-4-5 (Anthropic), gemini-2.5-flash (Google), Groq,
OpenRouter, then your Claude, ChatGPT or Copilot subscription. It says
which, once:
functai: no model chosen, so using gpt-4.1-mini (environment ($OPENAI_API_KEY)). Choose one with functai.configure(lm=...).
For anything you'll run twice, choose explicitly: the default depends on the machine.
Naming a model¶
A model is a string. The provider is found from the name, or given as a prefix:
| model | provider | key it uses |
|---|---|---|
"gpt-4.1-mini", "o4-mini", "gpt-5" |
OpenAI | OPENAI_API_KEY |
"claude-haiku-4-5", "claude-sonnet-4-5" |
Anthropic | ANTHROPIC_API_KEY |
"gemini-2.5-flash" |
GEMINI_API_KEY |
|
"groq:openai/gpt-oss-120b" |
Groq | GROQ_API_KEY |
"openrouter:qwen/qwen3-32b" |
OpenRouter | OPENROUTER_API_KEY |
"ollama:qwen3:8b" |
a local Ollama | none |
"claude:claude-sonnet-4-5", "chatgpt:gpt-5.5" |
your subscription | a login |
The litellm spellings ("openai/gpt-4o", "anthropic/claude-sonnet-4-5")
work too. Requests go straight to each provider's API through
lm15; no provider SDK is
installed.
Where settings come from¶
Settings are looked up at every call, from the most specific place to the most general. The first place that sets a value wins:
- one copy:
fn.using(lm="…")returns a copy with other settings; - the function:
@ai(lm="…"), orfn.lm = "…"later; - a block:
with functai.configure(lm="…"):; - the whole program:
functai.configure(lm="…").
@ai
def capital(country: str) -> str:
"""The country's capital city."""
...
capital("Australia") # from configure(): gpt-4.1-mini
'Canberra'
with functai.configure(lm="gpt-4.1-nano"):
print(capital("Canada")) # this block only
Ottawa
fast = capital.using(lm="gpt-4.1-nano", temperature=0)
fast("Brazil") # a copy; `capital` is unchanged
'Brasília'
functai.settings reads the effective values:
functai.settings.lm
'gpt-4.1-mini'
A with configure(...) block applies to the current thread and to the
threads functai starts from it (for example in evaluate(num_threads=8)),
not to threads started elsewhere.
Changing a function after the fact¶
Everything can be set when the function is defined, changed on it later, or changed on a copy, which leaves the original alone:
| at definition | afterwards | on a copy | |
|---|---|---|---|
| model | @ai(lm="gpt-4.1") |
fn.lm = "gpt-4.1" |
fn.using(lm="gpt-4.1") |
| connection | @ai(client=...) |
fn.using(client=...) |
fn.using(client=...) |
| layout | @ai(adapter="chat") |
fn.adapter = "chat" |
fn.using(adapter="chat") |
| chat template | @ai(template=[...]) |
fn.template = [...] |
fn.using(template=[...]) |
A bad value (an unknown layout, a template that can't be read back, a connection object where a model name belongs) is refused where you write it, before anything changes.
The model and the connection are separate¶
lm= names the model. client= is how to reach it, for the rare case
you build that yourself with lm15: a second account's key, a proxy, a
company gateway.
import lm15
@ai(lm="gpt-4.1-mini", client=lm15.OpenAILM(api_key=OTHER_KEY))
def f(text: str) -> str: ...
A model prefix naming another provider than the client's is refused. Credentials and connections are never saved with a program: the machine that loads it uses its own.
All settings¶
| setting | meaning |
|---|---|
lm |
the model |
api_key, base_url |
for the provider lm points to; an explicit key beats every other |
auth |
saved logins: on by default; a path for another credentials file; False for none |
client |
the lm15 connection, when you build it yourself (below) |
temperature, max_tokens, seed, top_p, stop, … |
sampling: any lm15 Config field |
adapter, template |
the prompt format |
module |
"predict" (default), "cot" (reasoning first), "react" |
tools, max_steps, tool_errors |
the tool loop |
approve |
tools that ask first (functai.tool) |
cache_replies, replicate |
replies kept, and asked again |
retries, api_retries, cache_replies |
reliability |
capabilities |
what the model can do, when you know better than functai's table |
optimizer, teacher, teacher_lm |
optimization defaults |
debug |
print one line per call |
A misspelled setting is an error, not silently ignored:
try:
functai.configure(temprature=0)
except TypeError as error:
print(error)
configure: unknown setting(s) ['temprature']. functai settings: ['adapter', 'api_key', 'api_retries', 'approve', 'auth', 'autocompile', 'autocompile_n', 'autogen_instructions', 'autoinstruct', 'bake_where', 'base_url', 'cache_replies', 'caller', 'capabilities', 'client', 'debug', 'escalate_below', 'escalate_to', 'include_fn_name_in_instructions', 'instruction_autorefine_calls', 'instruction_autorefine_max_examples', 'instruction_lm', 'journal', 'lm', 'log_calls', 'log_content', 'max_steps', 'module', 'observers', 'on_unreadable', 'optimizer', 'plugins', 'program_observers', 'program_plugins', 'replicate', 'retries', 'teacher', 'teacher_lm', 'tool_errors']; lm15 Config fields: ['cache', 'extensions', 'frequency_penalty', 'logprobs', 'max_tokens', 'presence_penalty', 'probabilities', 'reasoning', 'response_format', 'seed', 'service_tier', 'stop', 'store', 'temperature', 'tool_choice', 'top_k', 'top_p', 'user_id']
Settings some models refuse (OpenAI's reasoning models take no
temperature; the ChatGPT backend no max_tokens) are left out of those
requests with a one-time warning, so one configure(...) works across
models.