Fixes recommend_model() returning empty string on Apple Silicon
when MLX is the recommended engine. Also adds gguf_file and
mlx_repo download metadata, and estimated_download_gb helper.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Eight tool modules (file_write, apply_patch, git_tool, db_query,
pdf_tool, image_tool, audio_tool, knowledge_tools) were missing from
openjarvis/tools/__init__.py. Their @ToolRegistry.register() decorators
never fired, so the /v1/tools endpoint never returned them and the
web UI agent wizard showed an incomplete tool list.
Add the missing imports and a regression test that checks all expected
tool names are in the registry after package import.
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: add query complexity analyzer with CLI and UI integration
Classify incoming queries by difficulty (trivial→very_complex) to
suggest appropriate token budgets for local vs. cloud routing.
- Add score_complexity() with weighted signals (length, code, math,
reasoning, multi-step, creative) and token tier mapping
- Wire into `jarvis ask`: auto-suggest max_tokens when not set by user,
show complexity in --profile output, log at DEBUG level
- Add complexity metadata to /v1/chat/completions API response
- Display complexity tier and score in frontend XRayFooter
- Extend RoutingContext with complexity_score, suggested_max_tokens,
has_reasoning fields
- Update HeuristicRouter to route on complexity_score instead of
raw query_length
- Remove duplicated regex patterns from router.py (use complexity
module as single source of truth)
- Add 30 unit tests for complexity module
- Fix existing router tests for new complexity-based routing
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: pass temperature and max_tokens from UI settings to backend
The settings page stores temperature and max_tokens in the frontend
store, but these values were never included in the chat API request.
The backend Pydantic model defaults max_tokens to 1024 when the field
is absent, which is too low for thinking models like qwen3.5 — they
consume all tokens on reasoning and return empty content.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: pass UI settings to backend and auto-bump max_tokens from complexity
- Pass temperature and max_tokens from frontend Settings store to the
backend API (cherry-picked from fix/ui-max-tokens-passthrough)
- Server-side: bump max_tokens when the complexity analyzer suggests a
higher budget (e.g. for thinking models on complex queries), never
reduce below the client-requested value
- Fixes empty responses with thinking models (e.g. qwen3.5) that
consumed all tokens on internal reasoning
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: raise token budget tiers to prevent empty responses on thinking models
Double all tier budgets (trivial: 512→1024, simple: 1024→2048, etc.)
so that thinking models like qwen3.5 have enough headroom for internal
chain-of-thought plus visible output, even on simple queries.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: CI lint and test failures
- Break long lines in routes.py to satisfy 88-char limit (E501)
- Add intelligence.max_tokens and temperature to mocked config in
test_ask_router.py so complexity analyzer can compare against int
instead of MagicMock
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: line too long in test_complexity.py (E501)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add `stream: bool` parameter to `POST /v1/managed-agents/{id}/messages`.
When `stream=true`, the agent processes the message synchronously and
returns an SSE stream (OpenAI-compatible format) with token-by-token
response, tool result events, and usage metadata.
This enables real-time voice assistants and chat UIs to receive agent
responses as they are generated, rather than polling for completion.
- Extend SendMessageRequest with `stream` field (default: false)
- Add _stream_managed_agent() helper using asyncio.to_thread()
- Build AgentContext from conversation history for multi-turn support
- Emit tool_results as named SSE events
- Persist agent response in DB after streaming completes
- Add 6 new tests covering streaming behavior
- Update agents.md documentation with streaming examples
* feat: add CLI commands: config, registry, tool
* fix: address review feedback on CLI config/registry/tool commands
- Replace non-existent `create_config_template` with `generate_default_toml`
to fix ImportError when config file is missing
- Deduplicate registry maps into shared `_load_registry_map()` helper
- Remove dead `_get_registry_class()` function and its tests
- Route JSON output to stdout for pipeability (`jarvis config show json | jq`)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Jon Saad-Falcon <41205309+jonsaadfalcon@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The energy_wh_saved and flops_saved values were orders of magnitude too
high because a scaling factor that grows linearly with N was applied to
the energy calculation, making it scale as O(N³) instead of O(N²).
Replace the buggy scale-factor approach with a direct FLOP-to-energy
conversion using each provider's per-token constants. Add regression
tests to prevent recurrence.
Closes#95
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: add MiniMax as cloud inference provider
Add MiniMax M2.5 and M2.5-highspeed as a 5th cloud provider alongside
OpenAI, Anthropic, Google, and OpenRouter. Uses the OpenAI-compatible
API at api.minimax.io/v1 via the existing openai SDK dependency.
Changes:
- Add MiniMax client init, generate, and streaming in CloudEngine
- Add MiniMax models to model catalog with correct pricing
- Add MINIMAX_API_KEY environment variable support
- Add temperature clamping (0.01-1.0) per MiniMax API constraints
- Add 19 unit tests and 3 integration tests
- Update docs and README with MiniMax provider info
* feat: upgrade MiniMax default model to M2.7
- Add MiniMax-M2.7 and MiniMax-M2.7-highspeed to model list
- Set MiniMax-M2.7 as default model (first in list)
- Keep all previous models (M2.5, M2.5-highspeed) as alternatives
- Update pricing table with M2.7 entries
- Update model catalog with M2.7 specs
- Update docs to list all available MiniMax models
- Update unit tests (23 passing) and integration tests (3 passing)
---------
Co-authored-by: Octopus <octo-patch@users.noreply.github.com>
- Catch any Exception from Tavily (not just specific error types)
- Fall back to DuckDuckGo for any error, making the tool more robust
- Fix test mocks to use builtins.__import__ for proper local import mocking
- Simplify test_execute_tavily_error to test generic exception handling
When Tavily API is unavailable (no API key, import error, or API error),
the web_search tool now falls back to DuckDuckGo search instead of
failing. This ensures the tool always works for users without a
Tavily API key.
- Add DuckDuckGo search as fallback using ddgs package
- Catch specific Tavily exceptions (MissingAPIKeyError, InvalidAPIKeyError,
ForbiddenError, UsageLimitExceededError, TimeoutError, BadRequestError)
- Add logger.debug calls to log when falling back to DuckDuckGo
- Use ddgs instead of deprecated duckduckgo-search package name
- Add test for DuckDuckGo fallback result formatting
- Simplify test mocking to use consistent monkeypatch patterns
Closes#81
Add _annotate_anthropic_cache helper that annotates system messages
with cache_control for Anthropic prompt caching.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add optional prompt_builder parameter to BaseAgent. When provided,
_build_messages() uses builder.build() output instead of raw system_prompt.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Sort the check_ssrf import alphabetically to fix ruff I001. Mock the
SSRF check in URL-fetching tests since it requires the Rust backend
which isn't available in CI. Add a dedicated test verifying SSRF
rejection.
- Engine discovery (#73): get_engine() now falls back to any healthy
engine when the explicitly-requested key fails, instead of returning
None. Fixes LM Studio (and other non-default engines) not being found
when deploying agents.
- FTS5 case sensitivity & scoring (#67): Add explicit unicode61 tokenizer
to the FTS5 virtual table for case-insensitive search. Replace ambiguous
`rank` column with explicit `bm25()` call with column weights (id=0,
content=1, source=0.5) to produce correct positive scores. Includes
auto-migration for existing databases.
- Interactive engine selection (#72): `jarvis init` now detects running
engines and presents an interactive picker. Also accepts `--engine`
flag to skip the prompt. Engine choice flows through to config
generation.
- Windows desktop support (#68): Add RAM detection via wmic, Windows
binary paths (LOCALAPPDATA, ProgramFiles, cargo), port cleanup via
netstat+taskkill, and USERPROFILE fallback for HOME env var.
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: add interactive agent confirmation mode and fix tool loading
- Add `interactive` and `confirm_callback` params to ToolUsingAgent and
NativeReActAgent so CLI sessions can prompt user before tool execution
- Fix tool resolution in `ask` and `chat` commands to fall back to
config.tools.enabled when no --tools flag is provided
- Register shell_exec tool in tools/__init__.py auto-discovery
- Add dspy and gepa as optional learning extras (learning-dspy, learning-gepa)
- Fix openjarvis-rust lock version (1.0.0 → 0.1.0)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(cli): support configured tool defaults and confirmations
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat: model catalogue with download/delete and auto-pull Qwen3.5
- Desktop boot: start server immediately with fallback model (qwen3:0.6b),
then pull preferred model (qwen3.5:4b) and remaining Qwen3.5 variants
that fit in RAM in the background. No more broken "Select model" state.
- Backend: add POST /v1/models/pull and DELETE /v1/models/{name} endpoints
so the frontend can trigger model downloads and deletions via Ollama.
- Frontend: redesign CommandPalette (Cmd+K) with two tabs — "Installed"
shows pulled models with select/delete, "Download Models" shows a
catalogue of popular models plus a custom model input field.
- Fix ollama_has_model() to use exact tag matching instead of prefix
matching, preventing false positives.
* fix: streaming, model switching, second-largest default, and tests
- Streaming: use direct engine streaming for non-tool requests so tokens
arrive in real-time instead of being batched by the agent bridge.
Add error handling to _handle_stream so engine errors surface as
content chunks instead of silent failures.
- Model selection: pick the second-largest Qwen3.5 model that fits
(leaves headroom for OS/apps) instead of the absolute largest.
- Model switching: abort in-flight stream when the user changes models
mid-generation, preventing stale-model errors. Improve error messages
in catch blocks.
- Tests: add tests/server/test_model_management.py with 11 tests
covering model pull/delete endpoints, streaming error resilience,
direct-engine streaming bypass, and model listing. All 100 server
tests pass.
The SecurityHeadersMiddleware ran before CORSMiddleware (Starlette
executes middleware in LIFO order) and added headers to OPTIONS
preflight requests. The Content-Security-Policy: default-src 'self'
header told the browser to reject cross-origin connections, so fetch()
from the Tauri webview (https://tauri.localhost) to the API server
(http://127.0.0.1) was blocked — causing "Failed to get response" on
every chat message in the desktop app.
Two fixes:
- Skip security headers on OPTIONS requests so CORS preflight works
- Remove Content-Security-Policy from API responses — it is a
document-level browser policy irrelevant to JSON API responses and
breaks any cross-origin API consumer
* chore: create learning subdirectory structure (routing, agents, intelligence)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* feat: extract classify_query to routing/_utils.py
Move the classify_query() function and its regex patterns into a shared
utility module so multiple routing policies can import it without
depending on the full trace_policy module.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* refactor: move routing files to learning/routing/ subdirectory
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* feat: create LearnedRouterPolicy merging trace-driven + SFT routing
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* feat: add conditional Algolia DocSearch integration
Add Algolia DocSearch as an optional search upgrade — native lunr.js
search remains the default until credentials are configured. Includes
CDN assets, Jinja2 conditional config injection, init script with
graceful fallback, light/dark theme CSS, improved search tokenization
for snake_case/dotted identifiers, and search boosts for key pages.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* refactor: move agent_evolver and skill_discovery to learning/agents/
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* refactor: move learning/orchestrator to learning/intelligence/orchestrator
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* refactor: delete removed learning policies, rewrite __init__.py, clean up api_routes
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* feat: add SFT/GRPO/DSPy/GEPA config dataclasses, update LearningConfig
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* feat: add general-purpose SFT trainer (intelligence/sft_trainer.py)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: update stale imports in multi_model_router example
Update imports to use new learning/routing/ paths after the
subdirectory reorganization. Replace BanditRouterPolicy with
LearnedRouterPolicy.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* feat: add general-purpose GRPO trainer (intelligence/grpo_trainer.py)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* feat: add DSPy agent optimizer (agents/dspy_optimizer.py)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* feat: add GEPA agent optimizer (agents/gepa_optimizer.py)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* feat: add learning-dspy and learning-gepa optional dependency extras
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: update integration test to check for learned policy instead of grpo
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: clean up stale APIs and unused params in examples
- deep_research: remove system_prompt and max_turns params not accepted
by Jarvis.ask(), inline system prompt into the query instead
- doc_qa: remove unused --top-k CLI arg that was never passed to the API
- multi_model_router: fix select_model() call to match single-arg signature
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: import SFT/GRPO trainers in intelligence/__init__.py for registry
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* chore: remove .md file changes from PR
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* chore: restore search boost frontmatter for key docs pages
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
OLLAMA_HOST env var was unconditionally overriding the host parameter
in OllamaEngine.__init__, even when explicitly set via config.toml.
Priority is now: config.toml > OLLAMA_HOST env var > hardcoded default.