{"record":{"id":"3004bc1d063eb92a","repo":"JuliusBrussee/caveman","slug":"autogen-tools-and-workbench-are-mutually-exclusive","errorCode":null,"errorMessage":"AutoGen tools and workbench are mutually exclusive","messagePattern":"AutoGen tools and workbench are mutually exclusive","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"packages/middleware/python/caveman_middleware/autogen.py","lineNumber":499,"sourceCode":"        return _loaded(cls(workbench, runtime=runtime, scope=config.scope, runtime_key=config.runtime_key))\n\n\ndef with_caveman_model(model_client, *, runtime, scope, runtime_key=\"default\"):\n    \"\"\"Wrap an existing client; recovery-free unless paired with the workbench.\"\"\"\n    return CavemanChatCompletionClient(model_client, runtime=runtime, scope=scope, runtime_key=runtime_key)\n\n\ndef with_caveman_agent(options: dict, *, runtime, scope, runtime_key=\"default\") -> dict:\n    \"\"\"Return native AssistantAgent constructor options, leaving its loop intact.\n\n    Accepts the native ``tools`` list or ``workbench`` (including a workbench\n    list), retaining tool order and the original native executor for every call.\n    \"\"\"\n    model = CavemanChatCompletionClient(options[\"model_client\"], runtime=runtime, scope=scope, runtime_key=runtime_key)\n    if runtime.mode == \"off\" or not model.version_supported:\n        return {**options, \"model_client\": model}\n    if options.get(\"tools\") and options.get(\"workbench\") is not None:\n        raise ValueError(\"AutoGen tools and workbench are mutually exclusive\")\n    workbench = options.get(\"workbench\")\n    if workbench is None:\n        tools = [t if isinstance(t, BaseTool) else FunctionTool(t, description=t.__doc__ or \"\") for t in options.get(\"tools\", [])]\n        workbench = StaticStreamWorkbench(tools)\n    result = {**options, \"model_client\": model,\n              \"workbench\": CavemanWorkbench(workbench, runtime=runtime, scope=scope, runtime_key=runtime_key)}\n    result.pop(\"tools\", None)\n    return result\n","sourceCodeStart":481,"sourceCodeEnd":508,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/3ee70a102609e550bd2e68004bf5990a9341c851/packages/middleware/python/caveman_middleware/autogen.py#L481-L508","documentation":"with_caveman_agent wraps an AutoGen agent's options with the Caveman middleware. AutoGen supports passing tools either as a `tools` list or as a `workbench`, but not both simultaneously. The library throws this ValueError to prevent an ambiguous configuration where the executor could not decide which tool surface to instrument while retaining tool order and the original native executor.","triggerScenarios":"Calling with_caveman_agent(options) where options contains a non-empty `tools` list AND options['workbench'] is not None. Only raised when runtime.mode is not 'off' and the model client version is supported; in off mode the check is skipped.","commonSituations":"Migrating from tools=[...] to a workbench-based agent but leaving the old tools list in place; merging a shared base options dict that sets tools with per-agent overrides that set workbench; passing both because an example combined them.","solutions":["Remove the `tools` key from options and keep the workbench (recommended, workbench is the newer surface).","Remove or set workbench=None and keep the plain tools list.","If you only want tools without instrumentation, check runtime.mode or use the raw model client instead of with_caveman_agent."],"exampleFix":"// before\noptions = {\"model_client\": client, \"tools\": [my_tool], \"workbench\": wb}\nagent = with_caveman_agent(options)\n// after\noptions = {\"model_client\": client, \"workbench\": wb}\nagent = with_caveman_agent(options)","handlingStrategy":"validation","validationCode":"if options.get(\"tools\") and options.get(\"workbench\") is not None:\n    raise ValueError(\"Pass either tools or workbench to with_caveman_agent, not both\")","typeGuard":"def has_conflicting_tool_surface(options: dict) -> bool:\n    return bool(options.get(\"tools\")) and options.get(\"workbench\") is not None","tryCatchPattern":null,"preventionTips":["Standardize agents on one tool surface (workbench preferred) in your codebase.","Strip `tools` from shared base option dicts before adding `workbench` overrides.","Add a unit test asserting each agent's options contain only one of tools/workbench."],"tags":["autogen","configuration","tools","workbench"],"backgroundTag":"mutually-exclusive-options","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"}