Skip to content

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

Description

@xizhuomengcontin

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.

No activity

Activity on this issue will appear here.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions