ModelEngine-Group / ModelEngine-Group/nexent

OpenAIModel: default observer is the class, not an instance — TypeError on any call that omits it

Open Beginner friendly
#3,921 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Python
Stars
5.9k
Forks
731
Avg merge
19h 34m
Merged PRs (30d)
172

Description

Summary

OpenAIModel.__init__ declares observer: MessageObserver = MessageObserver — the default value is the class object, not an instance. Any caller that omits observer crashes as soon as the streaming path fires, because the unbound method is called with only one argument.

Reproduction
from nexent.core.models import OpenAIModel

m = OpenAIModel(model_id="gpt-4o-mini", api_key="sk-...")   # observer omitted
m([{"role": "user", "content": [{"type": "text", "text": "Say hello in three words."}]}])
    self.observer.add_model_new_token(new_token)
TypeError: MessageObserver.add_model_new_token() missing 1 required positional argument: 'new_token'

The HTTP request to the provider succeeds first — the failure happens while handling the response, so the call is billed and then thrown away, which makes it a slightly expensive way to find out.

Passing an instance works:

from nexent.core.utils.observer import MessageObserver
m = OpenAIModel(observer=MessageObserver(), model_id="gpt-4o-mini", api_key="sk-...")
# nexent replied: the stub answered
Where

nexent/core/models/openai_llm.py:18:

def __init__(self, observer: MessageObserver = MessageObserver, temperature=0.2, ...):

The docstring right below says "observer: MessageObserver instance for tracking model output", so the intent looks clear and this reads like a missing ().

Suggested fix

Either default to None and build one lazily — the usual way to avoid a shared mutable default:

def __init__(self, observer: MessageObserver | None = None, ...):
    self.observer = observer if observer is not None else MessageObserver()

or make it required. = MessageObserver() in the signature would also work but gives every instance the same observer, which is probably not what you want.

Same shape appears in openai_vlm.py and openai_long_context_model.py if they share the signature — I did not check those.

Environment

nexent 1.8.1, Python 3.12, Windows. Found while pointing a model at a local OpenAI-compatible endpoint.

Unrelated but possibly useful

While I was here: omitting api_base leaves OpenAIServerModel to construct the client without a base_url, so the OpenAI SDK falls back to OPENAI_BASE_URL. That makes it easy to point nexent at a local endpoint for testing without touching config — I confirmed the resolved value is the local address and that the run reproduces byte-for-byte offline afterwards. Mentioning it only because "how do I aim this at my own endpoint" is a common question; happy to leave it out if it is noise here.

Contributor guide

Open the contributing guide

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 nexent/core/models/openai_llm.py at OpenAIModel.init and inspect the observer default and assignment. Check the related signatures in openai_vlm.py and openai_long_context_model.py, then reproduce a call with observer omitted. Done means the streaming path no longer raises the missing-argument TypeError and each model gets an appropriate observer instance.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
ai, backend
Issue type
Bug
Difficulty
2/5
Estimated time
1-3 hours
Activity status
Active
Clarity
Clearly specified
Newbie friendliness
86/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.