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
- Wrap a real AutoGen ChatCompletionClient, e.g. OpenAIChatCompletionClient(model='gpt-4o', ...), and pass that instance.
- If you have a serialized CavemanModelConfig/ComponentModel, load it via ChatCompletionClient.load_component(...) before wrapping.
- Confirm the object implements the ChatCompletionClient protocol from the same installed autogen version (0.7/0.8) the middleware supports.
- 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
- Always build the inner client with AutoGen factories (OpenAIChatCompletionClient, etc.), never raw SDK clients.
- Verify your autogen-core/autogen-ext versions match the middleware's supported range (0.7-0.8).
- Keep model definitions as ComponentModel configs and load via load_component for consistency.
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
- Expected an AutoGen Workbench or list of workbenches
- AutoGen requires a stable Caveman Scope for each agent or…
- Expected a native Strands Model
- Expected an existing native LlamaIndex LLM
- Expected an existing native Pydantic AI Model
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)