{"record":{"id":"1d3c6cf1e3300f19","repo":"JuliusBrussee/caveman","slug":"caveman-delegates-through-public-chat-with-tools-methods","errorCode":null,"errorMessage":"Caveman delegates through public chat_with_tools methods","messagePattern":"Caveman delegates through public chat_with_tools methods","errorType":"exception","errorClass":"NotImplementedError","httpStatus":null,"severity":"error","filePath":"packages/middleware/python/caveman_middleware/llama_index.py","lineNumber":628,"sourceCode":"\n    async def astream_chat(self, messages, **kwargs):\n        return self._astream(self.wrapped.astream_chat, messages, None, kwargs)\n\n    def chat_with_tools(self, tools, user_msg=None, chat_history=None, **kwargs):\n        return self._call(self.wrapped.chat_with_tools, chat_history or [], tools, {\"user_msg\": user_msg, **kwargs})\n\n    async def achat_with_tools(self, tools, user_msg=None, chat_history=None, **kwargs):\n        return await self._acall(self.wrapped.achat_with_tools, chat_history or [], tools, {\"user_msg\": user_msg, **kwargs})\n\n    def stream_chat_with_tools(self, tools, user_msg=None, chat_history=None, **kwargs):\n        return self._stream(self.wrapped.stream_chat_with_tools, chat_history or [], tools, {\"user_msg\": user_msg, **kwargs})\n\n    async def astream_chat_with_tools(self, tools, user_msg=None, chat_history=None, **kwargs):\n        return self._astream(self.wrapped.astream_chat_with_tools, chat_history or [], tools, {\"user_msg\": user_msg, **kwargs})\n\n    def _prepare_chat_with_tools(self, *args, **kwargs):\n        # Abstract extension contract only; public tool methods delegate above.\n        raise NotImplementedError(\"Caveman delegates through public chat_with_tools methods\")\n\n    def get_tool_calls_from_response(self, response, **kwargs):\n        return self.wrapped.get_tool_calls_from_response(response, **kwargs)\n\n    def complete(self, prompt, formatted=False, **kwargs):\n        return self._call(self.wrapped.complete, prompt, None, {\"formatted\": formatted, **kwargs}, passthrough=\"no_candidate\")\n\n    async def acomplete(self, prompt, formatted=False, **kwargs):\n        return await self._acall(self.wrapped.acomplete, prompt, None, {\"formatted\": formatted, **kwargs}, passthrough=\"no_candidate\")\n\n    def stream_complete(self, prompt, formatted=False, **kwargs):\n        return self._passive_stream(self.wrapped.stream_complete, (prompt,), {\"formatted\": formatted, **kwargs}, \"no_candidate\")\n\n    async def astream_complete(self, prompt, formatted=False, **kwargs):\n        return await self._passive_astream(self.wrapped.astream_complete, (prompt,), {\"formatted\": formatted, **kwargs}, \"no_candidate\")\n\n    def structured_predict(self, *args, **kwargs):\n        return self._call(lambda _: self.wrapped.structured_predict(*args, **kwargs), None, None, {}, passthrough=\"structured_output\")","sourceCodeStart":610,"sourceCodeEnd":646,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/3ee70a102609e550bd2e68004bf5990a9341c851/packages/middleware/python/caveman_middleware/llama_index.py#L610-L646","documentation":"_prepare_chat_with_tools is the abstract extension hook of the LlamaIndex LLM contract; caveman's wrapper intentionally does not implement it because all public chat_with_tools/astream methods delegate to the wrapped LLM. Calling it raises NotImplementedError by design.","triggerScenarios":"Code that calls the wrapper's _prepare_chat_with_tools directly, or a LlamaIndex internal/agent path that relies on the protected hook instead of the public methods.","commonSituations":"Custom agents or subclasses invoking the private hook, framework upgrades that changed the delegation flow, or tests probing the abstract contract.","solutions":["Use the public chat_with_tools / astream_chat_with_tools / stream_chat_with_tools methods instead of the private hook","Build your agent loop on top of the public tool-calling API the wrapper delegates","If you need custom preparation logic, subclass the native LLM, not the caveman wrapper"],"exampleFix":"// before\nprepared = caveman_llm._prepare_chat_with_tools(tools, user_msg=msg)\n// after\nresponse = caveman_llm.chat_with_tools(tools, user_msg=msg)","handlingStrategy":"fallback","validationCode":"if hasattr(fn, \"__name__\") and fn.__name__ == \"_prepare_chat_with_tools\":\n    raise RuntimeError(\"use the public chat_with_tools API\")","typeGuard":"def uses_public_tool_api(obj) -> bool:\n    return callable(getattr(obj, \"chat_with_tools\", None)) or callable(getattr(obj, \"astream_chat_with_tools\", None))","tryCatchPattern":"try:\n    return llm._prepare_chat_with_tools(*args, **kwargs)\nexcept NotImplementedError:\n    return llm.chat_with_tools(*args, **kwargs)","preventionTips":["Build agent loops on the public chat_with_tools methods only","Never call underscore-prefixed hooks on the caveman wrapper","Review custom agents after LlamaIndex upgrades for private-API usage"],"tags":["python","not-implemented","llamaindex","api-misuse"],"backgroundTag":"abstract-method-not-implemented","analyzedSha":"3ee70a102609e550bd2e68004bf5990a9341c851","analyzedAt":"2026-09-20T15:53:39.229Z","contentChangedAt":"2026-09-20T15:53:39.229Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}