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

Packages

Package details

extension

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:

  1. Live model discovery — the ollama-cloud provider registers the full live catalog (/v1/models + /api/show per 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, /model open, and pi update --models, persisting through pi's own model store. A baked-in fallback list covers first launch.
  2. Agent toolsollama_web_search and ollama_web_fetch (the cloud's entire non-chat surface: 24 h disk cache, expand=<index> zero-API-call expansion, offset/full chunked reads, refresh bypass, negative-caching of dead pages), plus the plan-gated ollama_embed, all callable by the LLM, all config-driven.
  3. A setup menu/ollama-setup opens 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/show details, pricing table, refresh), web-tools settings (cache config, stats, clear, smoke test), and a docs browser.
  4. 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.
  5. 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 refreshModels machinery; 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 with promptSnippets; [ollama]-prefixed so they group visibly by origin in the prompt.
  • Harness-level self-awareness. promptGuidelines lines injected into every chat's system prompt point at the config file, this README, and docs/ — "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/usage endpoint; 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

  1. Get an Ollama Cloud API key: ollama.com → Settings → Keys.
  2. 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 set account.apiKey in ~/.pi/agent/ollama_cloud.json by hand, or use /login, or export OLLAMA_API_KEY — the resolution chain is: config → auth.json → env.)
  3. Pick a model with /model — the provider id is ollama-cloud (unchanged from pi-ollama-cloud, so pinned sessions and defaultProvider settings keep working).
  4. Say "search the web for …" in any session — the agent calls ollama_web_search (or ollama_web_fetch to read a page).
  5. 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_startmessage_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 /model listable 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) or no 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):

  1. version in package.json — the extension release
  2. the provider block in package.json (apiVerifiedAgainst, specSnapshot) — what that release was verified against
  3. re-sync docs/ in the extension folder from upstream
  4. the behavior-reference dates in README.md / docs/
  5. 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 /cost and 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 report prompt_eval_cached_count — see docs/behavior.md).
  • OLLAMA_API_BASE never redirects this extension: the cloud target is always the config's baseUrl.