Commit Graph
28 Commits
Author SHA1 Message Date
590addae4c test(engine): expect EngineConnectionError for vllm 404 (fix main CI) (#530)
#463 made the OpenAI-compatible engine wrap upstream HTTP errors (incl.
404) in EngineConnectionError with an actionable message, but
test_invalid_model_404 still asserted the raw httpx.HTTPStatusError, so it
broke on main once #463 landed (#463 was a stale fork PR with no CI, so it
wasn't caught pre-merge). Expect EngineConnectionError now, asserting the
httpx.HTTPStatusError is preserved as the chained cause. Whole tests/engine
suite is green again.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-10 14:09:37 -07:00
c76c80d6b4 fix: forward tools through OpenRouter engine (#511)
* fix: forward tools through OpenRouter engine

The OpenRouter chat completion path built the request from only
`model`, `messages`, `max_tokens`, and `temperature`. `tools` and
`tool_choice` passed via `kwargs` were silently dropped, so
function definitions never reached the model. Symptom on a
managed deep_research agent: the model answered every query from
its own prior knowledge and never invoked `knowledge_search`,
`knowledge_sql`, etc.

The same path also discarded `choice.message.tool_calls` from the
response — when a model did return a tool call (verified directly
against OpenRouter with `google/gemma-4-31b-it:free` and
`nvidia/nemotron-3-ultra-550b-a55b:free`), the agent loop never
saw it.

This patch:
- forwards `tools` and `tool_choice` into the OpenAI-compatible
  request in both `_generate_openrouter` (sync) and
  `_stream_openrouter` (async stream),
- extracts `tool_calls` from the response in `_generate_openrouter`
  in the same shape used by the OpenAI / Anthropic paths.

Verified end-to-end against a `deep_research` managed agent using
an OpenRouter preset with Gemma 4 31B + Nemotron 3 Ultra fallback:
before, the agent stated "I don't have access to your vault";
after, it calls `knowledge_search`, cites results, and produces a
structured answer.

* test(engine): regression test for OpenRouter tool forwarding

Asserts the OpenRouter path forwards tools/tool_choice to the
OpenAI-compatible API and parses tool_calls back into the result (#511).
Verified: passes on the fix, fails (KeyError 'tools') against pre-fix main.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Jon Saad-Falcon <jonsaadfalcon@gmail.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-10 12:30:22 -07:00
690b95e050 perf(serve): parallelize engine discovery + async version check (#470)
`jarvis serve` startup was slow on two counts addressed here:

1. discover_engines() probed each registered engine's health() serially,
   and every probe is a blocking network check with its own ~2s timeout —
   so N dead/slow localhost ports cost N*2s. Run the probes concurrently
   in a ThreadPoolExecutor; the existing healthy.sort() normalizes order,
   so the result is identical to the serial version. health() impls are
   read-only on per-instance HTTP clients with no shared mutable state.

2. The PyPI update check ran a blocking urlopen (up to 3s on a cache
   miss) inline before dispatch, delaying every command. Move it to a
   daemon thread — it's best-effort and never raises.

Together these remove ~10-30s from cold startup. Adds a regression test
asserting discovery probes overlap (concurrency), not just that output
is unchanged (covered by existing tests).

Note: the larger ~30-40s win — the duplicate SystemBuilder.build() in
serve.py — is NOT addressed here; it's not redundant (the second build
wires a JarvisSystem the inline path never constructs) and needs a
design pass. Deferred.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-01 13:17:38 -07:00
b5926400e2 fix(engine): retry without temperature on unsupported_value 400 (#466)
Some OpenAI models (e.g. gpt-5, the default for a fresh cloud install)
reject a non-default temperature with HTTP 400 "Unsupported value:
'temperature' does not support 0.7 ... Only the default (1) value is
supported." — so the user's very first prompt fails (#426).

Detect that specific 400 (param=temperature + unsupported_value/"only the
default"/"does not support") and retry the create() once without
temperature, mirroring the tools-400 retry in the Ollama and
OpenAI-compat engines. Unrelated 400s are re-raised unchanged.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-01 10:26:17 -07:00
bd1ab115ec fix(engine): convert apple_fm_shim cumulative snapshots to deltas (#464)
Apple FM's stream_response yields cumulative text snapshots, but
OpenAI-compatible clients concatenate delta.content — so streamed
responses were duplicated/stuttered (#378). Diff each snapshot against
the last and emit only the incremental suffix; fall back to the full
snapshot if the model revises earlier text, and skip empty deltas.

Rebased on #377 (apple_fm_sdk migration): uses the options= streaming
API. Adds a stubbed-SDK regression test asserting deltas are
incremental, not cumulative.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-01 10:11:53 -07:00
5270149ea5 fix(engine): modernize apple_fm_shim for the public apple-fm-sdk (#377)
* fix(engine): modernize apple_fm_shim for the public apple-fm-sdk

Apple released the official Foundation Models Python SDK as
`apple/python-apple-fm-sdk` (import name `apple_fm_sdk`, distribution
name `apple-fm-sdk`). The shim's `import apple_fm` predates the public
SDK and the per-call API has since moved as well; running the shim
against current `apple-fm-sdk` v0.1.1 fails on import, then on the
`/health` call shape, and again on every `respond` / `stream_response`
keyword.

This change brings the shim up to date with the real SDK without
altering its external OpenAI-compatible contract:

- Import `apple_fm_sdk` (the public name). Update the missing-package
  error to point at the GitHub repo since the SDK isn't on PyPI;
  installation is `uv pip install -e <clone>`.
- `SystemLanguageModel.is_available()` is an instance method and now
  returns `(bool, SystemLanguageModelUnavailableReason | None)`. The
  health endpoint instantiates the model and unpacks the tuple, and
  surfaces the reason string when the model is unavailable.
- `LanguageModelSession.respond` and `.stream_response` no longer
  accept `max_tokens` / `temperature` positionally; they take a
  `GenerationOptions` instance via the `options` kwarg. The shim now
  builds a `GenerationOptions(temperature=..., maximum_response_tokens=...)`
  from each `ChatRequest` and threads it through both paths.
  `temperature` is now actually honored (the previous code dropped it
  silently).
- Add `response_model=None` to the `/v1/chat/completions` decorator so
  FastAPI doesn't try to build a Pydantic field for the
  `JSONResponse | StreamingResponse` union return type — that fails
  with the FastAPI version pinned in the `server` extra.
- Update the module docstring to reference macOS 26 + Apple
  Intelligence (the SDK's actual minimum), not macOS 15.

## How was this tested?

- `uv pip install -e ./python-apple-fm-sdk` against a local clone of
  Apple's repo on macOS 26 / M5 Max with Apple Intelligence enabled.
- `uv sync --extra dev --extra server` then `uv run uvicorn
  openjarvis.engine.apple_fm_shim:app --host 127.0.0.1 --port 8079`.
- `GET /health` → `{"status": "ok"}` (200).
- `GET /v1/models` → lists `apple-fm`.
- `POST /v1/chat/completions` with messages + temperature +
  max_tokens → returns a real Apple Intelligence completion.
- End-to-end through OpenJarvis: add `[engine.apple_fm]
  host = "http://localhost:8079"` to `~/.openjarvis/config.toml`,
  then `jarvis ask --engine apple_fm --model apple-fm "..."` returns
  the same Apple FM response routed through the OpenAI-compatible
  engine wrapper.
- `uv run ruff check src/openjarvis/engine/apple_fm_shim.py` and
  `uv run ruff format --check src/openjarvis/engine/apple_fm_shim.py`
  both pass.

* test(engine): add stubbed-SDK tests for apple_fm_shim migration

Covers the apple_fm -> apple_fm_sdk migration: GenerationOptions carries
temperature + max_tokens and is passed via options= to respond() and
stream_response(), and /health unpacks the (available, reason) tuple
from SystemLanguageModel().is_available().

The real apple-fm-sdk is not installable in CI (not on PyPI; macOS 26 +
Apple Intelligence only), so the tests inject a stub SDK into
sys.modules. They verify the shim's OpenAI-compat wiring, not Apple's
real SDK behavior.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Jon Saad-Falcon <41205309+jonsaadfalcon@users.noreply.github.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-01 09:23:43 -07:00
Eddie Richterandkrypticmouse 86c42e4e4a tests: wrap lemonade host override signature 2026-05-20 20:20:53 +00:00
Eddie Richterandkrypticmouse a65cd12ea2 lemonade: update default host and recommended model 2026-05-20 20:20:53 +00:00
Jon Saad-FalconandGitHub f41cf420be feat(mining): Pearl mining integration
Consolidates NVIDIA vLLM, Apple Silicon, CPU Pearl mining support, CLI/docs, and live H100 validation.
2026-05-05 19:11:15 -07:00
Eddie Richter d9983077f0 Fixing incorrect v0 api name to v1 2026-03-30 21:28:24 -06:00
Eddie Richter 8f6ee7ae10 Fixing lemonade server when used in proxy 2026-03-30 20:50:55 -06:00
Eddie Richter f3d122e9c1 Addinig initial lemonade support 2026-03-30 20:50:53 -06:00
Jon Saad-FalconandClaude Opus 4.6 869d9256fd fix: resolve all lint errors after merge with main
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 13:33:42 -07:00
b39dbedcc4 feat: fix external MCP server integration and add streaming tool-call support
Rebased and cleaned-up version of PR #113 by @mricharz, resolved against
current main (including Codex engine, Gemini thought_signature, and
agent manager fixes merged since the original PR).

MCP Transport & Client:
- StreamableHTTPTransport with session tracking, SSE parsing, timeouts
- MCPClient.initialize() sends proper MCP handshake (protocolVersion,
  capabilities, clientInfo) + notifications/initialized
- Fix StdioTransport constructor: command=[command] + args
- MCPRequest.to_dict() with notification support (id=None)

External MCP Discovery:
- _discover_external_mcp supports both url (HTTP) and command (stdio)
- Per-server include_tools / exclude_tools filtering
- MCP clients persisted on JarvisSystem for runtime lifetime

Streaming Tool-Call Support (stream_full):
- StreamChunk dataclass in _stubs.py (content, tool_calls, finish_reason, usage)
- Default stream_full() on InferenceEngine ABC wraps stream() for backward compat
- _OpenAICompatibleEngine.stream_full() with SSE parsing
- CloudEngine: _stream_full_openai (OpenAI/OpenRouter/MiniMax/Codex routing)
               _stream_full_anthropic (event-based → OpenAI delta format)
- InstrumentedEngine, MultiEngine: stream_full delegation
- GuardrailsEngine: stream_full with post-hoc security scanning (FIXED:
  original PR bypassed output scanning — now accumulates and scans like stream())

Other improvements:
- _prepare_anthropic_messages() extracted to eliminate duplication
- Default tool_choice=auto when tools are provided (OpenAI compat engines)
- MCP tool injection into managed agent streaming path
- Documentation: docs/user-guide/mcp-external-servers.md

Tests: ~59 new tests across 8 test files, all passing.

Closes PR #113

Co-Authored-By: mricharz <mricharz@users.noreply.github.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 18:09:54 +00:00
krypticmouseandClaude Opus 4.6 63ae942116 feat: add Codex cloud engine for ChatGPT Plus/Pro subscribers
Adds `codex/` prefixed model support using the OpenAI Responses API —
the same protocol used by zeroclaw and other Codex-compatible tools.

Live-tested against gpt-5-mini-2025-08-07 with:
- Generate (non-streaming): confirmed working
- System prompt → instructions mapping: confirmed working
- SSE streaming: confirmed working (9 chunks)
- End-to-end via `jarvis ask`: confirmed working

Implementation:
- Default endpoint: api.openai.com/v1/responses (standard API key)
- Override via OPENAI_CODEX_BASE_URL for ChatGPT OAuth tokens
  (e.g. chatgpt.com/backend-api/codex)
- Auth via OPENAI_CODEX_API_KEY env var
- Responses API format: input array, instructions field, output_text extraction
- Handles reasoning+message output blocks correctly
- SSE streaming parses response.output_text.delta events

Models: codex/gpt-4o, codex/gpt-4o-mini, codex/o3-mini,
        codex/gpt-5-mini, codex/gpt-5-mini-2025-08-07

Usage:
  export OPENAI_CODEX_API_KEY="your-api-key-or-oauth-token"
  jarvis ask "Hello" --model codex/gpt-5-mini-2025-08-07

Closes #134

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 01:54:36 +00:00
Jon Saad-FalconandClaude Sonnet 4.6 e4d036dd50 test: add live integration test stubs for gemma_cpp engine
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-25 13:18:13 -07:00
Jon Saad-FalconandClaude Sonnet 4.6 16025571fd style: fix lint issues in gemma_cpp engine
Move mid-file imports to top of test file to resolve E402 violations and apply ruff formatting to both gemma_cpp.py and test_gemma_cpp.py.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-25 13:17:16 -07:00
Jon Saad-FalconandClaude Sonnet 4.6 d08360ca21 feat: wire gemma_cpp engine into discovery and optional imports
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-25 13:15:06 -07:00
Jon Saad-FalconandClaude Sonnet 4.6 411e822916 test: add config resolution tests for gemma_cpp engine
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-25 12:40:24 -07:00
Jon Saad-FalconandClaude Sonnet 4.6 8b0fd702c4 feat: implement gemma_cpp health checks and model listing
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-25 12:39:38 -07:00
Jon Saad-FalconandClaude Opus 4.6 8c2ee6843a feat: implement gemma_cpp engine lifecycle and inference methods
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 12:38:25 -07:00
Jon Saad-FalconandClaude Sonnet 4.6 9cd822611e feat: add GemmaCppEngine skeleton with chat template formatting
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-25 12:36:35 -07:00
Jon Saad-FalconandClaude Sonnet 4.6 d589bfb55d feat: add GemmaCppEngineConfig and wire into EngineConfig
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-25 12:34:48 -07:00
Prathap PandGitHub c2756964a7 fix(channels): wire channel→agent handler and fix Telegram send pipeline (#94)
* fix(channels): wire channel→agent handler and fix Telegram send pipeline

* format code

* add supported tests
2026-03-20 18:35:18 -07:00
62eaa70736 feat: add MiniMax as cloud inference provider with M2.7 default (#85)
* 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>
2026-03-20 04:16:33 -07:00
Tarun SureshandClaude Opus 4.6 981f665913 feat: add Anthropic prompt cache breakpoint annotation
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>
2026-03-16 18:42:54 +00:00
cc1e14f1c3 fix: engine discovery fallback, FTS5 case/scoring, init engine picker, Windows support (#74)
- 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>
2026-03-15 16:23:39 -07:00
Jon Saad-Falconandkrypticmouse 8798e2ee4f init commit 2026-03-12 17:29:39 +00:00