JuliusBrussee/caveman · error · TypeError

Expected an AutoGen ChatCompletionClient

Error message

Expected an AutoGen ChatCompletionClient

What it means

CavemanChatCompletionClient is a wrapper/decorator around an AutoGen ChatCompletionClient, not a standalone model implementation. Its constructor type-checks the wrapped model_client and raises this TypeError if it is not an instance of autogen_ext's (or autogen_core's) ChatCompletionClient, since all calls are delegated to it.

Solutions

  1. Wrap a real AutoGen ChatCompletionClient, e.g. OpenAIChatCompletionClient(model='gpt-4o', ...), and pass that instance.
  2. If you have a serialized CavemanModelConfig/ComponentModel, load it via ChatCompletionClient.load_component(...) before wrapping.
  3. Confirm the object implements the ChatCompletionClient protocol from the same installed autogen version (0.7/0.8) the middleware supports.
  4. Never pass model name strings; resolve them into a client first.

Example fix

// before
client = CavemanChatCompletionClient('gpt-4o', runtime=rt, scope=scope)
// after
from autogen_ext.models.openai import OpenAIChatCompletionClient
inner = OpenAIChatCompletionClient(model='gpt-4o')
client = CavemanChatCompletionClient(inner, runtime=rt, scope=scope)
Defensive patterns

Strategy: type-guard

Validate before calling

from autogen_core.models import ChatCompletionClient
if not isinstance(inner, ChatCompletionClient):
    raise TypeError("model_client must be a ChatCompletionClient")

Type guard

def is_chat_completion_client(obj) -> bool:
    from autogen_core.models import ChatCompletionClient
    return isinstance(obj, ChatCompletionClient)

Try / catch

try:
    client = CavemanChatCompletionClient(inner, runtime=rt, scope=scope)
except TypeError as e:
    if "ChatCompletionClient" in str(e):
        inner = ChatCompletionClient.load_component(inner_config)
        client = CavemanChatCompletionClient(inner, runtime=rt, scope=scope)
    else:
        raise

Prevention

When it happens

Trigger: Passing a raw OpenAI/Anthropic client object, a config dict, a string model name, or None as the model_client argument to CavemanChatCompletionClient(...).

Common situations: Constructing the underlying client via a helper that returns a different type (e.g. OpenAIChatCompletion built manually vs its config), passing a model name string hoping it resolves, or passing an object from an incompatible AutoGen major version.

Related errors


AI-assisted analysis of JuliusBrussee/caveman@3ee70a1026 (2026-09-20). Data as JSON: /api/errors/1eb98837e149a560. Report an issue: GitHub.

Appendix: source

Thrown at packages/middleware/python/caveman_middleware/autogen.py:167

    scope: Scope
    runtime_key: str = "default"


class CavemanWorkbenchConfig(BaseModel):
    workbench: ComponentModel | list[ComponentModel]
    scope: Scope
    runtime_key: str = "default"


class CavemanChatCompletionClient(ChatCompletionClient, Component[CavemanModelConfig]):
    """Delegate every native call; model-only use is recovery-free."""
    component_provider_override = "caveman_middleware.autogen.CavemanChatCompletionClient"
    component_config_schema = CavemanModelConfig
    component_type = "model"

    def __init__(self, model_client: ChatCompletionClient, *, runtime, scope: Scope, runtime_key="default"):
        if not isinstance(model_client, ChatCompletionClient):
            raise TypeError("Expected an AutoGen ChatCompletionClient")
        self.model_client = model_client
        self.runtime, self.scope = _check(runtime, scope), scope
        self.runtime_key = runtime_key
        self.version_supported = _version_supported()
        if not self.version_supported and self.runtime.mode != "off":
            self.runtime.decline("unsupported_version")
        # Public configuration inspection occurs once, and only safe route
        # metadata is retained. Unknown contracts stay recovery-free.
        try:
            config = model_client.dump_component().config
            self.model_id = config.get("model")
            self.recovery_contract = not any(config.get(k) for k in ("response_format", "json_output", "output_format", "output_config", "extra_body"))
            self.recovery_contract &= config.get("tool_choice", "auto") == "auto"
        except (TypeError, ValueError, NotImplementedError, AttributeError):
            self.model_id, self.recovery_contract = None, False

    @property
    def capabilities(self):

View on GitHub (pinned to 3ee70a1026)