From af5c22700c5f60370ea5c48e2c11620ab98a9d4d Mon Sep 17 00:00:00 2001 From: Xingyao Wang Date: Tue, 6 Jan 2026 16:43:23 -0500 Subject: [PATCH] Bump condenser defaults: max_size 120->240 (#12267) Co-authored-by: openhands --- .../versions/086_bump_condenser_defaults.py | 61 +++++++++++++++++++ frontend/src/services/settings.ts | 2 +- .../src/utils/has-advanced-settings-set.ts | 2 +- .../app_conversation_service_base.py | 2 +- openhands/server/session/session.py | 2 +- .../test_app_conversation_service_base.py | 12 ++-- 6 files changed, 71 insertions(+), 10 deletions(-) create mode 100644 enterprise/migrations/versions/086_bump_condenser_defaults.py diff --git a/enterprise/migrations/versions/086_bump_condenser_defaults.py b/enterprise/migrations/versions/086_bump_condenser_defaults.py new file mode 100644 index 0000000000..7d8327130c --- /dev/null +++ b/enterprise/migrations/versions/086_bump_condenser_defaults.py @@ -0,0 +1,61 @@ +"""bump condenser defaults: max_size 120->240 + +Revision ID: 086 +Revises: 085 +Create Date: 2026-01-05 + +""" + +from typing import Sequence, Union + +import sqlalchemy as sa +from alembic import op +from sqlalchemy.sql import column, table + +# revision identifiers, used by Alembic. +revision: str = '086' +down_revision: Union[str, None] = '085' +branch_labels: Union[str, Sequence[str], None] = None +depends_on: Union[str, Sequence[str], None] = None + + +def upgrade() -> None: + """Upgrade schema. + + Update existing users with condenser_max_size=120 or NULL to 240. + This covers both users who had the old default (120) explicitly set + and users who had NULL (which defaulted to 120 in the application code). + The SDK default for keep_first will be used automatically. + """ + user_settings_table = table( + 'user_settings', + column('condenser_max_size', sa.Integer), + ) + # Update users with explicit 120 value + op.execute( + user_settings_table.update() + .where(user_settings_table.c.condenser_max_size == 120) + .values(condenser_max_size=240) + ) + # Update users with NULL value (which defaulted to 120 in application code) + op.execute( + user_settings_table.update() + .where(user_settings_table.c.condenser_max_size.is_(None)) + .values(condenser_max_size=240) + ) + + +def downgrade() -> None: + """Downgrade schema. + + Note: This sets all 240 values back to NULL (not 120) since we can't + distinguish between users who had 120 vs NULL before the upgrade. + """ + user_settings_table = table( + 'user_settings', column('condenser_max_size', sa.Integer) + ) + op.execute( + user_settings_table.update() + .where(user_settings_table.c.condenser_max_size == 240) + .values(condenser_max_size=None) + ) diff --git a/frontend/src/services/settings.ts b/frontend/src/services/settings.ts index e4a04b1e87..939912ea65 100644 --- a/frontend/src/services/settings.ts +++ b/frontend/src/services/settings.ts @@ -15,7 +15,7 @@ export const DEFAULT_SETTINGS: Settings = { remote_runtime_resource_factor: 1, provider_tokens_set: {}, enable_default_condenser: true, - condenser_max_size: 120, + condenser_max_size: 240, enable_sound_notifications: false, user_consents_to_analytics: false, enable_proactive_conversation_starters: false, diff --git a/frontend/src/utils/has-advanced-settings-set.ts b/frontend/src/utils/has-advanced-settings-set.ts index 8e3de2be9c..4cb51178e4 100644 --- a/frontend/src/utils/has-advanced-settings-set.ts +++ b/frontend/src/utils/has-advanced-settings-set.ts @@ -27,7 +27,7 @@ export const hasAdvancedSettingsSet = ( settings.agent !== undefined && settings.agent !== DEFAULT_SETTINGS.agent; // Default is true, so only check if explicitly disabled const hasDisabledCondenser = settings.enable_default_condenser === false; - // Check if condenser size differs from default (default is 120) + // Check if condenser size differs from default (default is 240) const hasCustomCondenserSize = settings.condenser_max_size !== undefined && settings.condenser_max_size !== null && diff --git a/openhands/app_server/app_conversation/app_conversation_service_base.py b/openhands/app_server/app_conversation/app_conversation_service_base.py index aa6add73fe..87a8a15e3d 100644 --- a/openhands/app_server/app_conversation/app_conversation_service_base.py +++ b/openhands/app_server/app_conversation/app_conversation_service_base.py @@ -380,7 +380,7 @@ class AppConversationServiceBase(AppConversationService, ABC): Returns: Configured LLMSummarizingCondenser instance """ - # LLMSummarizingCondenser has defaults: max_size=120, keep_first=4 + # LLMSummarizingCondenser SDK defaults: max_size=240, keep_first=2 condenser_kwargs = { 'llm': llm.model_copy( update={ diff --git a/openhands/server/session/session.py b/openhands/server/session/session.py index 2e6e33d32c..d3f337bd8d 100644 --- a/openhands/server/session/session.py +++ b/openhands/server/session/session.py @@ -234,7 +234,7 @@ class WebSession: # The order matters: with the browser output first, the summarizer # will only see the most recent browser output, which should keep # the summarization cost down. - max_events_for_condenser = settings.condenser_max_size or 120 + max_events_for_condenser = settings.condenser_max_size or 240 default_condenser_config = CondenserPipelineConfig( condensers=[ ConversationWindowCondenserConfig(), diff --git a/tests/unit/app_server/test_app_conversation_service_base.py b/tests/unit/app_server/test_app_conversation_service_base.py index db31d8d3d2..5cedd5997f 100644 --- a/tests/unit/app_server/test_app_conversation_service_base.py +++ b/tests/unit/app_server/test_app_conversation_service_base.py @@ -320,9 +320,9 @@ def test_create_condenser_default_agent_with_none_max_size(mock_condenser_class) # Assert mock_condenser_class.assert_called_once() call_kwargs = mock_condenser_class.call_args[1] - # When condenser_max_size is None, max_size should not be passed (uses SDK default of 120) + # When condenser_max_size is None, max_size should not be passed (uses SDK default of 240) assert 'max_size' not in call_kwargs - # keep_first is never passed (uses SDK default of 4) + # keep_first is never passed (uses SDK default of 2) assert 'keep_first' not in call_kwargs assert call_kwargs['llm'].usage_id == 'condenser' mock_llm.model_copy.assert_called_once() @@ -358,7 +358,7 @@ def test_create_condenser_default_agent_with_custom_max_size(mock_condenser_clas mock_condenser_class.assert_called_once() call_kwargs = mock_condenser_class.call_args[1] assert call_kwargs['max_size'] == 150 # Custom value should be used - # keep_first is never passed (uses SDK default of 4) + # keep_first is never passed (uses SDK default of 2) assert 'keep_first' not in call_kwargs assert call_kwargs['llm'].usage_id == 'condenser' mock_llm.model_copy.assert_called_once() @@ -393,9 +393,9 @@ def test_create_condenser_plan_agent_with_none_max_size(mock_condenser_class): # Assert mock_condenser_class.assert_called_once() call_kwargs = mock_condenser_class.call_args[1] - # When condenser_max_size is None, max_size should not be passed (uses SDK default of 120) + # When condenser_max_size is None, max_size should not be passed (uses SDK default of 240) assert 'max_size' not in call_kwargs - # keep_first is never passed (uses SDK default of 4) + # keep_first is never passed (uses SDK default of 2) assert 'keep_first' not in call_kwargs assert call_kwargs['llm'].usage_id == 'planning_condenser' mock_llm.model_copy.assert_called_once() @@ -431,7 +431,7 @@ def test_create_condenser_plan_agent_with_custom_max_size(mock_condenser_class): mock_condenser_class.assert_called_once() call_kwargs = mock_condenser_class.call_args[1] assert call_kwargs['max_size'] == 200 # Custom value should be used - # keep_first is never passed (uses SDK default of 4) + # keep_first is never passed (uses SDK default of 2) assert 'keep_first' not in call_kwargs assert call_kwargs['llm'].usage_id == 'planning_condenser' mock_llm.model_copy.assert_called_once()