Skip to content

fix(sessions): accept base SessionSettings overrides on subclasses - #4820

Open
rioyu123 wants to merge 2 commits into
openai:mainfrom
rioyu123:fix/session-settings-subclass-resolve
Open

fix(sessions): accept base SessionSettings overrides on subclasses#4820
rioyu123 wants to merge 2 commits into
openai:mainfrom
rioyu123:fix/session-settings-subclass-resolve

Conversation

@rioyu123

@rioyu123 rioyu123 commented Sep 2, 2026

Copy link
Copy Markdown

Summary

SessionSettings.resolve() rejected a base SessionSettings override when the stored settings object was a subclass. This also broke the public Runner.run(..., run_config=RunConfig(session_settings=...)) path because a dict override is first normalized to the base class.

This change preserves the concrete session-settings subtype while allowing the exact base SessionSettings override specifically at the resolve() overlay boundary. It also keeps the existing construction boundary intact: a RunConfig field narrowed to a custom settings subtype still normalizes dictionaries to that subtype and rejects incompatible base or sibling instances before session history is read or the model is invoked.

Value

  • Makes typed and dictionary overrides work for custom SessionSettings subclasses.
  • Preserves subclass-only fields and the concrete result type.
  • Preserves declared-subtype validation for custom RunConfig subclasses.
  • Prevents silent cross-copying between unrelated settings subclasses.
  • Fails before model execution for invalid overrides, avoiding observable model-side effects.

Implementation

  • Recognize an exact base SessionSettings object only inside SessionSettings.resolve() before subtype coercion.
  • Keep _coerce_session_settings() delegated to coerce_dataclass_config, preserving strict declared-type normalization everywhere else.
  • Read missing subclass-only override fields as None, so the stored subclass value is retained.

Test plan

  • Added direct regressions for base-class override acceptance and sibling-subclass rejection.
  • Added Runner-level coverage for typed and dictionary overrides plus sibling rejection, including an assertion that the model is not invoked.
  • Added RunConfig subclass coverage proving dictionaries normalize to the declared settings subtype and incompatible base instances are rejected.
  • Final focused modules: 107 passed.
  • Formatting, Ruff, optional-truthiness check, mypy, pyright, and serial tests passed.
  • Full parallel suite on Windows: 8329 passed, 81 skipped, 19 failed; the 19 failures match the untouched baseline on this host. Two locale-dependent failures pass with PYTHONUTF8=1; the remaining 17 require Windows symbolic-link privileges unavailable to this session.

Issue number

Closes #4819

Checks

  • I've added new tests, if relevant
  • I've run .agents/skills/code-change-verification/scripts/run.sh (GNU make is unavailable on this Windows host; I ran the equivalent Makefile commands directly)
  • I've confirmed all verification steps pass, apart from the 19 documented pre-existing Windows environment failures above
  • If using Codex, I've run /review before submitting this PR

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 2, 2026

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review Completed 2026-09-02T18:17:39.334236Z 928ba6a New commits
🔒 Security Review Completed 2026-09-02T18:18:12.644937Z 928ba6a New commits
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@sylvesterkaczmarek sylvesterkaczmarek left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

isinstance(value, SessionSettings) looks too broad here. It fixes the base-class override, but it also lets an unrelated sibling subclass bypass settings_type validation. resolve() then copies any same-named fields via getattr, so _TenantSessionSettings.resolve(_OtherSessionSettings(...)) can silently apply settings from the wrong type instead of rejecting them. Could this accept only the exact base SessionSettings or an instance of settings_type? A sibling-subclass regression would pin the boundary.

@rioyu123

rioyu123 commented Sep 2, 2026

Copy link
Copy Markdown
Author

Thanks — confirmed. I narrowed the fast path to the exact base SessionSettings or an instance of settings_type, so unrelated sibling subclasses still raise TypeError. I also added direct and Runner-level sibling regressions, including asserting that the model is not invoked.

SessionSettings.resolve() rejected the exact base SessionSettings override when the stored settings object was a subclass. RunConfig also normalizes dictionary overrides to the base type, so both public override forms failed through Runner.run.

Accept the exact base type or an instance compatible with the requested settings type, while preserving validation for unrelated sibling subclasses. Treat missing subclass-only override fields as unset so the stored subtype and its extra fields survive.

Add direct and Runner-level regressions for typed and dictionary overrides, sibling rejection, and failure before model invocation.
@rioyu123
rioyu123 force-pushed the fix/session-settings-subclass-resolve branch from 414c7a0 to 6f69d5c Compare September 2, 2026 17:31

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 6f69d5cb49

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/agents/memory/session_settings.py Outdated
Comment on lines +80 to +81
if type(value) is SessionSettings or isinstance(value, settings_type):
return value

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve declared subtype validation outside resolve

When an application subclasses RunConfig and narrows session_settings to a custom SessionSettings subtype, RunConfig.__post_init__ passes that declared subtype to this shared helper. The new exact-base exception returns a plain SessionSettings unchanged, so the field no longer has its declared normalized type and downstream provider code can fail when accessing subtype fields; previously coerce_dataclass_config rejected this value. Restrict the base-instance exception to the SessionSettings.resolve() overlay path rather than changing constructor coercion globally.

AGENTS.md reference: AGENTS.md:L145-L148

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks — good catch. I moved the exact-base SessionSettings exception into SessionSettings.resolve() only and restored _coerce_session_settings() to strict declared-type coercion. I also added RunConfig subclass regressions proving that a compatible dict normalizes to the declared settings subtype while an incompatible base instance is rejected. The focused session/RunConfig suite now passes 107 tests.

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.

SessionSettings.resolve() raises TypeError when a subclass instance receives a base SessionSettings (or dict) override via RunConfig

2 participants