The Apply Update endpoint used 'git pull origin {branch}', which fails
with a merge conflict if the working tree has any local modifications.
This happened to every install that had leftover version-string edits from
a prior partial update, leaving the old code running silently.
Fix: unconditionally stash (including untracked files) and reset to the
exact remote commit before reinstalling. The update now succeeds even
if the repo is dirty.
Bumps all version strings to 0.5.1 so existing 0.4.2 → 0.5.x installs
immediately receive the new updater logic.
The squash-merge from develop included all v0.5.0 feature commits but
excluded the version-bump commit (b4ed4fa), which was local-only and
never pushed to origin/develop before the PR merge. This left main
reporting v0.4.3 despite having all v0.5.0 code.
Fixes: __init__.py, app.py, pyproject.toml all now report 0.5.0.
Update-safe: only version string changes, zero functional diff.
* v0.4.3 — add kimi-k2.6 vision model, default all roles to nemotron-3-nano:30b
- KNOWN_CLOUD_MODELS: add kimi-k2.6 with [vision, tools, thinking, cloud]
- LLMConfig defaults: reranker/scraper/summary/default all set to nemotron-3-nano:30b
- available_models: add nemotron-3-nano:30b and kimi-k2.6
- dashboard.html: add kimi-k2.6 and nemotron-3-nano capability maps
- cli.py setup wizard: update all model prompts/default placeholders
- version bump: 0.4.2 → 0.4.3 in __init__.py, app.py, pyproject.toml
Update-safe: only new model registry entries + default value changes.
Existing configs preserve their saved values (pydantic).
Zero schema or API break.
* feat: cost-weighted analytics (usage_multiplier) + add deepseek-v4 models
- Add usage_multiplier column to analytics DB (auto-resolved from model name)
- Log weighted token totals (prompt*multiplier, completion*multiplier) in summary
- Show cost bars + weighted totals in dashboard analytics tab
- Add per-model multiplier column to analytics table
- Auto-compute total_tokens in analytics if not provided
- Add deepseek-v4-pro (1.0x) and deepseek-v4-flash (0.50x) to KNOWN_CLOUD_MODELS
- Add gemini-3-flash-preview to available_models and dashboard capability maps
- Add new models to config.py available_models list
* feat: auto-infer capabilities and multiplier for unknown models
Previously new Ollama Cloud models showed no capability badges and
assumed 1.00x multiplier until manually added to KNOWN_CLOUD_MODELS.
Now both backend (client.py) and frontend (dashboard.html) fall
back to name-based inference for unknown models:
- Capabilities: 'vision' from vl/gemma/gemini/kimi/deepseek, 'tools'
from coder/minimax/glm/mistral/gpt-oss/devstral/nemotron/deepseek,
'thinking' from deepseek/cogito/reason/think and kimi-k* families
- Multiplier: extract :XXb|:Xt from model name for size-based tier
(<=20b=0.25, <=80b=0.50, <=400b=0.75, >400b/1t=1.00), then fall
back to name heuristics (nano/mini/small=0.25, flash/gemma=0.50,
pro/mistral-large=1.00)
Models explicitly in KNOWN_CLOUD_MODELS still use exact values.
This means new models like deepseek-v5-pro or kimi-k3 show up
correctly in the dashboard immediately without code changes.
* feat: experimental Subscription Value Calculator (ROI) vs OpenRouter
Adds a new experimental feature to the Status tab that compares your
total subscription cost to what you'd pay with OpenRouter's cheapest
provider pricing.
Backend:
- guanaco/roi.py: Fetches live prices from OpenRouter API, matches
Ollama model names to OpenRouter model IDs via family inference
(exact → family prefix → same parameter size → size window →
global average), multiplies tokens by $/Mt cost.
- Calculates this week's cost, extrapolates to 100% weekly usage,
then monthly. Shows ROI multiplier (monthly value / subscription).
- /dashboard/api/roi/calculate: fresh calculation
- /dashboard/api/roi/last: cached last result
- /dashboard/api/roi/reset: clear data
- /dashboard/api/roi/config: enable/disable, set price tier
Dashboard (Status tab):
- Toggle to enable/disable the feature
- Pro (0/mo) / Max (00/mo) / Custom price selector
- Shows: this-week cost, weekly @ 100%, monthly value, ROI multiplier
- Per-model table: prompt_tokens, completion_tokens, $/Mt in/out,
total cost, % of usage
- Warnings for unmatched models and stale prices
- Reset button clears cached data
ROIConfig added to config.py (persisted in config.yaml).
* fix(config): v0.4.3 backward compat and install robustness
Changes:
- Add missing UsageConfig fields (last_plan, redirect_on_full, last_session_reset, last_weekly_reset, last_checked) to fix AttributeError crashes
- Add config migration layer in load_config() for v0.4.2 to v0.4.3+ configs
- Rename package guanaco to guanaco-llm-proxy to avoid PyPI collision
- Add Ollama API key validation during install.sh setup (fixes: use real key var, fix env file write, fix grep pattern)
- Add startup version sanity check detecting repo/venv mismatch
- Fix systemd service WorkingDirectory to repo checkout
- Add GUANACO_CONFIG_DIR env var to systemd service
* feat(v0.4.3): token estimation fallback, ROI dashboard, per-model usage breakdown
Token tracking:
- analytics.py: fall back to skimtoken estimation when API omits usage
(~15% error, better than zero). Proper total_tokens = prompt + completion.
Removed broken usage_multiplier column.
- router.py: unchanged — already passes usage → analytics correctly
Dashboard / ROI:
- roi.py: OpenRouter price fetcher with cache-hit discount logic
- dashboard.py: expose ROI config with cache_hit_pct slider
- dashboard.html: cost breakdown UI, per-model value scoring
Models:
- client.py: added minimax-m3, nemotron-3-ultra, kimi-k2.6 200k context
- client.py: per-model Ollama usage breakdown scraping from /settings
Config:
- config.py: backward compat for installs missing SearchConfig
* feat(usage-levels): scrape real per-tag usage tiers from ollama.com library pages
- Add _fetch_usage_level_sync() to parse ollama.com/library/{model} HTML
- Handle both top-level model badges (x-test-model-cost-slot-active)
and per-tag listings (x-test-model-tag-cost + x-test-model-tag-usage-slot-active)
- Add async fetch_usage_levels() with 1h global cache + parallel fetching
- Wire usage_multiplier into /v1/models and /api/ollama/models responses
- Replace heuristic _get_model_multiplier fallback with real scraped levels
- Fixes gemma3:4b/12b showing 1.00x instead of 0.25x, qwen3-vl 0.25x→0.75x, etc.
---------
Co-authored-by: evangit2 <evangit2@users.norereply.github.com>
Co-authored-by: Evan Rice <evanrice@vt.edu>
Features:
- Multi-account rotation: Add multiple Ollama Cloud accounts with API keys
and session cookies via Dashboard Accounts tab or API
- Quota-aware selection: AccountPool picker prefers least-loaded accounts;
new/untested accounts get priority so they're validated immediately
- Premium model routing: kimi-k2.6 and glm-5.1 restricted to Pro/Max
accounts only; free-tier accounts show warning badge in dashboard
- Account-level usage tracking: Per-account session/weekly percentages,
plan type, and reset timers displayed in Dashboard Accounts tab
- Analytics account_name: New DB column logs which account handled each
request; provider field also includes account suffix (ollama:ollama2)
- Dashboard Accounts tab: Full CRUD (add/remove/update-key/set-cookie/
check-usage/test), usage progress bars, plan badges, premium warnings
- Config backward compatibility: Old configs without ollama_accounts
auto-generate the primary account from legacy ollama_api_key + usage
fields; no data loss on save/restart cycles
APIs:
- GET /api/accounts - List all accounts with usage data
- POST /api/accounts/add - Add new account
- POST /api/accounts/remove - Remove account by name
- POST /api/accounts/test - Test account connectivity
- POST /api/accounts/update-key - Update API key
- POST /api/accounts/session-cookie - Set session cookie
- POST /api/accounts/check-usage - Refresh usage from Ollama settings
DB Migration:
- account_name TEXT column added to request_log (auto-migrated on startup)
All existing rows get NULL for account_name (no data loss)
Files changed:
- guanaco/accounts.py: AccountPool with model-aware filtering, PREMIUM_MODELS
- guanaco/analytics.py: account_name param in log_llm(), migration
- guanaco/app.py: AccountPool wiring into create_app()
- guanaco/config.py: ollama_accounts list, legacy sync, primary_account property
- guanaco/dashboard/dashboard.py: Account management API endpoints
- guanaco/dashboard/templates/dashboard.html: Accounts tab UI
- guanaco/router/router.py: _select_account(), provider=logging with account
- pyproject.toml: version 0.4.2
- checkForUpdate() now runs on page load (not just System tab click),
so stale 'update available' banners clear after refreshing
- checkForUpdate() retries on network errors (service may be restarting)
- saveHistoryConfig() now syncs UI state from server POST response,
preventing toggle drift on tab switch
- __init__.py version parsing uses strict semver regex, preventing
RC/pre-release versions like '0.4.0rc1' from overriding the hardcoded
fallback. Only clean X.Y.Z versions >= baseline are accepted.
- Bumped version to 0.4.1 across __init__.py, app.py, pyproject.toml
## Bug Fixes
- **Fallback stream payload**: _call_fallback_provider() now always injects
payload["stream"] = stream into the JSON body. Previously, stream was only
passed as a function arg for httpx routing, not included in the actual JSON.
This caused providers like Fireworks to reject requests with max_tokens > 4096
that lacked "stream": true in the body.
- **Summarize toggle persistence**: Two-part fix:
1. Backend: POST /api/config now returns the search config in the response
(was only returning llm + fallback, so the JS callback had no data to sync)
2. Frontend: saveSearchConfig() now uses the POST response to update toggle
state instead of re-fetching via GET, which could return stale/empty data
on partially-updated installs
- **Version single-source**: Removed all importlib.metadata version lookups.
app.py now hardcodes __version__ = "0.3.9". check_for_update and apply_update
import from guanaco.app. This fixes health endpoint returning stale versions
(e.g. "0.3.0" while dashboard showed "0.3.8")
- **More reliable auto-update restart**: Changed from systemctl restart to
stop + 1s gap + start, which is more reliable for killing stubborn processes
## Tests
- Added tests/test_fallback_stream.py covering streaming and non-streaming
fallback payload injection, and verifying original payloads aren't mutated
- Endpoints tab: restored search provider endpoints card with dynamic
rendering showing all endpoints per provider with HTTP method badges
- New Search tab with playground: test any search provider or get an
AI summary of results in one click
- Summarize feature (BETA): /dashboard/api/summarize endpoint that
searches the web then feeds results to the configured summary_model
for an AI-generated summary with source citations
- Handles thinking models (qwen3.5) correctly: checks both content
and reasoning fields, uses max_tokens=4096 to avoid empty responses
- Provider endpoints are now defined as class metadata (endpoints
property) and rendered dynamically instead of hardcoded in JS
- Bump version to 0.3.7
- app.py: use importlib.metadata for version (no cross-module import
that breaks under systemd/uvicorn). Fallback to hardcoded 0.3.6.
- dashboard: apply_update uses BackgroundTask for restart, so the HTTP
response is sent BEFORE the service kills itself. Previously the
response was lost because systemctl restart terminated the process.
- dashboard: add import validation step before restart — tests that
create_app() and importlib.metadata.version() both work. If the
update would crash-loop, the handler returns an error instead and
does NOT restart. This prevents the v0.3.3→0.3.5 bootloop scenario.
- dashboard: remove unused subprocess import in check_for_update.
- All version sources now use importlib.metadata consistently
(app.py, __init__.py, update check, health endpoint).
- Bump version to 0.3.6.
The dashboard update handler assumed venv was at project_dir/venv (repo-local),
but install.sh creates venv at ~/.guanaco/venv while cloning the repo to
~/.guanaco/repo. This caused 'No such file or directory' errors when applying
updates from the dashboard on systems installed via install.sh.
Now checks ~/.guanaco/venv first (install.sh default), then falls back to
repo-local venv. Bumps to v0.3.3.
Hermes sends models like 'glm-5.1:cloud' but _resolve_model only
handled '-cloud' (hyphen). The ':cloud' suffix passed through to
Ollama which didn't recognize it, returning empty responses and
causing 2+ minute timeouts in Hermes.
Now strips :cloud, :local, and -cloud suffixes before matching
against available_models. Bumps version to 0.3.1.
OpenAI-compatible LLM proxy that maximizes your Ollama Cloud subscription.
Features:
- /v1/chat/completions router with token tracking
- /v1/messages Anthropic proxy
- 8 search/scrape API emulators (Tavily, Exa, SearXNG, Firecrawl, Serper, Jina, Cohere, Brave)
- Automatic fallback to secondary providers with configurable timeouts
- Streaming support with first-chunk fast failover
- Web dashboard with analytics, config, and usage monitoring
- Caching layer (beta)
- CLI for setup, status, analytics, key management
- Docker and systemd deployment support
- Backward compatible with OCT (ollama-cloud-tools) installations