2b53566283 feat(memory): LLM-based NER + importance signal with cheap-signals short-circuit (#708) (#775)
* feat(memory): LLM-based NER + importance signal with cheap-signals short-circuit (#708)

Adds an Ollama-backed entity extractor and an LLM-derived importance
score as a new signal in the admission gate. Builds on merged Phase 2
(#733) without changing its surface — additive only.

Why LLM (not GLiNER): zero new deps (reuses existing reqwest + async
infra), reuses the same Ollama setup openhuman uses for embeddings,
better per-entity quality scales with model choice, no native ONNX
runtime to ship. Latency cost (~100-300ms per chunk on a small model
like qwen2.5:0.5b) is amortised by the short-circuit.

The admission gate stays a hybrid - cheap deterministic signals
always run; the LLM is one signal among many, never the sole arbiter.
Backwards-compatible: with default SignalWeights the LLM weight is 0.0
and behaviour matches pre-LLM Phase 2 exactly.

What's new:

- src/openhuman/memory/tree/score/extract/llm.rs: LlmEntityExtractor
  implementing the existing EntityExtractor trait. Posts a structured
  JSON request to an Ollama-compatible /api/chat endpoint asking for
  NER + an importance rating in one call. Span recovery via string
  search; hallucinated entities (surface not in source text) dropped.
  Soft fallback: HTTP failures log a warn and return empty extraction.
- ExtractedEntities gains llm_importance (Option<f32>) +
  llm_importance_reason (Option<String>); merge() takes max importance.
- ScoreSignals gains llm_importance (f32, defaults to 0.0).
- SignalWeights gains llm_importance (default 0.0; with_llm_enabled()
  helper sets it to 2.0 - comparable to metadata/source weights).
- signals::combine_cheap_only(): variant that excludes the LLM signal,
  used for the short-circuit decision in score_chunk.
- ScoringConfig gains llm_extractor (Option<Arc<dyn EntityExtractor>>),
  definite_keep_threshold (default 0.85), definite_drop_threshold
  (default 0.15). New with_llm_extractor() constructor.
- score_chunk() pipeline:
  1. Always-on regex extraction
  2. Cheap signals + combine_cheap_only -> cheap_total
  3. If cheap_total >= definite_keep OR <= definite_drop: skip LLM
  4. Else (borderline band): run LLM extractor, merge results, recompute
  5. Final combine + admission gate against drop_threshold
- mem_tree_score table gains nullable llm_importance + llm_importance_reason
  columns via idempotent ALTER TABLE migration.
- ScoreRow + upsert/get persist the new column.

What's not changed:
- Existing JSON-RPC surface
- Default behaviour (LLM weight=0, no llm_extractor configured = same
  outputs as before)
- mem_tree_chunks / mem_tree_entity_index schemas

Tests added:
- LLM extractor: prompt construction, JSON parsing, hallucination
  drop, importance clamping, strict vs lenient unknown-kind handling
- Score pipeline: short-circuit on definite_keep/definite_drop skips
  LLM call (verified via FakeLlm call counter); borderline band
  consults LLM once; LLM failure falls back gracefully without erroring

LLM-NER work follows up on #708. Parent: #711.

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

* fix(memory): address CI failures and CodeRabbit review on LLM-NER (#708)

CI was red on PR #775 due to 10 E0063 errors — existing test-only struct
literals for ExtractedEntities / ScoreSignals / ScoreRow weren't updated
when the new optional llm_importance fields landed. Local cargo check
on lib-only passed; cargo check --tests caught them, as does CI.

CodeRabbit also flagged four semantic issues:

1. LlmEntityExtractor::extract() returned Err on HTTP transport, non-2xx
   status, and JSON parse failures. The soft-fallback contract (documented
   in the module header) only worked because score_chunk catches errors;
   any other caller got a hard error. Now split into extract_or_empty()
   which logs a warn on each failure mode and returns
   ExtractedEntities::default() unconditionally. The public extract()
   stays on the async-trait surface but never returns Err.

2. find_char_span() always searched from byte offset 0, so when the LLM
   returned the same surface twice (explicitly asked for duplicates in
   the prompt), both entities got (start=0, end=len) — same span, not
   distinct mentions. Added find_char_span_from(haystack, needle,
   byte_from, char_from) that resumes from a cursor. into_extracted_entities
   now tracks a per-surface (byte_after, char_after) cursor in a
   HashMap<String, (usize, u32)>, so duplicate mentions get distinct
   non-overlapping spans and over-claim (LLM returns 3 "Alice" when
   source has 2) drops the excess entries with a debug log.

3. combine() always included w.llm_importance in the denominator even
   when llm_importance=0.0 because the LLM didn't run. That artificially
   lowered the total on short-circuit paths. score_chunk now branches on
   llm_consulted: uses combine() when LLM ran (importance actually
   contributes), combine_cheap_only() when LLM was skipped or failed
   (denominator excludes the LLM weight). Observable in two new tests:
   short_circuit_reports_cheap_only_total verifies r.total ==
   combine_cheap_only(r.signals, weights) and strictly exceeds
   combine(r.signals, weights) when llm_importance=0; llm_consulted_
   reports_full_total verifies the opposite path.

4. Same issue from a different angle (CodeRabbit flagged both). Fixed
   by the same llm_consulted branch.

Test-literal fixes:
- signals/ops.rs: ScoreSignals literals pick up llm_importance field;
  make_entities helper uses ..Default::default() on ExtractedEntities.
- extract/types.rs: three test ExtractedEntities literals gain
  llm_importance: None, llm_importance_reason: None.
- resolver.rs: canonicalise_batch_preserves_spans literal gains the
  two fields.
- score/store.rs: sample_row gains signals.llm_importance and
  llm_importance_reason.

New tests (7 added):
- find_char_span_from_advances_past_prior_match
- find_char_span_from_returns_none_after_exhaustion
- find_char_span_from_preserves_utf8
- find_char_span_from_rejects_non_char_boundary
- into_extracted_entities_gives_distinct_spans_to_duplicate_mentions
- into_extracted_entities_drops_extra_duplicate_when_source_only_has_one
- extract_soft_fallback_on_unreachable_endpoint (transport failure →
  empty extraction, not Err)
- short_circuit_reports_cheap_only_total
- llm_consulted_reports_full_total

cargo check --lib clean; cargo fmt clean. The integration-test rmeta
errors visible locally are stale-metadata artifacts from incremental
builds with prior struct shapes; CI's clean build resolves them and
tests/*.rs files do not construct any of the touched types.

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

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-22 13:26:00 -07:00
2026-04-21 14:14:15 +00:00
2026-04-22 04:42:12 +00:00
2026-02-20 13:03:15 +04:00
2026-02-20 13:03:15 +04:00

OpenHuman

The age of super intelligence is here. OpenHuman is your Personal AI super intelligence. Private, Simple and extremely powerful.

DiscordRedditX/TwitterDocs

Early Beta Platforms: desktop only Latest Release

The Tet

"The Tet. What a brilliant machine" — Morgan Freeman as he reminisces about alien superintelligence in the movie Oblivion

Early Beta — Under active development. Expect rough edges.

To install or get started, either download from the website over at tinyhumans.ai/openhuman or run

# For MacOS/Linux
curl -fsSL https://raw.githubusercontent.com/tinyhumansai/openhuman/main/scripts/install.sh | bash

# For Windows
irm https://raw.githubusercontent.com/tinyhumansai/openhuman/main/scripts/install.ps1 | iex

What is OpenHuman?

OpenHuman is an open-source agentic assistant that is designed to integrate with you in your daily life. Here's what makes OpenHuman special:

  • Simple, UI-first — A clean desktop experience and short onboarding paths so you can go from install to a working agent in a few clicks, without a config-first setup. You don't need a terminal to run OpenHuman.

  • One subscription, many providers — You only need one account to get access to many agentic APIs (AI Models, Search, Webhooks/Tunnels and other 3rd party APIs etc..), simplifying the experience to get a powerful agent going.

  • Rich Skills — Plug into Gmail, Slack, Notion, and the rest of your stack via rich, feature-backed skills. Connections are typically one click through setup wizards instead of wiring APIs by hand. Workflow data is kept on device, encrypted locally, and treated as yours: encryption and sensitive context stay on your machine. Webhooks give instant feedback into the agent when external systems or skills emit events, so the loop stays tight without constant polling.

  • Local knowledge base — Built from your data and your activity. How you work across tools, sessions, and connected services—so the agent gets rich, workflow-aware context, not a one-off chat transcript. Everything is stored on your machine and compounding over time without becoming a cloud dossier. Channels, skills and ongoing conversations feed the same loop so day-to-day context does not reset every session.

  • Local AI model — The Rust core exposes local AI paths (and the desktop bundle can ship local/bundled runners where applicable) for the workloads above—vision snippets, speech helpers, summarization, tooling—so sensitive steps can stay off the cloud when you choose.

  • Deep desktop integrations — OpenHuman is a native desktop assistant, not a web-only chat: memory-aware keyboard autocomplete, voice (STT listening and TTS replies), screen intelligence that understands what is on screen and feeds your local context, plus windowing and OS-level permissions—so the agent meets you on the machine, not trapped in a browser tab.

Architecture: docs/ARCHITECTURE.md. Contributor orientation: CONTRIBUTING.md. Running from source: docs/install.md.

Highlights

  • Neocortex — local-first knowledge base that learns from your data and activity, compounding context across tools and sessions.
  • The Subconscious — background self-learning loops that turn everyday usage into workflow-aware intelligence.
  • Screen Intelligence — the agent sees what's on your screen and feeds it into your local context.
  • Inline Autocomplete — memory-aware keyboard autocomplete anywhere on your desktop.
  • Voice (STT + TTS) — speak to OpenHuman and hear it reply, natively on the desktop.
  • Skills & Integrations — one-click skills for Gmail, Slack, Notion and the rest of your stack, with local encryption and webhooks for instant feedback.
  • Messaging Channels — inbound/outbound across the channels you already use, routed through your agent.
  • Teams & Organizations — shared workspaces for collaborating with an agent across a team.
  • Rewards & Achievements — gamified progression as your agent grows with you.
  • Privacy & Security — workflow data stays on device, encrypted locally, and treated as yours.

OpenHuman vs other agents

High-level comparison (products evolve—verify against each vendor). OpenHuman is built to minimize vendor sprawl, keep workflow knowledge on-device, and ship deep desktop features—not only chat.

Claude Code/Cowork OpenClaw Hermes Agent OpenHuman
Open-source 🚫 Proprietary MIT MIT GNU
Simple to start Desktop + CLI ⚠️ Terminal-first ⚠️ Terminal-first Clean UI, minutes
Cost ⚠️ Sub + add-ons ⚠️ BYO models ⚠️ BYO models Local-friendly
Memory & KB Chat-scoped ⚠️ Plugin-reliant Self-learning 🚀 Local KB + learning
API sprawl 🚫 Extra keys 🚫 BYOK 🚫 Multi-vendor One account
Extensibility MCP SKILL.md SKILL.md 🚀 Rich Skills
Desktop integration ⚠️ Basic ⚠️ Light ⚠️ Light STT/TTS/screen/more

Contributors Hall of Fame

Show some love and end up in the hall of fame

OpenHuman contributors
S
Description
No description provided
Readme GPL-3.0
214 MiB
Languages
Rust 59.1%
TypeScript 37.9%
JavaScript 1.6%
Shell 1.2%
CSS 0.1%