-
Notifications
You must be signed in to change notification settings - Fork 22
Description
Priority: Critical
Related: #199
Philosophy
Celeste is a primitives layer. Dict tools are passed through to the provider as-is by design — celeste does not validate tool schemas, names, or structure beyond what it needs for its own dispatch logic. The provider API is the source of truth for what constitutes a valid tool definition. This is the same principle as extra_body: celeste forwards, the provider validates.
This means {"random": "garbage"} reaching the provider and getting rejected there is correct behavior. Celeste should not gate-keep provider-specific tool formats it doesn't know about.
Actual bug
The real issue is that non-dict, non-Tool items (e.g., 42, "string") are silently dropped from the tools list. The for-loop's if/elif chain has no else clause — items that don't match any branch are skipped without error. The user thinks they passed a tool, but it vanishes.
for item in validated_value:
if isinstance(item, Tool):
...
elif isinstance(item, dict) and "name" in item:
...
elif isinstance(item, dict):
...
# ← non-dict items silently ignored hereFix
Add an else clause raising TypeError for items that are neither Tool instances nor dicts:
else:
msg = f"Expected Tool instance or dict, got {type(item).__name__}"
raise TypeError(msg)Apply to all 4 ToolsMapper.map() implementations:
src/celeste/protocols/chatcompletions/parameters.pysrc/celeste/protocols/openresponses/parameters.pysrc/celeste/providers/anthropic/messages/parameters.pysrc/celeste/providers/google/generate_content/parameters.py