pi-ollama-cloud-link
Unified pi extension for the Ollama Cloud account: live model discovery with capability/pricing metadata, web search/fetch agent tools with a disk cache, a live /ollama-setup account management TUI (quota bars, per-model spend, catalog browser), and an ol
Package details
Install pi-ollama-cloud-link from npm and Pi will load the resources declared by the package manifest.
$ pi install npm:pi-ollama-cloud-link- Package
pi-ollama-cloud-link- Version
1.1.0- Published
- Sep 18, 2026
- Downloads
- 18.1K/mo · 18.1K/wk
- Author
- prawnbear
- License
- MIT
- Types
- extension
- Size
- 509.4 KB
- Dependencies
- 0 dependencies · 3 peers
Pi manifest JSON
{
"extensions": [
"./index.ts"
]
}Security note
Pi packages can execute code and influence agent behavior. Review the source before installing third-party packages.
README
pi-ollama-cloud-link
A pi extension that connects pi
to your Ollama Cloud account (https://ollama.com) — and keeps everything
about that account in one place: the model catalog, the web tools, your quota
and spend, and the docs.
No hardcoded endpoints, no manual model lists. One extension, five capabilities:
- Live model discovery — the
ollama-cloudprovider registers the full live catalog (/v1/models+/api/showper model) with capability metadata (tools/thinking/vision), context windows, thinking-level maps, per-1M-token equivalent pricing, and probed max output limits. Refreshes on startup,/modelopen, andpi update --models, persisting through pi's own model store. A baked-in fallback list covers first launch. - Agent tools —
ollama_web_searchandollama_web_fetch(the cloud's entire non-chat surface: 24 h disk cache,expand=<index>zero-API-call expansion,offset/fullchunked reads,refreshbypass, negative-caching of dead pages), plus the plan-gatedollama_embed, all callable by the LLM, all config-driven. - A setup menu —
/ollama-setupopens a navigable TUI: first-run key wizard, live account status (quota bars, per-model request counts and per-model spend, catalog health, version), account settings, model catalog browser (/api/showdetails, pricing table, refresh), web-tools settings (cache config, stats, clear, smoke test), and a docs browser. - An Ollama Cloud status bar — while an ollama-cloud model is active,
an extra below-editor row appears (your footer is never touched): account
label, session + turn USD cost and tok/s derived locally from the
session's own messages, plus polled quota bars per limit bucket and your
4-week spend from
/api/usage(throttled to a 60 s floor). Switching to a non-cloud model removes the row. - Harness self-awareness — every chat, including virgin ones, is told
where its Ollama Cloud machinery lives (config file, README, and the
docs/API ground truth), so the agent can operate it directly.
Why this exists
Ollama Cloud is a paid, remote account whose catalog, quota, and spend
change constantly; pi, by contrast, learns about models from a static file
and knows nothing about your plan. The job of this extension is to erase that
gap in both directions: keep /model mirroring the live catalog, and keep
your quota and spend visible while you chat — without ever hammering an
undocumented endpoint.
This extension replaces (and is the successor to) the third-party
pi-ollama-cloud package, rebuilt to the architecture and rigor of
pi-lemonade-link: the account paradigm replaces lemonade's instance
paradigm — there is exactly one Ollama Cloud account (one API key, one plan),
so there are no instance arrays, no per-tool server arguments, and no
default-by-position semantics.
Design philosophy — built pi-native
- Dynamic truth over static files. The catalog refreshes from the live
API through pi's own
refreshModelsmachinery; capability annotations, context windows, and thinking maps come from/api/show, never guesses. - Native tools, not protocol shims. The web tools are plain
pi.registerTool()functions withpromptSnippets;[ollama]-prefixed so they group visibly by origin in the prompt. - Harness-level self-awareness.
promptGuidelineslines injected into every chat's system prompt point at the config file, this README, anddocs/— "what can my cloud API do?" resolves deterministically in a virgin chat. - Everything configurable, nothing hardcoded. Every endpoint is a path key
in
ollama_cloud.json. Upstream API drift is config, not code. - Live state where it matters. The status panel re-fetches live while
open; the status bar derives turn cost and tok/s from the session's own
message events (never another client's numbers) and throttles its account
polls to protect the undocumented
/api/usageendpoint; failures keep last-known values under an honest marker. - Failures speak. Auth errors name every place a key can live; transport
failures say what to do; the setup menu diagnoses reachability. Never a
bare
fetch failed. - One entry point.
/ollama-setup— every capability navigable from one TUI menu. - Ground truth snapshotted.
docs/holds the official openapi.yaml and Ollama documentation, plus a behavior reference recording what was empirically confirmed on the cloud, on which date.
Quick start
- Get an Ollama Cloud API key: ollama.com → Settings → Keys.
- Run
/ollama-setup— on a fresh install (no key resolvable) it opens a first-run wizard; paste the key, it verifies against the live usage endpoint and registers the provider immediately. (Or setaccount.apiKeyin~/.pi/agent/ollama_cloud.jsonby hand, or use/login, or exportOLLAMA_API_KEY— the resolution chain is: config → auth.json → env.) - Pick a model with
/model— the provider id isollama-cloud(unchanged frompi-ollama-cloud, so pinned sessions anddefaultProvidersettings keep working). - Say "search the web for …" in any session — the agent calls
ollama_web_search(orollama_web_fetchto read a page). - While an ollama-cloud model is active, watch the below-editor bar:
🦙☁️ ollama-cloud · $0.23 sess · $0.0041 turn · 14.9 tok/s · 5h ▕███░░░░▏ 34% · 7d ▕█░░░░░▏ 7% · $31.22 4wk
How the pieces work
Model discovery
pi invokes the provider's refreshModels callback on startup, /model open,
and pi update --models. The callback: fetches GET /v1/models (public),
then POST /api/show per model (8 workers), and maps:
/api/show says |
pi registers |
|---|---|
capabilities includes tools |
included (pi drives tool-calling models) |
capabilities includes thinking |
reasoning: true + a thinking-level map (models.dev-sourced reasoning_options + a live-probed off override table) |
capabilities includes vision |
image input enabled |
model_info.*.context_length |
context window |
| probed limits table | max output tokens (fallback 32768) |
| pricing table (ollama.com/pricing) | per-1M-token equivalent costs for /cost |
Fallbacks, in order: persisted snapshot (removals stick across sessions) →
baked-in models.generated.ts (first launch). An empty assembled catalog is
never published — a bad refresh keeps the last good catalog.
Web tools
| Tool | What it does | Notes |
|---|---|---|
ollama_web_search |
Web search via /api/web_search |
up to max_results (default 5, max 10); 500-char snippets marked [truncated]; expand=<index> returns a result's full content from cache (0 API calls); refresh=true bypasses; # live query / # from cache provenance tail |
ollama_web_fetch |
Page fetch via /api/web_fetch |
3000-char chunks via offset=N; full=true for the remainder; failures negative-cached 15 min (auth/429/5xx/transport failures are never cached); diagnostic failure messages |
ollama_embed |
Embeddings via /api/embed |
Plan-gated — registered only when a probe succeeds (embeddingsTool: "auto"); the verdict is memoized for 24h (cache/pi-ollama-cloud-link/embed-probe.json) so /reload and other rebinds make no network call and log nothing on the expected paths |
The disk cache lives at ~/.pi/agent/cache/pi-ollama-cloud-link/cache.json
(0600, atomic writes, 500 entries/kind cap). On first use it migrates the
old pi-ollama-cloud cache file so your cached searches carry forward.
Status bar (below-editor widget)
Shown only while an ollama-cloud model is active (session_start /
model_select / turn_start lifecycle; turn_start is the idempotent
safety net). The layout is fixed — every segment is present from the first
render with … placeholders, and the quota segment set is decided on the
first successful poll and held fixed (the /api/usage bucket shape has
flipped repeatedly upstream; segments never appear or vanish mid-session):
| Segment | Source |
|---|---|
🦙☁️ <label> |
config account.label |
$X sess · $X turn |
session-local: the session's own usage (input/output × per-1M rates). Unpriced models show unpriced, not a fake $0 |
<n> tok/s |
session-local: message_start → message_end timing over usage.output |
5h/7d/30d ▕██░▏ N% |
polled /api/usage limit buckets, colored green <60 / yellow 60–79 / red ≥80. N is the API's own 0–1 usage fraction as a percent — the fraction's denominator (the plan's billing meter, cf. the website's "Monthly usage $X of $Y") is not exposed by the API, so nothing is pegged against it. A bucket over 1.0 renders as a striped red OVER bar with the raw percent |
$X 4wk |
polled /api/usage activity cost — the dollar value of the last-4-weeks usage (rolling window; a different section, unit, and rate basis than the cap-fraction bars) |
barPollMs: 0 drops the polled segments; statusBar: false disables the row.
Usage fetching is durably throttled through a snapshot
(cache/pi-ollama-cloud-link/usage-snapshot.json): a fresh snapshot serves
every consumer with zero network calls — rebinds (/reload, /new,
/resume, /fork), restarts, and after-turn refreshes are free, and the bar
renders last-known quota instantly at startup. The Account status panel
shares the snapshot (60s min-age) and memoizes the public catalog/version
fetches, so an open panel re-fetches politely (default statusPollMs: 5000,
usage at most once a minute).
/ollama-setup menu
| Menu | What you can do |
|---|---|
| Account status | Live panel: quota bars per bucket, per-model request counts, per-model 4-week spend, activity cost/period, catalog count, API version, key source — re-fetches every statusPollMs while open, each section degrading independently |
| Account settings | Edit label/description, set the API key (config), test connection |
| Model catalog | List catalog, per-model /api/show details (capabilities, family, params, model_info, pricing, max output), pricing table, refresh provider |
| Web tools | Toggle the tools on/off, cache settings (TTLs, cap, snippet/chunk, path), cache stats, clear cache, live smoke test |
| Documentation | Browse the snapshotted docs/ (openapi.yaml, API reference, capability docs, behavior reference) |
| Spec freshness | The docs/ vs upstream drift check (3 watched files, relevance triage that names what moved), interactive: live status, re-check now, one-keystroke re-sync of the snapshot (all 18 files; ground truth only — the extension code is not updated), specCheck toggle |
Every change is saved to the config and takes effect immediately — no
restart or /reload needed.
Configuration
Everything lives in ~/.pi/agent/ollama_cloud.json — created for you on
first run (a commented starter; /ollama-setup offers the first-run wizard).
The tracked ollama_cloud.example.json in this folder is the canonical,
fully-commented reference. JSONC-style comments are allowed in the hand-edited
file; saving from /ollama-setup strips them.
A minimal working config is one object:
{ "account": { "label": "ollama-cloud", "apiKey": "sk-…" } }
The full shape and every default:
{
"account": { "label": "ollama-cloud", "apiKey": "", "description": "" },
"baseUrl": "https://ollama.com",
"chatPath": "/v1",
"modelsPath": "/v1/models",
"tagsPath": "/api/tags",
"showPath": "/api/show",
"usagePath": "/api/usage",
"versionPath": "/api/version",
"mePath": "/api/me",
"webSearchPath": "/api/web_search",
"webFetchPath": "/api/web_fetch",
"embedPath": "/api/embed",
"webTools": true,
"defaultSearchResults": 5,
"searchCachePath": "",
"searchTtlHours": 24,
"searchFailTtlMinutes": 15,
"searchMaxEntries": 500,
"searchSnippetChars": 500,
"searchChunkChars": 3000,
"webToolsTimeoutMs": 15000,
"embeddingsTool": "auto",
"defaultEmbeddingModel": "",
"requestTimeoutMs": 10000,
"usageTimeoutMs": 10000,
"catalogRefreshCooldownMs": 14400000,
"statusBar": true,
"barPollMs": 300000,
"statusPollMs": 5000,
"specCheck": true,
"specCheckCooldownMs": 86400000,
"specCheckTimeoutMs": 5000
}
Env-var overrides (kept for muscle memory from pi-ollama-cloud):
PI_OLLAMA_WEB_TOOLS, PI_OLLAMA_SEARCH_TTL_HOURS,
PI_OLLAMA_SEARCH_FAIL_TTL_MINUTES, PI_OLLAMA_SEARCH_CACHE_PATH,
PI_OLLAMA_SEARCH_MAX_ENTRIES, PI_OLLAMA_SEARCH_SNIPPET_CHARS,
PI_OLLAMA_SEARCH_CHUNK_CHARS; and OLLAMA_API_KEY as the env rung of the
key-resolution chain (config → auth.json → env).
Offline & unreachable networks
- Startup never blocks beyond the request timeout.
- Only this provider is affected — every other provider is untouched.
- The persisted catalog keeps
/modellistable while offline (the pi-native refreshModels contract requires never returning[]); a credentialless network phase keeps the last good catalog. - Tools fail with actionable text naming the auth/network cause — never a
bare
fetch failed. - The setup menu's Test connection and status panel diagnose reachability;
the status bar marks the account
offline(keeping last-known values) orno API key.
Reference documentation (API ground truth)
See docs/README.md — the official openapi.yaml, the
Ollama HTTP API reference, capability docs, and the empirically-confirmed
cloud behavior reference (endpoint availability matrix, /api/usage shape
history, cache-token visibility, thinking-level probes). The docs are
advertised to the agent itself via the self-awareness guidelines.
Keeping the spec honest
Three snapshotted files are watched against upstream
(ollama/ollama): docs/openapi.yaml
(the local-server OpenAPI spec — it holds the /api/tags, /api/show,
/api/version, /api/embed paths the extension calls and the web-tool
schemas), docs/capabilities/web-search.mdx (governs the web tools), and
docs/api/openai-compatibility.mdx (governs the /v1 surface the provider
drives — the /v1 surface is absent from openapi.yaml, which is why that
mdx gets a SHA check of its own). At startup each file's local git blob SHA
(computed at runtime — nothing stored) is compared with the upstream file's
SHA from GitHub's API. The check is fire-and-forget, never delays startup,
runs at most once per specCheckCooldownMs (24 h) across restarts via a
durable verdict snapshot, and is silent when GitHub is unreachable.
A SHA mismatch does not automatically mean "out of date" — most upstream
churn is model-lifecycle machinery (/api/create, /api/pull, …) this
extension never calls, and warning over it trains users to ignore warnings.
So on openapi.yaml drift the check downloads the upstream yaml once and
triages it: changed sections are attributed to paths/schemas and classified
against the extension's implemented surface (its paths, their transitive
$ref closure, and the web-tool schemas that exist in the spec without a
path). The triage fails closed — anything unattributable counts as
implemented. Verdicts:
- current — every watched file matches upstream; nothing shown.
- changed — relevant drift (or a watched cloud doc moved): a warning that names exactly what moved in the extension's lane.
- changed-irrelevant — openapi.yaml moved only outside the implemented surface: a quiet info notice (the snapshot is stale until re-sync, but the extension is not out of date).
Manage everything interactively under /ollama-setup → Spec freshness:
live status (with the last triage's relevant/irrelevant breakdown),
re-check now, one-keystroke re-sync (downloads the current upstream docs
— all 18 files — into docs/), and the specCheck on/off toggle.
What re-sync means, plainly: it re-downloads the current upstream docs
(all 18 snapshotted files, all-or-nothing; the extension-authored
docs/README.md and docs/behavior.md are never touched) into this
extension's docs/ folder so you (or your agent) can compare what changed.
It does not update the extension code — the real fix for drift is updating
the extension if an update exists. Both the warning and the quiet notice
repeat on every session start until the snapshot matches upstream again;
the network check behind them runs at most once per cooldown window.
Why SHA comparison and not a version number: Ollama Cloud exposes no
meaningful version to peg against — its live /api/version returns a
stub 0.0.0, and the spec's own info.version never bumps — so the
snapshot's git blob SHA vs upstream is the only drift signal this
provider has. (The lemonade sibling, whose on-prem server self-reports a
real version in /v1/health, additionally pegs against that.)
Extension version & provider API
This extension's version (version in package.json) and the provider API
it was built and verified against are recorded together in the provider
block of package.json — Ollama Cloud exposes no version number of its own
(/api/version stubs 0.0.0), so the peg is a live-behavior confirmation
date plus the docs snapshot date. The account status panel shows the
extension version and the ground-truth verdict in its header.
On update, keep these in lockstep (all five record the same fact):
versioninpackage.json— the extension release- the
providerblock inpackage.json(apiVerifiedAgainst,specSnapshot) — what that release was verified against - re-sync
docs/in the extension folder from upstream - the behavior-reference dates in
README.md/docs/ - this README's version claims (the sections above)
A release that skips any of these is lying somewhere — the spec-freshness check (see Keeping the spec honest) is what catches #3 drifting silently.
Replacing pi-ollama-cloud
This extension is a drop-in replacement: the provider id is identical
(ollama-cloud), the tool names are identical
(ollama_web_search / ollama_web_fetch), and the web cache is migrated
automatically. Remove the old package, install this one, restart pi:
pi remove npm:pi-ollama-cloud
pi install npm:pi-ollama-cloud-link # or drop the folder into ~/.pi/agent/extensions/
Development
npm run check # typecheck (requires TypeScript: npx -y tsc)
npm run test # unit tests (pure functions; no network, no API key)
npm run smoke:live # live smoke against the real API (uses OLLAMA_API_KEY)
Data tables regenerate from upstream:
models.generated.ts / pricing.generated.ts (ollama.com catalogs and
pricing), reasoning.generated.ts (models.dev), limits.generated.ts
(probed, requires an API key). The package ships raw TypeScript sources
(no build step), so submodule imports use .ts extensions.
Notes
- Pricing is per-1M-token equivalent pay-as-you-go rates for comparable
usage in
/costand the status bar — Ollama Cloud is subscription-billed; your actual charges are the plan. - The OpenAI-compatible completions surface does not report cached tokens,
so cache pricing is informational on that surface (the native
/api/*endpoints do reportprompt_eval_cached_count— seedocs/behavior.md). OLLAMA_API_BASEnever redirects this extension: the cloud target is always the config'sbaseUrl.