{"record":{"id":"fbd18e9b1fa10911","repo":"JuliusBrussee/caveman","slug":"expected-a-native-langchain-basechatmodel","errorCode":null,"errorMessage":"Expected a native LangChain BaseChatModel","messagePattern":"Expected a native LangChain BaseChatModel","errorType":"validation","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"packages/middleware/python/caveman_middleware/langchain.py","lineNumber":272,"sourceCode":"def with_caveman_agent(options: dict, *, runtime, scope) -> dict:\n    \"\"\"Return native create_agent keyword arguments; do not run another loop.\"\"\"\n    middleware = CavemanMiddleware(runtime=runtime, scope=scope)\n    tools = list(options.get(\"tools\", []))\n    collision = any((tool.get(\"name\") if plain(tool) else getattr(tool, \"name\", None)) == \"caveman_retrieve\" for tool in tools)\n    if not collision and runtime.mode == \"compress\" and middleware.recovery_tool is not None:\n        tools.append(middleware.recovery_tool)\n    return {**options, \"tools\": tools, \"middleware\": [*options.get(\"middleware\", []), middleware]}\n\n\ndef with_caveman_model(model: BaseChatModel, *, runtime, scope):\n    \"\"\"Native model clone preserving bind_tools/structured helpers and callbacks.\n\n    The pinned providers' public batch APIs call invoke/ainvoke for each item,\n    so every item resolves its own RunnableConfig scope. This variant has no\n    recovery executor; use native agent middleware for recoverable lossiness.\n    \"\"\"\n    if not isinstance(model, BaseChatModel):\n        raise TypeError(\"Expected a native LangChain BaseChatModel\")\n    if not _supported(runtime) and runtime.mode != \"off\":\n        runtime.decline(\"unsupported_version\")\n    native = model.model_copy()\n    connection = _Connection(runtime, scope)\n\n    def messages(input):\n        if isinstance(input, PromptValue):\n            return input.to_messages()\n        if isinstance(input, str):\n            return convert_to_messages([(\"human\", input)])\n        return convert_to_messages(input)\n\n    def wrap_call(method):\n        @functools.wraps(method)\n        def call(input, config=None, **kwargs):\n            view, attempt = connection.prepare(messages(input), config)\n            if attempt is None:\n                return method(input, config, **kwargs)","sourceCodeStart":254,"sourceCodeEnd":290,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/3ee70a102609e550bd2e68004bf5990a9341c851/packages/middleware/python/caveman_middleware/langchain.py#L254-L290","documentation":"with_caveman_model wraps a LangChain chat model by copying it (model_copy) and must receive an instance of langchain_core BaseChatModel. Anything else — a wrapped pipeline, a string model name, a non-native provider wrapper — raises this TypeError.","triggerScenarios":"Calling with_caveman_model(runtime, scope, model=...) with a non-BaseChatModel: a model name string, a ChatPromptTemplate, a RunnableSequence (prompt | llm), or a third-party chat wrapper not subclassing BaseChatModel.","commonSituations":"Passing 'gpt-4o' instead of ChatOpenAI('gpt-4o'); wrapping the whole chain instead of just the LLM; using a community model class that subclasses Runnable rather than BaseChatModel.","solutions":["Pass an actual BaseChatModel instance, e.g. ChatOpenAI(model='gpt-4o').","Wrap only the LLM node of the chain, not the full RunnableSequence.","For custom providers, subclass BaseChatModel or use a community class that extends it."],"exampleFix":"// before\nwrapped = with_caveman_model(runtime, scope, 'gpt-4o')\n// after\nfrom langchain_openai import ChatOpenAI\nwrapped = with_caveman_model(runtime, scope, ChatOpenAI(model='gpt-4o'))","handlingStrategy":"type-guard","validationCode":"from langchain_core.language_models import BaseChatModel\nif not isinstance(model, BaseChatModel):\n    raise TypeError('with_caveman_model expects a BaseChatModel instance')","typeGuard":"def is_chat_model(m): return isinstance(m, BaseChatModel)","tryCatchPattern":"try:\n    wrapped = with_caveman_model(runtime, scope, model)\nexcept TypeError as e:\n    if 'BaseChatModel' in str(e):\n        raise RuntimeError(f'{model!r} is not a BaseChatModel; wrap the LLM, not the chain') from e\n    raise","preventionTips":["Never pass model-name strings; instantiate a provider chat model class first.","Wrap only the LLM node, never prompt|llm sequences.","Add a unit test asserting isinstance(model, BaseChatModel) before wrapping."],"tags":["type-mismatch","langchain","model","python"],"backgroundTag":"invalid-argument-value","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"}