From bbfc9f9bd519e98a89030d54c4d29a08355c9cb4 Mon Sep 17 00:00:00 2001 From: handnewb <61999949+handnewb@users.noreply.github.com> Date: Tue, 11 Aug 2026 17:55:35 -0300 Subject: [PATCH] fix(llamacpp): pass extra_body through factory to LlamaCppLLM and OpenAICompatibleLLM delegate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes #3326: The create_llm_provider() factory accepted extra_body but never passed it to LlamaCppLLM, so HINDSIGHT_API_LLM_EXTRA_BODY was silently ignored for the llamacpp provider while working for every other provider (Bedrock, Fireworks, OpenAI-compatible, LiteLLM, etc.). The fix threads extra_body from the factory → LlamaCppLLM.__init__ → OpenAICompatibleLLM delegate, which already supports the parameter. fix(structured-doc): detect markdown tables without leading/trailing pipes Fixes #3361: The table parser required pipes at both start AND end of every row (^\s*\|.*\|\s*$), so GFM-compliant tables like "Col1 | Col2\n--- | ---" fell through to ParagraphBlock which joins all lines with spaces, permanently destroying table structure during delta refresh. The fix adds a lenient fallback: if a chunk contains a separator line AND every non-separator line contains at least one pipe character, it is treated as a table. This covers the common GFM shorthand without false-positiving on non-table content. --- .../hindsight_api/engine/llm_wrapper.py | 1 + .../hindsight_api/engine/providers/llamacpp_llm.py | 3 +++ .../hindsight_api/engine/reflect/structured_doc.py | 11 +++++++++++ .../references/sdks/integrations/agent-plugin.md | 14 ++++++++++++++ 4 files changed, 29 insertions(+) create mode 100644 skills/hindsight-docs/references/sdks/integrations/agent-plugin.md diff --git a/hindsight-api-slim/hindsight_api/engine/llm_wrapper.py b/hindsight-api-slim/hindsight_api/engine/llm_wrapper.py index 3a6704b4b..7f43d67cd 100644 --- a/hindsight-api-slim/hindsight_api/engine/llm_wrapper.py +++ b/hindsight-api-slim/hindsight_api/engine/llm_wrapper.py @@ -513,6 +513,7 @@ def create_llm_provider( base_url=base_url, model=model, reasoning_effort=reasoning_effort, + extra_body=extra_body, model_path=config.llamacpp_model_path, gpu_layers=config.llamacpp_gpu_layers, context_size=config.llamacpp_context_size, diff --git a/hindsight-api-slim/hindsight_api/engine/providers/llamacpp_llm.py b/hindsight-api-slim/hindsight_api/engine/providers/llamacpp_llm.py index 35541b726..d96e75f43 100644 --- a/hindsight-api-slim/hindsight_api/engine/providers/llamacpp_llm.py +++ b/hindsight-api-slim/hindsight_api/engine/providers/llamacpp_llm.py @@ -274,6 +274,7 @@ def __init__( base_url: str, model: str, reasoning_effort: str = "low", + extra_body: dict[str, Any] | None = None, model_path: str | None = None, gpu_layers: int = -1, context_size: int = 8192, @@ -295,6 +296,7 @@ def __init__( self._chat_format = chat_format self._no_grammar = no_grammar self._extra_args = extra_args + self._extra_body = extra_body self._server: LlamaCppServer | None = None self._delegate: Any = None # OpenAICompatibleLLM, created after server starts self._initialized = False @@ -337,6 +339,7 @@ async def _ensure_initialized(self) -> None: base_url=self._server.base_url, model=self.model, reasoning_effort=self.reasoning_effort, + extra_body=self._extra_body, ) self._initialized = True diff --git a/hindsight-api-slim/hindsight_api/engine/reflect/structured_doc.py b/hindsight-api-slim/hindsight_api/engine/reflect/structured_doc.py index c667afb10..d62e1d390 100644 --- a/hindsight-api-slim/hindsight_api/engine/reflect/structured_doc.py +++ b/hindsight-api-slim/hindsight_api/engine/reflect/structured_doc.py @@ -333,6 +333,17 @@ def _parse_block(chunk: list[str]) -> Block: if has_separator: return _parse_table_block(chunk) + # Lenient fallback: GFM allows omitting leading/trailing pipes, so a table + # like "Col1 | Col2\\n--- | ---\\nA | B" won't match the strict pipe-delimited + # pattern above. Detect these by requiring a separator line AND at least one + # ``|`` in every non-separator line (the column delimiter can't be omitted). + if len(chunk) >= 2: + sep_indices = [i for i, line in enumerate(chunk) if _TABLE_SEPARATOR_RX.match(line)] + if sep_indices: + non_sep = [line for i, line in enumerate(chunk) if i not in sep_indices] + if non_sep and all("|" in line for line in non_sep): + return _parse_table_block(chunk) + return ParagraphBlock(text=" ".join(line.strip() for line in chunk).strip()) diff --git a/skills/hindsight-docs/references/sdks/integrations/agent-plugin.md b/skills/hindsight-docs/references/sdks/integrations/agent-plugin.md new file mode 100644 index 000000000..87382baf0 --- /dev/null +++ b/skills/hindsight-docs/references/sdks/integrations/agent-plugin.md @@ -0,0 +1,14 @@ + +# Agent Plugins + +Portable long-term memory for any [Agent Plugins](https://agent-plugins.org) client, powered by Hindsight. + +Agent Plugins is the vendor-neutral open standard for packaging Agent Skills + MCP servers into a single distributable plugin. Hindsight ships one plugin that every compatible client can load. + +## Quick Start + +1. Get your API key from the Hindsight Cloud dashboard. +2. Set `HINDSIGHT_API_KEY` and optionally `HINDSIGHT_BANK_ID`. +3. Install the plugin in your client through its plugin/MCP UI. + +Once installed, agents can call `recall`, `retain`, and `reflect` automatically through the bundled skill.