> ## Documentation Index
> Fetch the complete documentation index at: https://docs.cycls.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Models

> One builder for every provider: Anthropic natively, and any OpenAI-compatible endpoint through a base URL.

`cycls.LLM` holds everything about how an agent runs. It is immutable, so each
method returns a new builder and one base config can be branched safely.

```python theme={null}
llm = (
    cycls.LLM()
    .model("anthropic/claude-sonnet-4-6")
    .system("You are a helpful assistant.")
    .max_tokens(16_384)
    .context(200_000)
    .thinking("medium")
    .price(input=3, output=15, cache_read=0.30, cache_write=6)
)

async for ev in llm.run(context=context):
    yield ev
```

## Choosing a model

Model strings are always `vendor/model`. Anything that is not `anthropic/` goes
through the Chat Completions adapter, so `base_url` points it at the endpoint and
`api_key` supplies the key.

```python theme={null}
# Anthropic, native API. Reads ANTHROPIC_API_KEY
cycls.LLM().model("anthropic/claude-sonnet-4-6")

# OpenAI. Reads OPENAI_API_KEY
cycls.LLM().model("openai/gpt-5.4")

# Open weight models on their vendor endpoints
cycls.LLM().model("deepseek/deepseek-flash").base_url("https://api.deepseek.com/v1").api_key(os.environ["DEEPSEEK_API_KEY"])
cycls.LLM().model("moonshotai/kimi-k3").base_url("https://api.moonshot.ai/v1").api_key(os.environ["MOONSHOT_API_KEY"])
cycls.LLM().model("zai/glm-5.3").base_url("https://open.bigmodel.cn/api/paas/v4").api_key(os.environ["ZAI_API_KEY"])

# Your own inference server
cycls.LLM().model("local/kimi-k3").base_url("http://localhost:8000/v1").api_key("unused")
```

| Vendor prefix                                                  | Endpoint                               | Notes                                                                         |
| -------------------------------------------------------------- | -------------------------------------- | ----------------------------------------------------------------------------- |
| `anthropic`                                                    | built in                               | native path: cache breakpoints, extended thinking, server-side search and MCP |
| `openai`                                                       | built in                               | reads `OPENAI_API_KEY`                                                        |
| `deepseek`                                                     | `https://api.deepseek.com/v1`          | open weights, thinking toggle plus effort                                     |
| `moonshotai`, `kimi`, `moonshot`                               | `https://api.moonshot.ai/v1`           | open weights, effort tiers low, high, max                                     |
| `zai`, `zhipu`, `glm`                                          | `https://open.bigmodel.cn/api/paas/v4` | open weights, thinking is a binary toggle                                     |
| `qwen`, `dashscope`                                            | vendor endpoint                        | open weights, thinking budget in tokens                                       |
| `groq`, `xai`, `mistral`, `google`, `perplexity`, `openrouter` | vendor endpoint                        | standard `reasoning_effort`                                                   |
| `local`, `vllm`, `modal`, anything else                        | your URL                               | vLLM, SGLang, Ollama or a private gateway                                     |

The prefix selects the reasoning dialect, so use the vendor prefix that matches
the API you are calling, even when you self-host that model. See
[Self-hosted and open models](/guides/self-hosted-models) for serving your own.

<Note>
  Model identifiers change as vendors ship new versions. Check the vendor's
  documentation for the current name before pinning one in production.
</Note>

Keys come from the environment (`ANTHROPIC_API_KEY`, `OPENAI_API_KEY`) or from
`.api_key()`. Use `.headers()` for endpoints that authenticate outside the bearer
token, such as a Modal proxy or Cloudflare Access.

```python theme={null}
llm = (
    cycls.LLM()
    .model("modal/moonshotai/Kimi-K3")
    .base_url("https://your-workspace--kimi-k3.modal.direct/v1")
    .api_key("unused")
    .headers({"Modal-Key": os.environ["MODAL_TOKEN_ID"],
              "Modal-Secret": os.environ["MODAL_TOKEN_SECRET"]})
)
```

## Reasoning

`.thinking()` is one unified control, translated into each vendor's dialect.

```python theme={null}
cycls.LLM().thinking("adaptive")   # default, the model decides
cycls.LLM().thinking("low")        # or "medium", "high"
cycls.LLM().thinking(None)         # off where the provider allows it
```

