Skip to content

🐛 Bugfix: default OpenAIModel observer to a fresh instance, not the class - #3956

Open
XixianWasTaken wants to merge 1 commit into
ModelEngine-Group:developfrom
XixianWasTaken:fix/openai-model-default-observer
Open

XixianWasTaken wants to merge 1 commit into
ModelEngine-Group:developfrom
XixianWasTaken:fix/openai-model-default-observer

Conversation

@XixianWasTaken

Copy link
Copy Markdown

Summary

Fixes #3921

OpenAIModel.__init__ declared observer: MessageObserver = MessageObserver — the default was the class object, not an instance. Any caller that omitted observer crashed in the streaming path with:

TypeError: MessageObserver.add_model_new_token() missing 1 required positional argument: 'new_token'

…and only after the provider request had already succeeded (and been billed).

Changes

  • sdk/nexent/core/models/openai_llm.py
    • observer now defaults to None; __init__ builds a MessageObserver() lazily when it is omitted, so each model gets its own observer rather than a signature-level instance shared across every model. Explicit observers are stored unchanged.
    • Docstring updated to describe the default.
  • test/sdk/core/models/test_openai_llm_observer_default.py (new)
    • Default is a MessageObserver instance, not the class.
    • Two models constructed without an observer get distinct instances.
    • Explicit instance is preserved; explicit None behaves like omitting it.
    • Streaming call with observer omitted succeeds and the default observer captures the streamed tokens (client.chat.completions.create mocked, no network).

The subclasses mentioned in the issue (openai_vlm.py, openai_long_context_model.py) already declare observer as a required positional parameter, so they were not affected and are unchanged.

Verification

Backend venv per AGENTS.md (Python 3.11.9, uv sync --extra data-process --extra test, uv pip install -e "../sdk[dev]"), each file run in its own pytest process as test/run_all_test.py does:

File Result
test/sdk/core/models/test_openai_llm_observer_default.py 5 passed
test/sdk/core/models/test_openai_llm.py 94 passed
test/sdk/core/models/test_openai_long_context_model.py 22 passed
test/sdk/core/models/test_openai_vlm.py 18 passed
test/sdk/core/models/test_vlm_adapter.py 39 passed
test/sdk/core/models/test_dashscope_vlm_adapter.py 4 passed
test/sdk/core/agents/test_nexent_agent_context_runtime_factory.py 5 passed
test/backend/agents/test_context_budget_v2_pipeline.py 2 passed

With the fix temporarily reverted, the new test file fails 4/5 with the exact TypeError from the issue, confirming it reproduces the bug.

Ruff was not run locally (not available offline in this environment); added lines are within the SDK's 119-character limit and both files compile.

🤖 Generated with Claude Code

…lass

`OpenAIModel.__init__` declared `observer: MessageObserver = MessageObserver`,
so any caller that omitted `observer` stored the class object and crashed in
the streaming path with `TypeError: MessageObserver.add_model_new_token()
missing 1 required positional argument: 'new_token'` after the provider
request had already been billed.

Default `observer` to `None` and build a `MessageObserver()` lazily in
`__init__`, so each model gets its own observer instead of a shared
signature-level instance. Explicit observers are stored unchanged.

Add regression tests covering the default instance, per-instance isolation,
explicit `None`, and the streaming call that previously raised.

Fixes ModelEngine-Group#3921

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

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

1 participant