Skip to content

feat(api): auto-fetch Bonsai GGUF + GraphStore.ask/write surface#195

Merged
KailasMahavarkar merged 1 commit into
mainfrom
feat/bonsai-auto-fetch
May 6, 2026
Merged

feat(api): auto-fetch Bonsai GGUF + GraphStore.ask/write surface#195
KailasMahavarkar merged 1 commit into
mainfrom
feat/bonsai-auto-fetch

Conversation

@KailasMahavarkar
Copy link
Copy Markdown
Contributor

Three small wins so pip install graphstore[pro] is the only command users need to run, and so the agent-app surface (NL question -> answer, NL turn -> ingest) doesn't require knowing the DSL.

  • BonsaiIngestor: model_path now optional. When omitted, resolves via the new graphstore._models.resolve_bonsai_gguf helper which scans the HF cache and (by default) auto-downloads on miss. New optional quant= kwarg, defaulting to $GRAPHSTORE_BONSAI_QUANT or "TQ1_0".

  • store._locate_bonsai_gguf: was a duplicated scan_cache_dir block, now delegates to the same helper. Single source of truth shared by the ingestor and the calibration probe so the three call sites can never drift on repo/filename.

  • GraphStore.ask(question, *, limit=None, using=None) -> Result: thin wrapper over the existing ANSWER DSL verb with proper string escaping. Returns kind="answer". Requires a reader= callable.

  • GraphStore.write(text, *, msg_id, session_id="default", role="user", dry_run=False): NL turn -> DSL via a configured ingestor. Constructor accepts ingestor=<callable> factory invoked lazily on first call so the ingestor receives a fully-initialised GraphStore. Without a factory, write() raises with a copy-pasteable example.

  • README: the question example with dry_run=True was misleading because preview mode never invokes the reader. Replaced with two lines (one preview, one real) plus a one-liner note explaining the difference.

Verified end-to-end in container against the running playground: ask path returns Result(kind="answer", data={answer, cited_slots, ...}); no-reader / empty-question / no-ingestor guards raise the right errors; quoted-string escaping survives DSL parsing.

Three small wins so `pip install graphstore[pro]` is the only command users
need to run, and so the agent-app surface (NL question -> answer, NL turn
-> ingest) doesn't require knowing the DSL.

- BonsaiIngestor: model_path now optional. When omitted, resolves via the
  new graphstore._models.resolve_bonsai_gguf helper which scans the HF
  cache and (by default) auto-downloads on miss. New optional `quant=`
  kwarg, defaulting to $GRAPHSTORE_BONSAI_QUANT or "TQ1_0".

- store._locate_bonsai_gguf: was a duplicated scan_cache_dir block, now
  delegates to the same helper. Single source of truth shared by the
  ingestor and the calibration probe so the three call sites can never
  drift on repo/filename.

- GraphStore.ask(question, *, limit=None, using=None) -> Result: thin
  wrapper over the existing ANSWER DSL verb with proper string escaping.
  Returns kind="answer". Requires a reader= callable.

- GraphStore.write(text, *, msg_id, session_id="default", role="user",
  dry_run=False): NL turn -> DSL via a configured ingestor. Constructor
  accepts `ingestor=<callable>` factory invoked lazily on first call so
  the ingestor receives a fully-initialised GraphStore. Without a
  factory, write() raises with a copy-pasteable example.

- README: the question example with dry_run=True was misleading because
  preview mode never invokes the reader. Replaced with two lines (one
  preview, one real) plus a one-liner note explaining the difference.

Verified end-to-end in container against the running playground:
ask path returns Result(kind="answer", data={answer, cited_slots, ...});
no-reader / empty-question / no-ingestor guards raise the right errors;
quoted-string escaping survives DSL parsing.
@KailasMahavarkar KailasMahavarkar merged commit e16a2ba into main May 6, 2026
5 checks passed
@KailasMahavarkar KailasMahavarkar deleted the feat/bonsai-auto-fetch branch May 6, 2026 19:20
KailasMahavarkar added a commit that referenced this pull request May 7, 2026
…rite (#196)

Two follow-ups to #195:

- ANSWER's reader callable was previously unbounded - a hung local LLM
  would lock the executor thread indefinitely. Wrap the call in a daemon
  thread with `worker.join(timeout=N)`. On timeout the call returns with
  data["answer"] == "", data["error"] describing the timeout, and a
  meta["warnings"] entry; a warning is logged so the leak is visible.
  Threshold is configurable via `GraphStore(reader_timeout_seconds=...)`,
  default 60s. Constructor rejects non-positive values up-front.

  Trade-off: Python cannot cancel a running thread, so a genuinely stuck
  reader leaks one daemon per call. Acceptable - daemons don't block
  process exit, and demanding cancellable readers is too much to require
  from callers.

- README: Replaced no docs for `g.ask` / `g.write` (added in #195) with a
  small section showing the chat-app shape: ingestor= factory for NL
  writes, reader= for synthesis, the new reader_timeout_seconds knob,
  and a pointer to dry-run for question-shape routing through Bonsai.

Verified end-to-end in container: fast reader returns cleanly; slow
reader (5s) is bounded at 0.3s with proper error + warning surfaced;
exception readers are caught; reader_timeout_seconds=0 raises ValueError
at construction.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant