mpfaffenberger / mpfaffenberger/code_puppy
gemini_code_assist.py: model_name is a method (not property), local StreamedResponse shadows pydantic-ai's and drops tool calls; per-request clients and stale OAuth token
Nobody has claimed this yet.
- Dominant language
- Python
- Stars
- 814
- Forks
- 278
- Avg merge
- 2d 5h
- Merged PRs (30d)
- 76
Description
Problem
code_puppy/gemini_code_assist.py defines GeminiCodeAssistModel.model_name as a plain method, not a property (lines 59-61):
def model_name(self) -> str:
\"\"\"Return the model name.\"\"\"
return self._model_name
Every other Model implementation in the codebase (GeminiModel, RoundRobinModel) and pydantic-ai's own Model base class define model_name as a @property. Anywhere pydantic-ai (or code_puppy telemetry/messaging) reads model.model_name expecting a string, this class returns a bound method object — it stringifies as <bound method GeminiCodeAssistModel.model_name of ...> in logs/usage tracking and breaks comparisons like model.model_name == "gemini-...".
The local StreamedResponse class in the same file repeats the mistake for model_name(), usage() and timestamp() while pydantic-ai's real StreamedResponse exposes model_name/timestamp as properties.
Additional problems in the same file
- Name shadowing: the module defines its own
class StreamedResponsethat is notpydantic_ai.models.StreamedResponsebut the type annotationAsyncIterator[StreamedResponse]onrequest_streammakes it look like the pydantic-ai contract is satisfied. It implements neither_get_event_iteratornor the parts-manager protocol, so streaming via pydantic-ai will fail.gemini_model.pysolved the same problem correctly withGeminiStreamingResponse(StreamedResponse)— reuse that approach or rename the local class to something honest like_CodeAssistSSEReader. - Dead code:
StreamedResponse.get_response_parts()buildstool_calls = []and never populates it — function calls are silently dropped from streamed responses. - Per-request client construction:
request()andrequest_stream()create a freshhttpx.AsyncClientper call (lines 81, 108), so no connection pooling across turns; every other model in the repo holds a long-lived client. Accept an optionalhttp_clientlikeGeminiModeldoes. - Stale access token: the OAuth
access_tokenis captured once at construction inmodel_factory.get_model()and never refreshed, unlike the Claude OAuth path which refreshes proactively. Long sessions will start failing with 401s until the model is rebuilt.
Suggested fix
@property
def model_name(self) -> str:
return self._model_name
…and align the streaming implementation with gemini_model.GeminiStreamingResponse, inject a shared http_client, and resolve the token via a callable (e.g. get_valid_access_token) at request time.
Filed by Zen Reviewer A (code-puppy-60635a)
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start in code_puppy/gemini_code_assist.py, then compare its streaming implementation with GeminiStreamingResponse in gemini_model.py and inspect model_factory.get_model(). Verify the model and response interfaces match pydantic-ai, streamed tool calls are preserved, clients are reusable, and access tokens are resolved at request time.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- python
- Domain
- ai, backend
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 45/100