<Accordion title="How each vendor receives it">
  | Vendor prefix                                                                         | Wire format                                           |
  | ------------------------------------------------------------------------------------- | ----------------------------------------------------- |
  | `openai`, `azure`, `google`, `gemini`, `xai`, `grok`, `mistral`, `groq`, `perplexity` | `reasoning_effort: low \| medium \| high`             |
  | `zai`, `zhipu`, `glm`                                                                 | `thinking: {type: enabled \| disabled}`               |
  | `deepseek`                                                                            | the GLM toggle plus `reasoning_effort`                |
  | `qwen`, `dashscope`, `alibaba`                                                        | `enable_thinking` plus a token `thinking_budget`      |
  | `kimi`, `moonshot`, `moonshotai`                                                      | `reasoning_effort: low \| high \| max`                |
  | `openrouter`                                                                          | `reasoning: {effort}`                                 |
  | `anthropic`                                                                           | extended thinking, auto-disabled on models without it |

  A vendor with no mapping, such as a host prefix like `modal` or `vllm`, gets no
  reasoning parameter and prints one warning. Use `.extra_body()` for those.
</Accordion>

```python theme={null}
llm = cycls.LLM().extra_body({"reasoning_effort": "high", "top_p": 0.9})
```

`.extra_body()` merges after the built-in mapping, so your keys win. It is the
escape hatch for any parameter Cycls does not model.

## Budgets and cost

```python theme={null}
llm = (
    cycls.LLM()
    .max_tokens(16_384)     # output tokens per request, default 8k
    .context(200_000)       # model context window, default 1M
    .price(input=3, output=15, cache_read=0.30, cache_write=6)
)
```

`.context()` is what decides when compaction starts, so set it to match the model
you actually run. `.price()` takes USD per million tokens. With prices set, every
turn logs its cost and `cycls cost` and `cycls sql` can slice spend by user, chat
or model. Without it, costs report as zero.

## Vision and text-only models

Attachments are sent as base64 media by default. Text-only models reject that, so
turn vision off and the file stays in the workspace with a note naming it, which
the model can then open with a tool.

```python theme={null}
llm = cycls.LLM().model("zai/glm-5.3").base_url("https://open.bigmodel.cn/api/paas/v4").vision(False)
```

## Web search

```python theme={null}
llm = cycls.LLM().allowed_tools(["WebSearch"]).web_search("brave")   # default
llm = cycls.LLM().allowed_tools(["WebSearch"]).web_search("native")  # Anthropic server-side
```

`brave` is the portable search and fetch pair. It works on any model and needs
`BRAVE_API_KEY`. Without that key it falls back to the provider's native search
where one exists. `native` forces the provider's server-side search, which today
means Anthropic only.

## Full builder reference

| Method                                               | Purpose                                                          |
| ---------------------------------------------------- | ---------------------------------------------------------------- |
| `.model(str)`                                        | `vendor/model` string. Required                                  |
| `.system(str)`                                       | system prompt                                                    |
| `.tools(list)`                                       | [custom tool schemas](/agents/tools#custom-tools)                |
| `.on(name, fn, label=, icon=, details=)`             | handler for a custom tool                                        |
| `.allowed_tools(names)`                              | enable [built-in tools](/agents/tools)                           |
| `.instructions(path)`                                | workspace instructions file, default `AGENT.md`, `None` disables |
| `.skills(*dirs)`                                     | [ship skills](/agents/knowledge#skills) with the agent           |
| `.context(n)`                                        | context window in tokens, default 1M                             |
| `.max_tokens(n)`                                     | output tokens per request, default 8k                            |
| `.price(input=, output=, cache_read=, cache_write=)` | USD per million tokens                                           |
| `.thinking(spec)`                                    | `"adaptive"`, `"low"`, `"medium"`, `"high"` or `None`            |
| `.extra_body(params)`                                | vendor-specific request extras                                   |
| `.vision(bool)`                                      | accept base64 media, default `True`                              |
| `.web_search(mode)`                                  | `"brave"` or `"native"`                                          |
| `.mcp(*servers)`                                     | [remote MCP servers](/agents/connectors#mcp-servers)             |
| `.connectors(*objs)`                                 | [connector tools](/agents/connectors)                            |
| `.bash_timeout(secs)`                                | bash sandbox timeout, default 600                                |
| `.sandbox(network=False)`                            | cut bash off from the network                                    |
| `.base_url(url)`                                     | custom endpoint                                                  |
| `.api_key(key)`                                      | override the environment key                                     |
| `.headers(mapping)`                                  | extra HTTP headers on every model request                        |
| `.loop(fn)`                                          | [replace the loop](/agents/custom-loop)                          |
| `.run(context=, client=)`                            | execute, yielding events                                         |

## Next steps

<CardGroup cols={2}>
  <Card title="Tools" icon="wrench" href="/agents/tools">
    Built-in tools, custom handlers and approvals.
  </Card>

  <Card title="Self-hosted models" icon="server" href="/guides/self-hosted-models">
    Point an agent at vLLM, SGLang or a private endpoint.
  </Card>
</CardGroup>
