From 094d8ba7319068b85855959307b77b39a013b80d Mon Sep 17 00:00:00 2001 From: Safi Date: Mon, 11 May 2026 21:18:48 +0100 Subject: [PATCH] fix #821 #818 #820: universal help guard, --version flag, Ollama num_ctx fallback MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Universal -h/--help/-? guard after cmd dispatch: any help flag anywhere in argv stops execution and prints "Run 'graphify --help'" instead of triggering the subcommand — cursor/kiro/gemini install --help no longer silently installs; benchmark --help no longer crashes with FileNotFoundError (#821) - --version / -v / version subcommand: print graphify {__version__} and exit (#818) - GRAPHIFY_OLLAMA_NUM_CTX= now falls through to auto-derived num_ctx instead of hardcoding 131072 (the cap that causes OOM on constrained VRAM); pinned num_ctx < estimated input now triggers an explicit truncation warning with a suggested --token-budget correction (#820) Co-Authored-By: Claude Sonnet 4.6 --- CHANGELOG.md | 7 +++++++ graphify/__main__.py | 17 ++++++++++++++++- graphify/llm.py | 29 +++++++++++++++++++++++++---- pyproject.toml | 2 +- 4 files changed, 49 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 54da777..ed18ef5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,13 @@ Full release notes with details on each version: [GitHub Releases](https://github.com/safishamsi/graphify/releases) +## 0.7.15 (2026-05-11) + +- Fix: `-h`/`--help`/`-?` in any position now stops execution — previously `graphify cursor install --help` silently installed into Cursor; `graphify benchmark --help` crashed with FileNotFoundError (#821) +- Fix: `--version`, `-v`, and `graphify version` now print the installed version and exit (#818) +- Fix: `GRAPHIFY_OLLAMA_NUM_CTX=` no longer falls back to hardcoded 131072 (which exhausted VRAM) — it now falls through to the auto-derived value and prints a warning (#820) +- Fix: when `GRAPHIFY_OLLAMA_NUM_CTX` is set smaller than the estimated chunk size, graphify now warns explicitly that Ollama will silently truncate the prompt and suggests a corrected `--token-budget` (#820) + ## 0.7.14 (2026-05-11) - Fix: `_make_id` and `_normalize_id` now apply NFKC Unicode normalization before ID generation -- composed/decomposed forms of the same character (e.g. `é` typed vs pasted from a PDF) now produce the same node ID; switched from `.lower()` to `.casefold()` for correct Turkish/German/Greek case folding; both functions are now byte-for-byte equivalent (#811) diff --git a/graphify/__main__.py b/graphify/__main__.py index 5dbe7ac..cdb40ea 100644 --- a/graphify/__main__.py +++ b/graphify/__main__.py @@ -1122,7 +1122,11 @@ def main() -> None: for skill_dst in {Path.home() / cfg["skill_dst"] for cfg in _PLATFORM_CONFIG.values()}: _check_skill_version(skill_dst) - if len(sys.argv) < 2 or sys.argv[1] in ("-h", "--help"): + if len(sys.argv) >= 2 and sys.argv[1] in ("-v", "--version", "version"): + print(f"graphify {__version__}") + return + + if len(sys.argv) < 2 or sys.argv[1] in ("-h", "--help", "-?"): print("Usage: graphify ") print() print("Commands:") @@ -1227,6 +1231,17 @@ def main() -> None: return cmd = sys.argv[1] + + # Universal help guard: -h/--help/-? anywhere after the command shows help + # and stops — prevents flags from silently triggering destructive subcommands + # (e.g. "cursor install --help" was silently installing into Cursor, #821). + # Exempt: free-text commands (user string may contain these tokens), and + # "install"/"uninstall" which have their own per-subcommand help handlers. + _FREE_TEXT_CMDS = {"query", "explain", "path", "save-result", "install", "uninstall"} + if cmd not in _FREE_TEXT_CMDS and any(a in {"-h", "--help", "-?"} for a in sys.argv[2:]): + print(f"Run 'graphify --help' for full usage.") + return + if cmd == "install": # Default to windows platform on Windows, claude elsewhere default_platform = "windows" if platform.system() == "Windows" else "claude" diff --git a/graphify/llm.py b/graphify/llm.py index 9c78fcb..26787ed 100644 --- a/graphify/llm.py +++ b/graphify/llm.py @@ -289,17 +289,38 @@ def _call_openai_compat( # Capped at 131072 (enough for the default 60k token_budget); env var wins. if backend == "ollama": num_ctx_raw = os.environ.get("GRAPHIFY_OLLAMA_NUM_CTX", "").strip() + # Auto-derive num_ctx from actual chunk size regardless — used as the + # fallback and for the mismatch check below. + estimated_input = len(user_message) // _CHARS_PER_TOKEN + 400 + auto_num_ctx = min(estimated_input + max_completion_tokens + 2000, 131072) + auto_num_ctx = max(auto_num_ctx, 8192) if num_ctx_raw: try: num_ctx = int(num_ctx_raw) except ValueError: - num_ctx = 131072 + # Bad env var: fall through to auto-derivation (not 131072 — + # hardcoding the cap is what causes OOM on constrained VRAM). + print( + f"[graphify] GRAPHIFY_OLLAMA_NUM_CTX={num_ctx_raw!r} is not a valid integer; " + f"using auto-derived value ({auto_num_ctx}).", + file=sys.stderr, + ) + num_ctx = auto_num_ctx + else: + # Warn when the pinned value is smaller than the estimated input — + # Ollama silently truncates the prompt and returns empty responses. + if num_ctx < estimated_input: + print( + f"[graphify] warning: GRAPHIFY_OLLAMA_NUM_CTX={num_ctx} is smaller than " + f"the estimated chunk input (~{estimated_input} tokens). Ollama will " + f"silently truncate the prompt and return empty responses. " + f"Try --token-budget {max(1024, num_ctx // 3)} or increase NUM_CTX.", + file=sys.stderr, + ) else: # Estimate input tokens: user_message chars / 4 (standard BPE # heuristic) + 400 for the system prompt, then add output headroom. - estimated_input = len(user_message) // _CHARS_PER_TOKEN + 400 - num_ctx = min(estimated_input + max_completion_tokens + 2000, 131072) - num_ctx = max(num_ctx, 8192) # floor: never under-allocate badly + num_ctx = auto_num_ctx keep_alive = os.environ.get("GRAPHIFY_OLLAMA_KEEP_ALIVE", "30m") kwargs["extra_body"] = {"options": {"num_ctx": num_ctx}, "keep_alive": keep_alive} resp = client.chat.completions.create(**kwargs) diff --git a/pyproject.toml b/pyproject.toml index 90a343a..596fe80 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "graphifyy" -version = "0.7.14" +version = "0.7.15" description = "AI coding assistant skill (Claude Code, Codex, OpenCode, Cursor, Gemini CLI, Aider, OpenClaw, Factory Droid, Trae, Hermes, Kiro, Pi, Google Antigravity) - turn any folder of code, docs, papers, images, or videos into a queryable knowledge graph" readme = "README.md" license = { file = "LICENSE" }