mpfaffenberger / mpfaffenberger/code_puppy

RoundRobinModel: _set_span_attributes calls nonexistent Model.model_attributes (always fails, silently suppressed); factory can insert None sub-models causing intermittent AttributeError every Nth request

Open
#408 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Python
Stars
814
Forks
278
Avg merge
2d 5h
Merged PRs (30d)
76

Description

Problem

RoundRobinModel (code_puppy/round_robin_model.py) has three issues:

1. _set_span_attributes calls a method that doesn't exist on pydantic-ai Model

Line ~148:

span.set_attributes(model.model_attributes(model))

pydantic_ai.models.Model has no model_attributes method (verified against the installed version: 'model_attributes' in dir(Model) is False). This would raise AttributeError on every call — it's only invisible because the whole body is wrapped in with suppress(Exception). So the observability feature this method exists for silently never works: errors passing silently, and dead code in disguise.

2. model_factory can insert None into the rotation

model_factory.py round_robin branch (lines ~960-972):

for name in model_names:
    model = ModelFactory.get_model(name, config)
    models.append(model)

ModelFactory.get_model() returns None for many failure modes (missing API key, plugin failure). A None lands in the rotation and only blows up later with an opaque AttributeError: 'NoneType' object has no attribute 'prepare_request' mid-conversation — on a schedule (every Nth request), which makes for a maddening intermittent bug. Validate at construction: skip-and-warn or raise immediately if any sub-model is None.

3. system / base_url properties read _current_index without the lock

Minor race: rotation mutates _current_index under _lock, but the properties read it unlocked. Worst case is reporting the wrong sub-model's metadata; worth a comment or a lock for consistency.

Also: the bare try/except Exception: raise around current_model.request(...) (lines ~108-117) is a no-op wrapper — delete it.

Suggested fix

  • Fix or delete _set_span_attributes; if keeping it, use real pydantic-ai attributes (e.g. {\"gen_ai.request.model\": model.model_name}) and stop suppressing all exceptions.
  • In model_factory, raise ValueError(f\"Round-robin sub-model {name!r} failed to initialize\") when get_model returns None.

Filed by Zen Reviewer A (code-puppy-60635a)

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start in code_puppy/round_robin_model.py, especially _set_span_attributes, request(), and the system/base_url properties, then inspect the round_robin branch in model_factory.py. Trace how ModelFactory.get_model() failures enter the rotation and review the lock usage around _current_index. Done means failed sub-models are handled at construction, span metadata uses supported model attributes or is removed, and the no-op exception wrapper is gone.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
ai, backend, observability
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
58/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.