diff --git a/backend/alembic/env.py b/backend/alembic/env.py index 4560e32db6..19a299094c 100644 --- a/backend/alembic/env.py +++ b/backend/alembic/env.py @@ -161,7 +161,7 @@ def get_schema_options() -> ( name.strip() for name in schema_names_str.split(",") if name.strip() ] if schemas: - logger.info(f"Specific schema names specified: {schemas}") + logger.info("Specific schema names specified: %s", schemas) # Validate that only one method is used at a time range_filtering = tenant_range_start is not None or tenant_range_end is not None @@ -282,14 +282,17 @@ async def run_async_migrations() -> None: if schemas: # Use specific schema names directly without fetching all tenants - logger.info(f"Migrating specific schema names: {schemas}") + logger.info("Migrating specific schema names: %s", schemas) i_schema = 0 num_schemas = len(schemas) for schema in schemas: i_schema += 1 logger.info( - f"Migrating schema: index={i_schema} num_schemas={num_schemas} schema={schema}" + "Migrating schema: index=%s num_schemas=%s schema=%s", + i_schema, + num_schemas, + schema, ) try: async with engine.connect() as connection: @@ -300,7 +303,7 @@ async def run_async_migrations() -> None: ) await connection.commit() except Exception as e: - logger.error(f"Error migrating schema {schema}: {e}") + logger.error("Error migrating schema %s: %s", schema, e) if not continue_on_error: logger.error("--continue=true is not set, raising exception!") raise @@ -316,10 +319,14 @@ async def run_async_migrations() -> None: if tenant_range_start is not None or tenant_range_end is not None: logger.info( - f"Filtering tenants by range: start={tenant_range_start}, end={tenant_range_end}" + "Filtering tenants by range: start=%s, end=%s", + tenant_range_start, + tenant_range_end, ) logger.info( - f"Total tenants: {len(tenant_schemas)}, Filtered tenants: {len(filtered_tenant_schemas)}" + "Total tenants: %s, Filtered tenants: %s", + len(tenant_schemas), + len(filtered_tenant_schemas), ) i_tenant = 0 @@ -327,7 +334,10 @@ async def run_async_migrations() -> None: for schema in filtered_tenant_schemas: i_tenant += 1 logger.info( - f"Migrating schema: index={i_tenant} num_tenants={num_tenants} schema={schema}" + "Migrating schema: index=%s num_tenants=%s schema=%s", + i_tenant, + num_tenants, + schema, ) try: async with engine.connect() as connection: @@ -338,7 +348,7 @@ async def run_async_migrations() -> None: ) await connection.commit() except Exception as e: - logger.error(f"Error migrating schema {schema}: {e}") + logger.error("Error migrating schema %s: %s", schema, e) if not continue_on_error: logger.error("--continue=true is not set, raising exception!") raise @@ -384,10 +394,10 @@ def run_migrations_offline() -> None: if schemas: # Use specific schema names directly without fetching all tenants - logger.info(f"Migrating specific schema names: {schemas}") + logger.info("Migrating specific schema names: %s", schemas) for schema in schemas: - logger.info(f"Migrating schema: {schema}") + logger.info("Migrating schema: %s", schema) context.configure( url=url, target_metadata=target_metadata, @@ -421,14 +431,18 @@ def run_migrations_offline() -> None: if tenant_range_start is not None or tenant_range_end is not None: logger.info( - f"Filtering tenants by range: start={tenant_range_start}, end={tenant_range_end}" + "Filtering tenants by range: start=%s, end=%s", + tenant_range_start, + tenant_range_end, ) logger.info( - f"Total tenants: {len(tenant_schemas)}, Filtered tenants: {len(filtered_tenant_schemas)}" + "Total tenants: %s, Filtered tenants: %s", + len(tenant_schemas), + len(filtered_tenant_schemas), ) for schema in filtered_tenant_schemas: - logger.info(f"Migrating schema: {schema}") + logger.info("Migrating schema: %s", schema) context.configure( url=url, target_metadata=target_metadata, diff --git a/backend/alembic/run_multitenant_migrations.py b/backend/alembic/run_multitenant_migrations.py index c738213242..d4fbbc39f9 100755 --- a/backend/alembic/run_multitenant_migrations.py +++ b/backend/alembic/run_multitenant_migrations.py @@ -32,7 +32,6 @@ from onyx.db.engine.tenant_utils import get_all_tenant_ids from onyx.db.engine.tenant_utils import get_schemas_needing_migration from shared_configs.configs import TENANT_ID_PREFIX - # --------------------------------------------------------------------------- # Data types # --------------------------------------------------------------------------- diff --git a/backend/alembic/versions/01f8e6d95a33_populate_flow_mapping_data.py b/backend/alembic/versions/01f8e6d95a33_populate_flow_mapping_data.py index a44738eb65..51879ab9a7 100644 --- a/backend/alembic/versions/01f8e6d95a33_populate_flow_mapping_data.py +++ b/backend/alembic/versions/01f8e6d95a33_populate_flow_mapping_data.py @@ -8,7 +8,6 @@ Create Date: 2026-01-31 17:37:10.485558 from alembic import op - # revision identifiers, used by Alembic. revision = "01f8e6d95a33" down_revision = "d5c86e2c6dc6" diff --git a/backend/alembic/versions/027381bce97c_add_shortcut_option_for_users.py b/backend/alembic/versions/027381bce97c_add_shortcut_option_for_users.py index 3a7cf36863..2a29067c23 100644 --- a/backend/alembic/versions/027381bce97c_add_shortcut_option_for_users.py +++ b/backend/alembic/versions/027381bce97c_add_shortcut_option_for_users.py @@ -9,7 +9,6 @@ Create Date: 2025-01-14 12:14:00.814390 from alembic import op import sqlalchemy as sa - # revision identifiers, used by Alembic. revision = "027381bce97c" down_revision = "6fc7886d665d" diff --git a/backend/alembic/versions/03bf8be6b53a_rework_kg_config.py b/backend/alembic/versions/03bf8be6b53a_rework_kg_config.py index f780eefd00..7a744ed1b2 100644 --- a/backend/alembic/versions/03bf8be6b53a_rework_kg_config.py +++ b/backend/alembic/versions/03bf8be6b53a_rework_kg_config.py @@ -16,7 +16,6 @@ from sqlalchemy import text from alembic import op import sqlalchemy as sa - # revision identifiers, used by Alembic. revision = "03bf8be6b53a" down_revision = "65bc6e0f8500" diff --git a/backend/alembic/versions/03d085c5c38d_backfill_account_type.py b/backend/alembic/versions/03d085c5c38d_backfill_account_type.py index 2ead1876a9..1b5570558f 100644 --- a/backend/alembic/versions/03d085c5c38d_backfill_account_type.py +++ b/backend/alembic/versions/03d085c5c38d_backfill_account_type.py @@ -9,7 +9,6 @@ Create Date: 2026-03-25 16:00:00.000000 from alembic import op import sqlalchemy as sa - # revision identifiers, used by Alembic. revision = "03d085c5c38d" down_revision = "977e834c1427" diff --git a/backend/alembic/versions/03d710ccf29c_add_permission_sync_attempt_tables.py b/backend/alembic/versions/03d710ccf29c_add_permission_sync_attempt_tables.py index f0c641f3eb..c9d2c273c4 100644 --- a/backend/alembic/versions/03d710ccf29c_add_permission_sync_attempt_tables.py +++ b/backend/alembic/versions/03d710ccf29c_add_permission_sync_attempt_tables.py @@ -9,7 +9,6 @@ Create Date: 2025-09-11 13:30:00.000000 from alembic import op import sqlalchemy as sa - # revision identifiers, used by Alembic. revision = "03d710ccf29c" # Generate a new unique ID down_revision = "96a5702df6aa" diff --git a/backend/alembic/versions/07b98176f1de_code_interpreter_seed.py b/backend/alembic/versions/07b98176f1de_code_interpreter_seed.py index 554c36c59e..083d757b31 100644 --- a/backend/alembic/versions/07b98176f1de_code_interpreter_seed.py +++ b/backend/alembic/versions/07b98176f1de_code_interpreter_seed.py @@ -9,7 +9,6 @@ Create Date: 2026-02-23 15:55:07.606784 from alembic import op import sqlalchemy as sa - # revision identifiers, used by Alembic. revision = "07b98176f1de" down_revision = "7cb492013621" diff --git a/backend/alembic/versions/0816326d83aa_add_federated_connector_tables.py b/backend/alembic/versions/0816326d83aa_add_federated_connector_tables.py index be1a386fc9..865424a768 100644 --- a/backend/alembic/versions/0816326d83aa_add_federated_connector_tables.py +++ b/backend/alembic/versions/0816326d83aa_add_federated_connector_tables.py @@ -10,7 +10,6 @@ from alembic import op import sqlalchemy as sa from sqlalchemy.dialects import postgresql - # revision identifiers, used by Alembic. revision = "0816326d83aa" down_revision = "12635f6655b7" diff --git a/backend/alembic/versions/08a1eda20fe1_add_earliest_indexing_to_connector.py b/backend/alembic/versions/08a1eda20fe1_add_earliest_indexing_to_connector.py index 7df288e1da..4e53b583a8 100644 --- a/backend/alembic/versions/08a1eda20fe1_add_earliest_indexing_to_connector.py +++ b/backend/alembic/versions/08a1eda20fe1_add_earliest_indexing_to_connector.py @@ -9,7 +9,6 @@ Create Date: 2024-07-23 11:12:39.462397 from alembic import op import sqlalchemy as sa - # revision identifiers, used by Alembic. revision = "08a1eda20fe1" down_revision = "8a87bd6ec550" diff --git a/backend/alembic/versions/09995b8811eb_add_theme_preference_to_user.py b/backend/alembic/versions/09995b8811eb_add_theme_preference_to_user.py index 43bda42ca4..c906be26f3 100644 --- a/backend/alembic/versions/09995b8811eb_add_theme_preference_to_user.py +++ b/backend/alembic/versions/09995b8811eb_add_theme_preference_to_user.py @@ -10,7 +10,6 @@ from alembic import op import sqlalchemy as sa from onyx.db.enums import ThemePreference - # revision identifiers, used by Alembic. revision = "09995b8811eb" down_revision = "3d1cca026fe8" diff --git a/backend/alembic/versions/0bb4558f35df_add_scim_username_to_scim_user_mapping.py b/backend/alembic/versions/0bb4558f35df_add_scim_username_to_scim_user_mapping.py index a130a6aa23..def41204b9 100644 --- a/backend/alembic/versions/0bb4558f35df_add_scim_username_to_scim_user_mapping.py +++ b/backend/alembic/versions/0bb4558f35df_add_scim_username_to_scim_user_mapping.py @@ -9,7 +9,6 @@ Create Date: 2026-02-20 10:45:30.340188 from alembic import op import sqlalchemy as sa - # revision identifiers, used by Alembic. revision = "0bb4558f35df" down_revision = "631fd2504136" diff --git a/backend/alembic/versions/0cd424f32b1d_user_file_data_preparation_and_backfill.py b/backend/alembic/versions/0cd424f32b1d_user_file_data_preparation_and_backfill.py index 55e2ef6e24..03ada7619f 100644 --- a/backend/alembic/versions/0cd424f32b1d_user_file_data_preparation_and_backfill.py +++ b/backend/alembic/versions/0cd424f32b1d_user_file_data_preparation_and_backfill.py @@ -41,7 +41,7 @@ def upgrade() -> None: ).scalar_one() if null_count > 0: - logger.info(f"Generating UUIDs for {null_count} user_file records...") + logger.info("Generating UUIDs for %s user_file records...", null_count) # Populate in batches to avoid long locks batch_size = 10000 @@ -70,9 +70,9 @@ def upgrade() -> None: if updated < batch_size: break - logger.info(f" Updated {total_updated}/{null_count} records...") + logger.info(" Updated %s/%s records...", total_updated, null_count) - logger.info(f"Generated UUIDs for {total_updated} user_file records") + logger.info("Generated UUIDs for %s user_file records", total_updated) # Verify all records have UUIDs remaining_null = bind.execute( @@ -108,7 +108,7 @@ def upgrade() -> None: ).scalar_one() if null_count > 0: - logger.info(f"Updating {null_count} persona__user_file records...") + logger.info("Updating %s persona__user_file records...", null_count) # Update in batches batch_size = 10000 @@ -140,9 +140,9 @@ def upgrade() -> None: if updated < batch_size: break - logger.info(f" Updated {total_updated}/{null_count} records...") + logger.info(" Updated %s/%s records...", total_updated, null_count) - logger.info(f"Updated {total_updated} persona__user_file records") + logger.info("Updated %s persona__user_file records", total_updated) # Verify all records are populated remaining_null = bind.execute( @@ -181,7 +181,7 @@ def upgrade() -> None: ) ) - logger.info(f"Created {result.rowcount} user_project records from chat_folder") + logger.info("Created %s user_project records from chat_folder", result.rowcount) # === Step 4: Populate chat_session.project_id === chat_session_columns = [ @@ -202,7 +202,7 @@ def upgrade() -> None: ).scalar_one() if null_count > 0: - logger.info(f"Updating {null_count} chat_session records...") + logger.info("Updating %s chat_session records...", null_count) result = bind.execute( text( @@ -216,7 +216,7 @@ def upgrade() -> None: ) ) - logger.info(f"Updated {result.rowcount} chat_session records") + logger.info("Updated %s chat_session records", result.rowcount) # Verify all records are populated remaining_null = bind.execute( @@ -230,7 +230,8 @@ def upgrade() -> None: if remaining_null > 0: logger.warning( - f"Warning: {remaining_null} chat_session records could not be mapped to projects" + "Warning: %s chat_session records could not be mapped to projects", + remaining_null, ) # === Step 5: Update plaintext FileRecord IDs/display names to UUID scheme === @@ -251,7 +252,7 @@ def upgrade() -> None: legacy_count = bind.execute(count_query).scalar_one() if legacy_count and legacy_count > 0: - logger.info(f"Found {legacy_count} legacy plaintext file records to update") + logger.info("Found %s legacy plaintext file records to update", legacy_count) # Update display_name first for readability (safe regardless of rename) bind.execute( @@ -281,7 +282,7 @@ def upgrade() -> None: ) ) logger.info( - f"Updated {result.rowcount} plaintext file_record ids to UUID scheme" + "Updated %s plaintext file_record ids to UUID scheme", result.rowcount ) # === Step 6: Ensure document_id_migrated default TRUE and backfill existing FALSE === @@ -338,7 +339,7 @@ def upgrade() -> None: ) ) - logger.info(f"Updated status for {result.rowcount} user_file records") + logger.info("Updated status for %s user_file records", result.rowcount) logger.info("Migration 2 (data preparation) completed successfully") diff --git a/backend/alembic/versions/0ebb1d516877_add_ccpair_deletion_failure_message.py b/backend/alembic/versions/0ebb1d516877_add_ccpair_deletion_failure_message.py index 030df76048..b1321b40c8 100644 --- a/backend/alembic/versions/0ebb1d516877_add_ccpair_deletion_failure_message.py +++ b/backend/alembic/versions/0ebb1d516877_add_ccpair_deletion_failure_message.py @@ -9,7 +9,6 @@ Create Date: 2024-09-10 15:03:48.233926 from alembic import op import sqlalchemy as sa - # revision identifiers, used by Alembic. revision = "0ebb1d516877" down_revision = "52a219fb5233" diff --git a/backend/alembic/versions/114a638452db_add_default_app_mode_to_user.py b/backend/alembic/versions/114a638452db_add_default_app_mode_to_user.py index 8df7fd6585..2d207ec8b0 100644 --- a/backend/alembic/versions/114a638452db_add_default_app_mode_to_user.py +++ b/backend/alembic/versions/114a638452db_add_default_app_mode_to_user.py @@ -9,7 +9,6 @@ Create Date: 2026-02-09 18:57:08.274640 from alembic import op import sqlalchemy as sa - # revision identifiers, used by Alembic. revision = "114a638452db" down_revision = "feead2911109" diff --git a/backend/alembic/versions/12635f6655b7_drive_canonical_ids.py b/backend/alembic/versions/12635f6655b7_drive_canonical_ids.py index 4653586ff9..4fcf55e63a 100644 --- a/backend/alembic/versions/12635f6655b7_drive_canonical_ids.py +++ b/backend/alembic/versions/12635f6655b7_drive_canonical_ids.py @@ -155,7 +155,7 @@ def update_document_id_in_database( # print(f"Successfully updated database tables for document {old_doc_id} -> {new_doc_id}") except Exception as e: # If the full INSERT fails, try a more basic version with only core columns - logger.warning(f"Full INSERT failed, trying basic version: {e}") + logger.warning("Full INSERT failed, trying basic version: %s", e) bind.execute( sa.text( """ @@ -273,7 +273,7 @@ def update_document_id_in_database( ) # print(f"Successfully updated chunk_stats ID field for document {old_doc_id} -> {new_doc_id}") except Exception as e: - logger.warning(f"Some KG/chunk tables may not exist or failed to update: {e}") + logger.warning("Some KG/chunk tables may not exist or failed to update: %s", e) # Step 3: Delete the old document row (this should now be safe since all FKs point to new row) bind.execute( @@ -498,7 +498,7 @@ def delete_document_from_db(current_doc_id: str, index_name: str) -> None: except Exception as e: logger.warning( - f"Some KG/chunk tables may not exist or failed to delete from: {e}" + "Some KG/chunk tables may not exist or failed to delete from: %s", e ) # Finally delete the document itself @@ -581,7 +581,7 @@ def upgrade() -> None: # Continue with other documents instead of failing the entire migration continue - logger.info(f"Migration complete. Updated {updated_count} Google Drive documents") + logger.info("Migration complete. Updated %s Google Drive documents", updated_count) def downgrade() -> None: diff --git a/backend/alembic/versions/16c37a30adf2_user_file_relationship_migration.py b/backend/alembic/versions/16c37a30adf2_user_file_relationship_migration.py index 8ce34b7395..9e30ba0b02 100644 --- a/backend/alembic/versions/16c37a30adf2_user_file_relationship_migration.py +++ b/backend/alembic/versions/16c37a30adf2_user_file_relationship_migration.py @@ -60,7 +60,7 @@ def upgrade() -> None: to_migrate = bind.execute(count_query).scalar_one() if to_migrate > 0: - logger.info(f"Creating {to_migrate} persona-file relationships...") + logger.info("Creating %s persona-file relationships...", to_migrate) # Migrate in batches to avoid memory issues batch_size = 10000 @@ -94,11 +94,11 @@ def upgrade() -> None: break logger.info( - f" Migrated {total_inserted}/{to_migrate} relationships..." + " Migrated %s/%s relationships...", total_inserted, to_migrate ) logger.info( - f"Created {total_inserted} persona__user_file relationships" + "Created %s persona__user_file relationships", total_inserted ) # === Step 2: Add foreign key for chat_session.project_id === @@ -142,7 +142,7 @@ def upgrade() -> None: to_create = bind.execute(count_query).scalar_one() if to_create > 0: - logger.info(f"Creating {to_create} project-file relationships...") + logger.info("Creating %s project-file relationships...", to_create) # Insert in batches batch_size = 10000 @@ -175,9 +175,11 @@ def upgrade() -> None: if inserted < batch_size: break - logger.info(f" Created {total_inserted}/{to_create} relationships...") + logger.info( + " Created %s/%s relationships...", total_inserted, to_create + ) - logger.info(f"Created {total_inserted} project__user_file relationships") + logger.info("Created %s project__user_file relationships", total_inserted) # === Step 4: Create index on chat_session.project_id === try: @@ -229,7 +231,7 @@ def downgrade() -> None: # Clear project__user_file relationships (but keep the table for migration 1 to handle) if "project__user_file" in inspector.get_table_names(): result = bind.execute(text("DELETE FROM project__user_file")) - logger.info(f"Cleared {result.rowcount} records from project__user_file") + logger.info("Cleared %s records from project__user_file", result.rowcount) # Remove migrated persona__user_file relationships # Only remove those that came from folder relationships @@ -255,7 +257,7 @@ def downgrade() -> None: ) ) logger.info( - f"Removed {result.rowcount} migrated persona__user_file relationships" + "Removed %s migrated persona__user_file relationships", result.rowcount ) logger.info("Downgrade completed successfully") diff --git a/backend/alembic/versions/19c0ccb01687_migrate_to_contextual_rag_model.py b/backend/alembic/versions/19c0ccb01687_migrate_to_contextual_rag_model.py index 89550e8b66..3edfa0a24f 100644 --- a/backend/alembic/versions/19c0ccb01687_migrate_to_contextual_rag_model.py +++ b/backend/alembic/versions/19c0ccb01687_migrate_to_contextual_rag_model.py @@ -9,7 +9,6 @@ Create Date: 2026-02-12 11:21:41.798037 import sqlalchemy as sa from alembic import op - # revision identifiers, used by Alembic. revision = "19c0ccb01687" down_revision = "9c54986124c6" diff --git a/backend/alembic/versions/1b8206b29c5d_add_user_delete_cascades.py b/backend/alembic/versions/1b8206b29c5d_add_user_delete_cascades.py index fca19c7aff..22fd4bdf9e 100644 --- a/backend/alembic/versions/1b8206b29c5d_add_user_delete_cascades.py +++ b/backend/alembic/versions/1b8206b29c5d_add_user_delete_cascades.py @@ -8,7 +8,6 @@ Create Date: 2024-09-18 11:48:59.418726 from alembic import op - # revision identifiers, used by Alembic. revision = "1b8206b29c5d" down_revision = "35e6853a51d5" diff --git a/backend/alembic/versions/1d78c0ca7853_remove_voice_provider_deleted_column.py b/backend/alembic/versions/1d78c0ca7853_remove_voice_provider_deleted_column.py index 5ba8d8ba81..b19ae7740a 100644 --- a/backend/alembic/versions/1d78c0ca7853_remove_voice_provider_deleted_column.py +++ b/backend/alembic/versions/1d78c0ca7853_remove_voice_provider_deleted_column.py @@ -9,7 +9,6 @@ Create Date: 2026-03-26 11:30:53.883127 from alembic import op import sqlalchemy as sa - # revision identifiers, used by Alembic. revision = "1d78c0ca7853" down_revision = "a3f8b2c1d4e5" diff --git a/backend/alembic/versions/1f2a3b4c5d6e_add_internet_search_and_content_providers.py b/backend/alembic/versions/1f2a3b4c5d6e_add_internet_search_and_content_providers.py index 89a4713d4c..a6a1033acf 100644 --- a/backend/alembic/versions/1f2a3b4c5d6e_add_internet_search_and_content_providers.py +++ b/backend/alembic/versions/1f2a3b4c5d6e_add_internet_search_and_content_providers.py @@ -10,7 +10,6 @@ from alembic import op import sqlalchemy as sa from sqlalchemy.dialects import postgresql - # revision identifiers, used by Alembic. revision = "1f2a3b4c5d6e" down_revision = "9drpiiw74ljy" diff --git a/backend/alembic/versions/2020d417ec84_single_onyx_craft_migration.py b/backend/alembic/versions/2020d417ec84_single_onyx_craft_migration.py index 4b480618eb..06aef3ffc0 100644 --- a/backend/alembic/versions/2020d417ec84_single_onyx_craft_migration.py +++ b/backend/alembic/versions/2020d417ec84_single_onyx_craft_migration.py @@ -22,7 +22,6 @@ from alembic import op import sqlalchemy as sa from sqlalchemy.dialects import postgresql - # revision identifiers, used by Alembic. revision = "2020d417ec84" down_revision = "41fa44bef321" diff --git a/backend/alembic/versions/238b84885828_add_foreign_key_to_user__external_user_.py b/backend/alembic/versions/238b84885828_add_foreign_key_to_user__external_user_.py index 1117f156e5..9dd7c10cd4 100644 --- a/backend/alembic/versions/238b84885828_add_foreign_key_to_user__external_user_.py +++ b/backend/alembic/versions/238b84885828_add_foreign_key_to_user__external_user_.py @@ -8,7 +8,6 @@ Create Date: 2025-05-19 17:15:33.424584 from alembic import op - # revision identifiers, used by Alembic. revision = "238b84885828" down_revision = "a7688ab35c45" diff --git a/backend/alembic/versions/25a5501dc766_group_permissions_phase1.py b/backend/alembic/versions/25a5501dc766_group_permissions_phase1.py index 250318882d..9fbf90eee5 100644 --- a/backend/alembic/versions/25a5501dc766_group_permissions_phase1.py +++ b/backend/alembic/versions/25a5501dc766_group_permissions_phase1.py @@ -14,7 +14,6 @@ from onyx.db.enums import AccountType from onyx.db.enums import GrantSource from onyx.db.enums import Permission - # revision identifiers, used by Alembic. revision = "25a5501dc766" down_revision = "b728689f45b1" diff --git a/backend/alembic/versions/2666d766cb9b_google_oauth2.py b/backend/alembic/versions/2666d766cb9b_google_oauth2.py index 399fa72b95..268b71aa3e 100644 --- a/backend/alembic/versions/2666d766cb9b_google_oauth2.py +++ b/backend/alembic/versions/2666d766cb9b_google_oauth2.py @@ -10,7 +10,6 @@ import fastapi_users_db_sqlalchemy import sqlalchemy as sa from alembic import op - # revision identifiers, used by Alembic. revision = "2666d766cb9b" down_revision = "6d387b3196c2" diff --git a/backend/alembic/versions/27fb147a843f_add_timestamps_to_user_table.py b/backend/alembic/versions/27fb147a843f_add_timestamps_to_user_table.py index 1f7f077759..87ef6ed89d 100644 --- a/backend/alembic/versions/27fb147a843f_add_timestamps_to_user_table.py +++ b/backend/alembic/versions/27fb147a843f_add_timestamps_to_user_table.py @@ -9,7 +9,6 @@ Create Date: 2026-03-08 17:18:40.828644 from alembic import op import sqlalchemy as sa - # revision identifiers, used by Alembic. revision = "27fb147a843f" down_revision = "b5c4d7e8f9a1" diff --git a/backend/alembic/versions/2955778aa44c_add_chunk_count_to_document.py b/backend/alembic/versions/2955778aa44c_add_chunk_count_to_document.py index 8e345f7af4..02e8a243db 100644 --- a/backend/alembic/versions/2955778aa44c_add_chunk_count_to_document.py +++ b/backend/alembic/versions/2955778aa44c_add_chunk_count_to_document.py @@ -9,7 +9,6 @@ Create Date: 2025-01-04 11:39:43.268612 from alembic import op import sqlalchemy as sa - # revision identifiers, used by Alembic. revision = "2955778aa44c" down_revision = "c0aab6edb6dd" diff --git a/backend/alembic/versions/2acdef638fc2_add_switchover_type_field.py b/backend/alembic/versions/2acdef638fc2_add_switchover_type_field.py index 3604e929e6..44e8e35829 100644 --- a/backend/alembic/versions/2acdef638fc2_add_switchover_type_field.py +++ b/backend/alembic/versions/2acdef638fc2_add_switchover_type_field.py @@ -11,7 +11,6 @@ import sqlalchemy as sa from onyx.db.enums import SwitchoverType - # revision identifiers, used by Alembic. revision = "2acdef638fc2" down_revision = "a4f23d6b71c8" diff --git a/backend/alembic/versions/2b75d0a8ffcb_user_file_schema_cleanup.py b/backend/alembic/versions/2b75d0a8ffcb_user_file_schema_cleanup.py index 7843fd5b94..26b46e5ca9 100644 --- a/backend/alembic/versions/2b75d0a8ffcb_user_file_schema_cleanup.py +++ b/backend/alembic/versions/2b75d0a8ffcb_user_file_schema_cleanup.py @@ -50,7 +50,8 @@ def upgrade() -> None: if orphaned_count > 0: logger.warning( - f"WARNING: {orphaned_count} chat_session records still have folder_id without project_id. Proceeding anyway." + "WARNING: %s chat_session records still have folder_id without project_id. Proceeding anyway.", + orphaned_count, ) # === Step 2: Drop chat_session.folder_id === @@ -80,7 +81,7 @@ def upgrade() -> None: if remaining > 0: logger.warning( - f"WARNING: Dropping persona__user_folder with {remaining} records" + "WARNING: Dropping persona__user_folder with %s records", remaining ) op.drop_table("persona__user_folder") @@ -94,7 +95,7 @@ def upgrade() -> None: remaining = bind.execute(text("SELECT COUNT(*) FROM chat_folder")).scalar_one() if remaining > 0: - logger.warning(f"WARNING: Dropping chat_folder with {remaining} records") + logger.warning("WARNING: Dropping chat_folder with %s records", remaining) op.drop_table("chat_folder") logger.info("Dropped chat_folder table") diff --git a/backend/alembic/versions/2b90f3af54b8_usage_limits.py b/backend/alembic/versions/2b90f3af54b8_usage_limits.py index afec940d9a..69fd8ca3bc 100644 --- a/backend/alembic/versions/2b90f3af54b8_usage_limits.py +++ b/backend/alembic/versions/2b90f3af54b8_usage_limits.py @@ -9,7 +9,6 @@ Create Date: 2026-01-03 16:55:30.449692 from alembic import op import sqlalchemy as sa - # revision identifiers, used by Alembic. revision = "2b90f3af54b8" down_revision = "9a0296d7421e" diff --git a/backend/alembic/versions/2c2430828bdf_add_unique_constraint_to_inputprompt_.py b/backend/alembic/versions/2c2430828bdf_add_unique_constraint_to_inputprompt_.py index 74b539edb9..7e15a6ee93 100644 --- a/backend/alembic/versions/2c2430828bdf_add_unique_constraint_to_inputprompt_.py +++ b/backend/alembic/versions/2c2430828bdf_add_unique_constraint_to_inputprompt_.py @@ -8,7 +8,6 @@ Create Date: 2026-01-20 16:01:54.314805 from alembic import op - # revision identifiers, used by Alembic. revision = "2c2430828bdf" down_revision = "fb80bdd256de" diff --git a/backend/alembic/versions/2cdeff6d8c93_set_built_in_to_default.py b/backend/alembic/versions/2cdeff6d8c93_set_built_in_to_default.py index 20dd2cc05c..3e8441bd67 100644 --- a/backend/alembic/versions/2cdeff6d8c93_set_built_in_to_default.py +++ b/backend/alembic/versions/2cdeff6d8c93_set_built_in_to_default.py @@ -8,7 +8,6 @@ Create Date: 2025-02-11 14:57:51.308775 from alembic import op - # revision identifiers, used by Alembic. revision = "2cdeff6d8c93" down_revision = "f5437cc136c5" diff --git a/backend/alembic/versions/2f80c6a2550f_add_chat_session_specific_temperature_.py b/backend/alembic/versions/2f80c6a2550f_add_chat_session_specific_temperature_.py index 464a73576f..89e1689c79 100644 --- a/backend/alembic/versions/2f80c6a2550f_add_chat_session_specific_temperature_.py +++ b/backend/alembic/versions/2f80c6a2550f_add_chat_session_specific_temperature_.py @@ -9,7 +9,6 @@ Create Date: 2025-01-31 10:30:27.289646 from alembic import op import sqlalchemy as sa - # revision identifiers, used by Alembic. revision = "2f80c6a2550f" down_revision = "33ea50e88f24" diff --git a/backend/alembic/versions/2f95e36923e6_add_indexing_coordination.py b/backend/alembic/versions/2f95e36923e6_add_indexing_coordination.py index 80bcc98eec..0627e0845e 100644 --- a/backend/alembic/versions/2f95e36923e6_add_indexing_coordination.py +++ b/backend/alembic/versions/2f95e36923e6_add_indexing_coordination.py @@ -9,7 +9,6 @@ Create Date: 2025-07-10 16:17:57.762182 from alembic import op import sqlalchemy as sa - # revision identifiers, used by Alembic. revision = "2f95e36923e6" down_revision = "0816326d83aa" diff --git a/backend/alembic/versions/31bd8c17325e_targeted_reindex_schema.py b/backend/alembic/versions/31bd8c17325e_targeted_reindex_schema.py index 2f7f232254..74b0e520bc 100644 --- a/backend/alembic/versions/31bd8c17325e_targeted_reindex_schema.py +++ b/backend/alembic/versions/31bd8c17325e_targeted_reindex_schema.py @@ -11,7 +11,6 @@ from alembic import op import sqlalchemy as sa from sqlalchemy.dialects import postgresql - # revision identifiers, used by Alembic. revision = "31bd8c17325e" down_revision = "14162713706c" diff --git a/backend/alembic/versions/33cb72ea4d80_single_tool_call_per_message.py b/backend/alembic/versions/33cb72ea4d80_single_tool_call_per_message.py index c60f249826..08ff7c14cf 100644 --- a/backend/alembic/versions/33cb72ea4d80_single_tool_call_per_message.py +++ b/backend/alembic/versions/33cb72ea4d80_single_tool_call_per_message.py @@ -9,7 +9,6 @@ Create Date: 2024-11-01 12:51:01.535003 from alembic import op import sqlalchemy as sa - # revision identifiers, used by Alembic. revision = "33cb72ea4d80" down_revision = "5b29123cd710" diff --git a/backend/alembic/versions/33ea50e88f24_foreign_key_input_prompts.py b/backend/alembic/versions/33ea50e88f24_foreign_key_input_prompts.py index 88c6df4212..6c9edb6183 100644 --- a/backend/alembic/versions/33ea50e88f24_foreign_key_input_prompts.py +++ b/backend/alembic/versions/33ea50e88f24_foreign_key_input_prompts.py @@ -8,7 +8,6 @@ Create Date: 2025-01-29 10:54:22.141765 from alembic import op - # revision identifiers, used by Alembic. revision = "33ea50e88f24" down_revision = "a6df6b88ef81" diff --git a/backend/alembic/versions/35e518e0ddf4_properly_cascade.py b/backend/alembic/versions/35e518e0ddf4_properly_cascade.py index 9b238d2adc..0769902440 100644 --- a/backend/alembic/versions/35e518e0ddf4_properly_cascade.py +++ b/backend/alembic/versions/35e518e0ddf4_properly_cascade.py @@ -8,7 +8,6 @@ Create Date: 2024-09-20 21:24:04.891018 from alembic import op - # revision identifiers, used by Alembic. revision = "35e518e0ddf4" down_revision = "91a0a4d62b14" diff --git a/backend/alembic/versions/3934b1bc7b62_update_github_connector_repo_name_to_.py b/backend/alembic/versions/3934b1bc7b62_update_github_connector_repo_name_to_.py index 498135a60c..ca7e4d6d5d 100644 --- a/backend/alembic/versions/3934b1bc7b62_update_github_connector_repo_name_to_.py +++ b/backend/alembic/versions/3934b1bc7b62_update_github_connector_repo_name_to_.py @@ -40,7 +40,7 @@ def upgrade() -> None: for connector_id, config in github_connectors: try: if not config: - logger.warning(f"Connector {connector_id} has no config, skipping") + logger.warning("Connector %s has no config, skipping", connector_id) continue # Parse the config if it's a string @@ -68,7 +68,7 @@ def upgrade() -> None: ) updated_count += 1 except Exception as e: - logger.error(f"Error updating connector {connector_id}: {str(e)}") + logger.error("Error updating connector %s: %s", connector_id, str(e)) def downgrade() -> None: @@ -89,7 +89,7 @@ def downgrade() -> None: ) ).fetchall() - logger.debug(f"Found {len(github_connectors)} GitHub connectors to rollback") + logger.debug("Found %s GitHub connectors to rollback", len(github_connectors)) # Revert each GitHub connector to use repo_name instead of repositories reverted_count = 0 @@ -123,4 +123,4 @@ def downgrade() -> None: ) reverted_count += 1 except Exception as e: - logger.error(f"Error reverting connector {connector_id}: {str(e)}") + logger.error("Error reverting connector %s: %s", connector_id, str(e)) diff --git a/backend/alembic/versions/3a7802814195_add_alternate_assistant_to_chat_message.py b/backend/alembic/versions/3a7802814195_add_alternate_assistant_to_chat_message.py index bfde0162ba..a704e95153 100644 --- a/backend/alembic/versions/3a7802814195_add_alternate_assistant_to_chat_message.py +++ b/backend/alembic/versions/3a7802814195_add_alternate_assistant_to_chat_message.py @@ -9,7 +9,6 @@ Create Date: 2024-06-05 11:18:49.966333 from alembic import op import sqlalchemy as sa - # revision identifiers, used by Alembic. revision = "3a7802814195" down_revision = "23957775e5f5" diff --git a/backend/alembic/versions/3a78dba1080a_user_file_legacy_data_cleanup.py b/backend/alembic/versions/3a78dba1080a_user_file_legacy_data_cleanup.py index d0e63c3725..dbe5f171b9 100644 --- a/backend/alembic/versions/3a78dba1080a_user_file_legacy_data_cleanup.py +++ b/backend/alembic/versions/3a78dba1080a_user_file_legacy_data_cleanup.py @@ -39,7 +39,7 @@ def batch_delete( return 0 logger.info( - f"Starting batch deletion of {total_count} records from {table_name}..." + "Starting batch deletion of %s records from %s...", total_count, table_name ) # Determine appropriate ARRAY type @@ -66,16 +66,23 @@ def batch_delete( batch_num = (i // batch_size) + 1 if batch_num % 10 == 0 or i + batch_size >= total_count: logger.info( - f" Deleted {min(i + batch_size, total_count)}/{total_count} records " - f"({total_deleted} actual) from {table_name}" + " Deleted %s/%s records (%s actual) from %s", + min(i + batch_size, total_count), + total_count, + total_deleted, + table_name, ) except Exception as e: - logger.error(f"Failed to delete batch {(i // batch_size) + 1}: {e}") + logger.error("Failed to delete batch %s: %s", i // batch_size + 1, e) failed_batches.append((i, min(i + batch_size, total_count))) if failed_batches: logger.warning( - f"Failed to delete {len(failed_batches)} batches from {table_name}. Total deleted: {total_deleted}/{total_count}" + "Failed to delete %s batches from %s. Total deleted: %s/%s", + len(failed_batches), + table_name, + total_deleted, + total_count, ) # Fail the migration to avoid silently succeeding on partial cleanup raise RuntimeError( @@ -115,7 +122,7 @@ def upgrade() -> None: doc_ids = [r[0] for r in doc_rows] if doc_ids: - logger.info(f"Found {len(doc_ids)} user-file documents to delete") + logger.info("Found %s user-file documents to delete", len(doc_ids)) # Delete dependent rows first tables_to_clean = [ @@ -130,17 +137,17 @@ def upgrade() -> None: deleted = batch_delete( bind, table_name, column_name, doc_ids, id_type="str" ) - logger.info(f"Deleted {deleted} records from {table_name}") + logger.info("Deleted %s records from %s", deleted, table_name) # Delete document_by_connector_credential_pair entries deleted = batch_delete( bind, "document_by_connector_credential_pair", "id", doc_ids, id_type="str" ) - logger.info(f"Deleted {deleted} document_by_connector_credential_pair records") + logger.info("Deleted %s document_by_connector_credential_pair records", deleted) # Delete documents themselves deleted = batch_delete(bind, "document", "id", doc_ids, id_type="str") - logger.info(f"Deleted {deleted} document records") + logger.info("Deleted %s document records", deleted) else: logger.info("No user-file documents found to delete") @@ -162,7 +169,8 @@ def upgrade() -> None: if cc_pair_ids: logger.info( - f"Found {len(cc_pair_ids)} user-file connector_credential_pairs to clean up" + "Found %s user-file connector_credential_pairs to clean up", + len(cc_pair_ids), ) # Delete related records @@ -181,7 +189,7 @@ def upgrade() -> None: deleted = batch_delete( bind, table_name, column_name, cc_pair_ids, id_type="int" ) - logger.info(f"Deleted {deleted} records from {table_name}") + logger.info("Deleted %s records from %s", deleted, table_name) # === Step 3: Identify connectors and credentials to delete === logger.info("Identifying orphaned connectors and credentials...") @@ -255,14 +263,14 @@ def upgrade() -> None: deleted = batch_delete( bind, "connector_credential_pair", "id", cc_pair_ids, id_type="int" ) - logger.info(f"Deleted {deleted} connector_credential_pair records") + logger.info("Deleted %s connector_credential_pair records", deleted) # === Step 5: Delete orphaned connectors === if userfile_only_connector_ids: deleted = batch_delete( bind, "connector", "id", userfile_only_connector_ids, id_type="int" ) - logger.info(f"Deleted {deleted} orphaned connector records") + logger.info("Deleted %s orphaned connector records", deleted) # === Step 6: Delete orphaned credentials === if userfile_only_credential_ids: @@ -274,13 +282,13 @@ def upgrade() -> None: userfile_only_credential_ids, id_type="int", ) - logger.info(f"Deleted {deleted} credential__user_group records") + logger.info("Deleted %s credential__user_group records", deleted) # Delete credentials deleted = batch_delete( bind, "credential", "id", userfile_only_credential_ids, id_type="int" ) - logger.info(f"Deleted {deleted} orphaned credential records") + logger.info("Deleted %s orphaned credential records", deleted) logger.info("Migration 5 (legacy data cleanup) completed successfully") diff --git a/backend/alembic/versions/3bd4c84fe72f_improved_index.py b/backend/alembic/versions/3bd4c84fe72f_improved_index.py index b92ad09108..ac9b71222c 100644 --- a/backend/alembic/versions/3bd4c84fe72f_improved_index.py +++ b/backend/alembic/versions/3bd4c84fe72f_improved_index.py @@ -8,7 +8,6 @@ Create Date: 2025-02-26 13:07:56.217791 from alembic import op - # revision identifiers, used by Alembic. revision = "3bd4c84fe72f" down_revision = "8f43500ee275" diff --git a/backend/alembic/versions/3c5e35aa9af0_polling_document_count.py b/backend/alembic/versions/3c5e35aa9af0_polling_document_count.py index 75a89f969d..312d586194 100644 --- a/backend/alembic/versions/3c5e35aa9af0_polling_document_count.py +++ b/backend/alembic/versions/3c5e35aa9af0_polling_document_count.py @@ -9,7 +9,6 @@ Create Date: 2023-06-14 23:45:51.760440 import sqlalchemy as sa from alembic import op - # revision identifiers, used by Alembic. revision = "3c5e35aa9af0" down_revision = "27c6ecc08586" diff --git a/backend/alembic/versions/465f78d9b7f9_larger_access_tokens_for_oauth.py b/backend/alembic/versions/465f78d9b7f9_larger_access_tokens_for_oauth.py index 21d7e52386..b315ad1e80 100644 --- a/backend/alembic/versions/465f78d9b7f9_larger_access_tokens_for_oauth.py +++ b/backend/alembic/versions/465f78d9b7f9_larger_access_tokens_for_oauth.py @@ -9,7 +9,6 @@ Create Date: 2023-07-18 17:33:40.365034 from alembic import op import sqlalchemy as sa - # revision identifiers, used by Alembic. revision = "465f78d9b7f9" down_revision = "3c5e35aa9af0" diff --git a/backend/alembic/versions/4794bc13e484_update_prompt_length.py b/backend/alembic/versions/4794bc13e484_update_prompt_length.py index c80a9adad6..ff45f531fe 100644 --- a/backend/alembic/versions/4794bc13e484_update_prompt_length.py +++ b/backend/alembic/versions/4794bc13e484_update_prompt_length.py @@ -9,7 +9,6 @@ Create Date: 2025-04-02 11:26:36.180328 from alembic import op import sqlalchemy as sa - # revision identifiers, used by Alembic. revision = "4794bc13e484" down_revision = "f7505c5b0284" diff --git a/backend/alembic/versions/47a07e1a38f1_fix_invalid_model_configurations_state.py b/backend/alembic/versions/47a07e1a38f1_fix_invalid_model_configurations_state.py index bf43bc6acd..8e70570df0 100644 --- a/backend/alembic/versions/47a07e1a38f1_fix_invalid_model_configurations_state.py +++ b/backend/alembic/versions/47a07e1a38f1_fix_invalid_model_configurations_state.py @@ -16,7 +16,6 @@ from onyx.llm.well_known_providers.llm_provider_options import ( fetch_visible_model_names_for_provider_as_set, ) - # revision identifiers, used by Alembic. revision = "47a07e1a38f1" down_revision = "7a70b7664e37" diff --git a/backend/alembic/versions/47e5bef3a1d7_add_persona_categories.py b/backend/alembic/versions/47e5bef3a1d7_add_persona_categories.py index 89027dfa02..9b8af5e4bc 100644 --- a/backend/alembic/versions/47e5bef3a1d7_add_persona_categories.py +++ b/backend/alembic/versions/47e5bef3a1d7_add_persona_categories.py @@ -9,7 +9,6 @@ Create Date: 2024-11-05 18:55:02.221064 from alembic import op import sqlalchemy as sa - # revision identifiers, used by Alembic. revision = "47e5bef3a1d7" down_revision = "dfbe9e93d3c7" diff --git a/backend/alembic/versions/495cb26ce93e_create_knowlege_graph_tables.py b/backend/alembic/versions/495cb26ce93e_create_knowlege_graph_tables.py index 1b8cc02228..6bc4017c33 100644 --- a/backend/alembic/versions/495cb26ce93e_create_knowlege_graph_tables.py +++ b/backend/alembic/versions/495cb26ce93e_create_knowlege_graph_tables.py @@ -17,7 +17,6 @@ from onyx.configs.app_configs import DB_READONLY_PASSWORD from shared_configs.configs import MULTI_TENANT from shared_configs.configs import POSTGRES_DEFAULT_SCHEMA - # revision identifiers, used by Alembic. revision = "495cb26ce93e" down_revision = "ca04500b9ee8" diff --git a/backend/alembic/versions/4d58345da04a_lowercase_user_emails.py b/backend/alembic/versions/4d58345da04a_lowercase_user_emails.py index a3d94f0fbe..6a55e913b6 100644 --- a/backend/alembic/versions/4d58345da04a_lowercase_user_emails.py +++ b/backend/alembic/versions/4d58345da04a_lowercase_user_emails.py @@ -12,7 +12,6 @@ from alembic import op from sqlalchemy.exc import IntegrityError from sqlalchemy.sql import text - # revision identifiers, used by Alembic. revision = "4d58345da04a" down_revision = "f1ca58b2f2ec" @@ -51,7 +50,10 @@ def upgrade() -> None: next_email = f"{username.lower()}_{attempt}@{domain.lower()}" # Email conflict occurred, append `_1`, `_2`, etc., to the username logger.warning( - f"Conflict while lowercasing email: old_email={email} conflicting_email={new_email} next_email={next_email}" + "Conflict while lowercasing email: old_email=%s conflicting_email=%s next_email=%s", + email, + new_email, + next_email, ) new_email = next_email attempt += 1 diff --git a/backend/alembic/versions/4f8a2b3c1d9e_add_open_url_tool.py b/backend/alembic/versions/4f8a2b3c1d9e_add_open_url_tool.py index ec91df7b1f..b07992fd66 100644 --- a/backend/alembic/versions/4f8a2b3c1d9e_add_open_url_tool.py +++ b/backend/alembic/versions/4f8a2b3c1d9e_add_open_url_tool.py @@ -9,7 +9,6 @@ Create Date: 2025-11-24 12:00:00.000000 from alembic import op import sqlalchemy as sa - # revision identifiers, used by Alembic. revision = "4f8a2b3c1d9e" down_revision = "a852cbe15577" diff --git a/backend/alembic/versions/503883791c39_add_effective_permissions.py b/backend/alembic/versions/503883791c39_add_effective_permissions.py index d6c11eadf0..5b5177de88 100644 --- a/backend/alembic/versions/503883791c39_add_effective_permissions.py +++ b/backend/alembic/versions/503883791c39_add_effective_permissions.py @@ -20,7 +20,6 @@ from alembic import op import sqlalchemy as sa from sqlalchemy.dialects import postgresql - # revision identifiers, used by Alembic. revision = "503883791c39" down_revision = "b4b7e1028dfd" diff --git a/backend/alembic/versions/505c488f6662_merge_default_assistants_into_unified.py b/backend/alembic/versions/505c488f6662_merge_default_assistants_into_unified.py index 011b608787..d124742881 100644 --- a/backend/alembic/versions/505c488f6662_merge_default_assistants_into_unified.py +++ b/backend/alembic/versions/505c488f6662_merge_default_assistants_into_unified.py @@ -14,7 +14,6 @@ from uuid import UUID from alembic import op import sqlalchemy as sa - # revision identifiers, used by Alembic. revision = "505c488f6662" down_revision = "d09fc20a3c66" diff --git a/backend/alembic/versions/54a74a0417fc_danswerbot_onyxbot.py b/backend/alembic/versions/54a74a0417fc_danswerbot_onyxbot.py index 1de2960bb4..b7ffcca94b 100644 --- a/backend/alembic/versions/54a74a0417fc_danswerbot_onyxbot.py +++ b/backend/alembic/versions/54a74a0417fc_danswerbot_onyxbot.py @@ -8,7 +8,6 @@ Create Date: 2024-12-11 18:05:05.490737 from alembic import op - # revision identifiers, used by Alembic. revision = "54a74a0417fc" down_revision = "94dc3d0236f8" diff --git a/backend/alembic/versions/55546a7967ee_assistant_rework.py b/backend/alembic/versions/55546a7967ee_assistant_rework.py index 1237c8d4d7..8bbb9a96c8 100644 --- a/backend/alembic/versions/55546a7967ee_assistant_rework.py +++ b/backend/alembic/versions/55546a7967ee_assistant_rework.py @@ -10,7 +10,6 @@ from alembic import op import sqlalchemy as sa from sqlalchemy.dialects import postgresql - # revision identifiers, used by Alembic. revision = "55546a7967ee" down_revision = "61ff3651add4" diff --git a/backend/alembic/versions/57122d037335_add_python_tool_on_default.py b/backend/alembic/versions/57122d037335_add_python_tool_on_default.py index 256a13cc52..03ad1cbc57 100644 --- a/backend/alembic/versions/57122d037335_add_python_tool_on_default.py +++ b/backend/alembic/versions/57122d037335_add_python_tool_on_default.py @@ -9,7 +9,6 @@ Create Date: 2026-02-27 10:10:40.124925 from alembic import op import sqlalchemy as sa - # revision identifiers, used by Alembic. revision = "57122d037335" down_revision = "c0c937d5c9e5" diff --git a/backend/alembic/versions/5809c0787398_add_chat_sessions.py b/backend/alembic/versions/5809c0787398_add_chat_sessions.py index 7b114159b0..84085f963a 100644 --- a/backend/alembic/versions/5809c0787398_add_chat_sessions.py +++ b/backend/alembic/versions/5809c0787398_add_chat_sessions.py @@ -10,7 +10,6 @@ import fastapi_users_db_sqlalchemy from alembic import op import sqlalchemy as sa - # revision identifiers, used by Alembic. revision = "5809c0787398" down_revision = "d929f0c1c6af" diff --git a/backend/alembic/versions/58c50ef19f08_add_stale_column_to_user__external_user_.py b/backend/alembic/versions/58c50ef19f08_add_stale_column_to_user__external_user_.py index 68204289d3..9efab9d234 100644 --- a/backend/alembic/versions/58c50ef19f08_add_stale_column_to_user__external_user_.py +++ b/backend/alembic/versions/58c50ef19f08_add_stale_column_to_user__external_user_.py @@ -9,7 +9,6 @@ Create Date: 2025-06-25 14:08:14.162380 from alembic import op import sqlalchemy as sa - # revision identifiers, used by Alembic. revision = "58c50ef19f08" down_revision = "7b9b952abdf6" diff --git a/backend/alembic/versions/5ae8240accb3_add_research_agent_database_tables_and_.py b/backend/alembic/versions/5ae8240accb3_add_research_agent_database_tables_and_.py index 9fe1388918..327408a2e9 100644 --- a/backend/alembic/versions/5ae8240accb3_add_research_agent_database_tables_and_.py +++ b/backend/alembic/versions/5ae8240accb3_add_research_agent_database_tables_and_.py @@ -10,7 +10,6 @@ from alembic import op import sqlalchemy as sa from sqlalchemy.dialects import postgresql - # revision identifiers, used by Alembic. revision = "5ae8240accb3" down_revision = "b558f51620b4" diff --git a/backend/alembic/versions/5b29123cd710_nullable_search_settings_for_historic_.py b/backend/alembic/versions/5b29123cd710_nullable_search_settings_for_historic_.py index 9c83bfa874..7c6b60e391 100644 --- a/backend/alembic/versions/5b29123cd710_nullable_search_settings_for_historic_.py +++ b/backend/alembic/versions/5b29123cd710_nullable_search_settings_for_historic_.py @@ -9,7 +9,6 @@ Create Date: 2024-10-30 19:37:59.630704 from alembic import op import sqlalchemy as sa - # revision identifiers, used by Alembic. revision = "5b29123cd710" down_revision = "949b4a92a401" diff --git a/backend/alembic/versions/5c3dca366b35_backend_driven_notification_details.py b/backend/alembic/versions/5c3dca366b35_backend_driven_notification_details.py index 8aa951b1dc..a70d2b2b94 100644 --- a/backend/alembic/versions/5c3dca366b35_backend_driven_notification_details.py +++ b/backend/alembic/versions/5c3dca366b35_backend_driven_notification_details.py @@ -9,7 +9,6 @@ Create Date: 2026-01-06 16:03:11.413724 from alembic import op import sqlalchemy as sa - # revision identifiers, used by Alembic. revision = "5c3dca366b35" down_revision = "9087b548dd69" diff --git a/backend/alembic/versions/5d12a446f5c0_add_api_version_and_deployment_name_to_.py b/backend/alembic/versions/5d12a446f5c0_add_api_version_and_deployment_name_to_.py index 826044234e..e42a387d5d 100644 --- a/backend/alembic/versions/5d12a446f5c0_add_api_version_and_deployment_name_to_.py +++ b/backend/alembic/versions/5d12a446f5c0_add_api_version_and_deployment_name_to_.py @@ -9,7 +9,6 @@ Create Date: 2024-10-08 15:56:07.975636 from alembic import op import sqlalchemy as sa - # revision identifiers, used by Alembic. revision = "5d12a446f5c0" down_revision = "e4334d5b33ba" diff --git a/backend/alembic/versions/5e1c073d48a3_add_personal_access_token_table.py b/backend/alembic/versions/5e1c073d48a3_add_personal_access_token_table.py index 41fad4aff3..487347cba5 100644 --- a/backend/alembic/versions/5e1c073d48a3_add_personal_access_token_table.py +++ b/backend/alembic/versions/5e1c073d48a3_add_personal_access_token_table.py @@ -10,7 +10,6 @@ from alembic import op import sqlalchemy as sa from sqlalchemy.dialects import postgresql - # revision identifiers, used by Alembic. revision = "5e1c073d48a3" down_revision = "09995b8811eb" diff --git a/backend/alembic/versions/5e6f7a8b9c0d_update_default_persona_prompt.py b/backend/alembic/versions/5e6f7a8b9c0d_update_default_persona_prompt.py index 03a2ce6d46..5060ad0ded 100644 --- a/backend/alembic/versions/5e6f7a8b9c0d_update_default_persona_prompt.py +++ b/backend/alembic/versions/5e6f7a8b9c0d_update_default_persona_prompt.py @@ -9,7 +9,6 @@ Create Date: 2025-11-30 12:00:00.000000 from alembic import op import sqlalchemy as sa - # revision identifiers, used by Alembic. revision = "5e6f7a8b9c0d" down_revision = "4f8a2b3c1d9e" diff --git a/backend/alembic/versions/5e84129c8be3_add_docs_indexed_column_to_index_.py b/backend/alembic/versions/5e84129c8be3_add_docs_indexed_column_to_index_.py index 38bc5cf501..980d5b7f29 100644 --- a/backend/alembic/versions/5e84129c8be3_add_docs_indexed_column_to_index_.py +++ b/backend/alembic/versions/5e84129c8be3_add_docs_indexed_column_to_index_.py @@ -9,7 +9,6 @@ Create Date: 2023-08-10 21:43:09.069523 from alembic import op import sqlalchemy as sa - # revision identifiers, used by Alembic. revision = "5e84129c8be3" down_revision = "e6a4bbc13fe4" diff --git a/backend/alembic/versions/62c3a055a141_add_file_names_to_file_connector_config.py b/backend/alembic/versions/62c3a055a141_add_file_names_to_file_connector_config.py index 486defd86e..cfac069022 100644 --- a/backend/alembic/versions/62c3a055a141_add_file_names_to_file_connector_config.py +++ b/backend/alembic/versions/62c3a055a141_add_file_names_to_file_connector_config.py @@ -12,7 +12,6 @@ import json import os import logging - # revision identifiers, used by Alembic. revision = "62c3a055a141" down_revision = "3fc5d75723b3" diff --git a/backend/alembic/versions/631fd2504136_add_approx_chunk_count_in_vespa_to_.py b/backend/alembic/versions/631fd2504136_add_approx_chunk_count_in_vespa_to_.py index 40c4eb08f0..cd6314d4c8 100644 --- a/backend/alembic/versions/631fd2504136_add_approx_chunk_count_in_vespa_to_.py +++ b/backend/alembic/versions/631fd2504136_add_approx_chunk_count_in_vespa_to_.py @@ -9,7 +9,6 @@ Create Date: 2026-02-18 21:07:52.831215 from alembic import op import sqlalchemy as sa - # revision identifiers, used by Alembic. revision = "631fd2504136" down_revision = "c7f2e1b4a9d3" diff --git a/backend/alembic/versions/64bd5677aeb6_add_image_input_support_to_model_config.py b/backend/alembic/versions/64bd5677aeb6_add_image_input_support_to_model_config.py index b373114302..f86cea46d4 100644 --- a/backend/alembic/versions/64bd5677aeb6_add_image_input_support_to_model_config.py +++ b/backend/alembic/versions/64bd5677aeb6_add_image_input_support_to_model_config.py @@ -9,7 +9,6 @@ Create Date: 2025-09-28 15:48:12.003612 from alembic import op import sqlalchemy as sa - # revision identifiers, used by Alembic. revision = "64bd5677aeb6" down_revision = "b30353be4eec" diff --git a/backend/alembic/versions/65bc6e0f8500_remove_kg_subtype_from_db.py b/backend/alembic/versions/65bc6e0f8500_remove_kg_subtype_from_db.py index 53b2f6fa3a..d7ae4d6cda 100644 --- a/backend/alembic/versions/65bc6e0f8500_remove_kg_subtype_from_db.py +++ b/backend/alembic/versions/65bc6e0f8500_remove_kg_subtype_from_db.py @@ -9,7 +9,6 @@ Create Date: 2025-06-13 10:04:27.705976 from alembic import op import sqlalchemy as sa - # revision identifiers, used by Alembic. revision = "65bc6e0f8500" down_revision = "cec7ec36c505" diff --git a/backend/alembic/versions/689433b0d8de_add_hook_and_hook_execution_log_tables.py b/backend/alembic/versions/689433b0d8de_add_hook_and_hook_execution_log_tables.py index 1f12a349f2..9e51e9d5da 100644 --- a/backend/alembic/versions/689433b0d8de_add_hook_and_hook_execution_log_tables.py +++ b/backend/alembic/versions/689433b0d8de_add_hook_and_hook_execution_log_tables.py @@ -10,7 +10,6 @@ from alembic import op import sqlalchemy as sa from sqlalchemy.dialects.postgresql import UUID as PGUUID - # revision identifiers, used by Alembic. revision = "689433b0d8de" down_revision = "93a2e195e25c" diff --git a/backend/alembic/versions/699221885109_nullify_default_task_prompt.py b/backend/alembic/versions/699221885109_nullify_default_task_prompt.py index 9d277ed5dd..5eeeb7a053 100644 --- a/backend/alembic/versions/699221885109_nullify_default_task_prompt.py +++ b/backend/alembic/versions/699221885109_nullify_default_task_prompt.py @@ -9,7 +9,6 @@ Create Date: 2025-12-30 10:00:00.000000 from alembic import op import sqlalchemy as sa - # revision identifiers, used by Alembic. revision = "699221885109" down_revision = "7e490836d179" diff --git a/backend/alembic/versions/6b3b4083c5aa_persona_cleanup_and_featured.py b/backend/alembic/versions/6b3b4083c5aa_persona_cleanup_and_featured.py index b05ff686cf..e4c4695bf2 100644 --- a/backend/alembic/versions/6b3b4083c5aa_persona_cleanup_and_featured.py +++ b/backend/alembic/versions/6b3b4083c5aa_persona_cleanup_and_featured.py @@ -9,7 +9,6 @@ Create Date: 2026-02-26 12:00:00.000000 from alembic import op import sqlalchemy as sa - # revision identifiers, used by Alembic. revision = "6b3b4083c5aa" down_revision = "57122d037335" diff --git a/backend/alembic/versions/6f4f86aef280_add_queries_and_is_web_fetch_to_.py b/backend/alembic/versions/6f4f86aef280_add_queries_and_is_web_fetch_to_.py index bb57335919..c910ff3af5 100644 --- a/backend/alembic/versions/6f4f86aef280_add_queries_and_is_web_fetch_to_.py +++ b/backend/alembic/versions/6f4f86aef280_add_queries_and_is_web_fetch_to_.py @@ -10,7 +10,6 @@ from alembic import op import sqlalchemy as sa from sqlalchemy.dialects import postgresql - # revision identifiers, used by Alembic. revision = "6f4f86aef280" down_revision = "03d710ccf29c" diff --git a/backend/alembic/versions/6fc7886d665d_make_categories_labels_and_many_to_many.py b/backend/alembic/versions/6fc7886d665d_make_categories_labels_and_many_to_many.py index a2e02f6571..6557175974 100644 --- a/backend/alembic/versions/6fc7886d665d_make_categories_labels_and_many_to_many.py +++ b/backend/alembic/versions/6fc7886d665d_make_categories_labels_and_many_to_many.py @@ -9,7 +9,6 @@ Create Date: 2025-01-13 18:12:18.029112 from alembic import op import sqlalchemy as sa - # revision identifiers, used by Alembic. revision = "6fc7886d665d" down_revision = "3c6531f32351" diff --git a/backend/alembic/versions/7206234e012a_add_image_generation_config_table.py b/backend/alembic/versions/7206234e012a_add_image_generation_config_table.py index c90efde221..8437a5c18a 100644 --- a/backend/alembic/versions/7206234e012a_add_image_generation_config_table.py +++ b/backend/alembic/versions/7206234e012a_add_image_generation_config_table.py @@ -9,7 +9,6 @@ Create Date: 2025-12-21 00:00:00.000000 from alembic import op import sqlalchemy as sa - # revision identifiers, used by Alembic. revision = "7206234e012a" down_revision = "699221885109" diff --git a/backend/alembic/versions/72aa7de2e5cf_make_processing_mode_default_all_caps.py b/backend/alembic/versions/72aa7de2e5cf_make_processing_mode_default_all_caps.py index 3a3a6413be..85083de6e4 100644 --- a/backend/alembic/versions/72aa7de2e5cf_make_processing_mode_default_all_caps.py +++ b/backend/alembic/versions/72aa7de2e5cf_make_processing_mode_default_all_caps.py @@ -13,7 +13,6 @@ This migration fixes the ProcessingMode enum value mismatch: from alembic import op - # revision identifiers, used by Alembic. revision = "72aa7de2e5cf" down_revision = "2020d417ec84" diff --git a/backend/alembic/versions/7616121f6e97_add_enterprise_fields_to_scim_user_mapping.py b/backend/alembic/versions/7616121f6e97_add_enterprise_fields_to_scim_user_mapping.py index 1b74392fc6..b484fdc7ae 100644 --- a/backend/alembic/versions/7616121f6e97_add_enterprise_fields_to_scim_user_mapping.py +++ b/backend/alembic/versions/7616121f6e97_add_enterprise_fields_to_scim_user_mapping.py @@ -9,7 +9,6 @@ Create Date: 2026-02-23 12:00:00.000000 from alembic import op import sqlalchemy as sa - # revision identifiers, used by Alembic. revision = "7616121f6e97" down_revision = "07b98176f1de" diff --git a/backend/alembic/versions/767f1c2a00eb_count_chat_tokens.py b/backend/alembic/versions/767f1c2a00eb_count_chat_tokens.py index 03867b5335..e347097f88 100644 --- a/backend/alembic/versions/767f1c2a00eb_count_chat_tokens.py +++ b/backend/alembic/versions/767f1c2a00eb_count_chat_tokens.py @@ -9,7 +9,6 @@ Create Date: 2023-09-21 10:03:21.509899 from alembic import op import sqlalchemy as sa - # revision identifiers, used by Alembic. revision = "767f1c2a00eb" down_revision = "dba7f71618f5" diff --git a/backend/alembic/versions/77d07dffae64_forcibly_remove_more_enum_types_from_.py b/backend/alembic/versions/77d07dffae64_forcibly_remove_more_enum_types_from_.py index 76f2a7cea7..3575914afb 100644 --- a/backend/alembic/versions/77d07dffae64_forcibly_remove_more_enum_types_from_.py +++ b/backend/alembic/versions/77d07dffae64_forcibly_remove_more_enum_types_from_.py @@ -9,7 +9,6 @@ Create Date: 2023-11-01 12:33:01.999617 from alembic import op from sqlalchemy import String - # revision identifiers, used by Alembic. revision = "77d07dffae64" down_revision = "d61e513bef0a" diff --git a/backend/alembic/versions/795b20b85b4b_add_llm_group_permissions_control.py b/backend/alembic/versions/795b20b85b4b_add_llm_group_permissions_control.py index 6965818142..df4fbdeba5 100644 --- a/backend/alembic/versions/795b20b85b4b_add_llm_group_permissions_control.py +++ b/backend/alembic/versions/795b20b85b4b_add_llm_group_permissions_control.py @@ -9,7 +9,6 @@ Create Date: 2024-07-19 11:54:35.701558 from alembic import op import sqlalchemy as sa - revision = "795b20b85b4b" down_revision = "05c07bf07c00" branch_labels: None = None diff --git a/backend/alembic/versions/797089dfb4d2_persona_start_date.py b/backend/alembic/versions/797089dfb4d2_persona_start_date.py index d19c663b27..d635446c32 100644 --- a/backend/alembic/versions/797089dfb4d2_persona_start_date.py +++ b/backend/alembic/versions/797089dfb4d2_persona_start_date.py @@ -9,7 +9,6 @@ Create Date: 2024-09-11 14:51:49.785835 from alembic import op import sqlalchemy as sa - # revision identifiers, used by Alembic. revision = "797089dfb4d2" down_revision = "55546a7967ee" diff --git a/backend/alembic/versions/7b9b952abdf6_update_entities.py b/backend/alembic/versions/7b9b952abdf6_update_entities.py index 4d49180116..dc04b4cae0 100644 --- a/backend/alembic/versions/7b9b952abdf6_update_entities.py +++ b/backend/alembic/versions/7b9b952abdf6_update_entities.py @@ -11,7 +11,6 @@ import json from alembic import op import sqlalchemy as sa - # revision identifiers, used by Alembic. revision = "7b9b952abdf6" down_revision = "36e9220ab794" diff --git a/backend/alembic/versions/7cb492013621_code_interpreter_server_model.py b/backend/alembic/versions/7cb492013621_code_interpreter_server_model.py index 2fa5025ea3..ae44408c07 100644 --- a/backend/alembic/versions/7cb492013621_code_interpreter_server_model.py +++ b/backend/alembic/versions/7cb492013621_code_interpreter_server_model.py @@ -9,7 +9,6 @@ Create Date: 2026-02-22 18:54:54.007265 from alembic import op import sqlalchemy as sa - # revision identifiers, used by Alembic. revision = "7cb492013621" down_revision = "0bb4558f35df" diff --git a/backend/alembic/versions/7e490836d179_nullify_default_system_prompt.py b/backend/alembic/versions/7e490836d179_nullify_default_system_prompt.py index 33ae1d8080..096fb63345 100644 --- a/backend/alembic/versions/7e490836d179_nullify_default_system_prompt.py +++ b/backend/alembic/versions/7e490836d179_nullify_default_system_prompt.py @@ -9,7 +9,6 @@ Create Date: 2025-12-29 16:54:36.635574 from alembic import op import sqlalchemy as sa - # revision identifiers, used by Alembic. revision = "7e490836d179" down_revision = "c1d2e3f4a5b6" diff --git a/backend/alembic/versions/7f99be1cb9f5_add_index_for_getting_documents_just_by_.py b/backend/alembic/versions/7f99be1cb9f5_add_index_for_getting_documents_just_by_.py index cec472e1ee..0d78f24438 100644 --- a/backend/alembic/versions/7f99be1cb9f5_add_index_for_getting_documents_just_by_.py +++ b/backend/alembic/versions/7f99be1cb9f5_add_index_for_getting_documents_just_by_.py @@ -8,7 +8,6 @@ Create Date: 2023-10-15 22:48:15.487762 from alembic import op - # revision identifiers, used by Alembic. revision = "7f99be1cb9f5" down_revision = "78dbe7e38469" diff --git a/backend/alembic/versions/8188861f4e92_csv_to_tabular_chat_file_type.py b/backend/alembic/versions/8188861f4e92_csv_to_tabular_chat_file_type.py index dc39e9a342..4163185b3f 100644 --- a/backend/alembic/versions/8188861f4e92_csv_to_tabular_chat_file_type.py +++ b/backend/alembic/versions/8188861f4e92_csv_to_tabular_chat_file_type.py @@ -8,7 +8,6 @@ Create Date: 2026-03-31 19:23:05.753184 from alembic import op - # revision identifiers, used by Alembic. revision = "8188861f4e92" down_revision = "d8cdfee5df80" diff --git a/backend/alembic/versions/81c22b1e2e78_hierarchy_nodes_v1.py b/backend/alembic/versions/81c22b1e2e78_hierarchy_nodes_v1.py index f97a2e47f0..f02f91c345 100644 --- a/backend/alembic/versions/81c22b1e2e78_hierarchy_nodes_v1.py +++ b/backend/alembic/versions/81c22b1e2e78_hierarchy_nodes_v1.py @@ -12,7 +12,6 @@ from sqlalchemy.dialects import postgresql from onyx.configs.constants import DocumentSource - # revision identifiers, used by Alembic. revision = "81c22b1e2e78" down_revision = "72aa7de2e5cf" diff --git a/backend/alembic/versions/8405ca81cc83_notifications_constraint.py b/backend/alembic/versions/8405ca81cc83_notifications_constraint.py index 2502c0c3cb..b8a2d0669d 100644 --- a/backend/alembic/versions/8405ca81cc83_notifications_constraint.py +++ b/backend/alembic/versions/8405ca81cc83_notifications_constraint.py @@ -8,7 +8,6 @@ Create Date: 2026-01-07 16:43:44.855156 from alembic import op - # revision identifiers, used by Alembic. revision = "8405ca81cc83" down_revision = "a3c1a7904cd0" diff --git a/backend/alembic/versions/856bcbe14d79_add_missing_fk_indexes_for_hot_tables.py b/backend/alembic/versions/856bcbe14d79_add_missing_fk_indexes_for_hot_tables.py index dd3a300503..e748990de4 100644 --- a/backend/alembic/versions/856bcbe14d79_add_missing_fk_indexes_for_hot_tables.py +++ b/backend/alembic/versions/856bcbe14d79_add_missing_fk_indexes_for_hot_tables.py @@ -30,7 +30,6 @@ Create Date: 2026-04-19 18:00:00.000000 from alembic import op - # revision identifiers, used by Alembic. revision = "856bcbe14d79" down_revision = "91d150c361f6" diff --git a/backend/alembic/versions/87c52ec39f84_update_default_system_prompt.py b/backend/alembic/versions/87c52ec39f84_update_default_system_prompt.py index 57e31b8c6f..f9e690219e 100644 --- a/backend/alembic/versions/87c52ec39f84_update_default_system_prompt.py +++ b/backend/alembic/versions/87c52ec39f84_update_default_system_prompt.py @@ -9,7 +9,6 @@ Create Date: 2025-12-05 15:54:06.002452 from alembic import op import sqlalchemy as sa - # revision identifiers, used by Alembic. revision = "87c52ec39f84" down_revision = "7bd55f264e1b" diff --git a/backend/alembic/versions/8e1ac4f39a9f_enable_contextual_retrieval.py b/backend/alembic/versions/8e1ac4f39a9f_enable_contextual_retrieval.py index 2eed73a0a2..41e74ef277 100644 --- a/backend/alembic/versions/8e1ac4f39a9f_enable_contextual_retrieval.py +++ b/backend/alembic/versions/8e1ac4f39a9f_enable_contextual_retrieval.py @@ -9,7 +9,6 @@ Create Date: 2024-12-20 13:29:09.918661 from alembic import op import sqlalchemy as sa - # revision identifiers, used by Alembic. revision = "8e1ac4f39a9f" down_revision = "9aadf32dfeb4" diff --git a/backend/alembic/versions/8e26726b7683_chat_context_addition.py b/backend/alembic/versions/8e26726b7683_chat_context_addition.py index 23c8d4a8bf..3021eed450 100644 --- a/backend/alembic/versions/8e26726b7683_chat_context_addition.py +++ b/backend/alembic/versions/8e26726b7683_chat_context_addition.py @@ -9,7 +9,6 @@ Create Date: 2023-09-13 18:34:31.327944 from alembic import op import sqlalchemy as sa - # revision identifiers, used by Alembic. revision = "8e26726b7683" down_revision = "5809c0787398" diff --git a/backend/alembic/versions/8f43500ee275_add_index.py b/backend/alembic/versions/8f43500ee275_add_index.py index c99a229883..927bc38e74 100644 --- a/backend/alembic/versions/8f43500ee275_add_index.py +++ b/backend/alembic/versions/8f43500ee275_add_index.py @@ -8,7 +8,6 @@ Create Date: 2025-02-24 17:35:33.072714 from alembic import op - # revision identifiers, used by Alembic. revision = "8f43500ee275" down_revision = "da42808081e3" diff --git a/backend/alembic/versions/8ffcc2bcfc11_add_needs_persona_sync_to_user_file.py b/backend/alembic/versions/8ffcc2bcfc11_add_needs_persona_sync_to_user_file.py index 864dfa560d..197b5701a2 100644 --- a/backend/alembic/versions/8ffcc2bcfc11_add_needs_persona_sync_to_user_file.py +++ b/backend/alembic/versions/8ffcc2bcfc11_add_needs_persona_sync_to_user_file.py @@ -9,7 +9,6 @@ Create Date: 2026-02-23 10:48:48.343826 from alembic import op import sqlalchemy as sa - # revision identifiers, used by Alembic. revision = "8ffcc2bcfc11" down_revision = "7616121f6e97" diff --git a/backend/alembic/versions/9087b548dd69_seed_default_image_gen_config.py b/backend/alembic/versions/9087b548dd69_seed_default_image_gen_config.py index fe3c72f245..11d70261c4 100644 --- a/backend/alembic/versions/9087b548dd69_seed_default_image_gen_config.py +++ b/backend/alembic/versions/9087b548dd69_seed_default_image_gen_config.py @@ -9,7 +9,6 @@ Create Date: 2026-01-05 00:00:00.000000 from alembic import op import sqlalchemy as sa - # revision identifiers, used by Alembic. revision = "9087b548dd69" down_revision = "2b90f3af54b8" diff --git a/backend/alembic/versions/90b409d06e50_add_chat_compression_fields.py b/backend/alembic/versions/90b409d06e50_add_chat_compression_fields.py index 56b9709e17..3eb854b01a 100644 --- a/backend/alembic/versions/90b409d06e50_add_chat_compression_fields.py +++ b/backend/alembic/versions/90b409d06e50_add_chat_compression_fields.py @@ -9,7 +9,6 @@ Create Date: 2026-01-26 09:13:09.635427 from alembic import op import sqlalchemy as sa - # revision identifiers, used by Alembic. revision = "90b409d06e50" down_revision = "f220515df7b4" diff --git a/backend/alembic/versions/90e3b9af7da4_tag_fix.py b/backend/alembic/versions/90e3b9af7da4_tag_fix.py index 02a03c877f..4fcc2e3ea2 100644 --- a/backend/alembic/versions/90e3b9af7da4_tag_fix.py +++ b/backend/alembic/versions/90e3b9af7da4_tag_fix.py @@ -165,7 +165,7 @@ def remove_old_tags() -> None: ) ) n_deleted += result.rowcount - logger.info(f"Processed {len(batch)} documents and deleted {n_deleted} tags") + logger.info("Processed %s documents and deleted %s tags", len(batch), n_deleted) def active_search_settings() -> tuple[SearchSettings, SearchSettings | None]: diff --git a/backend/alembic/versions/93a2e195e25c_add_voice_provider_and_user_voice_prefs.py b/backend/alembic/versions/93a2e195e25c_add_voice_provider_and_user_voice_prefs.py index 8480387a43..95fdf66f83 100644 --- a/backend/alembic/versions/93a2e195e25c_add_voice_provider_and_user_voice_prefs.py +++ b/backend/alembic/versions/93a2e195e25c_add_voice_provider_and_user_voice_prefs.py @@ -12,7 +12,6 @@ from sqlalchemy import column from sqlalchemy import true from sqlalchemy.dialects import postgresql - # revision identifiers, used by Alembic. revision = "93a2e195e25c" down_revision = "27fb147a843f" diff --git a/backend/alembic/versions/93c15d6a6fbb_add_chunk_error_and_vespa_count_columns_.py b/backend/alembic/versions/93c15d6a6fbb_add_chunk_error_and_vespa_count_columns_.py index 4667ad3835..a3fde16f29 100644 --- a/backend/alembic/versions/93c15d6a6fbb_add_chunk_error_and_vespa_count_columns_.py +++ b/backend/alembic/versions/93c15d6a6fbb_add_chunk_error_and_vespa_count_columns_.py @@ -9,7 +9,6 @@ Create Date: 2026-02-11 23:07:34.576725 from alembic import op import sqlalchemy as sa - # revision identifiers, used by Alembic. revision = "93c15d6a6fbb" down_revision = "d3fd499c829c" diff --git a/backend/alembic/versions/949b4a92a401_remove_rt.py b/backend/alembic/versions/949b4a92a401_remove_rt.py index 5ef6d5a22e..8ddd09d4a7 100644 --- a/backend/alembic/versions/949b4a92a401_remove_rt.py +++ b/backend/alembic/versions/949b4a92a401_remove_rt.py @@ -18,7 +18,6 @@ from onyx.db.models import ( IndexAttempt, ) - # revision identifiers, used by Alembic. revision = "949b4a92a401" down_revision = "1b10e1fda030" diff --git a/backend/alembic/versions/94dc3d0236f8_make_document_set_description_optional.py b/backend/alembic/versions/94dc3d0236f8_make_document_set_description_optional.py index 2db6589ba9..af738a6ffc 100644 --- a/backend/alembic/versions/94dc3d0236f8_make_document_set_description_optional.py +++ b/backend/alembic/versions/94dc3d0236f8_make_document_set_description_optional.py @@ -9,7 +9,6 @@ Create Date: 2024-12-11 11:26:10.616722 from alembic import op import sqlalchemy as sa - # revision identifiers, used by Alembic. revision = "94dc3d0236f8" down_revision = "bf7a81109301" diff --git a/backend/alembic/versions/96a5702df6aa_mcp_tool_enabled.py b/backend/alembic/versions/96a5702df6aa_mcp_tool_enabled.py index 43508814bf..af7579bf11 100644 --- a/backend/alembic/versions/96a5702df6aa_mcp_tool_enabled.py +++ b/backend/alembic/versions/96a5702df6aa_mcp_tool_enabled.py @@ -9,7 +9,6 @@ Create Date: 2025-10-09 12:10:21.733097 from alembic import op import sqlalchemy as sa - # revision identifiers, used by Alembic. revision = "96a5702df6aa" down_revision = "40926a4dab77" diff --git a/backend/alembic/versions/977e834c1427_seed_default_groups.py b/backend/alembic/versions/977e834c1427_seed_default_groups.py index 75fce18483..4b002c102b 100644 --- a/backend/alembic/versions/977e834c1427_seed_default_groups.py +++ b/backend/alembic/versions/977e834c1427_seed_default_groups.py @@ -12,7 +12,6 @@ from alembic import op import sqlalchemy as sa from sqlalchemy.dialects.postgresql import insert as pg_insert - # revision identifiers, used by Alembic. revision = "977e834c1427" down_revision = "8188861f4e92" diff --git a/backend/alembic/versions/9a0296d7421e_add_is_auto_mode_to_llm_provider.py b/backend/alembic/versions/9a0296d7421e_add_is_auto_mode_to_llm_provider.py index e05b5d53b9..fc2bd0ded6 100644 --- a/backend/alembic/versions/9a0296d7421e_add_is_auto_mode_to_llm_provider.py +++ b/backend/alembic/versions/9a0296d7421e_add_is_auto_mode_to_llm_provider.py @@ -9,7 +9,6 @@ Create Date: 2025-12-17 18:14:29.620981 from alembic import op import sqlalchemy as sa - # revision identifiers, used by Alembic. revision = "9a0296d7421e" down_revision = "7206234e012a" diff --git a/backend/alembic/versions/9aadf32dfeb4_add_user_files.py b/backend/alembic/versions/9aadf32dfeb4_add_user_files.py index aa781de39e..1c2b8d2a2f 100644 --- a/backend/alembic/versions/9aadf32dfeb4_add_user_files.py +++ b/backend/alembic/versions/9aadf32dfeb4_add_user_files.py @@ -10,7 +10,6 @@ import sqlalchemy as sa import datetime from alembic import op - # revision identifiers, used by Alembic. revision = "9aadf32dfeb4" down_revision = "3781a5eb12cb" diff --git a/backend/alembic/versions/9b66d3156fc6_user_file_schema_additions.py b/backend/alembic/versions/9b66d3156fc6_user_file_schema_additions.py index 531c6402d5..c28a576659 100644 --- a/backend/alembic/versions/9b66d3156fc6_user_file_schema_additions.py +++ b/backend/alembic/versions/9b66d3156fc6_user_file_schema_additions.py @@ -255,7 +255,7 @@ def downgrade() -> None: for col in columns_to_drop: if col in columns: op.drop_column("user_file", col) - logger.info(f"Dropped {col} from user_file") + logger.info("Dropped %s from user_file", col) if "new_id" in columns: op.drop_constraint("uq_user_file_new_id", "user_file", type_="unique") diff --git a/backend/alembic/versions/9c00a2bccb83_chat_message_agentic.py b/backend/alembic/versions/9c00a2bccb83_chat_message_agentic.py index cec3b99931..a5e735ac44 100644 --- a/backend/alembic/versions/9c00a2bccb83_chat_message_agentic.py +++ b/backend/alembic/versions/9c00a2bccb83_chat_message_agentic.py @@ -9,7 +9,6 @@ Create Date: 2025-02-17 11:15:43.081150 from alembic import op import sqlalchemy as sa - # revision identifiers, used by Alembic. revision = "9c00a2bccb83" down_revision = "b7a7eee5aa15" diff --git a/backend/alembic/versions/a01bf2971c5d_update_default_tool_descriptions.py b/backend/alembic/versions/a01bf2971c5d_update_default_tool_descriptions.py index 8dae3da513..57b5eac4f3 100644 --- a/backend/alembic/versions/a01bf2971c5d_update_default_tool_descriptions.py +++ b/backend/alembic/versions/a01bf2971c5d_update_default_tool_descriptions.py @@ -9,7 +9,6 @@ Create Date: 2025-12-16 15:21:25.656375 from alembic import op import sqlalchemy as sa - # revision identifiers, used by Alembic. revision = "a01bf2971c5d" down_revision = "18b5b2524446" diff --git a/backend/alembic/versions/a3b8d9e2f1c4_make_scim_external_id_nullable.py b/backend/alembic/versions/a3b8d9e2f1c4_make_scim_external_id_nullable.py index 1d9cf7c3d7..777b631103 100644 --- a/backend/alembic/versions/a3b8d9e2f1c4_make_scim_external_id_nullable.py +++ b/backend/alembic/versions/a3b8d9e2f1c4_make_scim_external_id_nullable.py @@ -8,7 +8,6 @@ Create Date: 2026-03-02 from alembic import op - # revision identifiers, used by Alembic. revision = "a3b8d9e2f1c4" down_revision = "2664261bfaab" diff --git a/backend/alembic/versions/a4f23d6b71c8_add_llm_provider_persona_restrictions.py b/backend/alembic/versions/a4f23d6b71c8_add_llm_provider_persona_restrictions.py index ee8df4c3c1..abd8e4e738 100644 --- a/backend/alembic/versions/a4f23d6b71c8_add_llm_provider_persona_restrictions.py +++ b/backend/alembic/versions/a4f23d6b71c8_add_llm_provider_persona_restrictions.py @@ -9,7 +9,6 @@ Create Date: 2025-10-21 00:00:00.000000 from alembic import op import sqlalchemy as sa - # revision identifiers, used by Alembic. revision = "a4f23d6b71c8" down_revision = "5e1c073d48a3" diff --git a/backend/alembic/versions/a6fcd3d631f9_replace_document_sync_index_with_partial.py b/backend/alembic/versions/a6fcd3d631f9_replace_document_sync_index_with_partial.py index 0080f32b35..0ac54de1d5 100644 --- a/backend/alembic/versions/a6fcd3d631f9_replace_document_sync_index_with_partial.py +++ b/backend/alembic/versions/a6fcd3d631f9_replace_document_sync_index_with_partial.py @@ -21,7 +21,6 @@ Create Date: 2026-04-17 16:00:00.000000 from alembic import op import sqlalchemy as sa - # revision identifiers, used by Alembic. revision = "a6fcd3d631f9" down_revision = "d129f37b3d87" diff --git a/backend/alembic/versions/a8c2065484e6_add_auto_scroll_to_user_model.py b/backend/alembic/versions/a8c2065484e6_add_auto_scroll_to_user_model.py index b72ee45107..de5fd3b426 100644 --- a/backend/alembic/versions/a8c2065484e6_add_auto_scroll_to_user_model.py +++ b/backend/alembic/versions/a8c2065484e6_add_auto_scroll_to_user_model.py @@ -9,7 +9,6 @@ Create Date: 2024-11-22 17:34:09.690295 from alembic import op import sqlalchemy as sa - # revision identifiers, used by Alembic. revision = "a8c2065484e6" down_revision = "abe7378b8217" diff --git a/backend/alembic/versions/b30353be4eec_add_mcp_auth_performer.py b/backend/alembic/versions/b30353be4eec_add_mcp_auth_performer.py index e17b0f87dd..f881632300 100644 --- a/backend/alembic/versions/b30353be4eec_add_mcp_auth_performer.py +++ b/backend/alembic/versions/b30353be4eec_add_mcp_auth_performer.py @@ -10,7 +10,6 @@ from alembic import op import sqlalchemy as sa from onyx.db.enums import MCPAuthenticationPerformer, MCPTransport - # revision identifiers, used by Alembic. revision = "b30353be4eec" down_revision = "2b75d0a8ffcb" diff --git a/backend/alembic/versions/b388730a2899_nullable_preferences.py b/backend/alembic/versions/b388730a2899_nullable_preferences.py index c796e9d82e..b44806a93a 100644 --- a/backend/alembic/versions/b388730a2899_nullable_preferences.py +++ b/backend/alembic/versions/b388730a2899_nullable_preferences.py @@ -8,7 +8,6 @@ Create Date: 2025-02-17 18:49:22.643902 from alembic import op - # revision identifiers, used by Alembic. revision = "b388730a2899" down_revision = "1a03d2c2856b" diff --git a/backend/alembic/versions/b4b7e1028dfd_grant_basic_to_existing_groups.py b/backend/alembic/versions/b4b7e1028dfd_grant_basic_to_existing_groups.py index 9065beeaef..fd6a25593e 100644 --- a/backend/alembic/versions/b4b7e1028dfd_grant_basic_to_existing_groups.py +++ b/backend/alembic/versions/b4b7e1028dfd_grant_basic_to_existing_groups.py @@ -15,7 +15,6 @@ from collections.abc import Sequence from alembic import op import sqlalchemy as sa - # revision identifiers, used by Alembic. revision = "b4b7e1028dfd" down_revision = "b7bcc991d722" diff --git a/backend/alembic/versions/b4ef3ae0bf6e_add_user_oauth_token_to_slack_bot.py b/backend/alembic/versions/b4ef3ae0bf6e_add_user_oauth_token_to_slack_bot.py index cbc40996af..d58877035c 100644 --- a/backend/alembic/versions/b4ef3ae0bf6e_add_user_oauth_token_to_slack_bot.py +++ b/backend/alembic/versions/b4ef3ae0bf6e_add_user_oauth_token_to_slack_bot.py @@ -9,7 +9,6 @@ Create Date: 2025-08-26 17:47:41.788462 from alembic import op import sqlalchemy as sa - # revision identifiers, used by Alembic. revision = "b4ef3ae0bf6e" down_revision = "505c488f6662" diff --git a/backend/alembic/versions/b51c6844d1df_seed_memory_tool.py b/backend/alembic/versions/b51c6844d1df_seed_memory_tool.py index d387061fc4..c35d0b8fc6 100644 --- a/backend/alembic/versions/b51c6844d1df_seed_memory_tool.py +++ b/backend/alembic/versions/b51c6844d1df_seed_memory_tool.py @@ -9,7 +9,6 @@ Create Date: 2026-02-11 00:00:00.000000 from alembic import op import sqlalchemy as sa - # revision identifiers, used by Alembic. revision = "b51c6844d1df" down_revision = "93c15d6a6fbb" diff --git a/backend/alembic/versions/b558f51620b4_pause_finished_user_file_connectors.py b/backend/alembic/versions/b558f51620b4_pause_finished_user_file_connectors.py index 48bfb9e9b9..8615176549 100644 --- a/backend/alembic/versions/b558f51620b4_pause_finished_user_file_connectors.py +++ b/backend/alembic/versions/b558f51620b4_pause_finished_user_file_connectors.py @@ -8,7 +8,6 @@ Create Date: 2025-08-15 17:17:02.456704 from alembic import op - # revision identifiers, used by Alembic. revision = "b558f51620b4" down_revision = "90e3b9af7da4" diff --git a/backend/alembic/versions/b728689f45b1_rename_persona_is_visible_to_is_listed_.py b/backend/alembic/versions/b728689f45b1_rename_persona_is_visible_to_is_listed_.py index 70751ca4ff..a4784325f4 100644 --- a/backend/alembic/versions/b728689f45b1_rename_persona_is_visible_to_is_listed_.py +++ b/backend/alembic/versions/b728689f45b1_rename_persona_is_visible_to_is_listed_.py @@ -8,7 +8,6 @@ Create Date: 2026-03-23 12:36:26.607305 from alembic import op - # revision identifiers, used by Alembic. revision = "b728689f45b1" down_revision = "689433b0d8de" diff --git a/backend/alembic/versions/b72ed7a5db0e_remove_description_from_starter_messages.py b/backend/alembic/versions/b72ed7a5db0e_remove_description_from_starter_messages.py index cda2785bb8..96c597a96f 100644 --- a/backend/alembic/versions/b72ed7a5db0e_remove_description_from_starter_messages.py +++ b/backend/alembic/versions/b72ed7a5db0e_remove_description_from_starter_messages.py @@ -9,7 +9,6 @@ Create Date: 2024-11-03 15:55:28.944408 from alembic import op import sqlalchemy as sa - # revision identifiers, used by Alembic. revision = "b72ed7a5db0e" down_revision = "33cb72ea4d80" diff --git a/backend/alembic/versions/b7bcc991d722_assign_users_to_default_groups.py b/backend/alembic/versions/b7bcc991d722_assign_users_to_default_groups.py index a767f6bcd6..1979a1b991 100644 --- a/backend/alembic/versions/b7bcc991d722_assign_users_to_default_groups.py +++ b/backend/alembic/versions/b7bcc991d722_assign_users_to_default_groups.py @@ -10,7 +10,6 @@ from alembic import op import sqlalchemy as sa from sqlalchemy.dialects.postgresql import insert as pg_insert - # revision identifiers, used by Alembic. revision = "b7bcc991d722" down_revision = "03d085c5c38d" diff --git a/backend/alembic/versions/b7c2b63c4a03_add_background_reindex_enabled_field.py b/backend/alembic/versions/b7c2b63c4a03_add_background_reindex_enabled_field.py index ec3ac29380..57209cde16 100644 --- a/backend/alembic/versions/b7c2b63c4a03_add_background_reindex_enabled_field.py +++ b/backend/alembic/versions/b7c2b63c4a03_add_background_reindex_enabled_field.py @@ -11,7 +11,6 @@ import sqlalchemy as sa from onyx.db.enums import EmbeddingPrecision - # revision identifiers, used by Alembic. revision = "b7c2b63c4a03" down_revision = "f11b408e39d3" diff --git a/backend/alembic/versions/b7ec9b5b505f_adjust_prompt_length.py b/backend/alembic/versions/b7ec9b5b505f_adjust_prompt_length.py index 37e6313ee8..983b5e557a 100644 --- a/backend/alembic/versions/b7ec9b5b505f_adjust_prompt_length.py +++ b/backend/alembic/versions/b7ec9b5b505f_adjust_prompt_length.py @@ -9,7 +9,6 @@ Create Date: 2025-09-10 18:51:15.629197 from alembic import op import sqlalchemy as sa - # revision identifiers, used by Alembic. revision = "b7ec9b5b505f" down_revision = "abbfec3a5ac5" diff --git a/backend/alembic/versions/b896bbd0d5a7_backfill_is_internet_data_to_false.py b/backend/alembic/versions/b896bbd0d5a7_backfill_is_internet_data_to_false.py index abb21fe169..9115d28428 100644 --- a/backend/alembic/versions/b896bbd0d5a7_backfill_is_internet_data_to_false.py +++ b/backend/alembic/versions/b896bbd0d5a7_backfill_is_internet_data_to_false.py @@ -8,7 +8,6 @@ Create Date: 2024-07-16 15:21:05.718571 from alembic import op - # revision identifiers, used by Alembic. revision = "b896bbd0d5a7" down_revision = "44f856ae2a4a" diff --git a/backend/alembic/versions/ba98eba0f66a_add_support_for_litellm_proxy_in_.py b/backend/alembic/versions/ba98eba0f66a_add_support_for_litellm_proxy_in_.py index 719e606dae..82f7852c0f 100644 --- a/backend/alembic/versions/ba98eba0f66a_add_support_for_litellm_proxy_in_.py +++ b/backend/alembic/versions/ba98eba0f66a_add_support_for_litellm_proxy_in_.py @@ -9,7 +9,6 @@ Create Date: 2024-09-06 10:36:04.507332 from alembic import op import sqlalchemy as sa - # revision identifiers, used by Alembic. revision = "ba98eba0f66a" down_revision = "bceb1e139447" diff --git a/backend/alembic/versions/bceb1e139447_add_base_url_to_cloudembeddingprovider.py b/backend/alembic/versions/bceb1e139447_add_base_url_to_cloudembeddingprovider.py index 50bec01890..ecb73191c6 100644 --- a/backend/alembic/versions/bceb1e139447_add_base_url_to_cloudembeddingprovider.py +++ b/backend/alembic/versions/bceb1e139447_add_base_url_to_cloudembeddingprovider.py @@ -9,7 +9,6 @@ Create Date: 2024-08-28 17:00:52.554580 from alembic import op import sqlalchemy as sa - # revision identifiers, used by Alembic. revision = "bceb1e139447" down_revision = "a3795dce87be" diff --git a/backend/alembic/versions/bd2921608c3a_non_nullable_default_persona.py b/backend/alembic/versions/bd2921608c3a_non_nullable_default_persona.py index 15f653351f..51fcb3fad2 100644 --- a/backend/alembic/versions/bd2921608c3a_non_nullable_default_persona.py +++ b/backend/alembic/versions/bd2921608c3a_non_nullable_default_persona.py @@ -9,7 +9,6 @@ Create Date: 2024-09-20 10:28:37.992042 from alembic import op import sqlalchemy as sa - # revision identifiers, used by Alembic. revision = "bd2921608c3a" down_revision = "797089dfb4d2" diff --git a/backend/alembic/versions/bd7c3bf8beba_migrate_agent_responses_to_research_.py b/backend/alembic/versions/bd7c3bf8beba_migrate_agent_responses_to_research_.py index e7933952ff..764fe3de55 100644 --- a/backend/alembic/versions/bd7c3bf8beba_migrate_agent_responses_to_research_.py +++ b/backend/alembic/versions/bd7c3bf8beba_migrate_agent_responses_to_research_.py @@ -9,7 +9,6 @@ Create Date: 2025-08-18 11:33:27.098287 from alembic import op import sqlalchemy as sa - # revision identifiers, used by Alembic. revision = "bd7c3bf8beba" down_revision = "f8a9b2c3d4e5" diff --git a/backend/alembic/versions/be87a654d5af_persona_new_default_model_configuration_.py b/backend/alembic/versions/be87a654d5af_persona_new_default_model_configuration_.py index d476420806..099935ef55 100644 --- a/backend/alembic/versions/be87a654d5af_persona_new_default_model_configuration_.py +++ b/backend/alembic/versions/be87a654d5af_persona_new_default_model_configuration_.py @@ -9,7 +9,6 @@ Create Date: 2026-01-30 11:14:17.306275 from alembic import op import sqlalchemy as sa - # revision identifiers, used by Alembic. revision = "be87a654d5af" down_revision = "e7f8a9b0c1d2" diff --git a/backend/alembic/versions/bf7a81109301_delete_input_prompts.py b/backend/alembic/versions/bf7a81109301_delete_input_prompts.py index 6bc1330d93..76560c1afe 100644 --- a/backend/alembic/versions/bf7a81109301_delete_input_prompts.py +++ b/backend/alembic/versions/bf7a81109301_delete_input_prompts.py @@ -10,7 +10,6 @@ from alembic import op import sqlalchemy as sa import fastapi_users_db_sqlalchemy - # revision identifiers, used by Alembic. revision = "bf7a81109301" down_revision = "f7a894b06d02" diff --git a/backend/alembic/versions/c0aab6edb6dd_delete_workspace.py b/backend/alembic/versions/c0aab6edb6dd_delete_workspace.py index 0ead6dd907..e647f857ab 100644 --- a/backend/alembic/versions/c0aab6edb6dd_delete_workspace.py +++ b/backend/alembic/versions/c0aab6edb6dd_delete_workspace.py @@ -8,7 +8,6 @@ Create Date: 2024-12-17 14:37:07.660631 from alembic import op - # revision identifiers, used by Alembic. revision = "c0aab6edb6dd" down_revision = "35e518e0ddf4" diff --git a/backend/alembic/versions/c0c937d5c9e5_llm_provider_deprecate_fields.py b/backend/alembic/versions/c0c937d5c9e5_llm_provider_deprecate_fields.py index 7b82e942a5..86e8fd8976 100644 --- a/backend/alembic/versions/c0c937d5c9e5_llm_provider_deprecate_fields.py +++ b/backend/alembic/versions/c0c937d5c9e5_llm_provider_deprecate_fields.py @@ -9,7 +9,6 @@ Create Date: 2026-02-25 17:35:46.125102 from alembic import op import sqlalchemy as sa - # revision identifiers, used by Alembic. revision = "c0c937d5c9e5" down_revision = "8ffcc2bcfc11" diff --git a/backend/alembic/versions/c1d2e3f4a5b6_add_deep_research_tool.py b/backend/alembic/versions/c1d2e3f4a5b6_add_deep_research_tool.py index 35205d39ea..aeda606ba2 100644 --- a/backend/alembic/versions/c1d2e3f4a5b6_add_deep_research_tool.py +++ b/backend/alembic/versions/c1d2e3f4a5b6_add_deep_research_tool.py @@ -9,7 +9,6 @@ Create Date: 2025-12-18 16:00:00.000000 from alembic import op import sqlalchemy as sa - # revision identifiers, used by Alembic. revision = "c1d2e3f4a5b6" down_revision = "b8c9d0e1f2a3" diff --git a/backend/alembic/versions/c7e9f4a3b2d1_add_python_tool.py b/backend/alembic/versions/c7e9f4a3b2d1_add_python_tool.py index 7f4b1c3341..663a94e00d 100644 --- a/backend/alembic/versions/c7e9f4a3b2d1_add_python_tool.py +++ b/backend/alembic/versions/c7e9f4a3b2d1_add_python_tool.py @@ -10,7 +10,6 @@ from alembic import op import sqlalchemy as sa from sqlalchemy.dialects import postgresql - # revision identifiers, used by Alembic. revision = "c7e9f4a3b2d1" down_revision = "3c9a65f1207f" diff --git a/backend/alembic/versions/c8a93a2af083_personalization_user_info.py b/backend/alembic/versions/c8a93a2af083_personalization_user_info.py index 28d38b3d75..2b32fdad3c 100644 --- a/backend/alembic/versions/c8a93a2af083_personalization_user_info.py +++ b/backend/alembic/versions/c8a93a2af083_personalization_user_info.py @@ -10,7 +10,6 @@ from alembic import op import sqlalchemy as sa from sqlalchemy.dialects import postgresql - # revision identifiers, used by Alembic. revision = "c8a93a2af083" down_revision = "6f4f86aef280" diff --git a/backend/alembic/versions/ca04500b9ee8_add_cascade_deletes_to_agent_tables.py b/backend/alembic/versions/ca04500b9ee8_add_cascade_deletes_to_agent_tables.py index 2846f0f51d..e19b59e326 100644 --- a/backend/alembic/versions/ca04500b9ee8_add_cascade_deletes_to_agent_tables.py +++ b/backend/alembic/versions/ca04500b9ee8_add_cascade_deletes_to_agent_tables.py @@ -8,7 +8,6 @@ Create Date: 2025-05-30 16:03:51.112263 from alembic import op - # revision identifiers, used by Alembic. revision = "ca04500b9ee8" down_revision = "238b84885828" diff --git a/backend/alembic/versions/cbc03e08d0f3_add_opensearch_migration_tables.py b/backend/alembic/versions/cbc03e08d0f3_add_opensearch_migration_tables.py index 810cb1ddcb..d47b6f3ee5 100644 --- a/backend/alembic/versions/cbc03e08d0f3_add_opensearch_migration_tables.py +++ b/backend/alembic/versions/cbc03e08d0f3_add_opensearch_migration_tables.py @@ -9,7 +9,6 @@ Create Date: 2026-01-31 17:00:45.176604 from alembic import op import sqlalchemy as sa - # revision identifiers, used by Alembic. revision = "cbc03e08d0f3" down_revision = "be87a654d5af" diff --git a/backend/alembic/versions/cec7ec36c505_kgentity_parent.py b/backend/alembic/versions/cec7ec36c505_kgentity_parent.py index 895abf87ba..0497147983 100644 --- a/backend/alembic/versions/cec7ec36c505_kgentity_parent.py +++ b/backend/alembic/versions/cec7ec36c505_kgentity_parent.py @@ -9,7 +9,6 @@ Create Date: 2025-06-07 20:07:46.400770 from alembic import op import sqlalchemy as sa - # revision identifiers, used by Alembic. revision = "cec7ec36c505" down_revision = "495cb26ce93e" diff --git a/backend/alembic/versions/cf90764725d8_larger_refresh_tokens.py b/backend/alembic/versions/cf90764725d8_larger_refresh_tokens.py index 05a5683d74..7c32612efa 100644 --- a/backend/alembic/versions/cf90764725d8_larger_refresh_tokens.py +++ b/backend/alembic/versions/cf90764725d8_larger_refresh_tokens.py @@ -9,7 +9,6 @@ Create Date: 2025-04-04 10:56:39.769294 from alembic import op import sqlalchemy as sa - # revision identifiers, used by Alembic. revision = "cf90764725d8" down_revision = "4794bc13e484" diff --git a/backend/alembic/versions/d09fc20a3c66_seed_builtin_tools.py b/backend/alembic/versions/d09fc20a3c66_seed_builtin_tools.py index 2b0d1cf29f..6c10cd821d 100644 --- a/backend/alembic/versions/d09fc20a3c66_seed_builtin_tools.py +++ b/backend/alembic/versions/d09fc20a3c66_seed_builtin_tools.py @@ -9,7 +9,6 @@ Create Date: 2025-09-09 19:32:16.824373 from alembic import op import sqlalchemy as sa - # revision identifiers, used by Alembic. revision = "d09fc20a3c66" down_revision = "b7ec9b5b505f" diff --git a/backend/alembic/versions/d129f37b3d87_add_error_tracking_fields_to_index_.py b/backend/alembic/versions/d129f37b3d87_add_error_tracking_fields_to_index_.py index 6aa3a5c08b..0ecb433423 100644 --- a/backend/alembic/versions/d129f37b3d87_add_error_tracking_fields_to_index_.py +++ b/backend/alembic/versions/d129f37b3d87_add_error_tracking_fields_to_index_.py @@ -9,7 +9,6 @@ Create Date: 2026-04-06 19:11:18.261800 from alembic import op import sqlalchemy as sa - # revision identifiers, used by Alembic. revision = "d129f37b3d87" down_revision = "503883791c39" diff --git a/backend/alembic/versions/d1b637d7050a_sync_exa_api_key_to_content_provider.py b/backend/alembic/versions/d1b637d7050a_sync_exa_api_key_to_content_provider.py index fe2f95933a..4d106e7023 100644 --- a/backend/alembic/versions/d1b637d7050a_sync_exa_api_key_to_content_provider.py +++ b/backend/alembic/versions/d1b637d7050a_sync_exa_api_key_to_content_provider.py @@ -9,7 +9,6 @@ Create Date: 2026-01-09 15:54:15.646249 from alembic import op from sqlalchemy import text - # revision identifiers, used by Alembic. revision = "d1b637d7050a" down_revision = "d25168c2beee" diff --git a/backend/alembic/versions/d25168c2beee_tool_name_consistency.py b/backend/alembic/versions/d25168c2beee_tool_name_consistency.py index c33d4d6e5a..216562a18a 100644 --- a/backend/alembic/versions/d25168c2beee_tool_name_consistency.py +++ b/backend/alembic/versions/d25168c2beee_tool_name_consistency.py @@ -9,7 +9,6 @@ Create Date: 2026-01-11 17:54:40.135777 from alembic import op import sqlalchemy as sa - # revision identifiers, used by Alembic. revision = "d25168c2beee" down_revision = "8405ca81cc83" diff --git a/backend/alembic/versions/d3fd499c829c_add_file_reader_tool.py b/backend/alembic/versions/d3fd499c829c_add_file_reader_tool.py index 218a3d2fd4..df83f5ef59 100644 --- a/backend/alembic/versions/d3fd499c829c_add_file_reader_tool.py +++ b/backend/alembic/versions/d3fd499c829c_add_file_reader_tool.py @@ -9,7 +9,6 @@ Create Date: 2026-02-07 19:28:22.452337 from alembic import op import sqlalchemy as sa - # revision identifiers, used by Alembic. revision = "d3fd499c829c" down_revision = "114a638452db" diff --git a/backend/alembic/versions/d56ffa94ca32_add_file_content.py b/backend/alembic/versions/d56ffa94ca32_add_file_content.py index f788cdb700..e9ef84eab5 100644 --- a/backend/alembic/versions/d56ffa94ca32_add_file_content.py +++ b/backend/alembic/versions/d56ffa94ca32_add_file_content.py @@ -9,7 +9,6 @@ Create Date: 2026-02-06 15:29:34.192960 from alembic import op import sqlalchemy as sa - # revision identifiers, used by Alembic. revision = "d56ffa94ca32" down_revision = "01f8e6d95a33" diff --git a/backend/alembic/versions/d5c86e2c6dc6_add_cascade_delete_to_search_query_user_.py b/backend/alembic/versions/d5c86e2c6dc6_add_cascade_delete_to_search_query_user_.py index 09898bae6a..0964d93da8 100644 --- a/backend/alembic/versions/d5c86e2c6dc6_add_cascade_delete_to_search_query_user_.py +++ b/backend/alembic/versions/d5c86e2c6dc6_add_cascade_delete_to_search_query_user_.py @@ -8,7 +8,6 @@ Create Date: 2026-02-04 16:05:04.749804 from alembic import op - # revision identifiers, used by Alembic. revision = "d5c86e2c6dc6" down_revision = "90b409d06e50" diff --git a/backend/alembic/versions/d8cdfee5df80_add_skipped_to_userfilestatus.py b/backend/alembic/versions/d8cdfee5df80_add_skipped_to_userfilestatus.py index ab547288d2..1bb075b070 100644 --- a/backend/alembic/versions/d8cdfee5df80_add_skipped_to_userfilestatus.py +++ b/backend/alembic/versions/d8cdfee5df80_add_skipped_to_userfilestatus.py @@ -9,7 +9,6 @@ Create Date: 2026-04-01 10:47:12.593950 from alembic import op import sqlalchemy as sa - # revision identifiers, used by Alembic. revision = "d8cdfee5df80" down_revision = "1d78c0ca7853" diff --git a/backend/alembic/versions/d929f0c1c6af_feedback_feature.py b/backend/alembic/versions/d929f0c1c6af_feedback_feature.py index 52860552ec..5b2215f9a8 100644 --- a/backend/alembic/versions/d929f0c1c6af_feedback_feature.py +++ b/backend/alembic/versions/d929f0c1c6af_feedback_feature.py @@ -10,7 +10,6 @@ import fastapi_users_db_sqlalchemy from alembic import op import sqlalchemy as sa - # revision identifiers, used by Alembic. revision = "d929f0c1c6af" down_revision = "8aabb57f3b49" diff --git a/backend/alembic/versions/d961aca62eb3_update_status_length.py b/backend/alembic/versions/d961aca62eb3_update_status_length.py index 58de115f7d..a9dd81a8fd 100644 --- a/backend/alembic/versions/d961aca62eb3_update_status_length.py +++ b/backend/alembic/versions/d961aca62eb3_update_status_length.py @@ -9,7 +9,6 @@ Create Date: 2025-03-23 16:10:05.683965 from alembic import op import sqlalchemy as sa - # revision identifiers, used by Alembic. revision = "d961aca62eb3" down_revision = "cf90764725d8" diff --git a/backend/alembic/versions/d9ec13955951_remove__dim_suffix_from_model_name.py b/backend/alembic/versions/d9ec13955951_remove__dim_suffix_from_model_name.py index 0e84d5fe85..cd51742d5c 100644 --- a/backend/alembic/versions/d9ec13955951_remove__dim_suffix_from_model_name.py +++ b/backend/alembic/versions/d9ec13955951_remove__dim_suffix_from_model_name.py @@ -8,7 +8,6 @@ Create Date: 2024-08-20 16:31:32.955686 from alembic import op - # revision identifiers, used by Alembic. revision = "d9ec13955951" down_revision = "da4c21c69164" diff --git a/backend/alembic/versions/da42808081e3_migrate_jira_connectors_to_new_format.py b/backend/alembic/versions/da42808081e3_migrate_jira_connectors_to_new_format.py index 92897f54c0..0aee6342ea 100644 --- a/backend/alembic/versions/da42808081e3_migrate_jira_connectors_to_new_format.py +++ b/backend/alembic/versions/da42808081e3_migrate_jira_connectors_to_new_format.py @@ -13,7 +13,6 @@ import json from onyx.configs.constants import DocumentSource from onyx.connectors.jira.utils import extract_jira_project - # revision identifiers, used by Alembic. revision = "da42808081e3" down_revision = "f13db29f3101" diff --git a/backend/alembic/versions/dab04867cd88_add_composite_index_to_document_by_.py b/backend/alembic/versions/dab04867cd88_add_composite_index_to_document_by_.py index 7424c9666a..b6db5a9393 100644 --- a/backend/alembic/versions/dab04867cd88_add_composite_index_to_document_by_.py +++ b/backend/alembic/versions/dab04867cd88_add_composite_index_to_document_by_.py @@ -8,7 +8,6 @@ Create Date: 2024-12-13 22:43:20.119990 from alembic import op - # revision identifiers, used by Alembic. revision = "dab04867cd88" down_revision = "54a74a0417fc" diff --git a/backend/alembic/versions/dba7f71618f5_onyx_custom_tool_flow.py b/backend/alembic/versions/dba7f71618f5_onyx_custom_tool_flow.py index d19f60c7bc..5bfda7e0b9 100644 --- a/backend/alembic/versions/dba7f71618f5_onyx_custom_tool_flow.py +++ b/backend/alembic/versions/dba7f71618f5_onyx_custom_tool_flow.py @@ -9,7 +9,6 @@ Create Date: 2023-09-18 15:18:37.370972 from alembic import op import sqlalchemy as sa - # revision identifiers, used by Alembic. revision = "dba7f71618f5" down_revision = "d5645c915d0e" diff --git a/backend/alembic/versions/df0c7ad8a076_added_deletion_attempt_table.py b/backend/alembic/versions/df0c7ad8a076_added_deletion_attempt_table.py index 7ba81a490d..c92f3b6f25 100644 --- a/backend/alembic/versions/df0c7ad8a076_added_deletion_attempt_table.py +++ b/backend/alembic/versions/df0c7ad8a076_added_deletion_attempt_table.py @@ -9,7 +9,6 @@ Create Date: 2023-08-05 13:35:39.609619 from alembic import op import sqlalchemy as sa - # revision identifiers, used by Alembic. revision = "df0c7ad8a076" down_revision = "d7111c1238cd" diff --git a/backend/alembic/versions/df46c75b714e_add_default_vision_provider_to_llm_.py b/backend/alembic/versions/df46c75b714e_add_default_vision_provider_to_llm_.py index b42fe24145..173722af6b 100644 --- a/backend/alembic/versions/df46c75b714e_add_default_vision_provider_to_llm_.py +++ b/backend/alembic/versions/df46c75b714e_add_default_vision_provider_to_llm_.py @@ -9,7 +9,6 @@ Create Date: 2025-03-11 16:20:19.038945 from alembic import op import sqlalchemy as sa - # revision identifiers, used by Alembic. revision = "df46c75b714e" down_revision = "3934b1bc7b62" diff --git a/backend/alembic/versions/e4334d5b33ba_add_deployment_name_to_llmprovider.py b/backend/alembic/versions/e4334d5b33ba_add_deployment_name_to_llmprovider.py index 75cca33c57..eac6944f6e 100644 --- a/backend/alembic/versions/e4334d5b33ba_add_deployment_name_to_llmprovider.py +++ b/backend/alembic/versions/e4334d5b33ba_add_deployment_name_to_llmprovider.py @@ -9,7 +9,6 @@ Create Date: 2024-10-04 09:52:34.896867 from alembic import op import sqlalchemy as sa - # revision identifiers, used by Alembic. revision = "e4334d5b33ba" down_revision = "ac5eaac849f9" diff --git a/backend/alembic/versions/e6a4bbc13fe4_add_index_for_retrieving_latest_index_.py b/backend/alembic/versions/e6a4bbc13fe4_add_index_for_retrieving_latest_index_.py index 3c3e7d8a31..d02d240ab7 100644 --- a/backend/alembic/versions/e6a4bbc13fe4_add_index_for_retrieving_latest_index_.py +++ b/backend/alembic/versions/e6a4bbc13fe4_add_index_for_retrieving_latest_index_.py @@ -8,7 +8,6 @@ Create Date: 2023-08-10 12:37:23.335471 from alembic import op - # revision identifiers, used by Alembic. revision = "e6a4bbc13fe4" down_revision = "b082fec533f0" diff --git a/backend/alembic/versions/e7f8a9b0c1d2_create_anonymous_user.py b/backend/alembic/versions/e7f8a9b0c1d2_create_anonymous_user.py index 4ef03b3537..a6a33307e1 100644 --- a/backend/alembic/versions/e7f8a9b0c1d2_create_anonymous_user.py +++ b/backend/alembic/versions/e7f8a9b0c1d2_create_anonymous_user.py @@ -13,7 +13,6 @@ Create Date: 2026-01-15 14:00:00.000000 from alembic import op import sqlalchemy as sa - # revision identifiers, used by Alembic. revision = "e7f8a9b0c1d2" down_revision = "f7ca3e2f45d9" diff --git a/backend/alembic/versions/eaa3b5593925_add_default_slack_channel_config.py b/backend/alembic/versions/eaa3b5593925_add_default_slack_channel_config.py index 62d1bb46a6..9f2bc4a070 100644 --- a/backend/alembic/versions/eaa3b5593925_add_default_slack_channel_config.py +++ b/backend/alembic/versions/eaa3b5593925_add_default_slack_channel_config.py @@ -9,7 +9,6 @@ Create Date: 2025-02-03 18:07:56.552526 from alembic import op import sqlalchemy as sa - # revision identifiers, used by Alembic. revision = "eaa3b5593925" down_revision = "98a5008d8711" diff --git a/backend/alembic/versions/ee3f4b47fad5_added_alternate_model_to_chat_message.py b/backend/alembic/versions/ee3f4b47fad5_added_alternate_model_to_chat_message.py index 64ffdad25f..9feb777d90 100644 --- a/backend/alembic/versions/ee3f4b47fad5_added_alternate_model_to_chat_message.py +++ b/backend/alembic/versions/ee3f4b47fad5_added_alternate_model_to_chat_message.py @@ -9,7 +9,6 @@ Create Date: 2024-08-12 00:11:50.915845 from alembic import op import sqlalchemy as sa - # revision identifiers, used by Alembic. revision = "ee3f4b47fad5" down_revision = "2d2304e27d8c" diff --git a/backend/alembic/versions/f17bf3b0d9f1_embedding_provider_by_provider_type.py b/backend/alembic/versions/f17bf3b0d9f1_embedding_provider_by_provider_type.py index a141f946d2..aaf0be3f01 100644 --- a/backend/alembic/versions/f17bf3b0d9f1_embedding_provider_by_provider_type.py +++ b/backend/alembic/versions/f17bf3b0d9f1_embedding_provider_by_provider_type.py @@ -9,7 +9,6 @@ Create Date: 2024-08-21 13:13:31.120460 from alembic import op import sqlalchemy as sa - # revision identifiers, used by Alembic. revision = "f17bf3b0d9f1" down_revision = "351faebd379d" diff --git a/backend/alembic/versions/f1ca58b2f2ec_add_passthrough_auth_to_tool.py b/backend/alembic/versions/f1ca58b2f2ec_add_passthrough_auth_to_tool.py index 7a8b9b3b60..55679efdaa 100644 --- a/backend/alembic/versions/f1ca58b2f2ec_add_passthrough_auth_to_tool.py +++ b/backend/alembic/versions/f1ca58b2f2ec_add_passthrough_auth_to_tool.py @@ -11,7 +11,6 @@ from typing import Sequence, Union from alembic import op import sqlalchemy as sa - # revision identifiers, used by Alembic. revision: str = "f1ca58b2f2ec" down_revision: Union[str, None] = "c7bf5721733e" diff --git a/backend/alembic/versions/f220515df7b4_add_flow_mapping_table.py b/backend/alembic/versions/f220515df7b4_add_flow_mapping_table.py index afd32a0ecd..76fc587b95 100644 --- a/backend/alembic/versions/f220515df7b4_add_flow_mapping_table.py +++ b/backend/alembic/versions/f220515df7b4_add_flow_mapping_table.py @@ -10,7 +10,6 @@ from onyx.db.enums import LLMModelFlowType from alembic import op import sqlalchemy as sa - # revision identifiers, used by Alembic. revision = "f220515df7b4" down_revision = "9d1543a37106" diff --git a/backend/alembic/versions/f5437cc136c5_delete_non_search_assistants.py b/backend/alembic/versions/f5437cc136c5_delete_non_search_assistants.py index f6d5f4a41b..c44b8fccdc 100644 --- a/backend/alembic/versions/f5437cc136c5_delete_non_search_assistants.py +++ b/backend/alembic/versions/f5437cc136c5_delete_non_search_assistants.py @@ -8,7 +8,6 @@ Create Date: 2025-02-04 16:17:15.677256 from alembic import op - # revision identifiers, used by Alembic. revision = "f5437cc136c5" down_revision = "eaa3b5593925" diff --git a/backend/alembic/versions/f7505c5b0284_updated_constraints_for_ccpairs.py b/backend/alembic/versions/f7505c5b0284_updated_constraints_for_ccpairs.py index 88b3f46e7a..30a7a3cd2d 100644 --- a/backend/alembic/versions/f7505c5b0284_updated_constraints_for_ccpairs.py +++ b/backend/alembic/versions/f7505c5b0284_updated_constraints_for_ccpairs.py @@ -8,7 +8,6 @@ Create Date: 2025-04-01 17:50:42.504818 from alembic import op - # revision identifiers, used by Alembic. revision = "f7505c5b0284" down_revision = "f71470ba9274" diff --git a/backend/alembic/versions/f7a894b06d02_non_nullbale_slack_bot_id_in_channel_.py b/backend/alembic/versions/f7a894b06d02_non_nullbale_slack_bot_id_in_channel_.py index 370ab1f0d8..5bf1d7b2e2 100644 --- a/backend/alembic/versions/f7a894b06d02_non_nullbale_slack_bot_id_in_channel_.py +++ b/backend/alembic/versions/f7a894b06d02_non_nullbale_slack_bot_id_in_channel_.py @@ -9,7 +9,6 @@ Create Date: 2024-12-06 12:55:42.845723 from alembic import op import sqlalchemy as sa - # revision identifiers, used by Alembic. revision = "f7a894b06d02" down_revision = "9f696734098f" diff --git a/backend/alembic/versions/f7ca3e2f45d9_migrate_no_auth_data_to_placeholder.py b/backend/alembic/versions/f7ca3e2f45d9_migrate_no_auth_data_to_placeholder.py index 96a45ec394..5c4b7cd635 100644 --- a/backend/alembic/versions/f7ca3e2f45d9_migrate_no_auth_data_to_placeholder.py +++ b/backend/alembic/versions/f7ca3e2f45d9_migrate_no_auth_data_to_placeholder.py @@ -20,7 +20,6 @@ import sqlalchemy as sa from shared_configs.configs import MULTI_TENANT - # revision identifiers, used by Alembic. revision = "f7ca3e2f45d9" down_revision = "78ebc66946a0" diff --git a/backend/alembic/versions/f8a9b2c3d4e5_add_research_answer_purpose_to_chat_message.py b/backend/alembic/versions/f8a9b2c3d4e5_add_research_answer_purpose_to_chat_message.py index 1aa4bb046f..d26320f39f 100644 --- a/backend/alembic/versions/f8a9b2c3d4e5_add_research_answer_purpose_to_chat_message.py +++ b/backend/alembic/versions/f8a9b2c3d4e5_add_research_answer_purpose_to_chat_message.py @@ -9,7 +9,6 @@ Create Date: 2025-01-27 12:00:00.000000 from alembic import op import sqlalchemy as sa - # revision identifiers, used by Alembic. revision = "f8a9b2c3d4e5" down_revision = "5ae8240accb3" diff --git a/backend/alembic/versions/f9b8c7d6e5a4_update_parent_question_id_foreign_key_to_research_agent_iteration.py b/backend/alembic/versions/f9b8c7d6e5a4_update_parent_question_id_foreign_key_to_research_agent_iteration.py index 18ea4e349f..b521b344da 100644 --- a/backend/alembic/versions/f9b8c7d6e5a4_update_parent_question_id_foreign_key_to_research_agent_iteration.py +++ b/backend/alembic/versions/f9b8c7d6e5a4_update_parent_question_id_foreign_key_to_research_agent_iteration.py @@ -9,7 +9,6 @@ Create Date: 2025-01-27 12:00:00.000000 from alembic import op import sqlalchemy as sa - # revision identifiers, used by Alembic. revision = "f9b8c7d6e5a4" down_revision = "bd7c3bf8beba" diff --git a/backend/alembic/versions/febe9eaa0644_add_document_set_persona_relationship_.py b/backend/alembic/versions/febe9eaa0644_add_document_set_persona_relationship_.py index c1a6b23a4a..268b6abc3e 100644 --- a/backend/alembic/versions/febe9eaa0644_add_document_set_persona_relationship_.py +++ b/backend/alembic/versions/febe9eaa0644_add_document_set_persona_relationship_.py @@ -9,7 +9,6 @@ Create Date: 2023-09-24 13:06:24.018610 from alembic import op import sqlalchemy as sa - # revision identifiers, used by Alembic. revision = "febe9eaa0644" down_revision = "57b53544726e" diff --git a/backend/alembic/versions/feead2911109_add_opensearch_tenant_migration_columns.py b/backend/alembic/versions/feead2911109_add_opensearch_tenant_migration_columns.py index c05e22a552..e7f06ffdce 100644 --- a/backend/alembic/versions/feead2911109_add_opensearch_tenant_migration_columns.py +++ b/backend/alembic/versions/feead2911109_add_opensearch_tenant_migration_columns.py @@ -9,7 +9,6 @@ Create Date: 2026-02-10 17:46:34.029937 from alembic import op import sqlalchemy as sa - # revision identifiers, used by Alembic. revision = "feead2911109" down_revision = "175ea04c7087" diff --git a/backend/alembic_tenants/versions/34e3630c7f32_lowercase_multi_tenant_user_auth.py b/backend/alembic_tenants/versions/34e3630c7f32_lowercase_multi_tenant_user_auth.py index 217fae1f71..94f789a964 100644 --- a/backend/alembic_tenants/versions/34e3630c7f32_lowercase_multi_tenant_user_auth.py +++ b/backend/alembic_tenants/versions/34e3630c7f32_lowercase_multi_tenant_user_auth.py @@ -8,7 +8,6 @@ Create Date: 2025-02-26 15:03:01.211894 from alembic import op - # revision identifiers, used by Alembic. revision = "34e3630c7f32" down_revision = "a4f6ee863c47" diff --git a/backend/alembic_tenants/versions/3b45e0018bf1_add_new_available_tenant_table.py b/backend/alembic_tenants/versions/3b45e0018bf1_add_new_available_tenant_table.py index ac21fa6ef4..3c1aff6f36 100644 --- a/backend/alembic_tenants/versions/3b45e0018bf1_add_new_available_tenant_table.py +++ b/backend/alembic_tenants/versions/3b45e0018bf1_add_new_available_tenant_table.py @@ -10,7 +10,6 @@ import sqlalchemy as sa from alembic import op - # revision identifiers, used by Alembic. revision = "3b45e0018bf1" down_revision = "ac842f85f932" diff --git a/backend/alembic_tenants/versions/3b9f09038764_add_read_only_kg_user.py b/backend/alembic_tenants/versions/3b9f09038764_add_read_only_kg_user.py index a91e0f8a58..c0607c1d74 100644 --- a/backend/alembic_tenants/versions/3b9f09038764_add_read_only_kg_user.py +++ b/backend/alembic_tenants/versions/3b9f09038764_add_read_only_kg_user.py @@ -12,7 +12,6 @@ from alembic import op from onyx.configs.app_configs import DB_READONLY_PASSWORD from onyx.configs.app_configs import DB_READONLY_USER - # revision identifiers, used by Alembic. revision = "3b9f09038764" down_revision = "3b45e0018bf1" diff --git a/backend/alembic_tenants/versions/a4f6ee863c47_mapping_for_anonymous_user_path.py b/backend/alembic_tenants/versions/a4f6ee863c47_mapping_for_anonymous_user_path.py index d90eddee93..6e51bff28d 100644 --- a/backend/alembic_tenants/versions/a4f6ee863c47_mapping_for_anonymous_user_path.py +++ b/backend/alembic_tenants/versions/a4f6ee863c47_mapping_for_anonymous_user_path.py @@ -10,7 +10,6 @@ import sqlalchemy as sa from alembic import op - # revision identifiers, used by Alembic. revision = "a4f6ee863c47" down_revision = "14a83a331951" diff --git a/backend/alembic_tenants/versions/ac842f85f932_new_column_user_tenant_mapping.py b/backend/alembic_tenants/versions/ac842f85f932_new_column_user_tenant_mapping.py index 6fbad31fc1..5b523b4ea6 100644 --- a/backend/alembic_tenants/versions/ac842f85f932_new_column_user_tenant_mapping.py +++ b/backend/alembic_tenants/versions/ac842f85f932_new_column_user_tenant_mapping.py @@ -10,7 +10,6 @@ import sqlalchemy as sa from alembic import op - # revision identifiers, used by Alembic. revision = "ac842f85f932" down_revision = "34e3630c7f32" diff --git a/backend/ee/onyx/access/access.py b/backend/ee/onyx/access/access.py index 358d5d3c96..200478c8f4 100644 --- a/backend/ee/onyx/access/access.py +++ b/backend/ee/onyx/access/access.py @@ -74,7 +74,7 @@ def _get_access_for_documents( document = doc_id_map[document_id] source = doc_id_to_source_map.get(document_id) if source is None: - logger.error(f"Document {document_id} has no source") + logger.error("Document %s has no source", document_id) continue perm_sync_config = get_source_perm_sync_config(source) diff --git a/backend/ee/onyx/auth/users.py b/backend/ee/onyx/auth/users.py index 3d625666ab..bc50271d4d 100644 --- a/backend/ee/onyx/auth/users.py +++ b/backend/ee/onyx/auth/users.py @@ -28,7 +28,7 @@ def verify_auth_setting() -> None: logger.warning( "AUTH_TYPE='disabled' is no longer supported. Using 'basic' instead. Please update your configuration." ) - logger.notice(f"Using Auth Type: {AUTH_TYPE.value}") + logger.notice("Using Auth Type: %s", AUTH_TYPE.value) def get_default_admin_user_emails_() -> list[str]: diff --git a/backend/ee/onyx/background/celery/tasks/cleanup/tasks.py b/backend/ee/onyx/background/celery/tasks/cleanup/tasks.py index b2ae1e502f..0e1b5b03bc 100644 --- a/backend/ee/onyx/background/celery/tasks/cleanup/tasks.py +++ b/backend/ee/onyx/background/celery/tasks/cleanup/tasks.py @@ -34,6 +34,7 @@ def export_query_history_cleanup_task(*, tenant_id: str) -> None: continue logger.error( - f"Task with {task.task_id=} failed; it is being deleted now" + "Task with task.task_id=%r failed; it is being deleted now", + task.task_id, ) delete_task_with_id(db_session=db_session, task_id=task.task_id) diff --git a/backend/ee/onyx/background/celery/tasks/doc_permission_syncing/tasks.py b/backend/ee/onyx/background/celery/tasks/doc_permission_syncing/tasks.py index 3a1ec6103e..1c549e3bb2 100644 --- a/backend/ee/onyx/background/celery/tasks/doc_permission_syncing/tasks.py +++ b/backend/ee/onyx/background/celery/tasks/doc_permission_syncing/tasks.py @@ -146,11 +146,11 @@ def _is_external_doc_permissions_sync_due(cc_pair: ConnectorCredentialPair) -> b sync_config = get_source_perm_sync_config(cc_pair.connector.source) if sync_config is None: - logger.error(f"No sync config found for {cc_pair.connector.source}") + logger.error("No sync config found for %s", cc_pair.connector.source) return False if sync_config.doc_sync_config is None: - logger.error(f"No doc sync config found for {cc_pair.connector.source}") + logger.error("No doc sync config found for %s", cc_pair.connector.source) return False # if indexing also does perm sync, don't start running doc_sync until at @@ -446,7 +446,8 @@ def connector_permission_sync_generator_task( if payload.celery_task_id is None: logger.info( - f"connector_permission_sync_generator_task - Waiting for fence: fence={redis_connector.permissions.fence_key}" + "connector_permission_sync_generator_task - Waiting for fence: fence=%s", + redis_connector.permissions.fence_key, ) sleep(1) continue @@ -454,9 +455,9 @@ def connector_permission_sync_generator_task( payload_id = payload.id logger.info( - f"connector_permission_sync_generator_task - Fence found, continuing...: " - f"fence={redis_connector.permissions.fence_key} " - f"payload_id={payload.id}" + "connector_permission_sync_generator_task - Fence found, continuing...: fence=%s payload_id=%s", + redis_connector.permissions.fence_key, + payload.id, ) break @@ -529,7 +530,7 @@ def connector_permission_sync_generator_task( f"No doc sync func found for {source_type} with cc_pair={cc_pair_id}" ) - logger.info(f"Syncing docs for {source_type} with cc_pair={cc_pair_id}") + logger.info("Syncing docs for %s with cc_pair=%s", source_type, cc_pair_id) mark_doc_permission_sync_attempt_in_progress(attempt_id, db_session) @@ -978,9 +979,10 @@ class PermissionSyncCallback(IndexingHeartbeatInterface): elapsed = time.monotonic() - self.start_monotonic if elapsed > self.timeout_seconds: logger.warning( - f"PermissionSyncCallback - task timeout exceeded: " - f"elapsed={elapsed:.0f}s timeout={self.timeout_seconds}s " - f"cc_pair={self.redis_connector.cc_pair_id}" + "PermissionSyncCallback - task timeout exceeded: elapsed=%ss timeout=%ss cc_pair=%s", + format(elapsed, ".0f"), + self.timeout_seconds, + self.redis_connector.cc_pair_id, ) return True @@ -1001,12 +1003,12 @@ class PermissionSyncCallback(IndexingHeartbeatInterface): self.last_tag = tag except LockError: logger.exception( - f"PermissionSyncCallback - lock.reacquire exceptioned: " - f"lock_timeout={self.redis_lock.timeout} " - f"start={self.started} " - f"last_tag={self.last_tag} " - f"last_reacquired={self.last_lock_reacquire} " - f"now={datetime.now(timezone.utc)}" + "PermissionSyncCallback - lock.reacquire exceptioned: lock_timeout=%s start=%s last_tag=%s last_reacquired=%s now=%s", + self.redis_lock.timeout, + self.started, + self.last_tag, + self.last_lock_reacquire, + datetime.now(timezone.utc), ) redis_lock_dump(self.redis_lock, self.redis_client) diff --git a/backend/ee/onyx/background/celery/tasks/external_group_syncing/tasks.py b/backend/ee/onyx/background/celery/tasks/external_group_syncing/tasks.py index 8edcd4a3ce..bba54691d9 100644 --- a/backend/ee/onyx/background/celery/tasks/external_group_syncing/tasks.py +++ b/backend/ee/onyx/background/celery/tasks/external_group_syncing/tasks.py @@ -274,7 +274,8 @@ def try_creating_external_group_sync_task( # Dont kick off a new sync if the previous one is still running if redis_connector.external_group_sync.fenced: logger.warning( - f"Skipping external group sync for CC Pair {cc_pair_id} - already running." + "Skipping external group sync for CC Pair %s - already running.", + cc_pair_id, ) return None @@ -388,16 +389,16 @@ def connector_external_group_sync_generator_task( if payload.celery_task_id is None: logger.info( - f"connector_external_group_sync_generator_task - Waiting for fence: " - f"fence={redis_connector.external_group_sync.fence_key}" + "connector_external_group_sync_generator_task - Waiting for fence: fence=%s", + redis_connector.external_group_sync.fence_key, ) time.sleep(1) continue logger.info( - f"connector_external_group_sync_generator_task - Fence found, continuing...: " - f"fence={redis_connector.external_group_sync.fence_key} " - f"payload_id={payload.id}" + "connector_external_group_sync_generator_task - Fence found, continuing...: fence=%s payload_id=%s", + redis_connector.external_group_sync.fence_key, + payload.id, ) break @@ -479,7 +480,9 @@ def _perform_external_group_sync( db_session=db_session, ) logger.info( - f"Created external group sync attempt: {attempt_id} for cc_pair={cc_pair_id}" + "Created external group sync attempt: %s for cc_pair=%s", + attempt_id, + cc_pair_id, ) try: @@ -532,22 +535,25 @@ def _timed_perform_external_group_sync( # idle_in_transaction_session_timeout during long API calls), stale # rows would accumulate indefinitely. logger.info( - f"Removing stale external groups from prior cycle for {source_type} " - f"for cc_pair: {cc_pair_id}" + "Removing stale external groups from prior cycle for %s for cc_pair: %s", + source_type, + cc_pair_id, ) remove_stale_external_groups(db_session, cc_pair_id) logger.info( - f"Marking old external groups as stale for {source_type} for cc_pair: {cc_pair_id}" + "Marking old external groups as stale for %s for cc_pair: %s", + source_type, + cc_pair_id, ) mark_old_external_groups_as_stale(db_session, cc_pair_id) # Mark attempt as in progress mark_external_group_sync_attempt_in_progress(attempt_id, db_session) - logger.info(f"Marked external group sync attempt {attempt_id} as in progress") + logger.info("Marked external group sync attempt %s as in progress", attempt_id) logger.info( - f"Syncing external groups for {source_type} for cc_pair: {cc_pair_id}" + "Syncing external groups for %s for cc_pair: %s", source_type, cc_pair_id ) external_user_group_batch: list[ExternalUserGroup] = [] seen_users: set[str] = set() # Track unique users across all groups @@ -580,7 +586,7 @@ def _timed_perform_external_group_sync( if len(external_user_group_batch) >= _EXTERNAL_GROUP_BATCH_SIZE: logger.debug( - f"New external user groups: {external_user_group_batch}" + "New external user groups: %s", external_user_group_batch ) upsert_start = time.monotonic() upsert_external_groups( @@ -593,7 +599,7 @@ def _timed_perform_external_group_sync( external_user_group_batch = [] if external_user_group_batch: - logger.debug(f"New external user groups: {external_user_group_batch}") + logger.debug("New external user groups: %s", external_user_group_batch) upsert_start = time.monotonic() upsert_external_groups( db_session=db_session, @@ -613,14 +619,19 @@ def _timed_perform_external_group_sync( # TODO: add some notification to the admins here inc_group_sync_errors(connector_type) logger.exception( - f"Error syncing external groups for {source_type} for cc_pair: {cc_pair_id} {e}" + "Error syncing external groups for %s for cc_pair: %s %s", + source_type, + cc_pair_id, + e, ) raise e observe_group_sync_upsert_duration(cumulative_upsert_time, connector_type) logger.info( - f"Removing stale external groups for {source_type} for cc_pair: {cc_pair_id}" + "Removing stale external groups for %s for cc_pair: %s", + source_type, + cc_pair_id, ) remove_stale_external_groups(db_session, cc_pair_id) @@ -637,9 +648,11 @@ def _timed_perform_external_group_sync( errors_encountered=0, ) logger.info( - f"Completed external group sync attempt {attempt_id}: " - f"{total_groups_processed} groups, {total_users_processed} users, " - f"{total_group_memberships_synced} memberships" + "Completed external group sync attempt %s: %s groups, %s users, %s memberships", + attempt_id, + total_groups_processed, + total_users_processed, + total_group_memberships_synced, ) inc_group_sync_groups_processed(connector_type, total_groups_processed) diff --git a/backend/ee/onyx/background/celery/tasks/hooks/tasks.py b/backend/ee/onyx/background/celery/tasks/hooks/tasks.py index 17283a5142..23dea9fbeb 100644 --- a/backend/ee/onyx/background/celery/tasks/hooks/tasks.py +++ b/backend/ee/onyx/background/celery/tasks/hooks/tasks.py @@ -27,8 +27,9 @@ def hook_execution_log_cleanup_task(*, tenant_id: str) -> None: # noqa: ARG001 db_session.commit() if deleted: logger.info( - f"Deleted {deleted} hook execution log(s) older than " - f"{_HOOK_EXECUTION_LOG_RETENTION_DAYS} days." + "Deleted %s hook execution log(s) older than %s days.", + deleted, + _HOOK_EXECUTION_LOG_RETENTION_DAYS, ) except Exception: logger.exception("Failed to clean up hook execution logs") diff --git a/backend/ee/onyx/background/celery/tasks/query_history/tasks.py b/backend/ee/onyx/background/celery/tasks/query_history/tasks.py index 44ed89c2cc..e759f254b2 100644 --- a/backend/ee/onyx/background/celery/tasks/query_history/tasks.py +++ b/backend/ee/onyx/background/celery/tasks/query_history/tasks.py @@ -78,7 +78,7 @@ def export_query_history_task( ) except Exception: - logger.exception(f"Failed to export query history with {task_id=}") + logger.exception("Failed to export query history with task_id=%r", task_id) mark_task_as_finished_with_id( db_session=db_session, task_id=task_id, @@ -109,7 +109,7 @@ def export_query_history_task( ) except Exception: logger.exception( - f"Failed to save query history export file; {report_name=}" + "Failed to save query history export file; report_name=%r", report_name ) mark_task_as_finished_with_id( db_session=db_session, diff --git a/backend/ee/onyx/background/celery/tasks/ttl_management/tasks.py b/backend/ee/onyx/background/celery/tasks/ttl_management/tasks.py index 85ba73ff35..9c6e6fd61c 100644 --- a/backend/ee/onyx/background/celery/tasks/ttl_management/tasks.py +++ b/backend/ee/onyx/background/celery/tasks/ttl_management/tasks.py @@ -50,14 +50,16 @@ def perform_ttl_management_task( ) except Exception: logger.exception( - "Failed to delete chat session " - f"user_id={user_id} session_id={session_id}, " - "continuing with remaining sessions" + "Failed to delete chat session user_id=%s session_id=%s, continuing with remaining sessions", + user_id, + session_id, ) except Exception: logger.exception( - f"delete_chat_session exceptioned. user_id={user_id} session_id={session_id}" + "delete_chat_session exceptioned. user_id=%s session_id=%s", + user_id, + session_id, ) raise diff --git a/backend/ee/onyx/background/celery_utils.py b/backend/ee/onyx/background/celery_utils.py index 9ed8e20e51..b401c15854 100644 --- a/backend/ee/onyx/background/celery_utils.py +++ b/backend/ee/onyx/background/celery_utils.py @@ -21,6 +21,6 @@ def should_perform_chat_ttl_check( return True if check_task_is_live_and_not_timed_out(latest_task, db_session): - logger.debug(f"{task_name} is already being performed. Skipping.") + logger.debug("%s is already being performed. Skipping.", task_name) return False return True diff --git a/backend/ee/onyx/db/external_perm.py b/backend/ee/onyx/db/external_perm.py index 28af141b7b..b8019fd1c0 100644 --- a/backend/ee/onyx/db/external_perm.py +++ b/backend/ee/onyx/db/external_perm.py @@ -132,8 +132,9 @@ def upsert_external_groups( user_id = email_id_map.get(user_email.lower()) if user_id is None: logger.warning( - f"User in group {external_group.id}" - f" with email {user_email} not found" + "User in group %s with email %s not found", + external_group.id, + user_email, ) continue diff --git a/backend/ee/onyx/db/license.py b/backend/ee/onyx/db/license.py index b46c83e473..d7f8f50f58 100644 --- a/backend/ee/onyx/db/license.py +++ b/backend/ee/onyx/db/license.py @@ -163,7 +163,7 @@ def get_cached_license_metadata(tenant_id: str | None = None) -> LicenseMetadata ) return LicenseMetadata.model_validate_json(cached_str) except Exception as e: - logger.warning(f"Failed to parse cached license metadata: {e}") + logger.warning("Failed to parse cached license metadata: %s", e) return None @@ -234,7 +234,9 @@ def update_license_cache( ex=LICENSE_CACHE_TTL_SECONDS, ) - logger.info(f"License cache updated: {metadata.seats} seats, status={status.value}") + logger.info( + "License cache updated: %s seats, status=%s", metadata.seats, status.value + ) return metadata @@ -273,7 +275,7 @@ def refresh_license_cache( tenant_id=tenant_id, ) except ValueError as e: - logger.error(f"Failed to verify license during cache refresh: {e}") + logger.error("Failed to verify license during cache refresh: %s", e) invalidate_license_cache(tenant_id) return None diff --git a/backend/ee/onyx/db/standard_answer.py b/backend/ee/onyx/db/standard_answer.py index 45ddd02611..f39902f3d4 100644 --- a/backend/ee/onyx/db/standard_answer.py +++ b/backend/ee/onyx/db/standard_answer.py @@ -19,7 +19,7 @@ def check_category_validity(category_name: str) -> bool: Additionally, extremely long categories are not really usable / useful.""" if len(category_name) > 255: logger.error( - f"Category with name '{category_name}' is too long, cannot be used" + "Category with name '%s' is too long, cannot be used", category_name ) return False diff --git a/backend/ee/onyx/db/user_group.py b/backend/ee/onyx/db/user_group.py index 280f2c4e00..75560ffc61 100644 --- a/backend/ee/onyx/db/user_group.py +++ b/backend/ee/onyx/db/user_group.py @@ -666,9 +666,11 @@ def update_user_curator_relationship( ) logger.info( - f"user_making_change={user_making_change.email if user_making_change else 'None'} is " - f"updating the curator relationship for user={target_user.email} " - f"in group={user_group_id} to is_curator={set_curator_request.is_curator}" + "user_making_change=%s is updating the curator relationship for user=%s in group=%s to is_curator=%s", + user_making_change.email if user_making_change else "None", + target_user.email, + user_group_id, + set_curator_request.is_curator, ) relationship_to_update = ( diff --git a/backend/ee/onyx/external_permissions/confluence/group_sync.py b/backend/ee/onyx/external_permissions/confluence/group_sync.py index f0f570e550..54bc571aba 100644 --- a/backend/ee/onyx/external_permissions/confluence/group_sync.py +++ b/backend/ee/onyx/external_permissions/confluence/group_sync.py @@ -22,7 +22,7 @@ def _build_group_member_email_map( ) -> dict[str, set[str]]: group_member_emails: dict[str, set[str]] = {} for user in confluence_client.paginated_cql_user_retrieval(): - logger.info(f"Processing groups for user: {user}") + logger.info("Processing groups for user: %s", user) email = user.email if not email: @@ -35,7 +35,7 @@ def _build_group_member_email_map( user_name=user_name, ) else: - logger.error(f"user result missing username field: {user}") + logger.error("user result missing username field: %s", user) if not email: # If we still don't have an email, skip this user @@ -59,7 +59,9 @@ def _build_group_member_email_map( emit_background_error(msg, cc_pair_id=cc_pair_id) logger.error(msg) else: - logger.debug(f"Found groups {all_users_groups} for user with email {email}") + logger.debug( + "Found groups %s for user with email %s", all_users_groups, email + ) if not group_member_emails: msg = "No groups found for any users." @@ -91,13 +93,13 @@ def _build_group_member_email_map_from_onyx_users( group_member_emails: dict[str, set[str]] = {} for email in user_emails: - logger.info(f"Processing groups for user with email: {email}") + logger.info("Processing groups for user with email: %s", email) try: user_name = _infer_username_from_email(email) response = confluence_client.get_user_details_by_username(user_name) user_key = response.get("userKey") if not user_key: - logger.error(f"User key not found for user with email {email}") + logger.error("User key not found for user with email %s", email) continue all_users_groups: set[str] = set() @@ -112,10 +114,10 @@ def _build_group_member_email_map_from_onyx_users( logger.error(msg) else: logger.info( - f"Found groups {all_users_groups} for user with email {email}" + "Found groups %s for user with email %s", all_users_groups, email ) except Exception: - logger.exception(f"Error getting user details for user with email {email}") + logger.exception("Error getting user details for user with email %s", email) return group_member_emails diff --git a/backend/ee/onyx/external_permissions/confluence/page_access.py b/backend/ee/onyx/external_permissions/confluence/page_access.py index 19b3b65fc5..47cd7cf1d4 100644 --- a/backend/ee/onyx/external_permissions/confluence/page_access.py +++ b/backend/ee/onyx/external_permissions/confluence/page_access.py @@ -42,16 +42,16 @@ def _extract_read_access_restrictions( read_access_user_emails.append(email) else: logger.warning( - f"Email for user {user['username']} not found in Confluence" + "Email for user %s not found in Confluence", user["username"] ) else: if user.get("email") is not None: - logger.warning(f"Cant find email for user {user.get('displayName')}") + logger.warning("Cant find email for user %s", user.get("displayName")) logger.warning( "This user needs to make their email accessible in Confluence Settings" ) - logger.warning(f"no user email or username for {user}") + logger.warning("no user email or username for %s", user) # Extract the groups with read access read_access_group = read_access_restrictions.get("group", {}) @@ -135,8 +135,11 @@ def get_page_restrictions( # should be applied (the reason why we'd traverse more than one ancestor is if # the ancestor also is in "inherit" mode.) logger.debug( - f"Found user restrictions {ancestor_user_emails} and group restrictions {ancestor_group_names}" - f"for document {page_id} based on ancestor {ancestor}" + "Found user restrictions %s and group restrictions %sfor document %s based on ancestor %s", + ancestor_user_emails, + ancestor_group_names, + page_id, + ancestor, ) return ExternalAccess( external_user_emails=ancestor_user_emails, diff --git a/backend/ee/onyx/external_permissions/confluence/space_access.py b/backend/ee/onyx/external_permissions/confluence/space_access.py index 1f6afd283b..29a8ac0343 100644 --- a/backend/ee/onyx/external_permissions/confluence/space_access.py +++ b/backend/ee/onyx/external_permissions/confluence/space_access.py @@ -58,13 +58,15 @@ def _get_server_space_permissions( if user_email: user_emails.add(user_email) else: - logger.warning(f"Email for user {user_name} not found in Confluence") + logger.warning("Email for user %s not found in Confluence", user_name) if not user_emails and not group_names: logger.warning( "No user emails or group names found in Confluence space permissions" - f"\nSpace key: {space_key}" - f"\nSpace permissions: {space_permissions}" + "\nSpace key: %s" + "\nSpace permissions: %s", + space_key, + space_permissions, ) return ExternalAccess( @@ -126,10 +128,12 @@ def get_space_permission( and not space_permissions.external_user_group_ids ): logger.warning( - f"No permissions found for space '{space_key}'. This is very unlikely " + "No permissions found for space '%s'. This is very unlikely " "to be correct and is more likely caused by an access token with " "insufficient permissions. Make sure that the access token has Admin " - f"permissions for space '{space_key}'" + "permissions for space '%s'", + space_key, + space_key, ) # Prefix group IDs with source type if requested (for indexing path) @@ -169,7 +173,7 @@ def get_all_space_permissions( ] # Gets the permissions for each space - logger.debug(f"Got {len(all_space_keys)} spaces from confluence") + logger.debug("Got %s spaces from confluence", len(all_space_keys)) space_permissions_by_space_key: dict[str, ExternalAccess] = {} for space_key in all_space_keys: space_permissions = get_space_permission( diff --git a/backend/ee/onyx/external_permissions/github/doc_sync.py b/backend/ee/onyx/external_permissions/github/doc_sync.py index 031cbebe0e..f377d58f60 100644 --- a/backend/ee/onyx/external_permissions/github/doc_sync.py +++ b/backend/ee/onyx/external_permissions/github/doc_sync.py @@ -43,7 +43,7 @@ def github_doc_sync( This function checks each repository for visibility/team changes and updates document permissions accordingly without using checkpoints. """ - logger.info(f"Starting GitHub document sync for CC pair ID: {cc_pair.id}") + logger.info("Starting GitHub document sync for CC pair ID: %s", cc_pair.id) # Initialize GitHub connector with credentials github_connector: GithubConnector = GithubConnector( @@ -67,9 +67,9 @@ def github_doc_sync( try: repos = github_connector.fetch_configured_repos() - logger.info(f"Found {len(repos)} repositories to check") + logger.info("Found %s repositories to check", len(repos)) except Exception as e: - logger.error(f"Failed to fetch repositories: {e}") + logger.error("Failed to fetch repositories: %s", e) raise repo_to_doc_list_map: dict[str, list[DocumentRow]] = {} @@ -77,7 +77,7 @@ def github_doc_sync( existing_docs: list[DocumentRow] = fetch_all_existing_docs_fn( sort_order=SortOrder.ASC ) - logger.info(f"Found {len(existing_docs)} documents to check") + logger.info("Found %s documents to check", len(existing_docs)) for doc in existing_docs: try: doc_metadata = DocMetadata.model_validate_json(json.dumps(doc.doc_metadata)) @@ -85,19 +85,19 @@ def github_doc_sync( repo_to_doc_list_map[doc_metadata.repo] = [] repo_to_doc_list_map[doc_metadata.repo].append(doc) except Exception as e: - logger.error(f"Failed to parse doc metadata: {e} for doc {doc.id}") + logger.error("Failed to parse doc metadata: %s for doc %s", e, doc.id) continue - logger.info(f"Found {len(repo_to_doc_list_map)} documents to check") + logger.info("Found %s documents to check", len(repo_to_doc_list_map)) # Process each repository individually for repo in repos: try: - logger.info(f"Processing repository: {repo.id} (name: {repo.name})") + logger.info("Processing repository: %s (name: %s)", repo.id, repo.name) repo_doc_list: list[DocumentRow] = repo_to_doc_list_map.get( repo.full_name, [] ) if not repo_doc_list: logger.warning( - f"No documents found for repository {repo.id} ({repo.name})" + "No documents found for repository %s (%s)", repo.id, repo.name ) continue @@ -111,7 +111,9 @@ def github_doc_sync( if has_changes: logger.info( - f"Repository {repo.id} ({repo.name}) has changes, updating documents" + "Repository %s (%s) has changes, updating documents", + repo.id, + repo.name, ) # Get new external access permissions for this repository @@ -120,7 +122,9 @@ def github_doc_sync( ) logger.info( - f"Found {len(repo_doc_list)} documents for repository {repo.full_name}" + "Found %s documents for repository %s", + len(repo_doc_list), + repo.full_name, ) # Yield updated external access for each document @@ -134,12 +138,14 @@ def github_doc_sync( ) else: logger.info( - f"Repository {repo.id} ({repo.name}) has no changes, skipping" + "Repository %s (%s) has no changes, skipping", repo.id, repo.name ) except Exception as e: - logger.error(f"Error processing repository {repo.id} ({repo.name}): {e}") + logger.error( + "Error processing repository %s (%s): %s", repo.id, repo.name, e + ) - logger.info(f"GitHub document sync completed for CC pair ID: {cc_pair.id}") + logger.info("GitHub document sync completed for CC pair ID: %s", cc_pair.id) def _check_repository_for_changes( @@ -150,14 +156,14 @@ def _check_repository_for_changes( """ Check if repository has any permission changes (visibility or team updates). """ - logger.info(f"Checking repository {repo.id} ({repo.name}) for changes") + logger.info("Checking repository %s (%s) for changes", repo.id, repo.name) # Check for repository visibility changes using the sample document data if _is_repo_visibility_changed_from_groups( repo=repo, current_external_group_ids=current_external_group_ids, ): - logger.info(f"Repository {repo.id} ({repo.name}) has visibility changes") + logger.info("Repository %s (%s) has visibility changes", repo.id, repo.name) return True # Check for team membership changes if repository is private @@ -168,10 +174,10 @@ def _check_repository_for_changes( github_client=github_client, current_external_group_ids=current_external_group_ids, ): - logger.info(f"Repository {repo.id} ({repo.name}) has team changes") + logger.info("Repository %s (%s) has team changes", repo.id, repo.name) return True - logger.info(f"Repository {repo.id} ({repo.name}) has no changes") + logger.info("Repository %s (%s) has no changes", repo.id, repo.name) return False @@ -190,7 +196,7 @@ def _is_repo_visibility_changed_from_groups( True if visibility has changed """ current_repo_visibility = get_repository_visibility(repo) - logger.info(f"Current repository visibility: {current_repo_visibility.value}") + logger.info("Current repository visibility: %s", current_repo_visibility.value) # Build expected group IDs for current visibility collaborators_group_id = build_ext_group_name_for_onyx( @@ -216,13 +222,16 @@ def _is_repo_visibility_changed_from_groups( else: existing_repo_visibility = GitHubVisibility.PUBLIC - logger.info(f"Inferred existing visibility: {existing_repo_visibility.value}") + logger.info("Inferred existing visibility: %s", existing_repo_visibility.value) visibility_changed = existing_repo_visibility != current_repo_visibility if visibility_changed: logger.info( - f"Visibility changed for repo {repo.id} ({repo.name}): " - f"{existing_repo_visibility.value} -> {current_repo_visibility.value}" + "Visibility changed for repo %s (%s): %s -> %s", + repo.id, + repo.name, + existing_repo_visibility.value, + current_repo_visibility.value, ) return visibility_changed @@ -239,7 +248,10 @@ def _teams_updated_from_groups( # Fetch current team slugs for the repository current_teams = fetch_repository_team_slugs(repo=repo, github_client=github_client) logger.info( - f"Current teams for repository {repo.id} (name: {repo.name}): {current_teams}" + "Current teams for repository %s (name: %s): %s", + repo.id, + repo.name, + current_teams, ) # Build group IDs to exclude from team comparison (non-team groups) @@ -271,15 +283,20 @@ def _teams_updated_from_groups( current_team_ids.add(team_group_id) logger.info( - f"Existing team IDs: {existing_team_ids}, Current team IDs: {current_team_ids}" + "Existing team IDs: %s, Current team IDs: %s", + existing_team_ids, + current_team_ids, ) # Compare actual team IDs to detect changes teams_changed = current_team_ids != existing_team_ids if teams_changed: logger.info( - f"Team changes detected for repo {repo.id} (name: {repo.name}): " - f"existing={existing_team_ids}, current={current_team_ids}" + "Team changes detected for repo %s (name: %s): existing=%s, current=%s", + repo.id, + repo.name, + existing_team_ids, + current_team_ids, ) return teams_changed diff --git a/backend/ee/onyx/external_permissions/github/group_sync.py b/backend/ee/onyx/external_permissions/github/group_sync.py index 66a40ff64f..7577a1f710 100644 --- a/backend/ee/onyx/external_permissions/github/group_sync.py +++ b/backend/ee/onyx/external_permissions/github/group_sync.py @@ -45,7 +45,9 @@ def github_group_sync( for external_group in get_external_user_group( repo, github_connector.github_client ): - logger.info(f"External group: {external_group}") + logger.info("External group: %s", external_group) yield external_group except Exception as e: - logger.error(f"Error processing repository {repo.id} ({repo.name}): {e}") + logger.error( + "Error processing repository %s (%s): %s", repo.id, repo.name, e + ) diff --git a/backend/ee/onyx/external_permissions/github/utils.py b/backend/ee/onyx/external_permissions/github/utils.py index f9f9952da4..5f6eee1e95 100644 --- a/backend/ee/onyx/external_permissions/github/utils.py +++ b/backend/ee/onyx/external_permissions/github/utils.py @@ -47,16 +47,19 @@ def _run_with_retry( retry_count: int = 0, ) -> Optional[T]: """Execute a GitHub operation with retry on rate limit and exception handling.""" - logger.debug(f"Starting operation '{description}', attempt {retry_count + 1}") + logger.debug("Starting operation '%s', attempt %s", description, retry_count + 1) try: result = operation() - logger.debug(f"Operation '{description}' completed successfully") + logger.debug("Operation '%s' completed successfully", description) return result except RateLimitExceededException: if retry_count < MAX_RETRY_COUNT: sleep_after_rate_limit_exception(github_client) logger.warning( - f"Rate limit exceeded while {description}. Retrying... (attempt {retry_count + 1}/{MAX_RETRY_COUNT})" + "Rate limit exceeded while %s. Retrying... (attempt %s/%s)", + description, + retry_count + 1, + MAX_RETRY_COUNT, ) return _run_with_retry( operation, description, github_client, retry_count + 1 @@ -66,10 +69,10 @@ def _run_with_retry( logger.exception(error_msg) raise RuntimeError(error_msg) except GithubException as e: - logger.warning(f"GitHub API error during {description}: {e}") + logger.warning("GitHub API error during %s: %s", description, e) return None except Exception as e: - logger.exception(f"Unexpected error during {description}: {e}") + logger.exception("Unexpected error during %s: %s", description, e) return None @@ -96,7 +99,7 @@ def _fetch_organization_members( ) -> List[UserInfo]: """Fetch all organization members including owners and regular members.""" org_members: List[UserInfo] = [] - logger.info(f"Fetching organization members for {org_name}") + logger.info("Fetching organization members for %s", org_name) org = _run_with_retry( lambda: github_client.get_organization(org_name), @@ -104,7 +107,7 @@ def _fetch_organization_members( github_client, ) if not org: - logger.error(f"Failed to fetch organization {org_name}") + logger.error("Failed to fetch organization %s", org_name) raise RuntimeError(f"Failed to fetch organization {org_name}") member_objs: PaginatedList[NamedUser] | list[NamedUser] = ( @@ -120,7 +123,7 @@ def _fetch_organization_members( user_info = UserInfo(login=member.login, name=member.name, email=member.email) org_members.append(user_info) - logger.info(f"Fetched {len(org_members)} members for organization {org_name}") + logger.info("Fetched %s members for organization %s", len(org_members), org_name) return org_members @@ -131,7 +134,7 @@ def _fetch_repository_teams_detailed( ) -> List[TeamInfo]: """Fetch teams with access to the repository and their members.""" teams_data: List[TeamInfo] = [] - logger.info(f"Fetching teams for repository {repo.full_name}") + logger.info("Fetching teams for repository %s", repo.full_name) team_objs: PaginatedList[Team] | list[Team] = ( _run_with_retry( @@ -144,7 +147,10 @@ def _fetch_repository_teams_detailed( for team in team_objs: logger.info( - f"Processing team {team.name} (slug: {team.slug}) for repository {repo.full_name}" + "Processing team %s (slug: %s) for repository %s", + team.name, + team.slug, + repo.full_name, ) members: PaginatedList[NamedUser] | list[NamedUser] = ( @@ -163,9 +169,9 @@ def _fetch_repository_teams_detailed( team_info = TeamInfo(name=team.name, slug=team.slug, members=team_members) teams_data.append(team_info) - logger.info(f"Team {team.name} has {len(team_members)} members") + logger.info("Team %s has %s members", team.name, len(team_members)) - logger.info(f"Fetched {len(teams_data)} teams for repository {repo.full_name}") + logger.info("Fetched %s teams for repository %s", len(teams_data), repo.full_name) return teams_data @@ -175,7 +181,7 @@ def fetch_repository_team_slugs( retry_count: int = 0, # noqa: ARG001 ) -> List[str]: """Fetch team slugs with access to the repository.""" - logger.info(f"Fetching team slugs for repository {repo.full_name}") + logger.info("Fetching team slugs for repository %s", repo.full_name) teams_data: List[str] = [] team_objs: PaginatedList[Team] | list[Team] = ( @@ -190,7 +196,9 @@ def fetch_repository_team_slugs( for team in team_objs: teams_data.append(team.slug) - logger.info(f"Fetched {len(teams_data)} team slugs for repository {repo.full_name}") + logger.info( + "Fetched %s team slugs for repository %s", len(teams_data), repo.full_name + ) return teams_data @@ -201,7 +209,7 @@ def _get_collaborators_and_outside_collaborators( """Fetch and categorize collaborators into regular and outside collaborators.""" collaborators: List[UserInfo] = [] outside_collaborators: List[UserInfo] = [] - logger.info(f"Fetching collaborators for repository {repo.full_name}") + logger.info("Fetching collaborators for repository %s", repo.full_name) repo_collaborators: PaginatedList[NamedUser] | list[NamedUser] = ( _run_with_retry( @@ -241,7 +249,10 @@ def _get_collaborators_and_outside_collaborators( collaborators.append(info) logger.info( - f"Categorized {len(collaborators)} regular and {len(outside_collaborators)} outside collaborators for {repo.full_name}" + "Categorized %s regular and %s outside collaborators for %s", + len(collaborators), + len(outside_collaborators), + repo.full_name, ) return collaborators, outside_collaborators @@ -285,17 +296,19 @@ def get_repository_visibility(repo: Repository) -> GitHubVisibility: if hasattr(repo, "visibility"): visibility = repo.visibility logger.info( - f"Repository {repo.full_name} visibility from attribute: {visibility}" + "Repository %s visibility from attribute: %s", repo.full_name, visibility ) try: return GitHubVisibility(visibility) except ValueError: logger.warning( - f"Unknown visibility '{visibility}' for repo {repo.full_name}, defaulting to private" + "Unknown visibility '%s' for repo %s, defaulting to private", + visibility, + repo.full_name, ) return GitHubVisibility.PRIVATE - logger.info(f"Repository {repo.full_name} is private") + logger.info("Repository %s is private", repo.full_name) return GitHubVisibility.PRIVATE @@ -320,12 +333,14 @@ def get_external_access_permission( repo_visibility = get_repository_visibility(repo) logger.info( - f"Generating ExternalAccess for {repo.full_name}: visibility={repo_visibility.value}" + "Generating ExternalAccess for %s: visibility=%s", + repo.full_name, + repo_visibility.value, ) if repo_visibility == GitHubVisibility.PUBLIC: logger.info( - f"Repository {repo.full_name} is public - allowing access to all users" + "Repository %s is public - allowing access to all users", repo.full_name ) return ExternalAccess( external_user_emails=set(), @@ -334,7 +349,7 @@ def get_external_access_permission( ) elif repo_visibility == GitHubVisibility.PRIVATE: logger.info( - f"Repository {repo.full_name} is private - setting up restricted access" + "Repository %s is private - setting up restricted access", repo.full_name ) collaborators_group_id = form_collaborators_group_id(repo.id) @@ -361,7 +376,7 @@ def get_external_access_permission( ] group_ids.update(team_slugs) - logger.info(f"ExternalAccess groups for {repo.full_name}: {group_ids}") + logger.info("ExternalAccess groups for %s: %s", repo.full_name, group_ids) return ExternalAccess( external_user_emails=set(), external_user_group_ids=group_ids, @@ -370,7 +385,7 @@ def get_external_access_permission( else: # Internal repositories - accessible to organization members logger.info( - f"Repository {repo.full_name} is internal - accessible to org members" + "Repository %s is internal - accessible to org members", repo.full_name ) org_group_id = form_organization_group_id(repo.organization.id) if add_prefix: @@ -379,7 +394,7 @@ def get_external_access_permission( ext_group_name=org_group_id, ) group_ids = {org_group_id} - logger.info(f"ExternalAccess groups for {repo.full_name}: {group_ids}") + logger.info("ExternalAccess groups for %s: %s", repo.full_name, group_ids) return ExternalAccess( external_user_emails=set(), external_user_group_ids=group_ids, @@ -396,11 +411,13 @@ def get_external_user_group( """ repo_visibility = get_repository_visibility(repo) logger.info( - f"Generating ExternalUserGroups for {repo.full_name}: visibility={repo_visibility.value}" + "Generating ExternalUserGroups for %s: visibility=%s", + repo.full_name, + repo_visibility.value, ) if repo_visibility == GitHubVisibility.PRIVATE: - logger.info(f"Processing private repository {repo.full_name}") + logger.info("Processing private repository %s", repo.full_name) collaborators, outside_collaborators = ( _get_collaborators_and_outside_collaborators(github_client, repo) @@ -416,7 +433,7 @@ def get_external_user_group( # Expected per-user condition (login without a public email); # skip and warn so the login in the message doesn't explode # Sentry fingerprinting. - logger.warning(f"Collaborator {collab.login} has no email") + logger.warning("Collaborator %s has no email", collab.login) if user_emails: collaborators_group = ExternalUserGroup( @@ -424,7 +441,7 @@ def get_external_user_group( user_emails=list(user_emails), ) external_user_groups.append(collaborators_group) - logger.info(f"Created collaborators group with {len(user_emails)} emails") + logger.info("Created collaborators group with %s emails", len(user_emails)) # Create group for outside collaborators user_emails = set() @@ -432,7 +449,7 @@ def get_external_user_group( if collab.email: user_emails.add(collab.email) else: - logger.warning(f"Outside collaborator {collab.login} has no email") + logger.warning("Outside collaborator %s has no email", collab.login) if user_emails: outside_collaborators_group = ExternalUserGroup( @@ -441,7 +458,7 @@ def get_external_user_group( ) external_user_groups.append(outside_collaborators_group) logger.info( - f"Created outside collaborators group with {len(user_emails)} emails" + "Created outside collaborators group with %s emails", len(user_emails) ) # Create groups for teams @@ -451,7 +468,7 @@ def get_external_user_group( if member.email: user_emails.add(member.email) else: - logger.warning(f"Team member {member.login} has no email") + logger.warning("Team member %s has no email", member.login) if user_emails: team_group = ExternalUserGroup( @@ -460,16 +477,18 @@ def get_external_user_group( ) external_user_groups.append(team_group) logger.info( - f"Created team group {team.name} with {len(user_emails)} emails" + "Created team group %s with %s emails", team.name, len(user_emails) ) logger.info( - f"Created {len(external_user_groups)} ExternalUserGroups for private repository {repo.full_name}" + "Created %s ExternalUserGroups for private repository %s", + len(external_user_groups), + repo.full_name, ) return external_user_groups if repo_visibility == GitHubVisibility.INTERNAL: - logger.info(f"Processing internal repository {repo.full_name}") + logger.info("Processing internal repository %s", repo.full_name) org_group_id = form_organization_group_id(repo.organization.id) org_members = _fetch_organization_members( @@ -481,16 +500,18 @@ def get_external_user_group( if member.email: user_emails.add(member.email) else: - logger.warning(f"Org member {member.login} has no email") + logger.warning("Org member %s has no email", member.login) org_group = ExternalUserGroup( id=org_group_id, user_emails=list(user_emails), ) logger.info( - f"Created organization group with {len(user_emails)} emails for internal repository {repo.full_name}" + "Created organization group with %s emails for internal repository %s", + len(user_emails), + repo.full_name, ) return [org_group] - logger.info(f"Repository {repo.full_name} is public - no user groups needed") + logger.info("Repository %s is public - no user groups needed", repo.full_name) return [] diff --git a/backend/ee/onyx/external_permissions/gmail/doc_sync.py b/backend/ee/onyx/external_permissions/gmail/doc_sync.py index 1dccd5e11d..7a870332d4 100644 --- a/backend/ee/onyx/external_permissions/gmail/doc_sync.py +++ b/backend/ee/onyx/external_permissions/gmail/doc_sync.py @@ -79,7 +79,7 @@ def gmail_doc_sync( ) continue if slim_doc.external_access is None: - logger.warning(f"No permissions found for document {slim_doc.id}") + logger.warning("No permissions found for document %s", slim_doc.id) continue yield DocExternalAccess( diff --git a/backend/ee/onyx/external_permissions/google_drive/doc_sync.py b/backend/ee/onyx/external_permissions/google_drive/doc_sync.py index 042d6ca746..f8661f7948 100644 --- a/backend/ee/onyx/external_permissions/google_drive/doc_sync.py +++ b/backend/ee/onyx/external_permissions/google_drive/doc_sync.py @@ -115,7 +115,8 @@ def get_external_access_for_raw_gdrive_file( ) if len(permissions_list) != len(permission_ids) and retriever_drive_service: logger.warning( - f"Failed to get all permissions for file {doc_id} with retriever service, trying admin service" + "Failed to get all permissions for file %s with retriever service, trying admin service", + doc_id, ) backup_permissions_list = _get_permissions(admin_drive_service) permissions_list = _merge_permissions_lists( @@ -132,9 +133,11 @@ def get_external_access_for_raw_gdrive_file( # but the actual fetch can still return empty due to a 403. if not permissions_list: logger.info( - f"No permission info available for file {doc_id} " - f"(likely owned by a user outside of your organization). " - f"Falling back to granting access to retriever user: {fallback_user_email}" + "No permission info available for file %s " + "(likely owned by a user outside of your organization). " + "Falling back to granting access to retriever user: %s", + doc_id, + fallback_user_email, ) return ExternalAccess( external_user_emails={fallback_user_email}, @@ -170,7 +173,9 @@ def get_external_access_for_raw_gdrive_file( # skip the permission — warn instead of error so the doc_id # in the message doesn't explode Sentry fingerprinting. logger.warning( - f"Permission is type `user` but no email address is provided for document {doc_id}\n {permission}" + "Permission is type `user` but no email address is provided for document %s\n %s", + doc_id, + permission, ) elif permission.type == PermissionType.GROUP: # groups are represented as email addresses within Drive @@ -178,14 +183,17 @@ def get_external_access_for_raw_gdrive_file( group_emails.add(permission.email_address) else: logger.warning( - f"Permission is type `group` but no email address is provided for document {doc_id}\n {permission}" + "Permission is type `group` but no email address is provided for document %s\n %s", + doc_id, + permission, ) elif permission.type == PermissionType.DOMAIN and company_domain: if permission.domain == company_domain: public = True else: logger.warning( - f"Permission is type domain but does not match company domain:\n {permission}" + "Permission is type domain but does not match company domain:\n %s", + permission, ) elif permission.type == PermissionType.ANYONE: public = True @@ -244,7 +252,7 @@ def get_external_access_for_folder( # Get permission IDs from folder metadata permission_ids = folder.get("permissionIds") or [] if not permission_ids: - logger.debug(f"No permissionIds found for folder {folder_id}") + logger.debug("No permissionIds found for folder %s", folder_id) return ExternalAccess( external_user_emails=set(), external_user_group_ids=set(), @@ -267,13 +275,15 @@ def get_external_access_for_folder( if permission.email_address: user_emails.add(permission.email_address) else: - logger.warning(f"User permission without email for folder {folder_id}") + logger.warning("User permission without email for folder %s", folder_id) elif permission.type == PermissionType.GROUP: # Groups are represented as email addresses in Google Drive if permission.email_address: group_emails.add(permission.email_address) else: - logger.warning(f"Group permission without email for folder {folder_id}") + logger.warning( + "Group permission without email for folder %s", folder_id + ) elif permission.type == PermissionType.DOMAIN: # Domain permission - check if it matches company domain if permission.domain == google_domain: @@ -282,8 +292,11 @@ def get_external_access_for_folder( is_public = permission.allow_file_discovery is not False else: logger.debug( - f"Domain permission for {permission.domain} does not match " - f"company domain {google_domain} for folder {folder_id}" + "Domain permission for %s does not match " + "company domain %s for folder %s", + permission.domain, + google_domain, + folder_id, ) elif permission.type == PermissionType.ANYONE: # Only public if discoverable (allowFileDiscovery is not False) @@ -331,7 +344,7 @@ def gdrive_doc_sync( total_processed = 0 for slim_doc_batch in slim_doc_generator: - logger.info(f"Drive perm sync: Processing {len(slim_doc_batch)} documents") + logger.info("Drive perm sync: Processing %s documents", len(slim_doc_batch)) for slim_doc in slim_doc_batch: if callback: if callback.should_stop(): @@ -357,4 +370,4 @@ def gdrive_doc_sync( doc_id=slim_doc.id, ) total_processed += len(slim_doc_batch) - logger.info(f"Drive perm sync: Processed {total_processed} total documents") + logger.info("Drive perm sync: Processed %s total documents", total_processed) diff --git a/backend/ee/onyx/external_permissions/google_drive/group_sync.py b/backend/ee/onyx/external_permissions/google_drive/group_sync.py index dcd78d65d2..521094387b 100644 --- a/backend/ee/onyx/external_permissions/google_drive/group_sync.py +++ b/backend/ee/onyx/external_permissions/google_drive/group_sync.py @@ -65,7 +65,7 @@ def _get_all_folders( ): folder_id = folder["id"] if folder_id in seen_folder_ids: - logger.debug(f"Folder {folder_id} has already been seen. Skipping.") + logger.debug("Folder %s has already been seen. Skipping.", folder_id) continue seen_folder_ids.add(folder_id) @@ -94,7 +94,7 @@ def _get_all_folders( ] if not permissions and skip_folders_without_permissions: - logger.debug(f"Folder {folder_id} has no permissions. Skipping.") + logger.debug("Folder %s has no permissions. Skipping.", folder_id) continue all_folders.append( @@ -116,11 +116,12 @@ def _get_all_folders( # expired), not a bug — surface as a warning instead of an error. if isinstance(e, HttpError) and e.status_code == 401: logger.warning( - f"Google Drive returned 401 for user {user_email}; " - f"credentials may need to be reconnected. {e}" + "Google Drive returned 401 for user %s; credentials may need to be reconnected. %s", + user_email, + e, ) else: - logger.exception(f"Error getting folders for user {user_email}") + logger.exception("Error getting folders for user %s", user_email) failed_count += 1 if failed_count > MAX_FAILED_PERCENTAGE * len(user_emails): @@ -143,14 +144,18 @@ def _drive_folder_to_onyx_group( if permission.type == PermissionType.USER: if permission.email_address is None: logger.warning( - f"User email is None for folder {folder.id} permission {permission}" + "User email is None for folder %s permission %s", + folder.id, + permission, ) continue folder_member_emails.add(permission.email_address) elif permission.type == PermissionType.GROUP: if permission.email_address not in group_email_to_member_emails_map: logger.warning( - f"Group email {permission.email_address} for folder {folder.id} not found in group_email_to_member_emails_map" + "Group email %s for folder %s not found in group_email_to_member_emails_map", + permission.email_address, + folder.id, ) continue folder_member_emails.update( @@ -238,10 +243,11 @@ def _get_drive_members( except HttpError as e: if e.status_code in (403, 404): logger.warning( - f"Error getting permissions for drive id {drive_id}. " - f"User '{google_drive_connector.primary_admin_email}' likely " - f"does not have access to this drive (status={e.status_code}). " - f"Exception: {e}" + "Error getting permissions for drive id %s. User '%s' likely does not have access to this drive (status=%s). Exception: %s", + drive_id, + google_drive_connector.primary_admin_email, + e.status_code, + e, ) else: raise e @@ -261,7 +267,9 @@ def _drive_member_map_to_onyx_groups( for group_email in group_emails: if group_email not in group_email_to_member_emails_map: logger.warning( - f"Group email {group_email} for drive {drive_id} not found in group_email_to_member_emails_map" + "Group email %s for drive %s not found in group_email_to_member_emails_map", + group_email, + drive_id, ) continue drive_member_emails.update(group_email_to_member_emails_map[group_email]) @@ -348,7 +356,9 @@ def _build_onyx_groups( for group_email in group_emails: if group_email not in group_email_to_member_emails_map: logger.warning( - f"Group email {group_email} for drive {drive_id} not found in group_email_to_member_emails_map" + "Group email %s for drive %s not found in group_email_to_member_emails_map", + group_email, + drive_id, ) continue drive_member_emails.update(group_email_to_member_emails_map[group_email]) @@ -367,15 +377,18 @@ def _build_onyx_groups( if permission.type == PermissionType.USER: if permission.email_address is None: logger.warning( - f"User email is None for folder {folder.id} permission {permission}" + "User email is None for folder %s permission %s", + folder.id, + permission, ) continue folder_member_emails.add(permission.email_address) elif permission.type == PermissionType.GROUP: if permission.email_address not in group_email_to_member_emails_map: logger.warning( - f"Group email {permission.email_address} for folder {folder.id} " - "not found in group_email_to_member_emails_map" + "Group email %s for folder %s not found in group_email_to_member_emails_map", + permission.email_address, + folder.id, ) continue folder_member_emails.update( diff --git a/backend/ee/onyx/external_permissions/google_drive/permission_retrieval.py b/backend/ee/onyx/external_permissions/google_drive/permission_retrieval.py index 310f1f12d9..0163c962be 100644 --- a/backend/ee/onyx/external_permissions/google_drive/permission_retrieval.py +++ b/backend/ee/onyx/external_permissions/google_drive/permission_retrieval.py @@ -55,7 +55,9 @@ def get_permissions_by_ids( if len(filtered_permissions) < len(permission_ids): missing_ids = permission_id_set - {p.id for p in filtered_permissions if p.id} logger.warning( - f"Could not find all requested permission IDs for document {doc_id}. Missing IDs: {missing_ids}" + "Could not find all requested permission IDs for document %s. Missing IDs: %s", + doc_id, + missing_ids, ) return filtered_permissions diff --git a/backend/ee/onyx/external_permissions/jira/group_sync.py b/backend/ee/onyx/external_permissions/jira/group_sync.py index def0efd8ce..c20a4a0e55 100644 --- a/backend/ee/onyx/external_permissions/jira/group_sync.py +++ b/backend/ee/onyx/external_permissions/jira/group_sync.py @@ -73,7 +73,7 @@ def _get_group_member_emails( try: page = _fetch_group_member_page(jira_client, group_name, start_at) except Exception as e: - logger.error(f"Error fetching members for group {group_name}: {e}") + logger.error("Error fetching members for group %s: %s", group_name, e) raise members: list[dict[str, Any]] = page.get("values", []) @@ -89,7 +89,9 @@ def _get_group_member_emails( emails.add(email) else: logger.warning( - f"Atlassian user {member.get('accountId', 'unknown')} in group {group_name} has no visible email address" + "Atlassian user %s in group %s has no visible email address", + member.get("accountId", "unknown"), + group_name, ) if page.get("isLast", True) or not members: @@ -130,7 +132,7 @@ def jira_group_sync( if not group_names: raise ValueError(f"No groups found for cc_pair_id={cc_pair.id}") - logger.info(f"Found {len(group_names)} groups in Jira") + logger.info("Found %s groups in Jira", len(group_names)) for group_name in group_names: if not group_name: @@ -141,10 +143,10 @@ def jira_group_sync( group_name=group_name, ) if not member_emails: - logger.debug(f"No members found for group {group_name}") + logger.debug("No members found for group %s", group_name) continue - logger.debug(f"Found {len(member_emails)} members for group {group_name}") + logger.debug("Found %s members for group %s", len(member_emails), group_name) yield ExternalUserGroup( id=group_name, user_emails=list(member_emails), diff --git a/backend/ee/onyx/external_permissions/jira/page_access.py b/backend/ee/onyx/external_permissions/jira/page_access.py index 301b008cdc..fc430d4695 100644 --- a/backend/ee/onyx/external_permissions/jira/page_access.py +++ b/backend/ee/onyx/external_permissions/jira/page_access.py @@ -65,7 +65,9 @@ def _build_holder_map(permissions: list[dict]) -> dict[str, list[Holder]]: for raw_perm in permissions: if not hasattr(raw_perm, "raw"): - logger.warning(f"Expected a 'raw' field, but none was found: {raw_perm=}") + logger.warning( + "Expected a 'raw' field, but none was found: raw_perm=%r", raw_perm + ) continue permission = Permission(**raw_perm.raw) # ty: ignore[invalid-argument-type] @@ -78,14 +80,16 @@ def _build_holder_map(permissions: list[dict]) -> dict[str, list[Holder]]: # If this doesn't exist, then we cannot associate this permission to anyone; just skip. if not permission.holder: logger.warning( - f"Expected to find a permission holder, but none was found: {permission=}" + "Expected to find a permission holder, but none was found: permission=%r", + permission, ) continue type = permission.holder.get("type") if not type: logger.warning( - f"Expected to find the type of permission holder, but none was found: {permission=}" + "Expected to find the type of permission holder, but none was found: permission=%r", + permission, ) continue @@ -106,8 +110,8 @@ def _get_user_emails(user_holders: list[Holder]) -> list[str]: user_model = User.model_validate(raw_user_dict) except ValidationError: logger.error( - "Expected to be able to serialize the raw-user-dict into an instance of `User`, but validation failed;" - f"{raw_user_dict=}" + "Expected to be able to serialize the raw-user-dict into an instance of `User`, but validation failed;raw_user_dict=%r", + raw_user_dict, ) continue @@ -132,7 +136,7 @@ def _get_user_emails_and_groups_from_project_roles( if role_id: role_ids.append(role_id) else: - logger.warning(f"No value or parameter in projectRole holder: {holder}") + logger.warning("No value or parameter in projectRole holder: %s", holder) roles = [ jira_client.project_role(project=jira_project, id=role_id) @@ -144,7 +148,7 @@ def _get_user_emails_and_groups_from_project_roles( for role in roles: if not hasattr(role, "actors"): - logger.warning(f"Project role {role} has no actors attribute") + logger.warning("Project role %s has no actors attribute", role) continue for actor in role.actors: @@ -161,13 +165,14 @@ def _get_user_emails_and_groups_from_project_roles( if hasattr(actor, "actorUser"): account_id = getattr(actor.actorUser, "accountId", None) if not account_id: - logger.error(f"No accountId in actorUser: {actor.actorUser}") + logger.error("No accountId in actorUser: %s", actor.actorUser) continue user = jira_client.user(id=account_id) if not hasattr(user, "accountType") or user.accountType != "atlassian": logger.info( - f"Skipping user {account_id} because it is not an atlassian user" + "Skipping user %s because it is not an atlassian user", + account_id, ) continue @@ -181,7 +186,7 @@ def _get_user_emails_and_groups_from_project_roles( emails.append(user.emailAddress) continue - logger.debug(f"Skipping actor type: {actor}") + logger.debug("Skipping actor type: %s", actor) return emails, groups @@ -239,7 +244,7 @@ def _build_external_access_from_holder_map( if group_name: direct_groups.append(group_name) else: - logger.error(f"No parameter/value in group holder: {group_holder}") + logger.error("No parameter/value in group holder: %s", group_holder) external_user_emails = set(user_emails + project_role_user_emails) external_user_group_ids = set(project_role_groups + direct_groups) @@ -267,11 +272,11 @@ def get_project_permissions( ) if not hasattr(project_permissions, "permissions"): - logger.error(f"Project {jira_project} has no permissions attribute") + logger.error("Project %s has no permissions attribute", jira_project) return None if not isinstance(project_permissions.permissions, list): - logger.error(f"Project {jira_project} permissions is not a list") + logger.error("Project %s permissions is not a list", jira_project) return None holder_map = _build_holder_map(permissions=project_permissions.permissions) diff --git a/backend/ee/onyx/external_permissions/post_query_censoring.py b/backend/ee/onyx/external_permissions/post_query_censoring.py index 384bcf3ccd..74b15c4db8 100644 --- a/backend/ee/onyx/external_permissions/post_query_censoring.py +++ b/backend/ee/onyx/external_permissions/post_query_censoring.py @@ -69,7 +69,9 @@ def _post_query_chunk_censoring( censored_chunks = censor_chunks_for_source(chunks_for_source, user.email) except Exception as e: logger.exception( - f"Failed to censor chunks for source {source} so throwing out all chunks for this source and continuing: {e}" + "Failed to censor chunks for source %s so throwing out all chunks for this source and continuing: %s", + source, + e, ) continue diff --git a/backend/ee/onyx/external_permissions/salesforce/postprocessing.py b/backend/ee/onyx/external_permissions/salesforce/postprocessing.py index 5c99feb72d..6f02e8f823 100644 --- a/backend/ee/onyx/external_permissions/salesforce/postprocessing.py +++ b/backend/ee/onyx/external_permissions/salesforce/postprocessing.py @@ -57,10 +57,10 @@ def _get_objects_access_for_user_email_from_salesforce( user_id = get_salesforce_user_id_from_email(salesforce_client, user_email) end_time = time.monotonic() logger.info( - f"Time taken to get Salesforce user ID: {end_time - start_time} seconds" + "Time taken to get Salesforce user ID: %s seconds", end_time - start_time ) if user_id is None: - logger.warning(f"User '{user_email}' not found in Salesforce") + logger.warning("User '%s' not found in Salesforce", user_email) return None # This is the only query that is not cached in the function @@ -68,7 +68,7 @@ def _get_objects_access_for_user_email_from_salesforce( object_id_to_access = get_objects_access_for_user_id( salesforce_client, user_id, list(object_ids) ) - logger.debug(f"Object ID to access: {object_id_to_access}") + logger.debug("Object ID to access: %s", object_id_to_access) return object_id_to_access diff --git a/backend/ee/onyx/external_permissions/sharepoint/group_sync.py b/backend/ee/onyx/external_permissions/sharepoint/group_sync.py index 15111619ef..6560818d2f 100644 --- a/backend/ee/onyx/external_permissions/sharepoint/group_sync.py +++ b/backend/ee/onyx/external_permissions/sharepoint/group_sync.py @@ -45,7 +45,7 @@ def sharepoint_group_sync( if not site_descriptors: raise RuntimeError("No SharePoint sites found for group sync") - logger.info(f"Processing {len(site_descriptors)} sites for group sync") + logger.info("Processing %s sites for group sync", len(site_descriptors)) enumerate_all = connector_config.get( "exhaustive_ad_enumeration", SHAREPOINT_EXHAUSTIVE_AD_ENUMERATION @@ -55,7 +55,7 @@ def sharepoint_group_sync( sp_tenant_domain = connector.sp_tenant_domain sp_domain_suffix = connector.sharepoint_domain_suffix for site_descriptor in site_descriptors: - logger.debug(f"Processing site: {site_descriptor.url}") + logger.debug("Processing site: %s", site_descriptor.url) ctx = ClientContext(site_descriptor.url).with_access_token( lambda: acquire_token_for_rest(msal_app, sp_tenant_domain, sp_domain_suffix) @@ -72,6 +72,6 @@ def sharepoint_group_sync( # Yield each group for group in external_groups: logger.debug( - f"Found group: {group.id} with {len(group.user_emails)} members" + "Found group: %s with %s members", group.id, len(group.user_emails) ) yield group diff --git a/backend/ee/onyx/external_permissions/sharepoint/permission_utils.py b/backend/ee/onyx/external_permissions/sharepoint/permission_utils.py index 036b283c38..e0fa9044ad 100644 --- a/backend/ee/onyx/external_permissions/sharepoint/permission_utils.py +++ b/backend/ee/onyx/external_permissions/sharepoint/permission_utils.py @@ -63,7 +63,11 @@ def _graph_api_get( ): wait = min(int(resp.headers.get("Retry-After", str(2**attempt))), 60) logger.warning( - f"Graph API {resp.status_code} on attempt {attempt + 1}, retrying in {wait}s: {url}" + "Graph API %s on attempt %s, retrying in %ss: %s", + resp.status_code, + attempt + 1, + wait, + url, ) time.sleep(wait) continue @@ -73,7 +77,10 @@ def _graph_api_get( if attempt < GRAPH_API_MAX_RETRIES: wait = min(2**attempt, 60) logger.warning( - f"Graph API connection error on attempt {attempt + 1}, retrying in {wait}s: {url}" + "Graph API connection error on attempt %s, retrying in %ss: %s", + attempt + 1, + wait, + url, ) time.sleep(wait) continue @@ -132,7 +139,7 @@ def _get_azuread_group_guid_by_name( return None except Exception as e: - logger.error(f"Failed to get Azure AD group GUID for name {group_name}: {e}") + logger.error("Failed to get Azure AD group GUID for name %s: %s", group_name, e) return None @@ -150,7 +157,7 @@ def _extract_guid_from_claims_token(claims_token: str) -> str | None: return None except Exception as e: - logger.error(f"Failed to extract GUID from claims token {claims_token}: {e}") + logger.error("Failed to extract GUID from claims token %s: %s", claims_token, e) return None @@ -167,14 +174,14 @@ def _get_group_guid_from_identifier( if identifier.startswith("c:0") and "|" in identifier: guid = _extract_guid_from_claims_token(identifier) if guid: - logger.info(f"Extracted GUID {guid} from claims token {identifier}") + logger.info("Extracted GUID %s from claims token %s", guid, identifier) return guid # Try to search by display name as fallback return _get_azuread_group_guid_by_name(graph_client, identifier) except Exception as e: - logger.error(f"Failed to get group GUID from identifier {identifier}: {e}") + logger.error("Failed to get group GUID from identifier %s: %s", identifier, e) return None @@ -188,7 +195,7 @@ def _get_security_group_owners(graph_client: GraphClient, group_id: str) -> list ) owner_emails: list[str] = [] - logger.info(f"Owners: {owners}") + logger.info("Owners: %s", owners) for owner in owners: owner_data = owner.to_json() @@ -210,12 +217,14 @@ def _get_security_group_owners(graph_client: GraphClient, group_id: str) -> list owner_emails.append(user_principal_name) logger.info( - f"Retrieved {len(owner_emails)} owners from security group {group_id}" + "Retrieved %s owners from security group %s", len(owner_emails), group_id ) return owner_emails except Exception as e: - logger.error(f"Failed to get security group owners for group {group_id}: {e}") + logger.error( + "Failed to get security group owners for group %s: %s", group_id, e + ) return [] @@ -247,7 +256,7 @@ def _get_sharepoint_list_item_id(drive_item: DriveItem) -> str | None: return None except Exception as e: logger.error( - f"Error getting SharePoint list item ID for item {drive_item.id}: {e}" + "Error getting SharePoint list item ID for item %s: %s", drive_item.id, e ) raise e @@ -271,7 +280,7 @@ def _is_public_item( return True return False except Exception as e: - logger.error(f"Failed to check if item {drive_item.id} is public: {e}") + logger.error("Failed to check if item %s is public: %s", drive_item.id, e) return False @@ -285,7 +294,7 @@ def _is_public_login_name(login_name: str) -> bool: ] for pattern in public_login_patterns: if pattern in login_name: - logger.info(f"Login name {login_name} is public") + logger.info("Login name %s is public", login_name) return True return False @@ -313,7 +322,7 @@ def _get_sharepoint_groups( # `_get_next().execute_query()`, which re-fires this `page_loaded` callback # and recurses until Python hits its max recursion depth. for user in users.current_page: - logger.debug(f"User: {user.to_json()}") + logger.debug("User: %s", user.to_json()) if user.principal_type == USER_PRINCIPAL_TYPE and hasattr( user, "user_principal_name" ): @@ -324,7 +333,7 @@ def _get_sharepoint_groups( user_emails.add(email) else: logger.warning( - f"User don't have a user principal name: {user.login_name}" + "User don't have a user principal name: %s", user.login_name ) elif user.principal_type in [ AZURE_AD_GROUP_PRINCIPAL_TYPE, @@ -357,7 +366,7 @@ def _get_azuread_groups( group_id = _get_group_guid_from_identifier(graph_client, group_name) if not group_id: - logger.error(f"Failed to get Azure AD group GUID for name {group_name}") + logger.error("Failed to get Azure AD group GUID for name %s", group_name) return set(), set() group = graph_client.groups[group_id] groups: set[SharepointGroup] = set() @@ -372,7 +381,7 @@ def _get_azuread_groups( # and recurses until Python hits its max recursion depth. for member in members.current_page: member_data = member.to_json() - logger.debug(f"Member: {member_data}") + logger.debug("Member: %s", member_data) # Check for user-specific attributes user_principal_name = member_data.get("userPrincipalName") mail = member_data.get("mail") @@ -419,10 +428,10 @@ def _get_azuread_groups( if MICROSOFT_DOMAIN in email: email = email.replace(MICROSOFT_DOMAIN, "") user_emails.add(email) - logger.info(f"Added user: {user_principal_name or mail}") + logger.info("Added user: %s", user_principal_name or mail) elif is_group: if not display_name: - logger.error(f"No display name for group: {member_data.get('id')}") + logger.error("No display name for group: %s", member_data.get("id")) continue name = _get_group_name_with_suffix( member_data.get("id", ""), display_name, graph_client @@ -434,10 +443,10 @@ def _get_azuread_groups( name=name, ) ) - logger.info(f"Added group: {name}") + logger.info("Added group: %s", name) else: # Log unidentified members for debugging - logger.warning(f"Could not identify member type for: {member_data}") + logger.warning("Could not identify member type for: %s", member_data) sleep_and_retry( group.members.get_all(page_loaded=process_members), "get_azuread_groups" @@ -469,7 +478,7 @@ def _get_groups_and_members_recursively( visited_groups.add(group.login_name) visited_group_name_to_emails[group.name] = set() logger.info( - f"Processing group: {group.name} principal type: {group.principal_type}" + "Processing group: %s principal type: %s", group.name, group.principal_type ) if group.principal_type == SHAREPOINT_GROUP_PRINCIPAL_TYPE: group_info, user_emails = _get_sharepoint_groups( @@ -501,7 +510,7 @@ def _get_groups_and_members_recursively( # in sharepoint but it is removed from Azure AD. There is no actual documentation on this, but based on # our testing we have seen this happen. if e.response is not None and e.response.status_code == 404: - logger.warning(f"Group {group.login_name} not found") + logger.warning("Group %s not found", group.login_name) continue raise e @@ -537,7 +546,7 @@ def get_external_access_from_sharepoint( # via `_get_next().execute_query()`, which re-fires this `page_loaded` # callback and recurses until Python hits its max recursion depth. for assignment in role_assignments.current_page: - logger.debug(f"Assignment: {assignment.to_json()}") + logger.debug("Assignment: %s", assignment.to_json()) if assignment.role_definition_bindings: is_limited_access = True for role_definition_binding in assignment.role_definition_bindings: @@ -584,7 +593,7 @@ def get_external_access_from_sharepoint( if drive_item and drive_name: is_public = _is_public_item(drive_item, treat_sharing_link_as_public) if is_public: - logger.info(f"Item {drive_item.id} is public") + logger.info("Item %s is public", drive_item.id) return ExternalAccess( external_user_emails=set(), external_user_group_ids=set(), @@ -652,8 +661,8 @@ def get_external_access_from_sharepoint( ) group_ids.add(group_name.lower()) - logger.info(f"User emails: {len(user_emails)}") - logger.info(f"Group IDs: {len(group_ids)}") + logger.info("User emails: %s", len(user_emails)) + logger.info("Group IDs: %s", len(group_ids)) return ExternalAccess( external_user_emails=user_emails, @@ -687,9 +696,8 @@ def _enumerate_ad_groups_paginated( total_groups += 1 if total_groups > AD_GROUP_ENUMERATION_THRESHOLD: logger.warning( - f"Azure AD group enumeration exceeded {AD_GROUP_ENUMERATION_THRESHOLD} " - "groups — stopping to avoid excessive memory/API usage. " - "Remaining groups will be resolved from role assignments only." + "Azure AD group enumeration exceeded %s groups — stopping to avoid excessive memory/API usage. Remaining groups will be resolved from role assignments only.", + AD_GROUP_ENUMERATION_THRESHOLD, ) return @@ -712,7 +720,7 @@ def _enumerate_ad_groups_paginated( yield ExternalUserGroup(id=name, user_emails=member_emails) - logger.info(f"Enumerated {total_groups} Azure AD groups via paginated Graph API") + logger.info("Enumerated %s Azure AD groups via paginated Graph API", total_groups) def get_sharepoint_external_groups( diff --git a/backend/ee/onyx/external_permissions/utils.py b/backend/ee/onyx/external_permissions/utils.py index 6538c90d8e..e9152249a5 100644 --- a/backend/ee/onyx/external_permissions/utils.py +++ b/backend/ee/onyx/external_permissions/utils.py @@ -39,7 +39,7 @@ def generic_doc_sync( A `Generator` which yields existing and newly fetched external-access permissions. """ - logger.info(f"Starting {doc_source} doc sync for CC Pair ID: {cc_pair.id}") + logger.info("Starting %s doc sync for CC Pair ID: %s", doc_source, cc_pair.id) indexing_start: SecondsSinceUnixEpoch | None = ( cc_pair.connector.indexing_start.timestamp() @@ -49,12 +49,12 @@ def generic_doc_sync( newly_fetched_doc_ids: set[str] = set() - logger.info(f"Fetching all slim documents from {doc_source}") + logger.info("Fetching all slim documents from %s", doc_source) for doc_batch in slim_connector.retrieve_all_slim_docs_perm_sync( start=indexing_start, callback=callback, ): - logger.info(f"Got {len(doc_batch)} slim documents from {doc_source}") + logger.info("Got %s slim documents from %s", len(doc_batch), doc_source) if callback: if callback.should_stop(): @@ -83,7 +83,9 @@ def generic_doc_sync( external_access=doc.external_access, ) - logger.info(f"Querying existing document IDs for CC Pair ID: {cc_pair.id=}") + logger.info( + "Querying existing document IDs for CC Pair ID: cc_pair.id=%r", cc_pair.id + ) existing_doc_ids: list[str] = fetch_all_existing_docs_ids_fn() missing_doc_ids = set(existing_doc_ids) - newly_fetched_doc_ids @@ -92,14 +94,15 @@ def generic_doc_sync( return logger.warning( - f"Found {len(missing_doc_ids)=} documents that are in the DB but not present in fetch. Making them inaccessible." + "Found len(missing_doc_ids)=%r documents that are in the DB but not present in fetch. Making them inaccessible.", + len(missing_doc_ids), ) for missing_id in missing_doc_ids: - logger.warning(f"Removing access for {missing_id=}") + logger.warning("Removing access for missing_id=%r", missing_id) yield DocExternalAccess( doc_id=missing_id, external_access=ExternalAccess.empty(), ) - logger.info(f"Finished {doc_source} doc sync") + logger.info("Finished %s doc sync", doc_source) diff --git a/backend/ee/onyx/feature_flags/posthog_provider.py b/backend/ee/onyx/feature_flags/posthog_provider.py index 341d6fc62b..9c00323878 100644 --- a/backend/ee/onyx/feature_flags/posthog_provider.py +++ b/backend/ee/onyx/feature_flags/posthog_provider.py @@ -52,6 +52,6 @@ class PostHogFeatureFlagProvider(FeatureFlagProvider): except Exception as e: logger.error( - f"Error checking feature flag {flag_key} for user {user_id}: {e}" + "Error checking feature flag %s for user %s: %s", flag_key, user_id, e ) return False diff --git a/backend/ee/onyx/hooks/executor.py b/backend/ee/onyx/hooks/executor.py index f3802253d4..87e26745da 100644 --- a/backend/ee/onyx/hooks/executor.py +++ b/backend/ee/onyx/hooks/executor.py @@ -243,7 +243,7 @@ def _persist_result( log_session.commit() except Exception: logger.exception( - f"Failed to persist hook execution log for hook_id={hook_id}" + "Failed to persist hook execution log for hook_id=%s", hook_id ) # Update is_reachable separately — best-effort, non-critical. @@ -260,7 +260,7 @@ def _persist_result( ) reachable_session.commit() except Exception: - logger.warning(f"Failed to update is_reachable for hook_id={hook_id}") + logger.warning("Failed to update is_reachable for hook_id=%s", hook_id) # --------------------------------------------------------------------------- @@ -342,7 +342,9 @@ def _execute_hook_inner( outcome.error_message or "Hook execution failed.", ) logger.warning( - f"Hook execution failed (soft fail) for hook_id={hook_id}: {outcome.error_message}" + "Hook execution failed (soft fail) for hook_id=%s: %s", + hook_id, + outcome.error_message, ) return HookSoftFailed() @@ -379,7 +381,7 @@ def _execute_hook_impl( except Exception: if fail_strategy == HookFailStrategy.SOFT: logger.exception( - f"Unexpected error in hook execution (soft fail) for hook_id={hook_id}" + "Unexpected error in hook execution (soft fail) for hook_id=%s", hook_id ) return HookSoftFailed() raise diff --git a/backend/ee/onyx/onyxbot/slack/handlers/handle_standard_answers.py b/backend/ee/onyx/onyxbot/slack/handlers/handle_standard_answers.py index 73d562818b..e875111622 100644 --- a/backend/ee/onyx/onyxbot/slack/handlers/handle_standard_answers.py +++ b/backend/ee/onyx/onyxbot/slack/handlers/handle_standard_answers.py @@ -232,7 +232,7 @@ def _handle_standard_answers( return True except Exception as e: - logger.exception(f"Unable to send standard answer message: {e}") + logger.exception("Unable to send standard answer message: %s", e) return False else: return False diff --git a/backend/ee/onyx/search/process_search_query.py b/backend/ee/onyx/search/process_search_query.py index 63a80a0eff..fec0f874d4 100644 --- a/backend/ee/onyx/search/process_search_query.py +++ b/backend/ee/onyx/search/process_search_query.py @@ -94,10 +94,11 @@ def stream_search_query( ) if keyword_expansions: logger.debug( - f"Query expansion generated {len(keyword_expansions)} keyword queries" + "Query expansion generated %s keyword queries", + len(keyword_expansions), ) except Exception as e: - logger.warning(f"Query expansion failed: {e}; using original query only.") + logger.warning("Query expansion failed: %s; using original query only.", e) keyword_expansions = [] # Build list of all executed queries for tracking @@ -218,12 +219,14 @@ def stream_search_query( ) ) logger.debug( - f"LLM document selection evaluated {len(sections_to_evaluate)} sections, " - f"selected {len(selected_sections)} sections with doc IDs: {llm_selected_doc_ids}" + "LLM document selection evaluated %s sections, selected %s sections with doc IDs: %s", + len(sections_to_evaluate), + len(selected_sections), + llm_selected_doc_ids, ) except Exception as e: # Allowing a blanket exception here as this step is not critical and the rest of the results are still valid - logger.warning(f"LLM document selection failed: {e}") + logger.warning("LLM document selection failed: %s", e) llm_selection_failed = True elif run_llm_selection and not sections: # LLM selection requested but no sections to evaluate diff --git a/backend/ee/onyx/secondary_llm_flows/query_expansion.py b/backend/ee/onyx/secondary_llm_flows/query_expansion.py index 80ca1b66c9..2bb52b117d 100644 --- a/backend/ee/onyx/secondary_llm_flows/query_expansion.py +++ b/backend/ee/onyx/secondary_llm_flows/query_expansion.py @@ -84,9 +84,9 @@ def expand_keywords( seen_lower.add(query_lower) expanded_queries.append(query) - logger.debug(f"Keyword expansion generated {len(expanded_queries)} queries") + logger.debug("Keyword expansion generated %s queries", len(expanded_queries)) return expanded_queries except Exception as e: - logger.warning(f"Keyword expansion failed: {e}") + logger.warning("Keyword expansion failed: %s", e) return [] diff --git a/backend/ee/onyx/server/billing/api.py b/backend/ee/onyx/server/billing/api.py index 41f2610794..26ed206b43 100644 --- a/backend/ee/onyx/server/billing/api.py +++ b/backend/ee/onyx/server/billing/api.py @@ -89,11 +89,13 @@ def _is_billing_circuit_open() -> bool: redis_client = get_shared_redis_client() is_open = bool(redis_client.exists(BILLING_CIRCUIT_BREAKER_KEY)) logger.debug( - f"Circuit breaker check: key={BILLING_CIRCUIT_BREAKER_KEY}, is_open={is_open}" + "Circuit breaker check: key=%s, is_open=%s", + BILLING_CIRCUIT_BREAKER_KEY, + is_open, ) return is_open except Exception as e: - logger.error(f"Failed to check circuit breaker: {e}") + logger.error("Failed to check circuit breaker: %s", e) return False @@ -111,11 +113,12 @@ def _open_billing_circuit() -> None: # Verify it was set exists = redis_client.exists(BILLING_CIRCUIT_BREAKER_KEY) logger.warning( - f"Billing circuit breaker opened (TTL={BILLING_CIRCUIT_BREAKER_TTL_SECONDS}s, " - f"verified={exists}). Stripe billing requests are disabled until manually reset." + "Billing circuit breaker opened (TTL=%ss, verified=%s). Stripe billing requests are disabled until manually reset.", + BILLING_CIRCUIT_BREAKER_TTL_SECONDS, + exists, ) except Exception as e: - logger.error(f"Failed to open circuit breaker: {e}") + logger.error("Failed to open circuit breaker: %s", e) def _close_billing_circuit() -> None: @@ -129,7 +132,7 @@ def _close_billing_circuit() -> None: "Billing circuit breaker closed. Stripe billing requests re-enabled." ) except Exception as e: - logger.error(f"Failed to close circuit breaker: {e}") + logger.error("Failed to close circuit breaker: %s", e) def _get_license_data(db_session: Session) -> str | None: diff --git a/backend/ee/onyx/server/billing/service.py b/backend/ee/onyx/server/billing/service.py index 819128c8ff..f0c18c0bcf 100644 --- a/backend/ee/onyx/server/billing/service.py +++ b/backend/ee/onyx/server/billing/service.py @@ -122,7 +122,7 @@ async def _make_billing_request( detail = error_data.get("detail", detail) except Exception: pass - logger.error(f"{error_message}: {e.response.status_code} - {detail}") + logger.error("%s: %s - %s", error_message, e.response.status_code, detail) raise OnyxError( OnyxErrorCode.BAD_GATEWAY, detail, diff --git a/backend/ee/onyx/server/documents/cc_pair.py b/backend/ee/onyx/server/documents/cc_pair.py index fb66a75fb8..bfe242358b 100644 --- a/backend/ee/onyx/server/documents/cc_pair.py +++ b/backend/ee/onyx/server/documents/cc_pair.py @@ -81,10 +81,11 @@ def sync_cc_pair( ) logger.info( - f"Permissions sync cc_pair={cc_pair_id} " - f"connector_id={cc_pair.connector_id} " - f"credential_id={cc_pair.credential_id} " - f"{cc_pair.connector.name} connector." + "Permissions sync cc_pair=%s connector_id=%s credential_id=%s %s connector.", + cc_pair_id, + cc_pair.connector_id, + cc_pair.credential_id, + cc_pair.connector.name, ) payload_id = try_creating_permissions_sync_task( client_app, cc_pair_id, r, tenant_id @@ -95,7 +96,7 @@ def sync_cc_pair( detail="Permissions sync task creation failed.", ) - logger.info(f"Permissions sync queued: cc_pair={cc_pair_id} id={payload_id}") + logger.info("Permissions sync queued: cc_pair=%s id=%s", cc_pair_id, payload_id) return StatusResponse( success=True, @@ -155,10 +156,11 @@ def sync_cc_pair_groups( ) logger.info( - f"External group sync cc_pair={cc_pair_id} " - f"connector_id={cc_pair.connector_id} " - f"credential_id={cc_pair.credential_id} " - f"{cc_pair.connector.name} connector." + "External group sync cc_pair=%s connector_id=%s credential_id=%s %s connector.", + cc_pair_id, + cc_pair.connector_id, + cc_pair.credential_id, + cc_pair.connector.name, ) payload_id = try_creating_external_group_sync_task( client_app, cc_pair_id, r, tenant_id @@ -169,7 +171,7 @@ def sync_cc_pair_groups( detail="External group sync task creation failed.", ) - logger.info(f"External group sync queued: cc_pair={cc_pair_id} id={payload_id}") + logger.info("External group sync queued: cc_pair=%s id=%s", cc_pair_id, payload_id) return StatusResponse( success=True, diff --git a/backend/ee/onyx/server/enterprise_settings/api.py b/backend/ee/onyx/server/enterprise_settings/api.py index 0e969c8035..6c3f9c6ac6 100644 --- a/backend/ee/onyx/server/enterprise_settings/api.py +++ b/backend/ee/onyx/server/enterprise_settings/api.py @@ -70,7 +70,7 @@ async def refresh_access_token( user_manager: UserManager = Depends(get_user_manager), ) -> None: try: - logger.debug(f"Received response from Meechum auth URL for user {user.id}") + logger.debug("Received response from Meechum auth URL for user %s", user.id) # Extract new tokens new_access_token = refresh_token.access_token @@ -81,7 +81,7 @@ async def refresh_access_token( ) expires_at_timestamp = int(new_expiry.timestamp()) - logger.debug(f"Access token has been refreshed for user {user.id}") + logger.debug("Access token has been refreshed for user %s", user.id) await user_manager.oauth_callback( oauth_name="custom", @@ -93,17 +93,19 @@ async def refresh_access_token( associate_by_email=True, ) - logger.info(f"Successfully refreshed tokens for user {user.id}") + logger.info("Successfully refreshed tokens for user %s", user.id) except httpx.HTTPStatusError as e: if e.response.status_code == 401: - logger.warning(f"Full authentication required for user {user.id}") + logger.warning("Full authentication required for user %s", user.id) raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail="Full authentication required", ) logger.error( - f"HTTP error occurred while refreshing token for user {user.id}: {str(e)}" + "HTTP error occurred while refreshing token for user %s: %s", + user.id, + str(e), ) raise HTTPException( status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, @@ -111,7 +113,9 @@ async def refresh_access_token( ) except Exception as e: logger.error( - f"Unexpected error occurred while refreshing token for user {user.id}: {str(e)}" + "Unexpected error occurred while refreshing token for user %s: %s", + user.id, + str(e), ) raise HTTPException( status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, diff --git a/backend/ee/onyx/server/enterprise_settings/store.py b/backend/ee/onyx/server/enterprise_settings/store.py index 54f377719f..2ff446d2e3 100644 --- a/backend/ee/onyx/server/enterprise_settings/store.py +++ b/backend/ee/onyx/server/enterprise_settings/store.py @@ -101,7 +101,7 @@ def upload_logo(file: UploadFile | str, is_logotype: bool = False) -> bool: content: IO[Any] if isinstance(file, str): - logger.notice(f"Uploading logo from local path {file}") + logger.notice("Uploading logo from local path %s", file) if not os.path.isfile(file) or not is_valid_file_type(file): logger.error( "Invalid file type- only .png, .jpg, and .jpeg files are allowed" diff --git a/backend/ee/onyx/server/license/api.py b/backend/ee/onyx/server/license/api.py index a26e38668a..b9b8e0870f 100644 --- a/backend/ee/onyx/server/license/api.py +++ b/backend/ee/onyx/server/license/api.py @@ -187,10 +187,12 @@ async def claim_license( try: update_license_cache(payload, source=LicenseSource.AUTO_FETCH) except Exception as cache_error: - logger.warning(f"Failed to update license cache: {cache_error}") + logger.warning("Failed to update license cache: %s", cache_error) logger.info( - f"License claimed: seats={payload.seats}, expires={payload.expires_at.date()}" + "License claimed: seats=%s, expires=%s", + payload.seats, + payload.expires_at.date(), ) return LicenseResponse(success=True, license=payload) @@ -254,7 +256,7 @@ async def upload_license( try: update_license_cache(payload, source=LicenseSource.MANUAL_UPLOAD) except Exception as cache_error: - logger.warning(f"Failed to update license cache: {cache_error}") + logger.warning("Failed to update license cache: %s", cache_error) return LicenseUploadResponse( success=True, @@ -310,7 +312,7 @@ async def delete_license( try: invalidate_license_cache() except Exception as cache_error: - logger.warning(f"Failed to invalidate license cache: {cache_error}") + logger.warning("Failed to invalidate license cache: %s", cache_error) deleted = db_delete_license(db_session) diff --git a/backend/ee/onyx/server/middleware/license_enforcement.py b/backend/ee/onyx/server/middleware/license_enforcement.py index 03a73c4367..3458d528ab 100644 --- a/backend/ee/onyx/server/middleware/license_enforcement.py +++ b/backend/ee/onyx/server/middleware/license_enforcement.py @@ -113,7 +113,8 @@ def add_license_enforcement_middleware( ) except SQLAlchemyError as db_error: logger.warning( - f"[license_enforcement] Failed to check database for license: {db_error}" + "[license_enforcement] Failed to check database for license: %s", + db_error, ) if metadata: @@ -129,8 +130,9 @@ def add_license_enforcement_middleware( # when users are added/removed if metadata.used_seats > metadata.seats: logger.info( - f"[license_enforcement] Blocking request: " - f"seat limit exceeded ({metadata.used_seats}/{metadata.seats})" + "[license_enforcement] Blocking request: seat limit exceeded (%s/%s)", + metadata.used_seats, + metadata.seats, ) return JSONResponse( status_code=402, @@ -148,7 +150,8 @@ def add_license_enforcement_middleware( # Allow community features, but block EE-only features if _is_ee_only_path(path): logger.info( - f"[license_enforcement] Blocking EE-only path (no license): {path}" + "[license_enforcement] Blocking EE-only path (no license): %s", + path, ) return JSONResponse( status_code=402, @@ -165,13 +168,13 @@ def add_license_enforcement_middleware( ) is_gated = False except CACHE_TRANSIENT_ERRORS as e: - logger.warning(f"Failed to check license metadata: {e}") + logger.warning("Failed to check license metadata: %s", e) # Fail open - don't block users due to cache connectivity issues is_gated = False if is_gated: logger.info( - f"[license_enforcement] Blocking request (license expired): {path}" + "[license_enforcement] Blocking request (license expired): %s", path ) return JSONResponse( diff --git a/backend/ee/onyx/server/middleware/tenant_tracking.py b/backend/ee/onyx/server/middleware/tenant_tracking.py index 6bdebef94f..bf171d3200 100644 --- a/backend/ee/onyx/server/middleware/tenant_tracking.py +++ b/backend/ee/onyx/server/middleware/tenant_tracking.py @@ -40,7 +40,7 @@ def add_api_server_tenant_id_middleware( return await call_next(request) except Exception as e: - logger.exception(f"Error in tenant ID middleware: {str(e)}") + logger.exception("Error in tenant ID middleware: %s", str(e)) raise @@ -97,7 +97,7 @@ async def _get_tenant_id_from_request( return tenant_id except Exception as e: - logger.error(f"Error decoding anonymous user cookie: {str(e)}") + logger.error("Error decoding anonymous user cookie: %s", str(e)) # Continue and attempt to authenticate logger.debug( @@ -110,7 +110,7 @@ async def _get_tenant_id_from_request( return POSTGRES_DEFAULT_SCHEMA except Exception as e: - logger.error(f"Unexpected error in _get_tenant_id_from_request: {str(e)}") + logger.error("Unexpected error in _get_tenant_id_from_request: %s", str(e)) raise HTTPException(status_code=500, detail="Internal server error") finally: diff --git a/backend/ee/onyx/server/query_and_chat/query_backend.py b/backend/ee/onyx/server/query_and_chat/query_backend.py index 66569137f1..8b866ac49a 100644 --- a/backend/ee/onyx/server/query_and_chat/query_backend.py +++ b/backend/ee/onyx/server/query_and_chat/query_backend.py @@ -33,5 +33,5 @@ def get_standard_answer( ) return StandardAnswerResponse(standard_answers=standard_answers) except Exception as e: - logger.error(f"Error in get_standard_answer: {str(e)}", exc_info=True) + logger.error("Error in get_standard_answer: %s", str(e), exc_info=True) raise HTTPException(status_code=500, detail="An internal server error occurred") diff --git a/backend/ee/onyx/server/query_and_chat/search_backend.py b/backend/ee/onyx/server/query_and_chat/search_backend.py index c1aa7d8225..d6e579dce8 100644 --- a/backend/ee/onyx/server/query_and_chat/search_backend.py +++ b/backend/ee/onyx/server/query_and_chat/search_backend.py @@ -92,7 +92,7 @@ def handle_send_search_message( Returns: StreamingResponse with SSE if stream=True, otherwise SearchFullResponse. """ - logger.debug(f"Received search query: {request.search_query}") + logger.debug("Received search query: %s", request.search_query) if request.hybrid_alpha is None and ONYX_SEARCH_UI_USES_OPENSEARCH_KEYWORD_SEARCH: request.hybrid_alpha = 0.0 diff --git a/backend/ee/onyx/server/scim/api.py b/backend/ee/onyx/server/scim/api.py index 99bf0ae674..dc770a8030 100644 --- a/backend/ee/onyx/server/scim/api.py +++ b/backend/ee/onyx/server/scim/api.py @@ -569,7 +569,7 @@ def create_user( assign_user_to_default_groups__no_commit(db_session, user) except Exception: dal.rollback() - logger.exception(f"Failed to assign SCIM user {email} to default groups") + logger.exception("Failed to assign SCIM user %s to default groups", email) return _scim_error_response( 500, f"Failed to assign user {email} to default group" ) diff --git a/backend/ee/onyx/server/seeding.py b/backend/ee/onyx/server/seeding.py index 2e2347539f..b21a6dcb1c 100644 --- a/backend/ee/onyx/server/seeding.py +++ b/backend/ee/onyx/server/seeding.py @@ -79,8 +79,8 @@ def _seed_custom_tools(db_session: Session, tools: List[CustomToolSeed]) -> None logger.notice("Seeding Custom Tools") for tool in tools: try: - logger.debug(f"Attempting to seed tool: {tool.name}") - logger.debug(f"Reading definition from: {tool.definition_path}") + logger.debug("Attempting to seed tool: %s", tool.name) + logger.debug("Reading definition from: %s", tool.definition_path) with open(tool.definition_path, "r") as file: file_content = file.read() if not file_content.strip(): @@ -96,19 +96,21 @@ def _seed_custom_tools(db_session: Session, tools: List[CustomToolSeed]) -> None user_id=tool.user_id, ) db_session.add(db_tool) - logger.debug(f"Successfully added tool: {tool.name}") + logger.debug("Successfully added tool: %s", tool.name) except FileNotFoundError: logger.error( - f"Definition file not found for tool {tool.name}: {tool.definition_path}" + "Definition file not found for tool %s: %s", + tool.name, + tool.definition_path, ) except json.JSONDecodeError as e: logger.error( - f"Invalid JSON in definition file for tool {tool.name}: {str(e)}" + "Invalid JSON in definition file for tool %s: %s", tool.name, str(e) ) except Exception as e: - logger.error(f"Failed to seed tool {tool.name}: {str(e)}") + logger.error("Failed to seed tool %s: %s", tool.name, str(e)) db_session.commit() - logger.notice(f"Successfully seeded {len(tools)} Custom Tools") + logger.notice("Successfully seeded %s Custom Tools", len(tools)) def _seed_llms( @@ -189,7 +191,7 @@ def _seed_settings(settings: Settings) -> None: store_base_settings(settings) logger.notice("Successfully seeded Settings") except ValueError as e: - logger.error(f"Failed to seed Settings: {str(e)}") + logger.error("Failed to seed Settings: %s", str(e)) def _seed_enterprise_settings(seed_config: SeedConfiguration) -> None: @@ -243,10 +245,10 @@ def _seed_analytics_script(seed_config: SeedConfiguration) -> None: store_analytics_script(analytics_script) except FileNotFoundError: logger.error( - f"Analytics script file not found: {seed_config.analytics_script_path}" + "Analytics script file not found: %s", seed_config.analytics_script_path ) except ValueError as e: - logger.error(f"Failed to seed analytics script: {str(e)}") + logger.error("Failed to seed analytics script: %s", str(e)) def get_seed_config() -> SeedConfiguration | None: diff --git a/backend/ee/onyx/server/settings/api.py b/backend/ee/onyx/server/settings/api.py index a79eb54629..9125e3892a 100644 --- a/backend/ee/onyx/server/settings/api.py +++ b/backend/ee/onyx/server/settings/api.py @@ -51,13 +51,13 @@ def check_ee_features_enabled() -> bool: with get_session_with_current_tenant() as db_session: metadata = refresh_license_cache(db_session, tenant_id) except SQLAlchemyError as db_error: - logger.warning(f"Failed to load license from DB: {db_error}") + logger.warning("Failed to load license from DB: %s", db_error) if metadata and metadata.status != _BLOCKING_STATUS: # Has a valid license (GRACE_PERIOD/PAYMENT_REMINDER still allow EE features) return True except RedisError as e: - logger.warning(f"Failed to check license for EE features: {e}") + logger.warning("Failed to check license for EE features: %s", e) # Fail closed - if Redis is down, other things will break anyway return False @@ -103,7 +103,7 @@ def apply_license_status_to_settings(settings: Settings) -> Settings: metadata = refresh_license_cache(db_session, tenant_id) except SQLAlchemyError as db_error: logger.warning( - f"Failed to load license from DB for settings: {db_error}" + "Failed to load license from DB for settings: %s", db_error ) if metadata: @@ -127,7 +127,7 @@ def apply_license_status_to_settings(settings: Settings) -> Settings: settings.application_status = _BLOCKING_STATUS settings.ee_features_enabled = False except CACHE_TRANSIENT_ERRORS as e: - logger.warning(f"Failed to check license metadata for settings: {e}") + logger.warning("Failed to check license metadata for settings: %s", e) # Fail closed - disable EE features if we can't verify license settings.ee_features_enabled = False diff --git a/backend/ee/onyx/server/tenant_usage_limits.py b/backend/ee/onyx/server/tenant_usage_limits.py index 827595c88f..60eb3ce86f 100644 --- a/backend/ee/onyx/server/tenant_usage_limits.py +++ b/backend/ee/onyx/server/tenant_usage_limits.py @@ -49,7 +49,9 @@ def fetch_usage_limit_overrides() -> dict[str, TenantUsageLimitOverrides] | None result[tenant_id] = TenantUsageLimitOverrides(**override_data) except Exception as e: logger.warning( - f"Failed to parse usage limit overrides for tenant {tenant_id}: {e}" + "Failed to parse usage limit overrides for tenant %s: %s", + tenant_id, + e, ) return ( @@ -57,10 +59,12 @@ def fetch_usage_limit_overrides() -> dict[str, TenantUsageLimitOverrides] | None ) # if empty dictionary, something went wrong and we shouldn't enforce limits except requests.exceptions.RequestException as e: - logger.warning(f"Failed to fetch usage limit overrides from control plane: {e}") + logger.warning( + "Failed to fetch usage limit overrides from control plane: %s", e + ) return None except Exception as e: - logger.error(f"Error parsing usage limit overrides: {e}") + logger.error("Error parsing usage limit overrides: %s", e) return None @@ -81,7 +85,7 @@ def load_usage_limit_overrides() -> None: _tenant_usage_limit_overrides = overrides or _tenant_usage_limit_overrides if overrides: - logger.info(f"Loaded usage limit overrides for {len(overrides)} tenants") + logger.info("Loaded usage limit overrides for %s tenants", len(overrides)) else: logger.info("No tenant-specific usage limit overrides found") @@ -122,7 +126,9 @@ def get_tenant_usage_limit_overrides( _tenant_usage_limit_overrides is None and time_since > _ERROR_FETCH_INTERVAL ) or (time_since > _FETCH_INTERVAL): logger.debug( - f"Last fetch time: {_last_fetch_time}, time since last fetch: {time_since}" + "Last fetch time: %s, time since last fetch: %s", + _last_fetch_time, + time_since, ) load_usage_limit_overrides() diff --git a/backend/ee/onyx/server/tenants/anonymous_users_api.py b/backend/ee/onyx/server/tenants/anonymous_users_api.py index 54194fd516..8a0c552d34 100644 --- a/backend/ee/onyx/server/tenants/anonymous_users_api.py +++ b/backend/ee/onyx/server/tenants/anonymous_users_api.py @@ -62,7 +62,7 @@ async def set_anonymous_user_path_api( detail="The anonymous user path is already in use. Please choose a different path.", ) except Exception as e: - logger.exception(f"Failed to modify anonymous user path: {str(e)}") + logger.exception("Failed to modify anonymous user path: %s", str(e)) raise HTTPException( status_code=500, detail="An unexpected error occurred while modifying the anonymous user path", diff --git a/backend/ee/onyx/server/tenants/provisioning.py b/backend/ee/onyx/server/tenants/provisioning.py index 2d932e2e54..55b88cc719 100644 --- a/backend/ee/onyx/server/tenants/provisioning.py +++ b/backend/ee/onyx/server/tenants/provisioning.py @@ -109,16 +109,21 @@ async def get_or_provision_tenant( # The tenant was already dequeued from the pool — roll it back so # it doesn't end up orphaned (schema exists, but not assigned to anyone). logger.exception( - f"Migration failed for pre-provisioned tenant {_tenant_id}; rolling back" + "Migration failed for pre-provisioned tenant %s; rolling back", + _tenant_id, ) try: await rollback_tenant_provisioning(_tenant_id) except Exception: - logger.exception(f"Failed to rollback orphaned tenant {_tenant_id}") + logger.exception( + "Failed to rollback orphaned tenant %s", _tenant_id + ) raise # If we have a pre-provisioned tenant, assign it to the user await assign_tenant_to_user(tenant_id, email, referral_source) - logger.info(f"Assigned pre-provisioned tenant {tenant_id} to user {email}") + logger.info( + "Assigned pre-provisioned tenant %s to user %s", tenant_id, email + ) else: # If no pre-provisioned tenant is available, create a new one on-demand tenant_id = await create_tenant(email, referral_source) @@ -149,19 +154,19 @@ async def create_tenant( """ tenant_id = TENANT_ID_PREFIX + str(uuid.uuid4()) - logger.info(f"Creating new tenant {tenant_id} for user {email}") + logger.info("Creating new tenant %s for user %s", tenant_id, email) try: # Provision tenant on data plane await provision_tenant(tenant_id, email) except Exception as e: - logger.exception(f"Tenant provisioning failed: {str(e)}") + logger.exception("Tenant provisioning failed: %s", str(e)) # Attempt to rollback the tenant provisioning try: await rollback_tenant_provisioning(tenant_id) except Exception: - logger.exception(f"Failed to rollback tenant provisioning for {tenant_id}") + logger.exception("Failed to rollback tenant provisioning for %s", tenant_id) raise HTTPException(status_code=500, detail="Failed to provision tenant.") return tenant_id @@ -176,14 +181,14 @@ async def provision_tenant(tenant_id: str, email: str) -> None: status_code=409, detail="User already belongs to an organization" ) - logger.debug(f"Provisioning tenant {tenant_id} for user {email}") + logger.debug("Provisioning tenant %s for user %s", tenant_id, email) try: # Create the schema for the tenant if not create_schema_if_not_exists(tenant_id): - logger.debug(f"Created schema for tenant {tenant_id}") + logger.debug("Created schema for tenant %s", tenant_id) else: - logger.debug(f"Schema already exists for tenant {tenant_id}") + logger.debug("Schema already exists for tenant %s", tenant_id) # Set up the tenant with all necessary configurations await setup_tenant(tenant_id) @@ -192,7 +197,7 @@ async def provision_tenant(tenant_id: str, email: str) -> None: await assign_tenant_to_user(tenant_id, email) except Exception as e: - logger.exception(f"Failed to create tenant {tenant_id}") + logger.exception("Failed to create tenant %s", tenant_id) raise HTTPException( status_code=500, detail=f"Failed to create tenant: {str(e)}" ) @@ -219,7 +224,7 @@ async def notify_control_plane( ) as response: if response.status != 200: error_text = await response.text() - logger.error(f"Control plane tenant creation failed: {error_text}") + logger.error("Control plane tenant creation failed: %s", error_text) raise Exception( f"Failed to create tenant on control plane: {error_text}" ) @@ -230,7 +235,7 @@ async def rollback_tenant_provisioning(tenant_id: str) -> None: Logic to rollback tenant provisioning on data plane. Handles each step independently to ensure maximum cleanup even if some steps fail. """ - logger.info(f"Rolling back tenant provisioning for tenant_id: {tenant_id}") + logger.info("Rolling back tenant provisioning for tenant_id: %s", tenant_id) # Track if any part of the rollback fails rollback_errors = [] @@ -238,7 +243,7 @@ async def rollback_tenant_provisioning(tenant_id: str) -> None: # 1. Try to drop the tenant's schema try: drop_schema(tenant_id) - logger.info(f"Successfully dropped schema for tenant {tenant_id}") + logger.info("Successfully dropped schema for tenant %s", tenant_id) except Exception as e: error_msg = f"Failed to drop schema for tenant {tenant_id}: {str(e)}" logger.error(error_msg) @@ -254,7 +259,7 @@ async def rollback_tenant_provisioning(tenant_id: str) -> None: ).delete() db_session.commit() logger.info( - f"Successfully removed user mappings for tenant {tenant_id}" + "Successfully removed user mappings for tenant %s", tenant_id ) except Exception as e: db_session.rollback() @@ -279,7 +284,7 @@ async def rollback_tenant_provisioning(tenant_id: str) -> None: db_session.delete(available_tenant) db_session.commit() logger.info( - f"Removed tenant {tenant_id} from available tenants table" + "Removed tenant %s from available tenants table", tenant_id ) except Exception as e: db_session.rollback() @@ -291,9 +296,9 @@ async def rollback_tenant_provisioning(tenant_id: str) -> None: # Log summary of rollback operation if rollback_errors: - logger.error(f"Tenant rollback completed with {len(rollback_errors)} errors") + logger.error("Tenant rollback completed with %s errors", len(rollback_errors)) else: - logger.info(f"Tenant rollback completed successfully for tenant {tenant_id}") + logger.info("Tenant rollback completed successfully for tenant %s", tenant_id) def _build_model_configuration_upsert_requests( @@ -334,14 +339,14 @@ def configure_default_api_keys(db_session: Session) -> None: update_default_provider(provider.id, default_model, db_session) has_set_default_provider = True except Exception as e: - logger.error(f"Failed to configure {request.provider} provider: {e}") + logger.error("Failed to configure %s provider: %s", request.provider, e) # Configure OpenAI provider if OPENAI_DEFAULT_API_KEY: default_model = recommendations.get_default_model(OPENAI_PROVIDER_NAME) if default_model is None: logger.error( - f"No default model found for {OPENAI_PROVIDER_NAME} in recommendations" + "No default model found for %s in recommendations", OPENAI_PROVIDER_NAME ) default_model_name = default_model.name if default_model else "gpt-5.2" @@ -363,7 +368,7 @@ def configure_default_api_keys(db_session: Session) -> None: db_session, OPENAI_DEFAULT_API_KEY ) except Exception as e: - logger.error(f"Failed to create default image gen config: {e}") + logger.error("Failed to create default image gen config: %s", e) else: logger.info( "OPENAI_DEFAULT_API_KEY not set, skipping OpenAI provider configuration" @@ -374,7 +379,8 @@ def configure_default_api_keys(db_session: Session) -> None: default_model = recommendations.get_default_model(ANTHROPIC_PROVIDER_NAME) if default_model is None: logger.error( - f"No default model found for {ANTHROPIC_PROVIDER_NAME} in recommendations" + "No default model found for %s in recommendations", + ANTHROPIC_PROVIDER_NAME, ) default_model_name = ( default_model.name if default_model else "claude-sonnet-4-5" @@ -401,7 +407,8 @@ def configure_default_api_keys(db_session: Session) -> None: default_model = recommendations.get_default_model(VERTEXAI_PROVIDER_NAME) if default_model is None: logger.error( - f"No default model found for {VERTEXAI_PROVIDER_NAME} in recommendations" + "No default model found for %s in recommendations", + VERTEXAI_PROVIDER_NAME, ) default_model_name = default_model.name if default_model else "gemini-2.5-pro" @@ -432,7 +439,8 @@ def configure_default_api_keys(db_session: Session) -> None: default_model = recommendations.get_default_model(OPENROUTER_PROVIDER_NAME) if default_model is None: logger.error( - f"No default model found for {OPENROUTER_PROVIDER_NAME} in recommendations" + "No default model found for %s in recommendations", + OPENROUTER_PROVIDER_NAME, ) default_model_name = default_model.name if default_model else "z-ai/glm-4.7" @@ -549,7 +557,7 @@ async def submit_to_hubspot( response = await client.post(HUBSPOT_TRACKING_URL, json=data) if response.status_code != 200: - logger.error(f"Failed to submit to HubSpot: {response.text}") + logger.error("Failed to submit to HubSpot: %s", response.text) async def delete_user_from_control_plane(tenant_id: str, email: str) -> None: @@ -568,7 +576,7 @@ async def delete_user_from_control_plane(tenant_id: str, email: str) -> None: ) as response: if response.status != 200: error_text = await response.text() - logger.error(f"Control plane tenant creation failed: {error_text}") + logger.error("Control plane tenant creation failed: %s", error_text) raise Exception( f"Failed to delete tenant on control plane: {error_text}" ) @@ -601,7 +609,7 @@ def get_tenant_by_domain_from_control_plane( ) if response.status_code != 200: - logger.error(f"Control plane tenant lookup failed: {response.text}") + logger.error("Control plane tenant lookup failed: %s", response.text) return None response_data = response.json() @@ -614,7 +622,7 @@ def get_tenant_by_domain_from_control_plane( creator_email=response_data.get("creator_email"), ) except Exception as e: - logger.error(f"Error fetching tenant by domain: {str(e)}") + logger.error("Error fetching tenant by domain: %s", str(e)) return None @@ -645,7 +653,7 @@ async def get_available_tenant() -> str | None: # Remove the tenant from the available tenants table db_session.delete(available_tenant) db_session.commit() - logger.info(f"Using pre-provisioned tenant {tenant_id}") + logger.info("Using pre-provisioned tenant %s", tenant_id) return tenant_id else: db_session.rollback() @@ -689,7 +697,7 @@ async def setup_tenant(tenant_id: str) -> None: setup_onyx(db_session, tenant_id, cohere_enabled=cohere_enabled) except Exception as e: - logger.exception(f"Failed to set up tenant {tenant_id}") + logger.exception("Failed to set up tenant %s", tenant_id) raise e finally: if token is not None: @@ -711,5 +719,5 @@ async def assign_tenant_to_user( try: add_users_to_tenant([email], tenant_id) except Exception: - logger.exception(f"Failed to assign tenant {tenant_id} to user {email}") + logger.exception("Failed to assign tenant %s to user %s", tenant_id, email) raise Exception("Failed to assign tenant to user") diff --git a/backend/ee/onyx/server/tenants/proxy.py b/backend/ee/onyx/server/tenants/proxy.py index 6cdfb37ae5..c0a6fc5f50 100644 --- a/backend/ee/onyx/server/tenants/proxy.py +++ b/backend/ee/onyx/server/tenants/proxy.py @@ -196,7 +196,7 @@ async def forward_to_control_plane( detail = error_data.get("detail", detail) except Exception: pass - logger.error(f"Control plane returned {status_code}: {detail}") + logger.error("Control plane returned %s: %s", status_code, detail) raise HTTPException(status_code=status_code, detail=detail) except httpx.RequestError: logger.exception("Failed to connect to control plane") @@ -293,7 +293,7 @@ async def proxy_claim_license( license_data = result.get("license") if not tenant_id or not license_data: - logger.error(f"Control plane returned incomplete claim response: {result}") + logger.error("Control plane returned incomplete claim response: %s", result) raise HTTPException( status_code=502, detail="Control plane returned incomplete license data", @@ -410,7 +410,7 @@ async def proxy_license_fetch( license_data = result.get("license") if not license_data: - logger.error(f"Control plane returned incomplete license response: {result}") + logger.error("Control plane returned incomplete license response: %s", result) raise HTTPException( status_code=502, detail="Control plane returned incomplete license data", diff --git a/backend/ee/onyx/server/tenants/schema_management.py b/backend/ee/onyx/server/tenants/schema_management.py index 1bc6eb8cf9..5078a39683 100644 --- a/backend/ee/onyx/server/tenants/schema_management.py +++ b/backend/ee/onyx/server/tenants/schema_management.py @@ -37,7 +37,7 @@ def validate_tenant_id(tenant_id: str) -> bool: def run_alembic_migrations(schema_name: str) -> None: - logger.info(f"Starting Alembic migrations for schema: {schema_name}") + logger.info("Starting Alembic migrations for schema: %s", schema_name) try: current_dir = os.path.dirname(os.path.abspath(__file__)) @@ -65,11 +65,13 @@ def run_alembic_migrations(schema_name: str) -> None: # Run migrations programmatically logger.info( - f"Alembic migrations completed successfully for schema: {schema_name}" + "Alembic migrations completed successfully for schema: %s", schema_name ) except Exception as e: - logger.exception(f"Alembic migration failed for schema {schema_name}: {str(e)}") + logger.exception( + "Alembic migration failed for schema %s: %s", schema_name, str(e) + ) raise diff --git a/backend/ee/onyx/server/tenants/team_membership_api.py b/backend/ee/onyx/server/tenants/team_membership_api.py index 06ddf71f7c..d325087164 100644 --- a/backend/ee/onyx/server/tenants/team_membership_api.py +++ b/backend/ee/onyx/server/tenants/team_membership_api.py @@ -54,7 +54,9 @@ async def leave_organization( logger.debug("User deleted from control plane") except Exception as e: logger.exception( - f"Failed to delete user from control plane for tenant {tenant_id}: {e}" + "Failed to delete user from control plane for tenant %s: %s", + tenant_id, + e, ) raise HTTPException( status_code=500, diff --git a/backend/ee/onyx/server/tenants/user_invitations_api.py b/backend/ee/onyx/server/tenants/user_invitations_api.py index efd077e46a..f47b007001 100644 --- a/backend/ee/onyx/server/tenants/user_invitations_api.py +++ b/backend/ee/onyx/server/tenants/user_invitations_api.py @@ -30,7 +30,7 @@ async def request_invite( invite_self_to_tenant(user.email, invite_request.tenant_id) except Exception as e: logger.exception( - f"Failed to invite self to tenant {invite_request.tenant_id}: {e}" + "Failed to invite self to tenant %s: %s", invite_request.tenant_id, e ) raise HTTPException(status_code=500, detail=str(e)) @@ -63,7 +63,7 @@ async def accept_invite( try: accept_user_invite(user.email, invite_request.tenant_id) except Exception as e: - logger.exception(f"Failed to accept invite: {str(e)}") + logger.exception("Failed to accept invite: %s", str(e)) raise HTTPException(status_code=500, detail="Failed to accept invitation") @@ -78,5 +78,5 @@ async def deny_invite( try: deny_user_invite(user.email, invite_request.tenant_id) except Exception as e: - logger.exception(f"Failed to deny invite: {str(e)}") + logger.exception("Failed to deny invite: %s", str(e)) raise HTTPException(status_code=500, detail="Failed to deny invitation") diff --git a/backend/ee/onyx/server/tenants/user_mapping.py b/backend/ee/onyx/server/tenants/user_mapping.py index 67389beec7..0482cc253b 100644 --- a/backend/ee/onyx/server/tenants/user_mapping.py +++ b/backend/ee/onyx/server/tenants/user_mapping.py @@ -48,7 +48,7 @@ def get_tenant_id_for_email(email: str) -> str: db_session.commit() tenant_id = mapping.tenant_id except Exception as e: - logger.exception(f"Error getting tenant id for email {email}: {e}") + logger.exception("Error getting tenant id for email %s: %s", email, e) raise exceptions.UserNotExists() if tenant_id is None: @@ -126,10 +126,10 @@ def add_users_to_tenant(emails: list[str], tenant_id: str) -> None: # Commit the transaction db_session.commit() - logger.info(f"Successfully added users {emails} to tenant {tenant_id}") + logger.info("Successfully added users %s to tenant %s", emails, tenant_id) except Exception: - logger.exception(f"Failed to add users to tenant {tenant_id}") + logger.exception("Failed to add users to tenant %s", tenant_id) db_session.rollback() raise @@ -152,7 +152,7 @@ def remove_users_from_tenant(emails: list[str], tenant_id: str) -> None: db_session.commit() except Exception as e: logger.exception( - f"Failed to remove users from tenant {tenant_id}: {str(e)}" + "Failed to remove users from tenant %s: %s", tenant_id, str(e) ) db_session.rollback() @@ -229,7 +229,9 @@ def accept_user_invite(email: str, tenant_id: str) -> None: if active_mapping: db_session.delete(active_mapping) logger.info( - f"Deleted existing active mapping for user {email} in tenant {tenant_id}" + "Deleted existing active mapping for user %s in tenant %s", + email, + tenant_id, ) # Find the inactive mapping for this user and tenant @@ -253,16 +255,21 @@ def accept_user_invite(email: str, tenant_id: str) -> None: # Activate this mapping mapping.active = True db_session.commit() - logger.info(f"User {email} accepted invitation to tenant {tenant_id}") + logger.info( + "User %s accepted invitation to tenant %s", email, tenant_id + ) else: logger.warning( - f"No invitation found for user {email} in tenant {tenant_id}" + "No invitation found for user %s in tenant %s", email, tenant_id ) except Exception as e: db_session.rollback() logger.exception( - f"Failed to accept invitation for user {email} to tenant {tenant_id}: {str(e)}" + "Failed to accept invitation for user %s to tenant %s: %s", + email, + tenant_id, + str(e), ) raise @@ -273,7 +280,7 @@ def accept_user_invite(email: str, tenant_id: str) -> None: if email in invited_users: invited_users.remove(email) write_invited_users(invited_users) - logger.info(f"Removed {email} from invited users list after acceptance") + logger.info("Removed %s from invited users list after acceptance", email) finally: CURRENT_TENANT_ID_CONTEXTVAR.reset(token) @@ -297,10 +304,10 @@ def deny_user_invite(email: str, tenant_id: str) -> None: db_session.commit() if result: - logger.info(f"User {email} denied invitation to tenant {tenant_id}") + logger.info("User %s denied invitation to tenant %s", email, tenant_id) else: logger.warning( - f"No invitation found for user {email} in tenant {tenant_id}" + "No invitation found for user %s in tenant %s", email, tenant_id ) token = CURRENT_TENANT_ID_CONTEXTVAR.set(tenant_id) try: diff --git a/backend/ee/onyx/server/usage_limits.py b/backend/ee/onyx/server/usage_limits.py index 8170c1a129..d98a5f9e63 100644 --- a/backend/ee/onyx/server/usage_limits.py +++ b/backend/ee/onyx/server/usage_limits.py @@ -33,6 +33,6 @@ def is_tenant_on_trial(tenant_id: str) -> bool: return False except Exception as e: - logger.warning(f"Failed to fetch billing info for trial check: {e}") + logger.warning("Failed to fetch billing info for trial check: %s", e) # Default to trial limits on error (more restrictive = safer) return True diff --git a/backend/ee/onyx/server/user_group/api.py b/backend/ee/onyx/server/user_group/api.py index 0ca381a805..e7b8e80070 100644 --- a/backend/ee/onyx/server/user_group/api.py +++ b/backend/ee/onyx/server/user_group/api.py @@ -236,7 +236,7 @@ def set_user_curator( user_making_change=user, ) except ValueError as e: - logger.error(f"Error setting user curator: {e}") + logger.error("Error setting user curator: %s", e) raise HTTPException(status_code=404, detail=str(e)) diff --git a/backend/ee/onyx/utils/license.py b/backend/ee/onyx/utils/license.py index 076af1fba5..138f0e3b1a 100644 --- a/backend/ee/onyx/utils/license.py +++ b/backend/ee/onyx/utils/license.py @@ -91,11 +91,11 @@ def verify_license_signature(license_data: str) -> LicensePayload: logger.error("[verify_license] FAILED: Signature verification failed") raise ValueError("Invalid license signature") except json.JSONDecodeError as e: - logger.error(f"[verify_license] FAILED: JSON decode error: {e}") + logger.error("[verify_license] FAILED: JSON decode error: %s", e) raise ValueError("Invalid license format: not valid JSON") except (ValueError, KeyError, TypeError) as e: logger.error( - f"[verify_license] FAILED: Validation error: {type(e).__name__}: {e}" + "[verify_license] FAILED: Validation error: %s: %s", type(e).__name__, e ) raise ValueError(f"Invalid license format: {type(e).__name__}: {e}") except Exception: diff --git a/backend/ee/onyx/utils/posthog_client.py b/backend/ee/onyx/utils/posthog_client.py index 4fa14fec7a..e87c74212a 100644 --- a/backend/ee/onyx/utils/posthog_client.py +++ b/backend/ee/onyx/utils/posthog_client.py @@ -16,7 +16,7 @@ logger = setup_logger() def posthog_on_error(error: Any, items: Any) -> None: """Log any PostHog delivery errors.""" - logger.error(f"PostHog error: {error}, items: {items}") + logger.error("PostHog error: %s, items: %s", error, items) posthog: Posthog | None = None @@ -65,7 +65,7 @@ def capture_and_sync_with_alternate_posthog( marketing_posthog.capture(alternate_distinct_id, event, props) marketing_posthog.flush() except Exception as e: - logger.error(f"Error capturing marketing posthog event: {e}") + logger.error("Error capturing marketing posthog event: %s", e) try: if posthog and (cloud_user_id := props.get("onyx_cloud_user_id")): @@ -77,7 +77,7 @@ def capture_and_sync_with_alternate_posthog( properties=cloud_props, ) except Exception as e: - logger.error(f"Error identifying cloud posthog user: {e}") + logger.error("Error identifying cloud posthog user: %s", e) def alias_user(distinct_id: str, anonymous_id: str) -> None: @@ -93,7 +93,7 @@ def alias_user(distinct_id: str, anonymous_id: str) -> None: posthog.alias(previous_id=anonymous_id, distinct_id=distinct_id) posthog.flush() except Exception as e: - logger.error(f"Error aliasing PostHog user: {e}") + logger.error("Error aliasing PostHog user: %s", e) def get_anon_id_from_request(request: Any) -> str | None: @@ -137,5 +137,5 @@ def parse_posthog_cookie(cookie_value: str) -> dict[str, Any] | None: return cookie_data except (json.JSONDecodeError, KeyError, TypeError, AttributeError) as e: - logger.warning(f"Failed to parse cookie: {e}") + logger.warning("Failed to parse cookie: %s", e) return None diff --git a/backend/ee/onyx/utils/telemetry.py b/backend/ee/onyx/utils/telemetry.py index da265c9aa1..d691d11619 100644 --- a/backend/ee/onyx/utils/telemetry.py +++ b/backend/ee/onyx/utils/telemetry.py @@ -39,12 +39,12 @@ def event_telemetry( # Log the pre-enrichment properties so the real client IP (PII) never # reaches the application log aggregator. PostHog itself still receives # the enriched payload via the capture call below. - logger.info(f"Capturing PostHog event: {distinct_id} {event} {properties}") + logger.info("Capturing PostHog event: %s %s %s", distinct_id, event, properties) try: posthog.capture(distinct_id, event, enriched) posthog.flush() except Exception as e: - logger.error(f"Error capturing PostHog event: {e}") + logger.error("Error capturing PostHog event: %s", e) def identify_user( @@ -60,4 +60,4 @@ def identify_user( posthog.identify(distinct_id, enriched) posthog.flush() except Exception as e: - logger.error(f"Error identifying PostHog user: {e}") + logger.error("Error identifying PostHog user: %s", e) diff --git a/backend/model_server/encoders.py b/backend/model_server/encoders.py index df3fd6b221..528ab60753 100644 --- a/backend/model_server/encoders.py +++ b/backend/model_server/encoders.py @@ -54,12 +54,12 @@ def get_embedding_model( ) logger.info("RoPE pre-warm successful") except Exception as e: - logger.warning(f"RoPE pre-warm skipped/failed: {e}") + logger.warning("RoPE pre-warm skipped/failed: %s", e) global _GLOBAL_MODELS_DICT if model_name not in _GLOBAL_MODELS_DICT: - logger.notice(f"Loading {model_name}") + logger.notice("Loading %s", model_name) model = SentenceTransformer( model_name_or_path=model_name, trust_remote_code=True, @@ -94,7 +94,7 @@ def _concurrent_embedding( # the model to fail to encode texts. It's pretty rare and we want to allow # concurrent embedding, hence we retry (the specific error is # "RuntimeError: Already borrowed" and occurs in the transformers library) - logger.warning(f"Error encoding texts, retrying: {e}") + logger.warning("Error encoding texts, retrying: %s", e) time.sleep(ENCODING_RETRY_DELAY) return model.encode(texts, normalize_embeddings=normalize_embeddings) @@ -127,7 +127,10 @@ async def embed_text( if model_name is not None: logger.info( - f"Embedding {len(texts)} texts with {total_chars} total characters with local model: {model_name}" + "Embedding %s texts with %s total characters with local model: %s", + len(texts), + total_chars, + model_name, ) prefixed_texts = [f"{prefix}{text}" for text in texts] if prefix else texts @@ -149,16 +152,19 @@ async def embed_text( elapsed = time.monotonic() - start logger.info( - f"Successfully embedded {len(texts)} texts with {total_chars} total characters " - f"with local model {model_name} in {elapsed:.2f}" + "Successfully embedded %s texts with %s total characters with local model %s in %s", + len(texts), + total_chars, + model_name, + format(elapsed, ".2f"), ) logger.info( - f"event=embedding_model " - f"texts={len(texts)} " - f"chars={total_chars} " - f"model={model_name} " - f"gpu={gpu_type} " - f"elapsed={elapsed:.2f}" + "event=embedding_model texts=%s chars=%s model=%s gpu=%s elapsed=%s", + len(texts), + total_chars, + model_name, + gpu_type, + format(elapsed, ".2f"), ) else: logger.error("Model name not specified for embedding") @@ -217,7 +223,9 @@ async def process_embed_request( ) except Exception as e: logger.exception( - f"Error during embedding process: provider={embed_request.provider_type} model={embed_request.model_name}" + "Error during embedding process: provider=%s model=%s", + embed_request.provider_type, + embed_request.model_name, ) raise HTTPException( status_code=500, detail=f"Error during embedding process: {e}" diff --git a/backend/model_server/main.py b/backend/model_server/main.py index b0f6b9126d..79777fb582 100644 --- a/backend/model_server/main.py +++ b/backend/model_server/main.py @@ -70,7 +70,7 @@ def _move_files_recursively(source: Path, dest: Path, overwrite: bool = False) - @asynccontextmanager async def lifespan(app: FastAPI) -> AsyncGenerator: gpu_type = get_gpu_type() - logger.notice(f"Torch GPU Detection: gpu_type={gpu_type}") + logger.notice("Torch GPU Detection: gpu_type=%s", gpu_type) app.state.gpu_type = gpu_type @@ -82,12 +82,12 @@ async def lifespan(app: FastAPI) -> AsyncGenerator: logger.notice("Moved contents of temp_huggingface to huggingface cache.") except Exception as e: logger.warning( - f"Error moving contents of temp_huggingface to huggingface cache: {e}. " - "This is not a critical error and the model server will continue to run." + "Error moving contents of temp_huggingface to huggingface cache: %s. This is not a critical error and the model server will continue to run.", + e, ) torch.set_num_threads(max(MIN_THREADS_ML_MODELS, torch.get_num_threads())) - logger.notice(f"Torch Threads: {torch.get_num_threads()}") + logger.notice("Torch Threads: %s", torch.get_num_threads()) yield @@ -131,7 +131,9 @@ app = get_model_app() if __name__ == "__main__": logger.notice( - f"Starting Onyx Model Server on http://{MODEL_SERVER_ALLOWED_HOST}:{str(MODEL_SERVER_PORT)}/" + "Starting Onyx Model Server on http://%s:%s/", + MODEL_SERVER_ALLOWED_HOST, + str(MODEL_SERVER_PORT), ) - logger.notice(f"Model Server Version: {__version__}") + logger.notice("Model Server Version: %s", __version__) uvicorn.run(app, host=MODEL_SERVER_ALLOWED_HOST, port=MODEL_SERVER_PORT) diff --git a/backend/onyx/auth/captcha.py b/backend/onyx/auth/captcha.py index f5437b61e5..f6a5ff7fad 100644 --- a/backend/onyx/auth/captcha.py +++ b/backend/onyx/auth/captcha.py @@ -132,7 +132,7 @@ async def _reserve_token_or_raise(token: str) -> None: except CaptchaVerificationError: raise except Exception as e: - logger.error(f"Captcha replay cache error (failing open): {e}") + logger.error("Captcha replay cache error (failing open): %s", e) async def _release_token(token: str) -> None: @@ -143,7 +143,7 @@ async def _release_token(token: str) -> None: redis = await get_async_redis_connection() await redis.delete(_replay_cache_key(token)) except Exception as e: - logger.error(f"Captcha replay cache release error (ignored): {e}") + logger.error("Captcha replay cache release error (ignored): %s", e) def _check_token_freshness(create_time: str | None) -> None: @@ -154,13 +154,13 @@ def _check_token_freshness(create_time: str | None) -> None: try: ts = datetime.fromisoformat(create_time.replace("Z", "+00:00")) except ValueError: - logger.warning(f"Captcha createTime unparseable: {create_time!r}") + logger.warning("Captcha createTime unparseable: %r", create_time) raise CaptchaVerificationError( "Captcha verification failed: malformed createTime" ) age_seconds = (datetime.now(timezone.utc) - ts).total_seconds() if age_seconds > _TOKEN_MAX_AGE_SECONDS: - logger.warning(f"Captcha token stale: age={age_seconds:.1f}s") + logger.warning("Captcha token stale: age=%ss", format(age_seconds, ".1f")) raise CaptchaVerificationError("Captcha verification failed: token expired") @@ -172,40 +172,49 @@ def _evaluate_assessment( if not tp.valid: reason = tp.invalid_reason or "INVALID" - logger.warning(f"Captcha token invalid: reason={reason}") + logger.warning("Captcha token invalid: reason=%s", reason) raise CaptchaVerificationError(f"Captcha verification failed: {reason}") if RECAPTCHA_HOSTNAME_ALLOWLIST and ( tp.hostname is None or tp.hostname not in RECAPTCHA_HOSTNAME_ALLOWLIST ): - logger.warning(f"Captcha hostname mismatch: {tp.hostname!r}") + logger.warning("Captcha hostname mismatch: %r", tp.hostname) raise CaptchaVerificationError("Captcha verification failed: hostname mismatch") _check_token_freshness(tp.create_time) if tp.action != action.value: logger.warning( - f"Captcha action mismatch: got={tp.action!r} expected={action.value!r}" + "Captcha action mismatch: got=%r expected=%r", tp.action, action.value ) raise CaptchaVerificationError("Captcha verification failed: action mismatch") hard = _HARD_REJECT_REASONS.intersection(ra.reasons) if hard: - logger.warning(f"Captcha hard reject: reasons={sorted(hard)} score={ra.score}") + logger.warning( + "Captcha hard reject: reasons=%s score=%s", sorted(hard), ra.score + ) raise CaptchaVerificationError( f"Captcha verification failed: {', '.join(sorted(hard))}" ) if ra.score < RECAPTCHA_SCORE_THRESHOLD: logger.warning( - f"Captcha score below threshold: {ra.score} < {RECAPTCHA_SCORE_THRESHOLD} reasons={ra.reasons}" + "Captcha score below threshold: %s < %s reasons=%s", + ra.score, + RECAPTCHA_SCORE_THRESHOLD, + ra.reasons, ) raise CaptchaVerificationError( "Captcha verification failed: suspicious activity detected" ) logger.info( - f"Captcha verification passed: action={tp.action} score={ra.score} reasons={ra.reasons} hostname={tp.hostname}" + "Captcha verification passed: action=%s score=%s reasons=%s hostname=%s", + tp.action, + ra.score, + ra.reasons, + tp.hostname, ) @@ -246,7 +255,7 @@ async def verify_captcha_token(token: str, action: CaptchaAction) -> None: except CaptchaVerificationError: raise except Exception as e: - logger.error(f"Captcha verification failed unexpectedly: {e}") + logger.error("Captcha verification failed unexpectedly: %s", e) await _release_token(token) raise CaptchaVerificationError("Captcha verification service unavailable") diff --git a/backend/onyx/auth/disposable_email_validator.py b/backend/onyx/auth/disposable_email_validator.py index ce05e02a6e..02f7dd5373 100644 --- a/backend/onyx/auth/disposable_email_validator.py +++ b/backend/onyx/auth/disposable_email_validator.py @@ -83,7 +83,8 @@ class DisposableEmailValidator: try: logger.info( - f"Fetching disposable email domains from {DISPOSABLE_EMAIL_DOMAINS_URL}" + "Fetching disposable email domains from %s", + DISPOSABLE_EMAIL_DOMAINS_URL, ) with httpx.Client(timeout=10.0) as client: response = client.get(DISPOSABLE_EMAIL_DOMAINS_URL) @@ -93,7 +94,8 @@ class DisposableEmailValidator: if not isinstance(domains_list, list): logger.error( - f"Expected list from disposable domains URL, got {type(domains_list)}" + "Expected list from disposable domains URL, got %s", + type(domains_list), ) return self._fallback_domains.copy() @@ -104,14 +106,14 @@ class DisposableEmailValidator: domains.update(self._fallback_domains) logger.info( - f"Successfully fetched {len(domains)} disposable email domains" + "Successfully fetched %s disposable email domains", len(domains) ) return domains except httpx.HTTPError as e: - logger.warning(f"Failed to fetch disposable domains (HTTP error): {e}") + logger.warning("Failed to fetch disposable domains (HTTP error): %s", e) except Exception as e: - logger.warning(f"Failed to fetch disposable domains: {e}") + logger.warning("Failed to fetch disposable domains: %s", e) # On error, return fallback domains return self._fallback_domains.copy() diff --git a/backend/onyx/auth/email_utils.py b/backend/onyx/auth/email_utils.py index a123ead4b8..fa02d91fbd 100644 --- a/backend/onyx/auth/email_utils.py +++ b/backend/onyx/auth/email_utils.py @@ -243,7 +243,7 @@ def send_email_with_sendgrid( sg = sendgrid.SendGridAPIClient(api_key=SENDGRID_API_KEY) response = sg.client.mail.send.post(request_body=mail_json) # can raise if response.status_code != 202: - logger.warning(f"Unexpected status code {response.status_code}") + logger.warning("Unexpected status code %s", response.status_code) def send_email_with_smtplib( diff --git a/backend/onyx/auth/jwt.py b/backend/onyx/auth/jwt.py index afbfe8c507..4622dd5682 100644 --- a/backend/onyx/auth/jwt.py +++ b/backend/onyx/auth/jwt.py @@ -37,7 +37,7 @@ def _fetch_public_key_payload() -> tuple[str | dict[str, Any], PublicKeyFormat] response = requests.get(JWT_PUBLIC_KEY_URL) response.raise_for_status() except requests.RequestException as exc: - logger.error(f"Failed to fetch JWT public key: {str(exc)}") + logger.error("Failed to fetch JWT public key: %s", str(exc)) return None content_type = response.headers.get("Content-Type", "").lower() raw_body = response.text @@ -88,7 +88,7 @@ def _resolve_public_key_from_jwks( try: header = jwt.get_unverified_header(token) except PyJWTError as e: - logger.error(f"Unable to parse JWT header: {str(e)}") + logger.error("Unable to parse JWT header: %s", str(e)) return None keys = jwks_payload.get("keys", []) if isinstance(jwks_payload, dict) else [] @@ -123,7 +123,7 @@ def _resolve_public_key_from_jwks( try: return cast(RSAPublicKey, RSAAlgorithm.from_jwk(json.dumps(jwk))) except ValueError as e: - logger.error(f"Failed to construct RSA key from JWK: {str(e)}") + logger.error("Failed to construct RSA key from JWK: %s", str(e)) return None @@ -145,13 +145,13 @@ async def verify_jwt_token(token: str) -> dict[str, Any] | None: options={"verify_aud": False}, ) except InvalidTokenError as e: - logger.error(f"Invalid JWT token: {str(e)}") + logger.error("Invalid JWT token: %s", str(e)) if attempt < _PUBLIC_KEY_FETCH_ATTEMPTS - 1: _fetch_public_key_payload.cache_clear() continue return None except PyJWTError as e: - logger.error(f"JWT decoding error: {str(e)}") + logger.error("JWT decoding error: %s", str(e)) if attempt < _PUBLIC_KEY_FETCH_ATTEMPTS - 1: _fetch_public_key_payload.cache_clear() continue diff --git a/backend/onyx/auth/oauth_refresher.py b/backend/onyx/auth/oauth_refresher.py index 3b9b192a82..b4e39ca826 100644 --- a/backend/onyx/auth/oauth_refresher.py +++ b/backend/onyx/auth/oauth_refresher.py @@ -54,7 +54,7 @@ async def _test_expire_oauth_token( return True except Exception as e: - logger.exception(f"Error setting artificial expiration: {str(e)}") + logger.exception("Error setting artificial expiration: %s", str(e)) return False @@ -70,17 +70,19 @@ async def refresh_oauth_token( """ if not oauth_account.refresh_token: logger.warning( - f"No refresh token available for {user.email}'s {oauth_account.oauth_name} account" + "No refresh token available for %s's %s account", + user.email, + oauth_account.oauth_name, ) return False provider = oauth_account.oauth_name if provider not in REFRESH_ENDPOINTS: - logger.warning(f"Refresh endpoint not configured for provider: {provider}") + logger.warning("Refresh endpoint not configured for provider: %s", provider) return False try: - logger.info(f"Refreshing OAuth token for {user.email}'s {provider} account") + logger.info("Refreshing OAuth token for %s's %s account", user.email, provider) async with httpx.AsyncClient() as client: response = await client.post( @@ -96,7 +98,7 @@ async def refresh_oauth_token( if response.status_code != 200: logger.error( - f"Failed to refresh OAuth token: Status {response.status_code}" + "Failed to refresh OAuth token: Status %s", response.status_code ) return False @@ -140,11 +142,11 @@ async def refresh_oauth_token( updated_data, ) - logger.info(f"Successfully refreshed OAuth token for {user.email}") + logger.info("Successfully refreshed OAuth token for %s", user.email) return True except Exception as e: - logger.exception(f"Error refreshing OAuth token: {str(e)}") + logger.exception("Error refreshing OAuth token: %s", str(e)) return False @@ -174,7 +176,9 @@ async def check_and_refresh_oauth_tokens( oauth_account.expires_at and oauth_account.expires_at - now_timestamp < buffer_seconds ): - logger.info(f"OAuth token for {user.email} is about to expire - refreshing") + logger.info( + "OAuth token for %s is about to expire - refreshing", user.email + ) success = await refresh_oauth_token( user, oauth_account, db_session, user_manager ) diff --git a/backend/onyx/auth/oauth_token_manager.py b/backend/onyx/auth/oauth_token_manager.py index ab2a113e4b..0c83c2d0b2 100644 --- a/backend/onyx/auth/oauth_token_manager.py +++ b/backend/onyx/auth/oauth_token_manager.py @@ -45,7 +45,7 @@ class OAuthTokenManager: try: return self.refresh_token(user_token) except Exception as e: - logger.warning(f"Failed to refresh token: {e}") + logger.warning("Failed to refresh token: %s", e) return None else: return None diff --git a/backend/onyx/auth/permissions.py b/backend/onyx/auth/permissions.py index 2316f2f7d9..badf1894f7 100644 --- a/backend/onyx/auth/permissions.py +++ b/backend/onyx/auth/permissions.py @@ -89,7 +89,7 @@ def get_effective_permissions(user: User) -> set[Permission]: try: granted.add(Permission(p)) except ValueError: - logger.warning(f"Skipping unknown permission '{p}' for user {user.id}") + logger.warning("Skipping unknown permission '%s' for user %s", p, user.id) if Permission.FULL_ADMIN_PANEL_ACCESS in granted: return set(Permission) expanded = resolve_effective_permissions({p.value for p in granted}) diff --git a/backend/onyx/auth/signup_rate_limit.py b/backend/onyx/auth/signup_rate_limit.py index 04f3eab9ef..80ffdf453c 100644 --- a/backend/onyx/auth/signup_rate_limit.py +++ b/backend/onyx/auth/signup_rate_limit.py @@ -52,11 +52,11 @@ async def enforce_signup_rate_limit(request: Request) -> None: incr_result, _ = await pipe.execute() count = int(incr_result) except Exception as e: - logger.error(f"Signup rate-limit Redis error: {e}") + logger.error("Signup rate-limit Redis error: %s", e) return if count > _PER_IP_PER_HOUR: - logger.warning(f"Signup rate limit exceeded for ip={ip} count={count}") + logger.warning("Signup rate limit exceeded for ip=%s count=%s", ip, count) raise OnyxError( OnyxErrorCode.RATE_LIMITED, "Too many signup attempts from this network. Please wait before trying again.", diff --git a/backend/onyx/auth/users.py b/backend/onyx/auth/users.py index 1d15abb4ff..20ca59393d 100644 --- a/backend/onyx/auth/users.py +++ b/backend/onyx/auth/users.py @@ -180,7 +180,7 @@ def verify_auth_setting() -> None: "AUTH_TYPE='disabled' is no longer supported. Using 'basic' instead. Please update your configuration." ) - logger.notice(f"Using Auth Type: {AUTH_TYPE.value}") + logger.notice("Using Auth Type: %s", AUTH_TYPE.value) def get_display_email(email: str | None, space_less: bool = False) -> str: @@ -394,7 +394,8 @@ class UserManager(UUIDIDMixin, BaseUserManager[User, uuid.UUID]): else "unknown" ) logger.warning( - f"Blocked disposable email registration attempt: {domain}", + "Blocked disposable email registration attempt: %s", + domain, extra={"email_domain": domain}, ) raise @@ -850,7 +851,7 @@ class UserManager(UUIDIDMixin, BaseUserManager[User, uuid.UUID]): domain=None, secure=WEB_DOMAIN.startswith("https"), ) - logger.debug(f"Deleted anonymous user cookie for user {user.email}") + logger.debug("Deleted anonymous user cookie for user %s", user.email) except Exception: logger.exception("Error deleting anonymous user cookie") @@ -882,7 +883,7 @@ class UserManager(UUIDIDMixin, BaseUserManager[User, uuid.UUID]): token = CURRENT_TENANT_ID_CONTEXTVAR.set(tenant_id) try: user_count = await get_user_count() - logger.debug(f"Current tenant user count: {user_count}") + logger.debug("Current tenant user count: %s", user_count) # Link the anonymous PostHog session to the identified user so # that pre-signup session recordings merge into one person profile. @@ -979,7 +980,7 @@ class UserManager(UUIDIDMixin, BaseUserManager[User, uuid.UUID]): properties=properties, ) - logger.debug(f"User {user.id} has registered.") + logger.debug("User %s has registered.", user.id) optional_telemetry( record_type=RecordType.SIGN_UP, data={"action": "create"}, @@ -1017,7 +1018,7 @@ class UserManager(UUIDIDMixin, BaseUserManager[User, uuid.UUID]): verify_email_domain(user.email) logger.notice( - f"Verification requested for user {user.id}. Verification token: {token}" + "Verification requested for user %s. Verification token: %s", user.id, token ) user_count = await get_user_count() send_user_verification_email( @@ -1041,7 +1042,7 @@ class UserManager(UUIDIDMixin, BaseUserManager[User, uuid.UUID]): ) except Exception as e: logger.warning( - f"User attempted to login with invalid credentials: {str(e)}" + "User attempted to login with invalid credentials: %s", str(e) ) if not tenant_id: @@ -1436,7 +1437,7 @@ class FastAPIUserWithLogoutRouter(FastAPIUsers[models.UP, models.ID]): ) -> Response: try: user, token = user_token - logger.info(f"Processing token refresh request for user {user.email}") + logger.info("Processing token refresh request for user %s", user.email) # Check if user has OAuth accounts that need refreshing await check_and_refresh_oauth_tokens( @@ -1455,11 +1456,12 @@ class FastAPIUserWithLogoutRouter(FastAPIUsers[models.UP, models.ID]): refresh_method = getattr(strategy, "refresh_token") new_token = await refresh_method(token, user) logger.info( - f"Successfully refreshed session token for user {user.email}" + "Successfully refreshed session token for user %s", + user.email, ) return await backend.transport.get_login_response(new_token) except Exception as e: - logger.error(f"Error refreshing session token: {str(e)}") + logger.error("Error refreshing session token: %s", str(e)) # Fallback to logout and login if refresh fails await backend.logout(strategy, user, token) return await backend.login(strategy, user) @@ -1471,7 +1473,7 @@ class FastAPIUserWithLogoutRouter(FastAPIUsers[models.UP, models.ID]): await backend.logout(strategy, user, token) return await backend.login(strategy, user) except Exception as e: - logger.error(f"Unexpected error in refresh endpoint: {str(e)}") + logger.error("Unexpected error in refresh endpoint: %s", str(e)) raise HTTPException( status_code=status.HTTP_400_BAD_REQUEST, detail=f"Token refresh failed: {str(e)}", @@ -1807,7 +1809,9 @@ async def current_user_from_websocket( raise BasicAuthenticationError(detail="Access denied. Missing origin.") if not _is_same_origin(origin, WEB_DOMAIN): - logger.warning(f"WS auth: origin mismatch. Expected {WEB_DOMAIN}, got {origin}") + logger.warning( + "WS auth: origin mismatch. Expected %s, got %s", WEB_DOMAIN, origin + ) raise BasicAuthenticationError(detail="Access denied. Invalid origin.") # Validate WS token in Redis (single-use, deleted after retrieval) @@ -1820,7 +1824,7 @@ async def current_user_from_websocket( except BasicAuthenticationError: raise except Exception as e: - logger.error(f"WS auth: error during token validation: {e}") + logger.error("WS auth: error during token validation: %s", e) raise BasicAuthenticationError( detail="Authentication verification failed." ) from e @@ -1828,7 +1832,7 @@ async def current_user_from_websocket( # Get user from token data user = await _get_user_from_token_data(token_data) if user is None: - logger.warning(f"WS auth: user not found for id={token_data.get('sub')}") + logger.warning("WS auth: user not found for id=%s", token_data.get("sub")) raise BasicAuthenticationError( detail="Access denied. User not found or inactive." ) @@ -1838,12 +1842,12 @@ async def current_user_from_websocket( # Block limited users (same as current_user) if is_limited_user(user): - logger.warning(f"WS auth: user {user.email} is limited") + logger.warning("WS auth: user %s is limited", user.email) raise BasicAuthenticationError( detail="Access denied. User has limited permissions.", ) - logger.debug(f"WS auth: authenticated {user.email}") + logger.debug("WS auth: authenticated %s", user.email) return user diff --git a/backend/onyx/background/celery/apps/app_base.py b/backend/onyx/background/celery/apps/app_base.py index b0d0cffa41..840b5c35db 100644 --- a/backend/onyx/background/celery/apps/app_base.py +++ b/backend/onyx/background/celery/apps/app_base.py @@ -151,7 +151,9 @@ def on_task_postrun( if not task: return - task_logger.debug(f"Task {task.name} (ID: {task_id}) completed with state: {state}") + task_logger.debug( + "Task %s (ID: %s) completed with state: %s", task.name, task_id, state + ) if state not in READY_STATES: return @@ -165,13 +167,17 @@ def on_task_postrun( # Get tenant_id directly from kwargs- each celery task has a tenant_id kwarg if not kwargs: - logger.error(f"Task {task.name} (ID: {task_id}) is missing kwargs") + logger.error("Task %s (ID: %s) is missing kwargs", task.name, task_id) tenant_id = POSTGRES_DEFAULT_SCHEMA else: tenant_id = cast(str, kwargs.get("tenant_id", POSTGRES_DEFAULT_SCHEMA)) task_logger.debug( - f"Task {task.name} (ID: {task_id}) completed with state: {state} {f'for tenant_id={tenant_id}' if tenant_id else ''}" + "Task %s (ID: %s) completed with state: %s %s", + task.name, + task_id, + state, + f"for tenant_id={tenant_id}" if tenant_id else "", ) r = get_redis_client(tenant_id=tenant_id) @@ -238,7 +244,7 @@ def on_celeryd_init( # force=True. so we use force=True as a fallback. all_start_methods: list[str] = multiprocessing.get_all_start_methods() - logger.info(f"Multiprocessing all start methods: {all_start_methods}") + logger.info("Multiprocessing all start methods: %s", all_start_methods) try: multiprocessing.set_start_method("spawn") # fork is unsafe, set to spawn @@ -256,7 +262,7 @@ def on_celeryd_init( ) logger.info( - f"Multiprocessing selected start method: {multiprocessing.get_start_method()}" + "Multiprocessing selected start method: %s", multiprocessing.get_start_method() ) # Initialize tracing in workers if credentials are available. @@ -289,7 +295,9 @@ def wait_for_redis(sender: Any, **kwargs: Any) -> None: # noqa: ARG001 break logger.info( - f"Redis: Readiness probe ongoing. elapsed={time_elapsed:.1f} timeout={WAIT_LIMIT:.1f}" + "Redis: Readiness probe ongoing. elapsed=%s timeout=%s", + format(time_elapsed, ".1f"), + format(WAIT_LIMIT, ".1f"), ) time.sleep(WAIT_INTERVAL) @@ -328,7 +336,9 @@ def wait_for_db(sender: Any, **kwargs: Any) -> None: # noqa: ARG001 break logger.info( - f"Database: Readiness probe ongoing. elapsed={time_elapsed:.1f} timeout={WAIT_LIMIT:.1f}" + "Database: Readiness probe ongoing. elapsed=%s timeout=%s", + format(time_elapsed, ".1f"), + format(WAIT_LIMIT, ".1f"), ) time.sleep(WAIT_INTERVAL) @@ -343,7 +353,7 @@ def wait_for_db(sender: Any, **kwargs: Any) -> None: # noqa: ARG001 def on_secondary_worker_init(sender: Any, **kwargs: Any) -> None: # noqa: ARG001 - logger.info(f"Running as a secondary celery worker: pid={os.getpid()}") + logger.info("Running as a secondary celery worker: pid=%s", os.getpid()) # Set up variables for waiting on primary worker WAIT_INTERVAL = 5 @@ -358,7 +368,9 @@ def on_secondary_worker_init(sender: Any, **kwargs: Any) -> None: # noqa: ARG00 time_elapsed = time.monotonic() - time_start logger.info( - f"Primary worker is not ready yet. elapsed={time_elapsed:.1f} timeout={WAIT_LIMIT:.1f}" + "Primary worker is not ready yet. elapsed=%s timeout=%s", + format(time_elapsed, ".1f"), + format(WAIT_LIMIT, ".1f"), ) if time_elapsed > WAIT_LIMIT: msg = f"Primary worker was not ready within the timeout. ({WAIT_LIMIT} seconds). Exiting..." @@ -381,7 +393,7 @@ def on_worker_ready(sender: Any, **kwargs: Any) -> None: # noqa: ARG001 hostname: str = cast(str, sender.hostname) path = make_probe_path("readiness", hostname) path.touch() - logger.info(f"Readiness signal touched at {path}.") + logger.info("Readiness signal touched at %s.", path) def on_worker_shutdown(sender: Any, **kwargs: Any) -> None: # noqa: ARG001 diff --git a/backend/onyx/background/celery/apps/beat.py b/backend/onyx/background/celery/apps/beat.py index 0e58608bad..8b858974db 100644 --- a/backend/onyx/background/celery/apps/beat.py +++ b/backend/onyx/background/celery/apps/beat.py @@ -43,7 +43,8 @@ class DynamicTenantScheduler(PersistentScheduler): # Let the parent class handle store initialization self.setup_schedule() task_logger.info( - f"DynamicTenantScheduler initialized: reload_interval={self._reload_interval}" + "DynamicTenantScheduler initialized: reload_interval=%s", + self._reload_interval, ) self._liveness_probe_path = make_probe_path("liveness", "beat@hostname") @@ -103,7 +104,9 @@ class DynamicTenantScheduler(PersistentScheduler): "kwargs": task.get("kwargs", {}), } if options := task.get("options"): - task_logger.debug(f"Adding options to task {task_name}: {options}") + task_logger.debug( + "Adding options to task %s: %s", task_name, options + ) cloud_task["options"] = options new_schedule[task_name] = cloud_task @@ -120,7 +123,8 @@ class DynamicTenantScheduler(PersistentScheduler): for tenant_id in tenant_ids: if IGNORED_SYNCING_TENANT_LIST and tenant_id in IGNORED_SYNCING_TENANT_LIST: task_logger.debug( - f"Skipping tenant {tenant_id} as it is in the ignored syncing list" + "Skipping tenant %s as it is in the ignored syncing list", + tenant_id, ) continue @@ -128,7 +132,9 @@ class DynamicTenantScheduler(PersistentScheduler): task_name = task["name"] tenant_task_name = f"{task['name']}-{tenant_id}" - task_logger.debug(f"Creating task configuration for {tenant_task_name}") + task_logger.debug( + "Creating task configuration for %s", tenant_task_name + ) tenant_task = { "task": task["task"], "schedule": task["schedule"], @@ -136,7 +142,9 @@ class DynamicTenantScheduler(PersistentScheduler): } if options := task.get("options"): task_logger.debug( - f"Adding options to task {tenant_task_name}: {options}" + "Adding options to task %s: %s", + tenant_task_name, + options, ) tenant_task["options"] = options @@ -151,7 +159,7 @@ class DynamicTenantScheduler(PersistentScheduler): task_logger.debug("_try_updating_schedule starting") tenant_ids = get_all_tenant_ids() - task_logger.debug(f"Found {len(tenant_ids)} IDs") + task_logger.debug("Found %s IDs", len(tenant_ids)) # get current schedule and extract current tenants current_schedule = self.schedule.items() @@ -181,7 +189,9 @@ class DynamicTenantScheduler(PersistentScheduler): if not do_update: # exit early if nothing changed task_logger.info( - f"_try_updating_schedule - Schedule unchanged: tasks={len(new_schedule)} beat_multiplier={beat_multiplier}" + "_try_updating_schedule - Schedule unchanged: tasks=%s beat_multiplier=%s", + len(new_schedule), + beat_multiplier, ) return @@ -214,11 +224,15 @@ class DynamicTenantScheduler(PersistentScheduler): self.sync() task_logger.info( - f"_try_updating_schedule - Schedule updated: " - f"prev_num_tasks={len(current_schedule)} " - f"prev_beat_multiplier={self.last_beat_multiplier} " - f"tasks={len(new_schedule)} " - f"beat_multiplier={beat_multiplier}" + "_try_updating_schedule - Schedule updated: " + "prev_num_tasks=%s " + "prev_beat_multiplier=%s " + "tasks=%s " + "beat_multiplier=%s", + len(current_schedule), + self.last_beat_multiplier, + len(new_schedule), + beat_multiplier, ) self.last_beat_multiplier = beat_multiplier @@ -243,7 +257,7 @@ def on_beat_init(sender: Any, **kwargs: Any) -> None: app_base.wait_for_redis(sender, **kwargs) path = make_probe_path("readiness", "beat@hostname") path.touch() - task_logger.info(f"Readiness signal touched at {path}.") + task_logger.info("Readiness signal touched at %s.", path) # first time init of the scheduler after db has been init'ed scheduler: DynamicTenantScheduler = sender.scheduler diff --git a/backend/onyx/background/celery/apps/light.py b/backend/onyx/background/celery/apps/light.py index 2660d40002..c586125e47 100644 --- a/backend/onyx/background/celery/apps/light.py +++ b/backend/onyx/background/celery/apps/light.py @@ -96,7 +96,7 @@ def on_worker_init(sender: Worker, **kwargs: Any) -> None: logger.info("worker_init signal received.") logger.info( - f"Concurrency: {sender.concurrency}" # ty: ignore[unresolved-attribute] + "Concurrency: %s", sender.concurrency # ty: ignore[unresolved-attribute] ) SqlEngine.set_app_name(POSTGRES_CELERY_WORKER_LIGHT_APP_NAME) diff --git a/backend/onyx/background/celery/apps/monitoring.py b/backend/onyx/background/celery/apps/monitoring.py index bdfa7c7942..ae852fdd3d 100644 --- a/backend/onyx/background/celery/apps/monitoring.py +++ b/backend/onyx/background/celery/apps/monitoring.py @@ -62,7 +62,7 @@ def on_worker_init(sender: Any, **kwargs: Any) -> None: global _prometheus_collectors_ok logger.info("worker_init signal received.") - logger.info(f"Multiprocessing start method: {multiprocessing.get_start_method()}") + logger.info("Multiprocessing start method: %s", multiprocessing.get_start_method()) SqlEngine.set_app_name(POSTGRES_CELERY_WORKER_MONITORING_APP_NAME) SqlEngine.init_engine(pool_size=sender.concurrency, max_overflow=3) diff --git a/backend/onyx/background/celery/apps/primary.py b/backend/onyx/background/celery/apps/primary.py index 546ac6cc60..e0035e8766 100644 --- a/backend/onyx/background/celery/apps/primary.py +++ b/backend/onyx/background/celery/apps/primary.py @@ -126,7 +126,7 @@ def on_worker_init(sender: Worker, **kwargs: Any) -> None: app_base.wait_for_db(sender, **kwargs) app_base.wait_for_document_index_or_shutdown() - logger.info(f"Running as the primary celery worker: pid={os.getpid()}") + logger.info("Running as the primary celery worker: pid=%s", os.getpid()) # Less startup checks in multi-tenant case if MULTI_TENANT: @@ -142,13 +142,13 @@ def on_worker_init(sender: Worker, **kwargs: Any) -> None: connected_slaves: int = replication_info.get("connected_slaves", 0) logger.info( - f"Redis INFO REPLICATION: role={role} connected_slaves={connected_slaves}" + "Redis INFO REPLICATION: role=%s connected_slaves=%s", role, connected_slaves ) memory_info: dict[str, Any] = cast(dict, r.info("memory")) maxmemory_policy: str = cast(str, memory_info.get("maxmemory_policy", "")) - logger.info(f"Redis INFO MEMORY: maxmemory_policy={maxmemory_policy}") + logger.info("Redis INFO MEMORY: maxmemory_policy=%s", maxmemory_policy) # For the moment, we're assuming that we are the only primary worker # that should be running. @@ -238,7 +238,9 @@ def on_worker_init(sender: Worker, **kwargs: Any) -> None: except Exception: # If we can't check the task status, be conservative and continue logger.warning( - f"Could not verify Celery task status on startup for attempt {attempt.id}, task_id={attempt.celery_task_id}" + "Could not verify Celery task status on startup for attempt %s, task_id=%s", + attempt.id, + attempt.celery_task_id, ) diff --git a/backend/onyx/background/celery/celery_utils.py b/backend/onyx/background/celery/celery_utils.py index 799cbce299..a281b281fa 100644 --- a/backend/onyx/background/celery/celery_utils.py +++ b/backend/onyx/background/celery/celery_utils.py @@ -120,7 +120,7 @@ def _extract_from_batch( if failed_id: ids[failed_id] = None logger.warning( - f"Failed to retrieve document {failed_id}: {item.failure_message}" + "Failed to retrieve document %s: %s", failed_id, item.failure_message ) else: ids[item.id] = item.parent_hierarchy_raw_node_id diff --git a/backend/onyx/background/celery/memory_monitoring.py b/backend/onyx/background/celery/memory_monitoring.py index 2403077728..817866ec60 100644 --- a/backend/onyx/background/celery/memory_monitoring.py +++ b/backend/onyx/background/celery/memory_monitoring.py @@ -64,10 +64,16 @@ def emit_process_memory( metadata_str = f" {metadata_str}" if metadata_str else "" memory_logger.info( - f"PROCESS_MEMORY process_name={process_name} pid={pid} " - f"rss_mb={memory_info.rss / (1024 * 1024):.2f} " - f"vms_mb={memory_info.vms / (1024 * 1024):.2f} " - f"cpu={cpu_percent:.2f}{metadata_str}" + "PROCESS_MEMORY process_name=%s pid=%s " + "rss_mb=%s " + "vms_mb=%s " + "cpu=%s%s", + process_name, + pid, + format(memory_info.rss / (1024 * 1024), ".2f"), + format(memory_info.vms / (1024 * 1024), ".2f"), + format(cpu_percent, ".2f"), + metadata_str, ) except Exception: logger.exception("Error monitoring process memory.") diff --git a/backend/onyx/background/celery/tasks/docfetching/tasks.py b/backend/onyx/background/celery/tasks/docfetching/tasks.py index d63e2d4867..5a046b2d73 100644 --- a/backend/onyx/background/celery/tasks/docfetching/tasks.py +++ b/backend/onyx/background/celery/tasks/docfetching/tasks.py @@ -93,10 +93,10 @@ def _verify_indexing_attempt( ) logger.info( - f"docfetching_task - IndexAttempt verified: " - f"attempt_id={index_attempt_id} " - f"cc_pair={cc_pair_id} " - f"search_settings={search_settings_id}" + "docfetching_task - IndexAttempt verified: attempt_id=%s cc_pair=%s search_settings=%s", + index_attempt_id, + cc_pair_id, + search_settings_id, ) @@ -151,11 +151,11 @@ def _docfetching_task( logger.debug("Sentry DSN not provided, skipping Sentry initialization") logger.info( - f"Indexing spawned task starting: " - f"attempt={index_attempt_id} " - f"tenant={tenant_id} " - f"cc_pair={cc_pair_id} " - f"search_settings={search_settings_id}" + "Indexing spawned task starting: attempt=%s tenant=%s cc_pair=%s search_settings=%s", + index_attempt_id, + tenant_id, + cc_pair_id, + search_settings_id, ) redis_connector = RedisConnector(tenant_id, cc_pair_id) @@ -209,10 +209,11 @@ def _docfetching_task( ) logger.info( - f"Indexing spawned task running entrypoint: attempt={index_attempt_id} " - f"tenant={tenant_id} " - f"cc_pair={cc_pair_id} " - f"search_settings={search_settings_id}" + "Indexing spawned task running entrypoint: attempt=%s tenant=%s cc_pair=%s search_settings=%s", + index_attempt_id, + tenant_id, + cc_pair_id, + search_settings_id, ) # This is where the heavy/real work happens @@ -236,10 +237,11 @@ def _docfetching_task( except Exception as e: logger.exception( - f"Indexing spawned task failed: attempt={index_attempt_id} " - f"tenant={tenant_id} " - f"cc_pair={cc_pair_id} " - f"search_settings={search_settings_id}" + "Indexing spawned task failed: attempt=%s tenant=%s cc_pair=%s search_settings=%s", + index_attempt_id, + tenant_id, + cc_pair_id, + search_settings_id, ) # special bulletproofing ... truncate long exception messages @@ -253,7 +255,10 @@ def _docfetching_task( raise e logger.info( - f"Indexing spawned task finished: attempt={index_attempt_id} cc_pair={cc_pair_id} search_settings={search_settings_id}" + "Indexing spawned task finished: attempt=%s cc_pair=%s search_settings=%s", + index_attempt_id, + cc_pair_id, + search_settings_id, ) os._exit(0) # ensure process exits cleanly @@ -627,7 +632,8 @@ def docfetching_proxy_task( try: with get_session_with_current_tenant() as db_session: logger.exception( - f"Marking attempt {index_attempt_id} as canceled due to termination signal" + "Marking attempt %s as canceled due to termination signal", + index_attempt_id, ) mark_attempt_canceled( index_attempt_id, diff --git a/backend/onyx/background/celery/tasks/docprocessing/tasks.py b/backend/onyx/background/celery/tasks/docprocessing/tasks.py index 26754c9607..04761c9cf0 100644 --- a/backend/onyx/background/celery/tasks/docprocessing/tasks.py +++ b/backend/onyx/background/celery/tasks/docprocessing/tasks.py @@ -368,7 +368,9 @@ def monitor_indexing_attempt_progress( ) except Exception as e: logger.exception( - f"Failed to monitor document processing completion: attempt={attempt.id} error={str(e)}" + "Failed to monitor document processing completion: attempt=%s error=%s", + attempt.id, + str(e), ) # Mark the attempt as failed if monitoring fails @@ -386,7 +388,7 @@ def monitor_indexing_attempt_progress( # Try to clean up storage try: - logger.info(f"Cleaning up storage after monitoring failure: {storage}") + logger.info("Cleaning up storage after monitoring failure: %s", storage) storage.cleanup_all_batches() except Exception: logger.exception("Failed to cleanup storage after monitoring failure") @@ -416,7 +418,9 @@ def check_indexing_completion( task: Task, ) -> None: logger.info( - f"Checking for indexing completion: attempt={index_attempt_id} tenant={tenant_id}" + "Checking for indexing completion: attempt=%s tenant=%s", + index_attempt_id, + tenant_id, ) # Check if indexing is complete and all batches are processed @@ -427,12 +431,13 @@ def check_indexing_completion( ) logger.info( - f"Indexing status: " - f"indexing_completed={indexing_completed} " - f"batches_processed={batches_processed}/{batches_total if batches_total is not None else '?'} " - f"total_docs={coordination_status.total_docs} " - f"total_chunks={coordination_status.total_chunks} " - f"total_failures={coordination_status.total_failures}" + "Indexing status: indexing_completed=%s batches_processed=%s/%s total_docs=%s total_chunks=%s total_failures=%s", + indexing_completed, + batches_processed, + batches_total if batches_total is not None else "?", + coordination_status.total_docs, + coordination_status.total_chunks, + coordination_status.total_failures, ) # Update progress tracking and check for stalls @@ -465,9 +470,10 @@ def check_indexing_completion( if attempt and timed_out: if attempt.status == IndexingStatus.IN_PROGRESS: logger.error( - f"Indexing attempt {index_attempt_id} has been indexing for " - f"{stalled_timeout_hours // 2}-{stalled_timeout_hours} hours without progress. " - f"Marking it as failed." + "Indexing attempt %s has been indexing for %s-%s hours without progress. Marking it as failed.", + index_attempt_id, + stalled_timeout_hours // 2, + stalled_timeout_hours, ) mark_attempt_failed( index_attempt_id, db_session, failure_reason="Stalled indexing" @@ -496,9 +502,9 @@ def check_indexing_completion( attempt = get_index_attempt(db_session, index_attempt_id) if attempt and attempt.status == IndexingStatus.NOT_STARTED: logger.error( - f"Task {attempt.celery_task_id} attached to indexing attempt " - f"{index_attempt_id} does not exist in the queue. " - f"Marking indexing attempt as failed." + "Task %s attached to indexing attempt %s does not exist in the queue. Marking indexing attempt as failed.", + attempt.celery_task_id, + index_attempt_id, ) mark_attempt_failed( index_attempt_id, @@ -507,9 +513,11 @@ def check_indexing_completion( ) else: logger.info( - f"Indexing attempt {index_attempt_id} is {attempt.status}. " - f"{stalled_timeout_hours // 2}-{stalled_timeout_hours} hours without heartbeat " - "but task is in the queue. Likely underprovisioned docfetching worker." + "Indexing attempt %s is %s. %s-%s hours without heartbeat but task is in the queue. Likely underprovisioned docfetching worker.", + index_attempt_id, + attempt.status, + stalled_timeout_hours // 2, + stalled_timeout_hours, ) # Update last progress time so we won't time out again for # another `stalled_timeout_hours / 2` window. @@ -528,7 +536,7 @@ def check_indexing_completion( return # If processing is complete, handle completion - logger.info(f"Connector indexing finished for index attempt {index_attempt_id}.") + logger.info("Connector indexing finished for index attempt %s.", index_attempt_id) # All processing is complete total_failures = coordination_status.total_failures @@ -536,11 +544,13 @@ def check_indexing_completion( with get_session_with_current_tenant() as db_session: if total_failures == 0: attempt = mark_attempt_succeeded(index_attempt_id, db_session) - logger.info(f"Index attempt {index_attempt_id} completed successfully") + logger.info("Index attempt %s completed successfully", index_attempt_id) else: attempt = mark_attempt_partially_succeeded(index_attempt_id, db_session) logger.info( - f"Index attempt {index_attempt_id} completed with {total_failures} failures" + "Index attempt %s completed with %s failures", + index_attempt_id, + total_failures, ) # Update CC pair status if successful @@ -622,7 +632,7 @@ def check_indexing_completion( if attempt.status == IndexingStatus.SUCCESS: logger.info( - f"Resolving indexing entity errors for attempt {index_attempt_id}" + "Resolving indexing entity errors for attempt %s", index_attempt_id ) _resolve_indexing_entity_errors( cc_pair_id=attempt.connector_credential_pair_id, @@ -631,7 +641,7 @@ def check_indexing_completion( # Clean up FileStore storage (still needed for document batches during transition) try: - logger.info(f"Cleaning up storage after indexing completion: {storage}") + logger.info("Cleaning up storage after indexing completion: %s", storage) storage.cleanup_all_batches() except Exception: logger.exception("Failed to clean up document batches - continuing") @@ -653,7 +663,7 @@ def check_indexing_completion( "caught by the next attempt's start-of-run sweep." ) - logger.info(f"Database coordination completed for attempt {index_attempt_id}") + logger.info("Database coordination completed for attempt %s", index_attempt_id) def active_indexing_attempt( @@ -871,7 +881,7 @@ def check_for_indexing(self: Task, *, tenant_id: str) -> int | None: if is_fence(key_bytes) and not redis_client.sismember( OnyxRedisConstants.ACTIVE_FENCES, key_bytes ): - logger.warning(f"Adding {key_bytes} to the lookup table.") + logger.warning("Adding %s to the lookup table.", key_bytes) redis_client.sadd(OnyxRedisConstants.ACTIVE_FENCES, key_bytes) redis_client.set( @@ -1377,7 +1387,9 @@ def _check_failure_threshold( FAILURE_RATIO_THRESHOLD = 0.1 if total_failures > FAILURE_THRESHOLD and failure_ratio > FAILURE_RATIO_THRESHOLD: logger.error( - f"Connector run failed with '{total_failures}' errors after '{batch_num}' batches." + "Connector run failed with '%s' errors after '%s' batches.", + total_failures, + batch_num, ) if last_failure and last_failure.exception: raise last_failure.exception from last_failure.exception @@ -1421,7 +1433,7 @@ def _resolve_indexing_document_errors( if document_id not in doc_id_to_unresolved_errors: continue - logger.info(f"Resolving IndexAttemptError for document '{document_id}'") + logger.info("Resolving IndexAttemptError for document '%s'", document_id) for error in doc_id_to_unresolved_errors[document_id]: error.is_resolved = True db_session_temp.add(error) diff --git a/backend/onyx/background/celery/tasks/docprocessing/utils.py b/backend/onyx/background/celery/tasks/docprocessing/utils.py index a79900d43f..98e0bfba01 100644 --- a/backend/onyx/background/celery/tasks/docprocessing/utils.py +++ b/backend/onyx/background/celery/tasks/docprocessing/utils.py @@ -68,9 +68,10 @@ class IndexingCallbackBase(IndexingHeartbeatInterface): elapsed = time.monotonic() - self.start_monotonic if elapsed > self.timeout_seconds: logger.warning( - f"IndexingCallback Docprocessing - task timeout exceeded: " - f"elapsed={elapsed:.0f}s timeout={self.timeout_seconds}s " - f"cc_pair={self.redis_connector.cc_pair_id}" + "IndexingCallback Docprocessing - task timeout exceeded: elapsed=%ss timeout=%ss cc_pair=%s", + format(elapsed, ".0f"), + self.timeout_seconds, + self.redis_connector.cc_pair_id, ) return True @@ -107,12 +108,13 @@ class IndexingCallbackBase(IndexingHeartbeatInterface): self.last_tag = tag except LockError: logger.exception( - f"{self.__class__.__name__} - lock.reacquire exceptioned: " - f"lock_timeout={self.redis_lock.timeout} " - f"start={self.started} " - f"last_tag={self.last_tag} " - f"last_reacquired={self.last_lock_reacquire} " - f"now={datetime.now(timezone.utc)}" + "%s - lock.reacquire exceptioned: lock_timeout=%s start=%s last_tag=%s last_reacquired=%s now=%s", + self.__class__.__name__, + self.redis_lock.timeout, + self.started, + self.last_tag, + self.last_lock_reacquire, + datetime.now(timezone.utc), ) redis_lock_dump(self.redis_lock, self.redis_client) diff --git a/backend/onyx/background/celery/tasks/evals/tasks.py b/backend/onyx/background/celery/tasks/evals/tasks.py index f897aad96d..bce3132eb7 100644 --- a/backend/onyx/background/celery/tasks/evals/tasks.py +++ b/backend/onyx/background/celery/tasks/evals/tasks.py @@ -84,7 +84,10 @@ def scheduled_eval_task(self: Task, **kwargs: Any) -> None: # noqa: ARG001 run_timestamp = datetime.now(timezone.utc).strftime("%Y-%m-%d") logger.info( - f"Starting scheduled eval pipeline for project '{project_name}' with {len(dataset_names)} dataset(s): {dataset_names}" + "Starting scheduled eval pipeline for project '%s' with %s dataset(s): %s", + project_name, + len(dataset_names), + dataset_names, ) pipeline_start = datetime.now(timezone.utc) @@ -100,7 +103,9 @@ def scheduled_eval_task(self: Task, **kwargs: Any) -> None: # noqa: ARG001 try: logger.info( - f"Running scheduled eval for dataset: {dataset_name} (project: {project_name})" + "Running scheduled eval for dataset: %s (project: %s)", + dataset_name, + project_name, ) configuration = EvalConfigurationOptions( @@ -116,10 +121,10 @@ def scheduled_eval_task(self: Task, **kwargs: Any) -> None: # noqa: ARG001 remote_dataset_name=dataset_name, ) success = result.success - logger.info(f"Completed eval for {dataset_name}: success={success}") + logger.info("Completed eval for %s: success=%s", dataset_name, success) except Exception as e: - logger.exception(f"Failed to run scheduled eval for {dataset_name}") + logger.exception("Failed to run scheduled eval for %s", dataset_name) error_message = str(e) success = False @@ -140,5 +145,8 @@ def scheduled_eval_task(self: Task, **kwargs: Any) -> None: # noqa: ARG001 passed_count = sum(1 for r in results if r["success"]) logger.info( - f"Scheduled eval pipeline completed: {passed_count}/{len(results)} passed in {total_duration:.1f}s" + "Scheduled eval pipeline completed: %s/%s passed in %ss", + passed_count, + len(results), + format(total_duration, ".1f"), ) diff --git a/backend/onyx/background/celery/tasks/opensearch_migration/transformer.py b/backend/onyx/background/celery/tasks/opensearch_migration/transformer.py index ecd0e1e96c..e395b3ee43 100644 --- a/backend/onyx/background/celery/tasks/opensearch_migration/transformer.py +++ b/backend/onyx/background/celery/tasks/opensearch_migration/transformer.py @@ -202,9 +202,9 @@ def transform_vespa_chunks_to_opensearch_chunks( # function. if vespa_document_id in sanitized_to_original_doc_id_mapping: logger.warning( - f"Migration warning: Vespa document ID {vespa_document_id} does not match the document ID supplied " - f"{sanitized_to_original_doc_id_mapping[vespa_document_id]}. " - "The Vespa ID will be discarded." + "Migration warning: Vespa document ID %s does not match the document ID supplied %s. The Vespa ID will be discarded.", + vespa_document_id, + sanitized_to_original_doc_id_mapping[vespa_document_id], ) document_id = sanitized_to_original_doc_id_mapping.get( vespa_document_id, vespa_document_id @@ -287,9 +287,9 @@ def transform_vespa_chunks_to_opensearch_chunks( ) if not is_public and not acl_list: logger.warning( - f"Migration warning: Vespa chunk with document ID {vespa_document_id} and chunk index {chunk_index} has no " - "public ACL and no access control list. This does not make sense as it implies the document is never " - "searchable. Continuing with the migration..." + "Migration warning: Vespa chunk with document ID %s and chunk index %s has no public ACL and no access control list. This does not make sense as it implies the document is never searchable. Continuing with the migration...", + vespa_document_id, + chunk_index, ) chunk_tenant_id: str | None = vespa_chunk.get(TENANT_ID) @@ -338,9 +338,9 @@ def transform_vespa_chunks_to_opensearch_chunks( except Exception: traceback.print_exc() logger.exception( - f"Migration error: Error transforming Vespa chunk with document ID {vespa_chunk.get(DOCUMENT_ID)} " - f"and chunk index {vespa_chunk.get(CHUNK_ID)} into an OpenSearch chunk. Continuing with " - "the migration..." + "Migration error: Error transforming Vespa chunk with document ID %s and chunk index %s into an OpenSearch chunk. Continuing with the migration...", + vespa_chunk.get(DOCUMENT_ID), + vespa_chunk.get(CHUNK_ID), ) errored_chunks.append(vespa_chunk) diff --git a/backend/onyx/background/celery/tasks/pruning/tasks.py b/backend/onyx/background/celery/tasks/pruning/tasks.py index 03695ab967..baa4a1af15 100644 --- a/backend/onyx/background/celery/tasks/pruning/tasks.py +++ b/backend/onyx/background/celery/tasks/pruning/tasks.py @@ -238,18 +238,18 @@ def check_for_pruning(self: Task, *, tenant_id: str) -> bool | None: cc_pair_id=cc_pair_id, ) if not cc_pair: - logger.error(f"CC pair not found: {cc_pair_id}") + logger.error("CC pair not found: %s", cc_pair_id) continue if not _is_pruning_due(cc_pair): - logger.info(f"CC pair not due for pruning: {cc_pair_id}") + logger.info("CC pair not due for pruning: %s", cc_pair_id) continue payload_id = try_creating_prune_generator_task( self.app, cc_pair, db_session, r, tenant_id ) if not payload_id: - logger.info(f"Pruning not created: {cc_pair_id}") + logger.info("Pruning not created: %s", cc_pair_id) continue prune_dispatched = True @@ -328,7 +328,7 @@ def try_creating_prune_generator_task( is used to trigger prunes immediately, e.g. via the web ui. """ - logger.info(f"try_creating_prune_generator_task: cc_pair={cc_pair.id}") + logger.info("try_creating_prune_generator_task: cc_pair=%s", cc_pair.id) redis_connector = RedisConnector(tenant_id, cc_pair.id) @@ -336,7 +336,8 @@ def try_creating_prune_generator_task( count = redis_connector.prune.get_active_task_count() if count > 0: logger.info( - f"try_creating_prune_generator_task: cc_pair={cc_pair.id} no simultaneous pruning allowed" + "try_creating_prune_generator_task: cc_pair=%s no simultaneous pruning allowed", + cc_pair.id, ) return None @@ -352,7 +353,8 @@ def try_creating_prune_generator_task( acquired = lock.acquire(blocking_timeout=LOCK_TIMEOUT / 2) if not acquired: logger.info( - f"try_creating_prune_generator_task: cc_pair={cc_pair.id} lock not acquired" + "try_creating_prune_generator_task: cc_pair=%s lock not acquired", + cc_pair.id, ) return None @@ -360,28 +362,30 @@ def try_creating_prune_generator_task( # skip pruning if already pruning if redis_connector.prune.fenced: logger.info( - f"try_creating_prune_generator_task: cc_pair={cc_pair.id} already pruning" + "try_creating_prune_generator_task: cc_pair=%s already pruning", + cc_pair.id, ) return None # skip pruning if the cc_pair is deleting if redis_connector.delete.fenced: logger.info( - f"try_creating_prune_generator_task: cc_pair={cc_pair.id} deleting" + "try_creating_prune_generator_task: cc_pair=%s deleting", cc_pair.id ) return None # skip pruning if doc permissions sync is running if redis_connector.permissions.fenced: logger.info( - f"try_creating_prune_generator_task: cc_pair={cc_pair.id} permissions sync running" + "try_creating_prune_generator_task: cc_pair=%s permissions sync running", + cc_pair.id, ) return None db_session.refresh(cc_pair) if cc_pair.status == ConnectorCredentialPairStatus.DELETING: logger.info( - f"try_creating_prune_generator_task: cc_pair={cc_pair.id} deleting" + "try_creating_prune_generator_task: cc_pair=%s deleting", cc_pair.id ) return None @@ -507,7 +511,8 @@ def connector_pruning_generator_task( if payload.celery_task_id is None: logger.info( - f"connector_prune_generator_task - Waiting for fence: fence={redis_connector.prune.fence_key}" + "connector_prune_generator_task - Waiting for fence: fence=%s", + redis_connector.prune.fence_key, ) time.sleep(1) continue @@ -515,9 +520,9 @@ def connector_pruning_generator_task( payload_id = payload.id logger.info( - f"connector_prune_generator_task - Fence found, continuing...: " - f"fence={redis_connector.prune.fence_key} " - f"payload_id={payload.id}" + "connector_prune_generator_task - Fence found, continuing...: fence=%s payload_id=%s", + redis_connector.prune.fence_key, + payload.id, ) break diff --git a/backend/onyx/background/celery/tasks/vespa/document_sync.py b/backend/onyx/background/celery/tasks/vespa/document_sync.py index a1c2c0df1f..94ebabafd5 100644 --- a/backend/onyx/background/celery/tasks/vespa/document_sync.py +++ b/backend/onyx/background/celery/tasks/vespa/document_sync.py @@ -156,7 +156,8 @@ def try_generate_stale_document_sync_tasks( maybe_mark_tenant_active(tenant_id, caller="vespa_sync") logger.info( - f"Stale documents found (at least {stale_doc_count}). Generating sync tasks in one batch." + "Stale documents found (at least %s). Generating sync tasks in one batch.", + stale_doc_count, ) logger.info("generate_document_sync_tasks starting for all documents.") @@ -173,13 +174,15 @@ def try_generate_stale_document_sync_tasks( if tasks_generated >= max_tasks: logger.info( - f"generate_document_sync_tasks reached the task generation limit: " - f"tasks_generated={tasks_generated} max_tasks={max_tasks}" + "generate_document_sync_tasks reached the task generation limit: tasks_generated=%s max_tasks=%s", + tasks_generated, + max_tasks, ) else: logger.info( - f"generate_document_sync_tasks finished for all documents. " - f"tasks_generated={tasks_generated} total_docs_found={total_docs}" + "generate_document_sync_tasks finished for all documents. tasks_generated=%s total_docs_found=%s", + tasks_generated, + total_docs, ) set_document_sync_fence(r, tasks_generated) diff --git a/backend/onyx/background/indexing/checkpointing_utils.py b/backend/onyx/background/indexing/checkpointing_utils.py index 3f27e22308..8b6be2df35 100644 --- a/backend/onyx/background/indexing/checkpointing_utils.py +++ b/backend/onyx/background/indexing/checkpointing_utils.py @@ -98,9 +98,9 @@ def get_latest_valid_checkpoint( if not had_any_progress: logger.warning( - f"{_NUM_RECENT_ATTEMPTS_TO_CONSIDER} consecutive failed attempts without progress " - f"found for cc_pair={cc_pair_id}. Ignoring checkpoint to let the run start " - "from scratch." + "%s consecutive failed attempts without progress found for cc_pair=%s. Ignoring checkpoint to let the run start from scratch.", + _NUM_RECENT_ATTEMPTS_TO_CONSIDER, + cc_pair_id, ) return connector.build_dummy_checkpoint(), False @@ -141,7 +141,8 @@ def get_latest_valid_checkpoint( checkpoint = connector.build_dummy_checkpoint() if latest_valid_checkpoint_candidate is None: logger.info( - f"No valid checkpoint found for cc_pair={cc_pair_id}. Starting from scratch." + "No valid checkpoint found for cc_pair=%s. Starting from scratch.", + cc_pair_id, ) return checkpoint, False @@ -152,15 +153,15 @@ def get_latest_valid_checkpoint( ) except Exception: logger.exception( - f"Failed to load checkpoint from previous failed attempt with ID " - f"{latest_valid_checkpoint_candidate.id}. Falling back to default checkpoint." + "Failed to load checkpoint from previous failed attempt with ID %s. Falling back to default checkpoint.", + latest_valid_checkpoint_candidate.id, ) return checkpoint, False logger.info( - f"Using checkpoint from previous failed attempt with ID " - f"{latest_valid_checkpoint_candidate.id}. Previous checkpoint: " - f"{previous_checkpoint}" + "Using checkpoint from previous failed attempt with ID %s. Previous checkpoint: %s", + latest_valid_checkpoint_candidate.id, + previous_checkpoint, ) return previous_checkpoint, True diff --git a/backend/onyx/background/indexing/dask_utils.py b/backend/onyx/background/indexing/dask_utils.py index 1f1ccb566c..c11377ba78 100644 --- a/backend/onyx/background/indexing/dask_utils.py +++ b/backend/onyx/background/indexing/dask_utils.py @@ -28,6 +28,9 @@ class ResourceLogger(WorkerPlugin): memory_available_gb = psutil.virtual_memory().available / (1024.0**3) # You can now log these values or send them to a monitoring service logger.debug( - f"Worker {self.worker.address}: CPU usage {cpu_percent}%, Memory available {memory_available_gb}GB" + "Worker %s: CPU usage %s%%, Memory available %sGB", + self.worker.address, + cpu_percent, + memory_available_gb, ) await asyncio.sleep(self.log_interval) diff --git a/backend/onyx/background/indexing/job_client.py b/backend/onyx/background/indexing/job_client.py index 934a45c081..eb06055c7b 100644 --- a/backend/onyx/background/indexing/job_client.py +++ b/backend/onyx/background/indexing/job_client.py @@ -140,15 +140,19 @@ class SimpleJob: pid = self.process.pid logger.warning( - f"SimpleJob.terminate_and_wait - sending SIGTERM to job: id={self.id} pid={pid}" + "SimpleJob.terminate_and_wait - sending SIGTERM to job: id=%s pid=%s", + self.id, + pid, ) self.process.terminate() self.process.join(timeout=sigterm_grace_seconds) if self.process.is_alive(): logger.warning( - f"SimpleJob.terminate_and_wait - SIGTERM grace exceeded, sending SIGKILL: " - f"id={self.id} pid={pid} grace={sigterm_grace_seconds}s" + "SimpleJob.terminate_and_wait - SIGTERM grace exceeded, sending SIGKILL: id=%s pid=%s grace=%ss", + self.id, + pid, + sigterm_grace_seconds, ) self.process.kill() self.process.join() @@ -201,7 +205,7 @@ class SimpleJobClient: for job_id in current_job_ids: job = self.jobs.get(job_id) if job and job.done(): - logger.debug(f"Cleaning up job with id: '{job.id}'") + logger.debug("Cleaning up job with id: '%s'", job.id) del self.jobs[job.id] def submit( @@ -214,7 +218,9 @@ class SimpleJobClient: self._cleanup_completed_jobs() if len(self.jobs) >= self.n_workers: logger.debug( - f"No available workers to run job. Currently running '{len(self.jobs)}' jobs, with a limit of '{self.n_workers}'." + "No available workers to run job. Currently running '%s' jobs, with a limit of '%s'.", + len(self.jobs), + self.n_workers, ) return None diff --git a/backend/onyx/background/indexing/memory_tracer.py b/backend/onyx/background/indexing/memory_tracer.py index a23af2e922..05e21d82fd 100644 --- a/backend/onyx/background/indexing/memory_tracer.py +++ b/backend/onyx/background/indexing/memory_tracer.py @@ -19,7 +19,7 @@ class MemoryTracer: def start(self) -> None: """Start the memory tracer if interval is greater than 0.""" if self.interval > 0: - logger.debug(f"Memory tracer starting: interval={self.interval}") + logger.debug("Memory tracer starting: interval=%s", self.interval) tracemalloc.start(DANSWER_TRACEMALLOC_FRAMES) self._take_snapshot() @@ -56,9 +56,9 @@ class MemoryTracer: """Log the memory difference between two snapshots.""" stats = current.compare_to(previous, "traceback") for s in stats[: self.num_print_entries]: - logger.debug(f"Tracer diff: {s}") + logger.debug("Tracer diff: %s", s) for line in s.traceback.format(): - logger.debug(f"* {line}") + logger.debug("* %s", line) def increment_and_maybe_trace(self) -> None: """Increment counter and perform trace if interval is hit.""" @@ -68,7 +68,9 @@ class MemoryTracer: self.counter += 1 if self.counter % self.interval == 0: logger.debug( - f"Running trace comparison for batch {self.counter}. interval={self.interval}" + "Running trace comparison for batch %s. interval=%s", + self.counter, + self.interval, ) self._take_snapshot() if self.snapshot and self.snapshot_prev: @@ -80,7 +82,8 @@ class MemoryTracer: return logger.debug( - f"Running trace comparison between start and end of indexing. {self.counter} batches processed." + "Running trace comparison between start and end of indexing. %s batches processed.", + self.counter, ) self._take_snapshot() if self.snapshot and self.snapshot_first: diff --git a/backend/onyx/background/indexing/run_docfetching.py b/backend/onyx/background/indexing/run_docfetching.py index bd5ea1de53..246c2a9d3a 100644 --- a/backend/onyx/background/indexing/run_docfetching.py +++ b/backend/onyx/background/indexing/run_docfetching.py @@ -220,7 +220,7 @@ def strip_null_characters(doc_batch: list[Document]) -> list[Document]: for doc in doc_batch: if sys.getsizeof(doc) > MAX_FILE_SIZE_BYTES: logger.warning( - f"doc {doc.id} too large, Document size: {sys.getsizeof(doc)}" + "doc %s too large, Document size: %s", doc.id, sys.getsizeof(doc) ) cleaned_batch.append(sanitize_document_for_postgres(doc)) @@ -289,7 +289,9 @@ def _check_failure_threshold( FAILURE_RATIO_THRESHOLD = 0.1 if total_failures > FAILURE_THRESHOLD and failure_ratio > FAILURE_RATIO_THRESHOLD: logger.error( - f"Connector run failed with '{total_failures}' errors after '{batch_num}' batches." + "Connector run failed with '%s' errors after '%s' batches.", + total_failures, + batch_num, ) if last_failure and last_failure.exception: raise last_failure.exception from last_failure.exception @@ -331,10 +333,11 @@ def run_docfetching_entrypoint( credential_id = attempt.connector_credential_pair.credential_id logger.info( - f"Docfetching starting{tenant_str}: " - f"connector='{connector_name}' " - f"config='{connector_config}' " - f"credentials='{credential_id}'" + "Docfetching starting%s: connector='%s' config='%s' credentials='%s'", + tenant_str, + connector_name, + connector_config, + credential_id, ) raw_file_callback = build_raw_file_callback( @@ -366,10 +369,11 @@ def run_docfetching_entrypoint( ) logger.info( - f"Docfetching finished{tenant_str}: " - f"connector='{connector_name}' " - f"config='{connector_config}' " - f"credentials='{credential_id}'" + "Docfetching finished%s: connector='%s' config='%s' credentials='%s'", + tenant_str, + connector_name, + connector_config, + credential_id, ) INDEX_ATTEMPT_INFO_CONTEXTVAR.reset(token) @@ -393,11 +397,11 @@ def connector_document_extraction( start_time = time.monotonic() logger.info( - f"Document extraction starting: " - f"attempt={index_attempt_id} " - f"cc_pair={cc_pair_id} " - f"search_settings={search_settings_id} " - f"tenant={tenant_id}" + "Document extraction starting: attempt=%s cc_pair=%s search_settings=%s tenant=%s", + index_attempt_id, + cc_pair_id, + search_settings_id, + tenant_id, ) # Get batch storage (transition to IN_PROGRESS is handled by run_indexing_entrypoint) @@ -427,9 +431,9 @@ def connector_document_extraction( # Clear the indexing trigger if it was set, to prevent duplicate indexing attempts if index_attempt.connector_credential_pair.indexing_trigger is not None: logger.info( - "Clearing indexing trigger: " - f"cc_pair={index_attempt.connector_credential_pair.id} " - f"trigger={index_attempt.connector_credential_pair.indexing_trigger}" + "Clearing indexing trigger: cc_pair=%s trigger=%s", + index_attempt.connector_credential_pair.id, + index_attempt.connector_credential_pair.indexing_trigger, ) mark_ccpair_with_indexing_trigger( index_attempt.connector_credential_pair.id, None, db_session @@ -546,13 +550,15 @@ def connector_document_extraction( most_recent_attempt and most_recent_attempt.status.is_successful() ): logger.info( - f"Cleaning up all old batches for index attempt {index_attempt_id} before starting new run" + "Cleaning up all old batches for index attempt %s before starting new run", + index_attempt_id, ) batch_storage.cleanup_all_batches() checkpoint = connector_runner.connector.build_dummy_checkpoint() else: logger.info( - f"Getting latest valid checkpoint for index attempt {index_attempt_id}" + "Getting latest valid checkpoint for index attempt %s", + index_attempt_id, ) checkpoint, resuming_from_checkpoint = get_latest_valid_checkpoint( db_session=db_session, @@ -586,7 +592,8 @@ def connector_document_extraction( db_session.commit() else: logger.info( - f"Cleaning up all batches for index attempt {index_attempt_id} before starting new run" + "Cleaning up all batches for index attempt %s before starting new run", + index_attempt_id, ) # for non-checkpointed connectors, throw out batches from previous unsuccessful attempts # because we'll be getting those documents again anyways. @@ -615,7 +622,9 @@ def connector_document_extraction( # Main extraction loop while checkpoint.has_more: logger.info( - f"Running '{db_connector.source.value}' connector with checkpoint: {checkpoint}" + "Running '%s' connector with checkpoint: %s", + db_connector.source.value, + checkpoint, ) for ( document_batch, @@ -718,7 +727,9 @@ def connector_document_extraction( ) logger.debug( - f"Persisted and cached {len(hierarchy_node_batch_cleaned)} hierarchy nodes for attempt={index_attempt_id}" + "Persisted and cached %s hierarchy nodes for attempt=%s", + len(hierarchy_node_batch_cleaned), + index_attempt_id, ) # below is all document processing task, so if no batch we can just continue @@ -762,12 +773,13 @@ def connector_document_extraction( if doc_size > INDEXING_SIZE_WARNING_THRESHOLD: logger.warning( - f"Document size: doc='{doc.to_short_descriptor()}' " - f"size={doc_size} " - f"threshold={INDEXING_SIZE_WARNING_THRESHOLD}" + "Document size: doc='%s' size=%s threshold=%s", + doc.to_short_descriptor(), + doc_size, + INDEXING_SIZE_WARNING_THRESHOLD, ) - logger.debug(f"Indexing batch of documents: {batch_description}") + logger.debug("Indexing batch of documents: %s", batch_description) memory_tracer.increment_and_maybe_trace() if processing_mode == ProcessingMode.FILE_SYSTEM: @@ -835,10 +847,10 @@ def connector_document_extraction( total_doc_batches_queued += 1 logger.info( - f"Wrote documents to file system: " - f"batch_num={batch_num} " - f"docs={len(written_paths)} " - f"attempt={index_attempt_id}" + "Wrote documents to file system: batch_num=%s docs=%s attempt=%s", + batch_num, + len(written_paths), + index_attempt_id, ) else: # REGULAR mode (default): Full pipeline - store and queue docprocessing @@ -873,10 +885,10 @@ def connector_document_extraction( total_doc_batches_queued += 1 logger.info( - f"Queued document processing batch: " - f"batch_num={batch_num} " - f"docs={len(doc_batch_cleaned)} " - f"attempt={index_attempt_id}" + "Queued document processing batch: batch_num=%s docs=%s attempt=%s", + batch_num, + len(doc_batch_cleaned), + index_attempt_id, ) # Check checkpoint size periodically @@ -898,10 +910,10 @@ def connector_document_extraction( elapsed_time = time.monotonic() - start_time logger.info( - f"Document extraction completed: " - f"attempt={index_attempt_id} " - f"batches_queued={total_doc_batches_queued} " - f"elapsed={elapsed_time:.2f}s" + "Document extraction completed: attempt=%s batches_queued=%s elapsed=%ss", + index_attempt_id, + total_doc_batches_queued, + format(elapsed_time, ".2f"), ) # Set total batches in database to signal extraction completion. @@ -929,12 +941,14 @@ def connector_document_extraction( queue=OnyxCeleryQueues.SANDBOX, ) logger.info( - f"Triggered sandbox file sync for user {creator_id} source={source_value} after indexing complete" + "Triggered sandbox file sync for user %s source=%s after indexing complete", + creator_id, + source_value, ) except Exception as e: logger.exception( - f"Document extraction failed: attempt={index_attempt_id} error={str(e)}" + "Document extraction failed: attempt=%s error=%s", index_attempt_id, str(e) ) # Do NOT clean up batches on failure; future runs will use those batches @@ -946,7 +960,8 @@ def connector_document_extraction( # used in the future until the credentials are updated. with get_session_with_current_tenant() as db_session_temp: logger.exception( - f"Marking attempt {index_attempt_id} as canceled due to validation error." + "Marking attempt %s as canceled due to validation error.", + index_attempt_id, ) mark_attempt_canceled( index_attempt_id, @@ -980,8 +995,9 @@ def connector_document_extraction( if num_validation_errors >= VALIDATION_ERROR_THRESHOLD: logger.warning( - f"Connector {db_connector.id} has {num_validation_errors} consecutive validation" - f" errors. Marking the CC Pair as invalid." + "Connector %s has %s consecutive validation errors. Marking the CC Pair as invalid.", + db_connector.id, + num_validation_errors, ) update_connector_credential_pair( db_session=db_session_temp, @@ -993,7 +1009,8 @@ def connector_document_extraction( elif isinstance(e, ConnectorStopSignal): with get_session_with_current_tenant() as db_session_temp: logger.exception( - f"Marking attempt {index_attempt_id} as canceled due to stop signal." + "Marking attempt %s as canceled due to stop signal.", + index_attempt_id, ) mark_attempt_canceled( index_attempt_id, @@ -1010,7 +1027,8 @@ def connector_document_extraction( IndexingStatus.FAILED, ]: logger.info( - f"Attempt {index_attempt_id} is already failed/canceled, skipping marking as failed." + "Attempt %s is already failed/canceled, skipping marking as failed.", + index_attempt_id, ) raise e @@ -1042,12 +1060,14 @@ def reissue_old_batches( batch_storage.update_old_batches_to_new_index_attempt(old_batches) for batch_id in old_batches: logger.info( - f"Re-issuing docprocessing task for batch {batch_id} for index attempt {index_attempt_id}" + "Re-issuing docprocessing task for batch %s for index attempt %s", + batch_id, + index_attempt_id, ) path_info = batch_storage.extract_path_info(batch_id) if path_info is None: logger.warning( - f"Could not extract path info from batch {batch_id}, skipping" + "Could not extract path info from batch %s, skipping", batch_id ) continue if path_info.cc_pair_id != cc_pair_id: @@ -1074,6 +1094,9 @@ def reissue_old_batches( # is still in the filestore waiting for processing or not. last_batch_num = len(old_batches) + recent_batches logger.info( - f"Starting from batch {last_batch_num} due to re-issued batches: {old_batches}, completed batches: {recent_batches}" + "Starting from batch %s due to re-issued batches: %s, completed batches: %s", + last_batch_num, + old_batches, + recent_batches, ) return len(old_batches), recent_batches diff --git a/backend/onyx/background/periodic_poller.py b/backend/onyx/background/periodic_poller.py index a7e1493d54..80414d827e 100644 --- a/backend/onyx/background/periodic_poller.py +++ b/backend/onyx/background/periodic_poller.py @@ -102,7 +102,7 @@ def _run_scheduled_eval() -> None: ) except Exception: logger.exception( - f"Periodic poller - Failed scheduled eval for dataset {dataset_name}" + "Periodic poller - Failed scheduled eval for dataset %s", dataset_name ) @@ -208,7 +208,7 @@ def _try_run_periodic_task(task_def: _PeriodicTaskDef) -> None: task_def.last_run_at = now except Exception: logger.exception( - f"Periodic poller - Error running periodic task {task_def.name}" + "Periodic poller - Error running periodic task %s", task_def.name ) @@ -259,7 +259,9 @@ def _poller_loop(tenant_id: str) -> None: periodic_tasks = _build_periodic_tasks() logger.info( - f"Periodic poller started with {len(periodic_tasks)} periodic task(s): {[t.name for t in periodic_tasks]}" + "Periodic poller started with %s periodic task(s): %s", + len(periodic_tasks), + [t.name for t in periodic_tasks], ) while not _shutdown_event.is_set(): @@ -273,7 +275,7 @@ def _poller_loop(tenant_id: str) -> None: _try_run_periodic_task(task_def) except Exception: logger.exception( - f"Periodic poller - Unhandled error checking task {task_def.name}" + "Periodic poller - Unhandled error checking task %s", task_def.name ) _shutdown_event.wait(RECOVERY_INTERVAL_SECONDS) diff --git a/backend/onyx/background/task_utils.py b/backend/onyx/background/task_utils.py index 191f048614..e30814fffb 100644 --- a/backend/onyx/background/task_utils.py +++ b/backend/onyx/background/task_utils.py @@ -155,7 +155,7 @@ def drain_processing_loop(tenant_id: str) -> None: redis_locking=False, ) except Exception: - logger.exception(f"Failed to process user file {file_id}") + logger.exception("Failed to process user file %s", file_id) def drain_delete_loop(tenant_id: str) -> None: @@ -178,7 +178,7 @@ def drain_delete_loop(tenant_id: str) -> None: redis_locking=False, ) except Exception: - logger.exception(f"Failed to delete user file {file_id}") + logger.exception("Failed to delete user file %s", file_id) failed.add(file_id) @@ -202,5 +202,5 @@ def drain_project_sync_loop(tenant_id: str) -> None: redis_locking=False, ) except Exception: - logger.exception(f"Failed to sync user file {file_id}") + logger.exception("Failed to sync user file %s", file_id) failed.add(file_id) diff --git a/backend/onyx/chat/chat_utils.py b/backend/onyx/chat/chat_utils.py index efd79d290c..38f600c208 100644 --- a/backend/onyx/chat/chat_utils.py +++ b/backend/onyx/chat/chat_utils.py @@ -382,7 +382,7 @@ def _get_or_extract_plaintext( plaintext_io = file_store.read_file(plaintext_key, mode="b") return plaintext_io.read().decode("utf-8") except Exception: - logger.info(f"Cache miss for file with id={file_id}") + logger.info("Cache miss for file with id=%s", file_id) # Cache miss — extract and store. content_text = extract_fn() @@ -424,7 +424,9 @@ def load_chat_file( content_text = _get_or_extract_plaintext(cache_key, _extract) except Exception as e: logger.warning( - f"Failed to retrieve content for file {file_descriptor['id']}: {str(e)}" + "Failed to retrieve content for file %s: %s", + file_descriptor["id"], + str(e), ) # Get token count from UserFile if available @@ -440,7 +442,7 @@ def load_chat_file( token_count = user_file.token_count except (ValueError, TypeError) as e: logger.warning( - f"Failed to get token count for file {file_descriptor['id']}: {e}" + "Failed to get token count for file %s: %s", file_descriptor["id"], e ) return ChatLoadedFile( @@ -902,7 +904,7 @@ def build_python_chat_files_from_search_docs( record = file_store.read_file_record(doc.file_id) except Exception as e: logger.warning( - f"file_id={doc.file_id!r} not found in file store ({e}); skipping." + "file_id=%r not found in file store (%s); skipping.", doc.file_id, e ) continue @@ -911,8 +913,9 @@ def build_python_chat_files_from_search_docs( FileOrigin.CONNECTOR_FILE_UPLOAD, ): logger.warning( - f"file_id={doc.file_id!r} has origin={record.file_origin!r}, " - "not eligible for code-interpreter staging; skipping." + "file_id=%r has origin=%r, not eligible for code-interpreter staging; skipping.", + doc.file_id, + record.file_origin, ) continue @@ -920,7 +923,7 @@ def build_python_chat_files_from_search_docs( content = file_store.read_file(doc.file_id, mode="b").read() except Exception as e: logger.warning( - f"Failed to read bytes for file_id={doc.file_id!r}: {e}; skipping." + "Failed to read bytes for file_id=%r: %s; skipping.", doc.file_id, e ) continue diff --git a/backend/onyx/chat/citation_processor.py b/backend/onyx/chat/citation_processor.py index 3ed9df696f..bf933102b4 100644 --- a/backend/onyx/chat/citation_processor.py +++ b/backend/onyx/chat/citation_processor.py @@ -466,13 +466,15 @@ class DynamicCitationProcessor: num = int(num_str) except ValueError: # Invalid citation, skip it - logger.warning(f"Invalid citation number format: {num_str}") + logger.warning("Invalid citation number format: %s", num_str) continue # Check if we have a mapping for this citation number if num not in self.citation_to_doc: logger.warning( - f"Citation number {num} not found in mapping. Available: {list(self.citation_to_doc.keys())}" + "Citation number %s not found in mapping. Available: %s", + num, + list(self.citation_to_doc.keys()), ) continue diff --git a/backend/onyx/chat/compression.py b/backend/onyx/chat/compression.py index 89a049e2b1..2ad4a3bcc2 100644 --- a/backend/onyx/chat/compression.py +++ b/backend/onyx/chat/compression.py @@ -369,8 +369,10 @@ def compress_chat_history( chat_session_id = chat_history[0].chat_session_id logger.info( - f"Starting compression for session {chat_session_id}, " - f"history_len={len(chat_history)}, tokens_for_recent={compression_params.tokens_for_recent}" + "Starting compression for session %s, history_len=%s, tokens_for_recent=%s", + chat_session_id, + len(chat_history), + compression_params.tokens_for_recent, ) with ensure_trace( @@ -412,7 +414,9 @@ def compress_chat_history( tokenizer = get_tokenizer(None, None) summary_token_count = len(tokenizer.encode(summary_text)) logger.debug( - f"Generated summary ({summary_token_count} tokens): {summary_text[:200]}..." + "Generated summary (%s tokens): %s...", + summary_token_count, + summary_text[:200], ) # Create new summary as a ChatMessage @@ -429,9 +433,10 @@ def compress_chat_history( db_session.commit() logger.info( - f"Compressed {len(summary_content.older_messages)} messages into summary " - f"(session_id={chat_session_id}, " - f"summary_tokens={summary_token_count})" + "Compressed %s messages into summary (session_id=%s, summary_tokens=%s)", + len(summary_content.older_messages), + chat_session_id, + summary_token_count, ) return CompressionResult( @@ -440,7 +445,9 @@ def compress_chat_history( ) except Exception as e: - logger.exception(f"Compression failed for session {chat_session_id}: {e}") + logger.exception( + "Compression failed for session %s: %s", chat_session_id, e + ) db_session.rollback() return CompressionResult( summary_created=False, diff --git a/backend/onyx/chat/llm_loop.py b/backend/onyx/chat/llm_loop.py index 4773d3266f..ef6d4f4843 100644 --- a/backend/onyx/chat/llm_loop.py +++ b/backend/onyx/chat/llm_loop.py @@ -218,7 +218,8 @@ def _try_fallback_tool_extraction( ) if extracted_tool_calls: logger.info( - f"Extracted {len(extracted_tool_calls)} tool call(s) from response text as fallback" + "Extracted %s tool call(s) from response text as fallback", + len(extracted_tool_calls), ) return ( LlmStepResult( @@ -457,7 +458,8 @@ def construct_message_history( ] if forgotten_meta: logger.debug( - f"FileReader: building forgotten-files message for {[(m.file_id, m.filename) for m in forgotten_meta]}" + "FileReader: building forgotten-files message for %s", + [(m.file_id, m.filename) for m in forgotten_meta], ) forgotten_files_message = _create_file_tool_metadata_message( forgotten_meta, token_counter diff --git a/backend/onyx/chat/llm_step.py b/backend/onyx/chat/llm_step.py index 97bb19638f..efbe5a6264 100644 --- a/backend/onyx/chat/llm_step.py +++ b/backend/onyx/chat/llm_step.py @@ -483,7 +483,8 @@ def extract_tool_calls_from_response_text( ) logger.info( - f"Extracted {len(tool_calls)} tool call(s) from response text as fallback" + "Extracted %s tool call(s) from response text as fallback", + len(tool_calls), ) return tool_calls @@ -843,7 +844,9 @@ def translate_history_to_llm_format( content_parts.append(image_part) except Exception as e: logger.warning( - f"Failed to process image file {img_file.file_id}: {e}. Skipping image." + "Failed to process image file %s: %s. Skipping image.", + img_file.file_id, + e, ) user_msg = UserMessage( role="user", @@ -876,7 +879,8 @@ def translate_history_to_llm_format( else: logger.warning( - f"Unknown message type {msg.message_type} in history. Skipping message." + "Unknown message type %s in history. Skipping message.", + msg.message_type, ) # Apply model-specific formatting when translating to LLM format (e.g. OpenAI @@ -1010,7 +1014,8 @@ def run_llm_step_pkt_generator( if LOG_ONYX_MODEL_INTERACTIONS: logger.debug( - f"Message history:\n{_format_message_history_for_logging(llm_msg_history)}" + "Message history:\n%s", + _format_message_history_for_logging(llm_msg_history), ) id_to_tool_call_map: dict[int, dict[str, Any]] = {} @@ -1181,7 +1186,9 @@ def run_llm_step_pkt_generator( empty_chunk_count += 1 logger.warning( "LLM packet is empty (no content, reasoning, or tool calls). " - f"finish_reason={finish_reason}. Skipping: {packet}" + "finish_reason=%s. Skipping: %s", + finish_reason, + packet, ) continue @@ -1315,25 +1322,32 @@ def run_llm_step_pkt_generator( # Note: Content (AgentResponseDelta) doesn't need an explicit end packet - OverallStop handles it # Tool calls are handled by tool execution code and emit their own packets (e.g., SectionEnd) if LOG_ONYX_MODEL_INTERACTIONS: - logger.debug(f"Accumulated reasoning: {accumulated_reasoning}") - logger.debug(f"Accumulated answer: {accumulated_answer}") + logger.debug("Accumulated reasoning: %s", accumulated_reasoning) + logger.debug("Accumulated answer: %s", accumulated_answer) if tool_calls: tool_calls_str = "\n".join( f" - {tc.tool_name}: {json.dumps(tc.tool_args, indent=4)}" for tc in tool_calls ) - logger.debug(f"Tool calls:\n{tool_calls_str}") + logger.debug("Tool calls:\n%s", tool_calls_str) else: logger.debug("Tool calls: []") if actionable_chunk_count == 0: logger.warning( "LLM stream completed with no actionable deltas. " - f"chunks={stream_chunk_count}, empty_chunks={empty_chunk_count}, " - f"finish_reasons={sorted(finish_reasons)}, " - f"provider={llm.config.model_provider}, model={llm.config.model_name}, " - f"tool_choice={tool_choice}, tools_sent={len(tool_definitions)}" + "chunks=%s, empty_chunks=%s, " + "finish_reasons=%s, " + "provider=%s, model=%s, " + "tool_choice=%s, tools_sent=%s", + stream_chunk_count, + empty_chunk_count, + sorted(finish_reasons), + llm.config.model_provider, + llm.config.model_name, + tool_choice, + len(tool_definitions), ) return ( diff --git a/backend/onyx/chat/process_message.py b/backend/onyx/chat/process_message.py index 5804fff31f..e040d8cac4 100644 --- a/backend/onyx/chat/process_message.py +++ b/backend/onyx/chat/process_message.py @@ -277,7 +277,8 @@ def _fetch_cached_image_captions( ) except Exception: logger.warning( - f"Failed to fetch cached captions for user_file {user_file.id}", + "Failed to fetch cached captions for user_file %s", + user_file.id, exc_info=True, ) return [] @@ -354,7 +355,7 @@ def _extract_text_from_in_memory_file( return "\n\n".join(parts).strip() or None except Exception: - logger.warning(f"Failed to extract text from file {f.file_id}", exc_info=True) + logger.warning("Failed to extract text from file %s", f.file_id, exc_info=True) return None @@ -466,7 +467,8 @@ def extract_context_files( # Only the metadata is provided, with LLM using tools if not uf: logger.error( - f"File with id={f.file_id} in metadata-only path with no associated user file" + "File with id=%s in metadata-only path with no associated user file", + f.file_id, ) continue tool_metadata.append(_build_tool_metadata(uf)) @@ -477,7 +479,7 @@ def extract_context_files( if not text_content: continue if not uf: - logger.warning(f"No user file for file_id={f.file_id}") + logger.warning("No user file for file_id=%s", f.file_id) continue file_texts.append(text_content) file_metadata.append( @@ -980,7 +982,8 @@ def build_chat_turn( if all_injected_file_metadata: logger.debug( - f"FileReader: file metadata for LLM: {[(fid, m.filename) for fid, m in all_injected_file_metadata.items()]}" + "FileReader: file metadata for LLM: %s", + [(fid, m.filename) for fid, m in all_injected_file_metadata.items()], ) if summary_message is not None: @@ -1506,7 +1509,10 @@ def _stream_chat_turn( except EmptyLLMResponseError as e: stack_trace = traceback.format_exc() logger.warning( - f"LLM returned an empty response (provider={e.provider}, model={e.model}, tool_choice={e.tool_choice})" + "LLM returned an empty response (provider=%s, model=%s, tool_choice=%s)", + e.provider, + e.model, + e.tool_choice, ) yield StreamingError( error=e.client_error_msg, @@ -1522,7 +1528,7 @@ def _stream_chat_turn( db_session.rollback() except Exception as e: - logger.exception(f"Failed to process chat message due to {e}") + logger.exception("Failed to process chat message due to %s", e) stack_trace = traceback.format_exc() llm = setup.llms[0] if setup else None @@ -1689,7 +1695,7 @@ def llm_loop_completion_handle( final_answer = answer_tokens else: # Stopped by user - append stop message - logger.debug(f"Chat session {chat_session_id} stopped by user") + logger.debug("Chat session %s stopped by user", chat_session_id) if answer_tokens: final_answer = ( answer_tokens + " ... \n\nGeneration was stopped by the user." diff --git a/backend/onyx/chat/save_chat.py b/backend/onyx/chat/save_chat.py index 9bb3791d91..5a6393152e 100644 --- a/backend/onyx/chat/save_chat.py +++ b/backend/onyx/chat/save_chat.py @@ -87,7 +87,9 @@ def _create_and_link_tool_calls( tool_call_tokens = len(default_tokenizer.encode(arguments_json_str)) except Exception as e: logger.warning( - f"Failed to tokenize tool call arguments for {tool_call_info.tool_call_id}: {e}. Using length as (over) estimate." + "Failed to tokenize tool call arguments for %s: %s. Using length as (over) estimate.", + tool_call_info.tool_call_id, + e, ) arguments_json_str = json.dumps(tool_call_info.tool_call_arguments) tool_call_tokens = len(arguments_json_str) @@ -143,8 +145,9 @@ def _create_and_link_tool_calls( else: # Parent doesn't exist (likely cancelled) - skip this orphaned child logger.warning( - f"Skipping tool call '{tool_call_obj.tool_call_id}' with missing parent " - f"'{tool_call_info.parent_tool_call_id}' (likely cancelled during execution)" + "Skipping tool call '%s' with missing parent '%s' (likely cancelled during execution)", + tool_call_obj.tool_call_id, + tool_call_info.parent_tool_call_id, ) # Remove from DB session to prevent saving db_session.delete(tool_call_obj) @@ -283,11 +286,13 @@ def save_chat_turn( if is_project_file: logger.info( - f"Project file citation {search_doc_py.document_id} not in tool calls, creating it" + "Project file citation %s not in tool calls, creating it", + search_doc_py.document_id, ) else: logger.warning( - f"Citation doc {search_doc_py.document_id} not found in tool call search_docs, creating it" + "Citation doc %s not found in tool call search_docs, creating it", + search_doc_py.document_id, ) # Create the SearchDoc in the database diff --git a/backend/onyx/configs/app_configs.py b/backend/onyx/configs/app_configs.py index 23a973a4a5..fe2f311890 100644 --- a/backend/onyx/configs/app_configs.py +++ b/backend/onyx/configs/app_configs.py @@ -219,7 +219,8 @@ if _GOOGLE_OAUTH_SCOPE_OVERRIDE: ] except Exception: logger.exception( - f"Error configuring Google OAuth login scopes: {_GOOGLE_OAUTH_SCOPE_OVERRIDE}" + "Error configuring Google OAuth login scopes: %s", + _GOOGLE_OAUTH_SCOPE_OVERRIDE, ) # OpenID Connect configuration URL for OIDC integrations diff --git a/backend/onyx/connectors/airtable/airtable_connector.py b/backend/onyx/connectors/airtable/airtable_connector.py index 29458140be..ac72c07264 100644 --- a/backend/onyx/connectors/airtable/airtable_connector.py +++ b/backend/onyx/connectors/airtable/airtable_connector.py @@ -266,7 +266,7 @@ class AirtableConnector(LoadConnector): return attachment_response.content except requests.exceptions.HTTPError as e: if e.response.status_code == 410: - logger.info(f"Refreshing attachment for {filename}") + logger.info("Refreshing attachment for %s", filename) # Re-fetch the record to get a fresh URL refreshed_record = self.airtable_client.table( base_id, table_id @@ -283,7 +283,9 @@ class AirtableConnector(LoadConnector): attachment_response.raise_for_status() return attachment_response.content - logger.error(f"Failed to refresh attachment for {filename}") + logger.error( + "Failed to refresh attachment for %s", filename + ) raise attachment_content = get_attachment_with_retry(url, record_id) @@ -313,7 +315,7 @@ class AirtableConnector(LoadConnector): ) except Exception as e: logger.warning( - f"Failed to process attachment {filename}: {str(e)}" + "Failed to process attachment %s: %s", filename, str(e) ) return attachment_texts @@ -439,7 +441,10 @@ class AirtableConnector(LoadConnector): field_type = field_schema.type logger.debug( - f"Processing field '{field_name}' of type '{field_type}' for record '{record_id}'." + "Processing field '%s' of type '%s' for record '%s'.", + field_name, + field_type, + record_id, ) field_sections, field_metadata = self._process_field( @@ -457,7 +462,7 @@ class AirtableConnector(LoadConnector): metadata.update(field_metadata) if not sections: - logger.warning(f"No sections found for record {record_id}") + logger.warning("No sections found for record %s", record_id) return None # Include base name in semantic ID only in index_all mode @@ -505,7 +510,7 @@ class AirtableConnector(LoadConnector): if base_info.id == base_id: return base_info.name except Exception: - logger.debug(f"Could not resolve base name for {base_id}") + logger.debug("Could not resolve base name for %s", base_id) return None def _index_table( @@ -532,7 +537,10 @@ class AirtableConnector(LoadConnector): break logger.info( - f"Processing {len(records)} records from table '{table_schema.name}' in base '{base_name or base_id}'." + "Processing %s records from table '%s' in base '%s'.", + len(records), + table_schema.name, + base_name or base_id, ) if not records: @@ -572,7 +580,7 @@ class AirtableConnector(LoadConnector): if document: record_documents.append(document) except Exception as e: - logger.exception(f"Failed to process record {record['id']}") + logger.exception("Failed to process record %s", record["id"]) raise e if record_documents: @@ -599,23 +607,25 @@ class AirtableConnector(LoadConnector): def _load_all(self) -> GenerateDocumentsOutput: """Discover all bases and tables, then index everything.""" bases = self.airtable_client.bases() - logger.info(f"Discovered {len(bases)} Airtable base(s).") + logger.info("Discovered %s Airtable base(s).", len(bases)) for base_info in bases: base_id = base_info.id base_name = base_info.name - logger.info(f"Listing tables for base '{base_name}' ({base_id}).") + logger.info("Listing tables for base '%s' (%s).", base_name, base_id) try: base = self.airtable_client.base(base_id) tables = base.tables() except Exception: logger.exception( - f"Failed to list tables for base '{base_name}' ({base_id}), skipping." + "Failed to list tables for base '%s' (%s), skipping.", + base_name, + base_id, ) continue - logger.info(f"Found {len(tables)} table(s) in base '{base_name}'.") + logger.info("Found %s table(s) in base '%s'.", len(tables), base_name) for table in tables: try: @@ -626,6 +636,10 @@ class AirtableConnector(LoadConnector): ) except Exception: logger.exception( - f"Failed to index table '{table.name}' ({table.id}) in base '{base_name}' ({base_id}), skipping." + "Failed to index table '%s' (%s) in base '%s' (%s), skipping.", + table.name, + table.id, + base_name, + base_id, ) continue diff --git a/backend/onyx/connectors/asana/asana_api.py b/backend/onyx/connectors/asana/asana_api.py index fcc59b4d1a..be20f6c709 100755 --- a/backend/onyx/connectors/asana/asana_api.py +++ b/backend/onyx/connectors/asana/asana_api.py @@ -75,13 +75,13 @@ class AsanaAPI: projects_list.append(project_gid) else: logger.debug( - f"Skipping project: {project_gid} - not in accepted project_gids" + "Skipping project: %s - not in accepted project_gids", project_gid ) project_count += 1 if project_count % 100 == 0: - logger.info(f"Processed {project_count} projects") + logger.info("Processed %s projects", project_count) - logger.info(f"Found {len(projects_list)} projects to process") + logger.info("Found %s projects to process", len(projects_list)) # Asana tasks can belong to multiple projects and thus tasks # can get reported multiple times seen_task_gids: set[str] = set() @@ -90,10 +90,10 @@ class AsanaAPI: project_gid, start_date, start_seconds, seen_task_gids ): yield task - logger.info(f"Completed fetching {self.task_count} tasks from Asana") + logger.info("Completed fetching %s tasks from Asana", self.task_count) if self.api_error_count > 0: logger.warning( - f"Encountered {self.api_error_count} API errors during task fetching" + "Encountered %s API errors during task fetching", self.api_error_count ) def _get_tasks_for_project( @@ -109,26 +109,33 @@ class AsanaAPI: team_gid = team.get("gid") if project.get("archived"): - logger.info(f"Skipping archived project: {project_name} ({project_gid})") + logger.info("Skipping archived project: %s (%s)", project_name, project_gid) return if not team_gid: logger.info( - f"Skipping project without a team: {project_name} ({project_gid})" + "Skipping project without a team: %s (%s)", project_name, project_gid ) return if project.get("privacy_setting") == "private": if self.team_gid and team_gid != self.team_gid: logger.info( - f"Skipping private project not in configured team: {project_name} ({project_gid})" + "Skipping private project not in configured team: %s (%s)", + project_name, + project_gid, ) return logger.info( - f"Processing private project in configured team: {project_name} ({project_gid})" + "Processing private project in configured team: %s (%s)", + project_name, + project_gid, ) simple_start_date = start_date.split(".")[0].split("+")[0] logger.info( - f"Fetching tasks modified since {simple_start_date} for project: {project_name} ({project_gid})" + "Fetching tasks modified since %s for project: %s (%s)", + simple_start_date, + project_name, + project_gid, ) opts = { @@ -143,8 +150,8 @@ class AsanaAPI: gid = data["gid"] if gid in seen_task_gids: logger.debug( - f"Skipping duplicate Asana task {gid} " - f"(already yielded for another project)" + "Skipping duplicate Asana task %s (already yielded for another project)", + gid, ) continue seen_task_gids.add(gid) @@ -155,11 +162,13 @@ class AsanaAPI: runtime_seconds = end_seconds - start_seconds if runtime_seconds > 0: logger.info( - f"Processed {self.task_count} tasks in {runtime_seconds:.0f} seconds " - f"({self.task_count / runtime_seconds:.2f} tasks/second)" + "Processed %s tasks in %s seconds (%s tasks/second)", + self.task_count, + format(runtime_seconds, ".0f"), + format(self.task_count / runtime_seconds, ".2f"), ) - logger.debug(f"Processing Asana task: {data['name']}") + logger.debug("Processing Asana task: %s", data["name"]) text = self._construct_task_text(data) @@ -181,7 +190,9 @@ class AsanaAPI: yield task except Exception: logger.error( - f"Error processing task {data['gid']} in project {project_gid}", + "Error processing task %s in project %s", + data["gid"], + project_gid, exc_info=True, ) self.api_error_count += 1 @@ -229,7 +240,10 @@ class AsanaAPI: story_duration = time.time() - story_start logger.debug( - f"Processed {story_count} stories (including {comment_count} comments) in {story_duration:.2f} seconds" + "Processed %s stories (including %s comments) in %s seconds", + story_count, + comment_count, + format(story_duration, ".2f"), ) return text @@ -240,7 +254,9 @@ class AsanaAPI: self._user = self.users_api.get_user(user_gid, {"opt_fields": "name,email"}) if not self._user: - logger.warning(f"Unable to fetch user information for user_gid: {user_gid}") + logger.warning( + "Unable to fetch user information for user_gid: %s", user_gid + ) return {"name": "Unknown"} return self._user diff --git a/backend/onyx/connectors/asana/connector.py b/backend/onyx/connectors/asana/connector.py index 34169eb3a3..94cb0e1a49 100755 --- a/backend/onyx/connectors/asana/connector.py +++ b/backend/onyx/connectors/asana/connector.py @@ -40,7 +40,7 @@ class AsanaConnector(LoadConnector, PollConnector): self.batch_size = batch_size self.continue_on_failure = continue_on_failure logger.info( - f"AsanaConnector initialized with workspace_id: {asana_workspace_id}" + "AsanaConnector initialized with workspace_id: %s", asana_workspace_id ) def load_credentials(self, credentials: dict[str, Any]) -> dict[str, Any] | None: @@ -59,7 +59,7 @@ class AsanaConnector(LoadConnector, PollConnector): end: SecondsSinceUnixEpoch | None, # noqa: ARG002 ) -> GenerateDocumentsOutput: start_time = datetime.datetime.fromtimestamp(start).isoformat() - logger.info(f"Starting Asana poll from {start_time}") + logger.info("Starting Asana poll from %s", start_time) asana = asana_api.AsanaAPI( api_token=self.api_token, workspace_gid=self.workspace_id, @@ -73,12 +73,12 @@ class AsanaConnector(LoadConnector, PollConnector): docs_batch.append(doc) if len(docs_batch) >= self.batch_size: - logger.info(f"Yielding batch of {len(docs_batch)} documents") + logger.info("Yielding batch of %s documents", len(docs_batch)) yield docs_batch docs_batch = [] if docs_batch: - logger.info(f"Yielding final batch of {len(docs_batch)} documents") + logger.info("Yielding final batch of %s documents", len(docs_batch)) yield docs_batch logger.info("Asana poll completed") @@ -88,7 +88,7 @@ class AsanaConnector(LoadConnector, PollConnector): return self.poll_source(start=0, end=None) def _message_to_doc(self, task: asana_api.AsanaTask) -> Document: - logger.debug(f"Converting Asana task {task.id} to Document") + logger.debug("Converting Asana task %s to Document", task.id) return Document( id=task.id, sections=[TextSection(link=task.link, text=task.text)], diff --git a/backend/onyx/connectors/axero/connector.py b/backend/onyx/connectors/axero/connector.py index bc462e60b7..20dc4e2537 100644 --- a/backend/onyx/connectors/axero/connector.py +++ b/backend/onyx/connectors/axero/connector.py @@ -84,7 +84,7 @@ def _get_entities( total_records = data["TotalRecords"] contents = data["ResponseData"] pages_fetched += len(contents) - logger.debug(f"Fetched {pages_fetched} {ENTITY_NAME_MAP[entity_type]}") + logger.debug("Fetched %s %s", pages_fetched, ENTITY_NAME_MAP[entity_type]) for page in contents: update_time = time_str_to_utc(page["DateUpdated"]) @@ -138,7 +138,7 @@ def _map_post_to_parent( for ind, post in enumerate(posts): if (ind + 1) % 25 == 0: - logger.debug(f"Processed {ind + 1} posts or responses") + logger.debug("Processed %s posts or responses", ind + 1) post_time = time_str_to_utc( post.get("DateUpdated") or post.get("DateCreated") or epoch_str @@ -200,7 +200,7 @@ def _get_forums( total_records = data["TotalRecords"] contents = data["ResponseData"] pages_fetched += len(contents) - logger.debug(f"Fetched {pages_fetched} forums") + logger.debug("Fetched %s forums", pages_fetched) for page in contents: pages_to_return.append(page) diff --git a/backend/onyx/connectors/blob/connector.py b/backend/onyx/connectors/blob/connector.py index f1b5463a4c..6d5c6ebd6a 100644 --- a/backend/onyx/connectors/blob/connector.py +++ b/backend/onyx/connectors/blob/connector.py @@ -84,7 +84,7 @@ class BlobStorageConnector(LoadConnector, PollConnector): self, allow_images: bool ) -> None: """Set whether to process images in this connector.""" - logger.info(f"Setting allow_images to {allow_images}.") + logger.info("Setting allow_images to %s.", allow_images) self._allow_images = allow_images def _detect_bucket_region(self) -> None: @@ -103,11 +103,11 @@ class BlobStorageConnector(LoadConnector, PollConnector): ).get("HTTPHeaders", {}).get("x-amz-bucket-region") if self.bucket_region: - logger.debug(f"Detected bucket region: {self.bucket_region}") + logger.debug("Detected bucket region: %s", self.bucket_region) else: logger.warning("Bucket region not found in head_bucket response") except Exception as e: - logger.warning(f"Failed to detect bucket region via head_bucket: {e}") + logger.warning("Failed to detect bucket region via head_bucket: %s", e) def load_credentials(self, credentials: dict[str, Any]) -> dict[str, Any] | None: """Checks for boto3 credentials based on the bucket type. @@ -127,7 +127,7 @@ class BlobStorageConnector(LoadConnector, PollConnector): """ logger.debug( - f"Loading credentials for {self.bucket_name} or type {self.bucket_type}" + "Loading credentials for %s or type %s", self.bucket_name, self.bucket_type ) if self.bucket_type == BlobType.R2: @@ -156,7 +156,7 @@ class BlobStorageConnector(LoadConnector, PollConnector): "authentication_method", "access_key" ) logger.debug( - f"Using authentication method: {authentication_method} for S3 bucket." + "Using authentication method: %s for S3 bucket.", authentication_method ) if authentication_method == "access_key": logger.debug("Using access key authentication for S3 bucket.") @@ -283,7 +283,9 @@ class BlobStorageConnector(LoadConnector, PollConnector): if bytes_read > self.size_threshold + SIZE_THRESHOLD_BUFFER: logger.warning( - f"{key} exceeds size threshold of {self.size_threshold}. Skipping." + "%s exceeds size threshold of %s. Skipping.", + key, + self.size_threshold, ) return None @@ -418,7 +420,9 @@ class BlobStorageConnector(LoadConnector, PollConnector): and size_bytes > self.size_threshold ): logger.warning( - f"{file_name} exceeds size threshold of {self.size_threshold}. Skipping." + "%s exceeds size threshold of %s. Skipping.", + file_name, + self.size_threshold, ) continue @@ -426,7 +430,8 @@ class BlobStorageConnector(LoadConnector, PollConnector): if file_ext in OnyxFileExtensions.IMAGE_EXTENSIONS: if not self._allow_images: logger.debug( - f"Skipping image file: {key} (image processing not enabled)" + "Skipping image file: %s (image processing not enabled)", + key, ) continue @@ -461,7 +466,7 @@ class BlobStorageConnector(LoadConnector, PollConnector): yield batch batch = [] except Exception: - logger.exception(f"Error processing image {key}") + logger.exception("Error processing image %s", key) continue # Handle tabular files (xlsx, csv, tsv) — produce one @@ -510,7 +515,7 @@ class BlobStorageConnector(LoadConnector, PollConnector): yield batch batch = [] except Exception: - logger.exception(f"Error processing tabular file {key}") + logger.exception("Error processing tabular file %s", key) continue # Handle text and document files @@ -537,7 +542,7 @@ class BlobStorageConnector(LoadConnector, PollConnector): sections: list[TextSection | ImageSection] = [] if extraction_result.text_content.strip(): logger.debug( - f"Creating TextSection for {file_name} with link: {link}" + "Creating TextSection for %s with link: %s", file_name, link ) sections.append( TextSection( @@ -567,7 +572,7 @@ class BlobStorageConnector(LoadConnector, PollConnector): batch = [] except Exception: - logger.exception(f"Error decoding object {key} as UTF-8") + logger.exception("Error decoding object %s as UTF-8", key) if batch: yield batch diff --git a/backend/onyx/connectors/canvas/connector.py b/backend/onyx/connectors/canvas/connector.py index fe9605d5f9..9a95615e88 100644 --- a/backend/onyx/connectors/canvas/connector.py +++ b/backend/onyx/connectors/canvas/connector.py @@ -283,7 +283,7 @@ class CanvasConnector( @retry(tries=3, delay=1, backoff=2) def _list_pages(self, course_id: int) -> list[CanvasPage]: """Fetch all pages for a given course.""" - logger.debug(f"Fetching pages for course {course_id}") + logger.debug("Fetching pages for course %s", course_id) pages: list[CanvasPage] = [] for page in self.canvas_client.paginate( @@ -296,7 +296,7 @@ class CanvasConnector( @retry(tries=3, delay=1, backoff=2) def _list_assignments(self, course_id: int) -> list[CanvasAssignment]: """Fetch all assignments for a given course.""" - logger.debug(f"Fetching assignments for course {course_id}") + logger.debug("Fetching assignments for course %s", course_id) assignments: list[CanvasAssignment] = [] for page in self.canvas_client.paginate( @@ -311,7 +311,7 @@ class CanvasConnector( @retry(tries=3, delay=1, backoff=2) def _list_announcements(self, course_id: int) -> list[CanvasAnnouncement]: """Fetch all announcements for a given course.""" - logger.debug(f"Fetching announcements for course {course_id}") + logger.debug("Fetching announcements for course %s", course_id) announcements: list[CanvasAnnouncement] = [] for page in self.canvas_client.paginate( @@ -529,8 +529,9 @@ class CanvasConnector( ) if not announcement.posted_at: logger.debug( - f"Skipping announcement {announcement.id} in " - f"course {course_id}: no posted_at" + "Skipping announcement %s in course %s: no posted_at", + announcement.id, + course_id, ) continue if not _in_time_window(announcement.posted_at, start, end): @@ -596,7 +597,7 @@ class CanvasConnector( _handle_canvas_api_error(e) # NoReturn — always raises raise new_checkpoint.course_ids = [c.id for c in courses] - logger.info(f"Found {len(courses)} Canvas courses to process") + logger.info("Found %s Canvas courses to process", len(courses)) new_checkpoint.has_more = len(new_checkpoint.course_ids) > 0 return new_checkpoint @@ -648,8 +649,9 @@ class CanvasConnector( # the whole course rather than burning API calls on each stage. if oe.status_code == 404: logger.warning( - f"Canvas course {course_id} not found while fetching " - f"{stage} (HTTP 404). Skipping course." + "Canvas course %s not found while fetching %s (HTTP 404). Skipping course.", + course_id, + stage, ) yield ConnectorFailure( failed_entity=EntityFailure( @@ -661,8 +663,10 @@ class CanvasConnector( new_checkpoint.advance_course() else: logger.warning( - f"Failed to fetch {stage} for course {course_id}: {oe}. " - f"Skipping remainder of this stage." + "Failed to fetch %s for course %s: %s. Skipping remainder of this stage.", + stage, + course_id, + oe, ) yield ConnectorFailure( failed_entity=EntityFailure( @@ -681,8 +685,10 @@ class CanvasConnector( except Exception as e: # Unknown error — skip the stage and try to continue. logger.warning( - f"Failed to fetch {stage} for course {course_id}: {e}. " - f"Skipping remainder of this stage." + "Failed to fetch %s for course %s: %s. Skipping remainder of this stage.", + stage, + course_id, + e, ) yield ConnectorFailure( failed_entity=EntityFailure( diff --git a/backend/onyx/connectors/coda/connector.py b/backend/onyx/connectors/coda/connector.py index ac6e1b1900..2c56098721 100644 --- a/backend/onyx/connectors/coda/connector.py +++ b/backend/onyx/connectors/coda/connector.py @@ -145,7 +145,7 @@ class CodaConnector(LoadConnector, PollConnector): @retry(tries=3, delay=1, backoff=2) def _get_doc(self, doc_id: str) -> CodaDoc: """Fetch a specific Coda document by its ID.""" - logger.debug(f"Fetching Coda doc with ID: {doc_id}") + logger.debug("Fetching Coda doc with ID: %s", doc_id) try: response = self.coda_client.get(f"docs/{doc_id}") except CodaClientRequestFailedError as e: @@ -169,7 +169,7 @@ class CodaConnector(LoadConnector, PollConnector): @retry(tries=3, delay=1, backoff=2) def _get_page(self, doc_id: str, page_id: str) -> CodaPage: """Fetch a specific page from a Coda document.""" - logger.debug(f"Fetching Coda page with ID: {page_id}") + logger.debug("Fetching Coda page with ID: %s", page_id) try: response = self.coda_client.get(f"docs/{doc_id}/pages/{page_id}") except CodaClientRequestFailedError as e: @@ -193,7 +193,7 @@ class CodaConnector(LoadConnector, PollConnector): @retry(tries=3, delay=1, backoff=2) def _get_table(self, doc_id: str, table_id: str) -> CodaTable: """Fetch a specific table from a Coda document.""" - logger.debug(f"Fetching Coda table with ID: {table_id}") + logger.debug("Fetching Coda table with ID: %s", table_id) try: response = self.coda_client.get(f"docs/{doc_id}/tables/{table_id}") except CodaClientRequestFailedError as e: @@ -216,7 +216,7 @@ class CodaConnector(LoadConnector, PollConnector): @retry(tries=3, delay=1, backoff=2) def _get_row(self, doc_id: str, table_id: str, row_id: str) -> CodaRow: """Fetch a specific row from a Coda table.""" - logger.debug(f"Fetching Coda row with ID: {row_id}") + logger.debug("Fetching Coda row with ID: %s", row_id) try: response = self.coda_client.get( f"docs/{doc_id}/tables/{table_id}/rows/{row_id}" @@ -291,13 +291,13 @@ class CodaConnector(LoadConnector, PollConnector): if not next_page_token: break - logger.debug(f"Found {len(all_docs)} docs") + logger.debug("Found %s docs", len(all_docs)) return all_docs @retry(tries=3, delay=1, backoff=2) def _list_pages_in_doc(self, doc_id: str) -> List[CodaPage]: """List all pages in a Coda document.""" - logger.debug(f"Listing pages in Coda doc with ID: {doc_id}") + logger.debug("Listing pages in Coda doc with ID: %s", doc_id) pages: List[CodaPage] = [] endpoint = f"docs/{doc_id}/pages" @@ -340,13 +340,13 @@ class CodaConnector(LoadConnector, PollConnector): if not next_page_token: break - logger.debug(f"Found {len(pages)} pages in doc {doc_id}") + logger.debug("Found %s pages in doc %s", len(pages), doc_id) return pages @retry(tries=3, delay=1, backoff=2) def _fetch_page_content(self, doc_id: str, page_id: str) -> str: """Fetch the content of a Coda page.""" - logger.debug(f"Fetching content for page {page_id} in doc {doc_id}") + logger.debug("Fetching content for page %s in doc %s", page_id, doc_id) content_parts = [] next_page_token: str | None = None @@ -362,7 +362,7 @@ class CodaConnector(LoadConnector, PollConnector): ) except CodaClientRequestFailedError as e: if e.status_code == 404: - logger.debug(f"No content available for page {page_id}") + logger.debug("No content available for page %s", page_id) return "" raise @@ -384,7 +384,7 @@ class CodaConnector(LoadConnector, PollConnector): @retry(tries=3, delay=1, backoff=2) def _list_tables(self, doc_id: str) -> List[CodaTable]: """List all tables in a Coda document.""" - logger.debug(f"Listing tables in Coda doc with ID: {doc_id}") + logger.debug("Listing tables in Coda doc with ID: %s", doc_id) tables: List[CodaTable] = [] endpoint = f"docs/{doc_id}/tables" @@ -422,13 +422,13 @@ class CodaConnector(LoadConnector, PollConnector): if not next_page_token: break - logger.debug(f"Found {len(tables)} tables in doc {doc_id}") + logger.debug("Found %s tables in doc %s", len(tables), doc_id) return tables @retry(tries=3, delay=1, backoff=2) def _list_rows_and_values(self, doc_id: str, table_id: str) -> List[CodaRow]: """List all rows and their values in a table.""" - logger.debug(f"Listing rows in Coda table: {table_id} in Coda doc: {doc_id}") + logger.debug("Listing rows in Coda table: %s in Coda doc: %s", table_id, doc_id) rows: List[CodaRow] = [] endpoint = f"docs/{doc_id}/tables/{table_id}/rows" @@ -473,7 +473,7 @@ class CodaConnector(LoadConnector, PollConnector): if not next_page_token: break - logger.debug(f"Found {len(rows)} rows in table {table_id}") + logger.debug("Found %s rows in table %s", len(rows), table_id) return rows def _convert_page_to_document(self, page: CodaPage, content: str = "") -> Document: @@ -556,10 +556,10 @@ class CodaConnector(LoadConnector, PollConnector): def _iter_documents() -> Generator[Document, None, None]: docs = self._list_all_docs() - logger.info(f"Found {len(docs)} Coda docs to process") + logger.info("Found %s Coda docs to process", len(docs)) for doc in docs: - logger.debug(f"Processing doc: {doc.name} ({doc.id})") + logger.debug("Processing doc: %s (%s)", doc.name, doc.id) try: pages = self._list_pages_in_doc(doc.id) @@ -570,11 +570,13 @@ class CodaConnector(LoadConnector, PollConnector): content = self._fetch_page_content(doc.id, page.id) except Exception as e: logger.warning( - f"Failed to fetch content for page {page.id}: {e}" + "Failed to fetch content for page %s: %s", + page.id, + e, ) yield self._convert_page_to_document(page, content) except ConnectorValidationError as e: - logger.warning(f"Failed to list pages for doc {doc.id}: {e}") + logger.warning("Failed to list pages for doc %s: %s", doc.id, e) try: tables = self._list_tables(doc.id) @@ -584,11 +586,11 @@ class CodaConnector(LoadConnector, PollConnector): yield self._convert_table_with_rows_to_document(table, rows) except ConnectorValidationError as e: logger.warning( - f"Failed to list rows for table {table.id}: {e}" + "Failed to list rows for table %s: %s", table.id, e ) yield self._convert_table_with_rows_to_document(table, []) except ConnectorValidationError as e: - logger.warning(f"Failed to list tables for doc {doc.id}: {e}") + logger.warning("Failed to list tables for doc %s: %s", doc.id, e) return batch_generator(_iter_documents(), self.batch_size) @@ -603,7 +605,10 @@ class CodaConnector(LoadConnector, PollConnector): def _iter_documents() -> Generator[Document, None, None]: docs = self._list_all_docs() logger.info( - f"Polling {len(docs)} Coda docs for updates between {start} and {end}" + "Polling %s Coda docs for updates between %s and %s", + len(docs), + start, + end, ) for doc in docs: @@ -622,11 +627,13 @@ class CodaConnector(LoadConnector, PollConnector): content = self._fetch_page_content(doc.id, page.id) except Exception as e: logger.warning( - f"Failed to fetch content for page {page.id}: {e}" + "Failed to fetch content for page %s: %s", + page.id, + e, ) yield self._convert_page_to_document(page, content) except ConnectorValidationError as e: - logger.warning(f"Failed to list pages for doc {doc.id}: {e}") + logger.warning("Failed to list pages for doc %s: %s", doc.id, e) try: tables = self._list_tables(doc.id) @@ -659,7 +666,7 @@ class CodaConnector(LoadConnector, PollConnector): except ConnectorValidationError as e: logger.warning( - f"Failed to list rows for table {table.id}: {e}" + "Failed to list rows for table %s: %s", table.id, e ) if table_timestamp > start and table_timestamp <= end: yield self._convert_table_with_rows_to_document( @@ -667,7 +674,7 @@ class CodaConnector(LoadConnector, PollConnector): ) except ConnectorValidationError as e: - logger.warning(f"Failed to list tables for doc {doc.id}: {e}") + logger.warning("Failed to list tables for doc %s: %s", doc.id, e) return batch_generator(_iter_documents(), self.batch_size) @@ -676,13 +683,13 @@ class CodaConnector(LoadConnector, PollConnector): try: response = self.coda_client.get("whoami") logger.info( - f"Coda connector validated for user: {response.get('name', 'Unknown')}" + "Coda connector validated for user: %s", response.get("name", "Unknown") ) if self.workspace_id: params = {"workspaceId": self.workspace_id, "limit": "1"} self.coda_client.get("docs", params=params) - logger.info(f"Validated access to workspace: {self.workspace_id}") + logger.info("Validated access to workspace: %s", self.workspace_id) except CodaClientRequestFailedError as e: if e.status_code == 401: diff --git a/backend/onyx/connectors/confluence/connector.py b/backend/onyx/connectors/confluence/connector.py index 606f86a7aa..e4405f91e2 100644 --- a/backend/onyx/connectors/confluence/connector.py +++ b/backend/onyx/connectors/confluence/connector.py @@ -186,7 +186,7 @@ class ConfluenceConnector( self.continue_on_failure = continue_on_failure def set_allow_images(self, value: bool) -> None: - logger.info(f"Setting allow_images to {value}.") + logger.info("Setting allow_images to %s.", value) self.allow_images = value def _yield_space_hierarchy_nodes( @@ -470,7 +470,7 @@ class ConfluenceConnector( # Extract basic page information page_id = _get_page_id(page) page_title = page["title"] - logger.info(f"Converting page {page_title} to document") + logger.info("Converting page %s to document", page_title) page_url = build_confluence_document_id( self.wiki_base, page["_links"]["webui"], self.is_cloud ) @@ -532,7 +532,7 @@ class ConfluenceConnector( parent_hierarchy_raw_node_id=parent_hierarchy_raw_node_id, ) except Exception as e: - logger.error(f"Error converting page {page.get('id', 'unknown')}: {e}") + logger.error("Error converting page %s: %s", page.get("id", "unknown"), e) if is_atlassian_date_error(e): # propagate error to be caught and retried raise return ConnectorFailure( @@ -579,7 +579,8 @@ class ConfluenceConnector( if not self.allow_images: if media_type.startswith("image/"): logger.info( - f"Skipping attachment because allow images is False: {attachment['title']}" + "Skipping attachment because allow images is False: %s", + attachment["title"], ) continue @@ -587,12 +588,15 @@ class ConfluenceConnector( attachment, ): logger.info( - f"Skipping attachment because it is not an accepted file type: {attachment['title']}" + "Skipping attachment because it is not an accepted file type: %s", + attachment["title"], ) continue logger.info( - f"Processing attachment: {attachment['title']} attached to page {page['title']}" + "Processing attachment: %s attached to page %s", + attachment["title"], + page["title"], ) # Attachment document id: use the download URL for stable identity try: @@ -601,9 +605,9 @@ class ConfluenceConnector( ) except Exception as e: logger.warning( - f"Invalid attachment url for id {attachment['id']}, skipping" + "Invalid attachment url for id %s, skipping", attachment["id"] ) - logger.debug(f"Error building attachment url: {e}") + logger.debug("Error building attachment url: %s", e) continue try: response = convert_attachment_to_content( @@ -691,7 +695,8 @@ class ConfluenceConnector( attachment_docs.append(attachment_doc) except Exception as e: logger.error( - f"Failed to extract/summarize attachment {attachment['title']}", + "Failed to extract/summarize attachment %s", + attachment["title"], exc_info=e, ) if is_atlassian_date_error(e): @@ -773,7 +778,7 @@ class ConfluenceConnector( page_query_url = checkpoint.next_page_url or self._build_page_retrieval_url( start_ts, end, self.batch_size ) - logger.debug(f"page_query_url: {page_query_url}") + logger.debug("page_query_url: %s", page_query_url) # store the next page start for confluence server, cursor for confluence cloud def store_next_page_url(next_page_url: str) -> None: @@ -842,9 +847,8 @@ class ConfluenceConnector( except Exception as e: if is_atlassian_date_error(e) and start is not None: logger.warning( - "Confluence says we provided an invalid 'updated' field. This may indicate" - "a real issue, but can also appear during edge cases like daylight" - f"savings time changes. Retrying with a 1 hour offset. Error: {e}" + "Confluence says we provided an invalid 'updated' field. This may indicatea real issue, but can also appear during edge cases like daylightsavings time changes. Retrying with a 1 hour offset. Error: %s", + e, ) return self._fetch_document_batches(checkpoint, start - ONE_HOUR, end) raise diff --git a/backend/onyx/connectors/confluence/onyx_confluence.py b/backend/onyx/connectors/confluence/onyx_confluence.py index dadd2a7212..787010915c 100644 --- a/backend/onyx/connectors/confluence/onyx_confluence.py +++ b/backend/onyx/connectors/confluence/onyx_confluence.py @@ -339,9 +339,10 @@ class OnyxConfluence: else: if "WWW-Authenticate" in e.response.headers: logger.warning( - f"WWW-Authenticate: {e.response.headers['WWW-Authenticate']}" + "WWW-Authenticate: %s", + e.response.headers["WWW-Authenticate"], ) - logger.warning(f"Full error: {e.response.text}") + logger.warning("Full error: %s", e.response.text) raise e return @@ -394,7 +395,8 @@ class OnyxConfluence: confluence = Confluence(url=url, oauth2=oauth2_dict, **kwargs) else: logger.info( - f"Connecting to Confluence with Personal Access Token as user: {credentials['confluence_username']}" + "Connecting to Confluence with Personal Access Token as user: %s", + credentials["confluence_username"], ) if self._is_cloud: confluence = Confluence( @@ -461,7 +463,8 @@ class OnyxConfluence: except HTTPError as e: delay_until = _handle_http_error(e, attempt, MAX_RETRIES) logger.warning( - f"HTTPError in confluence call. Retrying in {delay_until} seconds..." + "HTTPError in confluence call. Retrying in %s seconds...", + delay_until, ) while time.monotonic() < delay_until: # in the future, check a signal here to exit @@ -533,7 +536,7 @@ class OnyxConfluence: url_suffix, "start", str(initial_start + ind) ) temp_url_suffix = update_param_in_path(temp_url_suffix, "limit", "1") - logger.info(f"Making recovery confluence call to {temp_url_suffix}") + logger.info("Making recovery confluence call to %s", temp_url_suffix) raw_response = self.get(path=temp_url_suffix, advanced_mode=True) raw_response.raise_for_status() @@ -543,13 +546,15 @@ class OnyxConfluence: if not latest_results: # no more results, break out of the loop logger.info( - f"No results found for call '{temp_url_suffix}'Stopping pagination." + "No results found for call '%s'Stopping pagination.", + temp_url_suffix, ) found_empty_page = True break except Exception: logger.exception( - f"Error in confluence call to {temp_url_suffix}. Continuing." + "Error in confluence call to %s. Continuing.", + temp_url_suffix, ) if found_empty_page: @@ -576,7 +581,7 @@ class OnyxConfluence: url_suffix = update_param_in_path(url_suffix, "limit", str(current_limit)) while url_suffix: - logger.debug(f"Making confluence call to {url_suffix}") + logger.debug("Making confluence call to %s", url_suffix) try: # Only pass params if they're not already in the URL to avoid duplicate # params accumulating. Confluence's _links.next already includes these. @@ -592,20 +597,22 @@ class OnyxConfluence: params=params, ) except Exception as e: - logger.exception(f"Error in confluence call to {url_suffix}") + logger.exception("Error in confluence call to %s", url_suffix) raise e try: raw_response.raise_for_status() except Exception as e: - logger.warning(f"Error in confluence call to {url_suffix}") + logger.warning("Error in confluence call to %s", url_suffix) # If the problematic expansion is in the url, replace it # with the replacement expansion and try again # If that fails, raise the error if _PROBLEMATIC_EXPANSIONS in url_suffix: logger.warning( - f"Replacing {_PROBLEMATIC_EXPANSIONS} with {_REPLACEMENT_EXPANSIONS} and trying again." + "Replacing %s with %s and trying again.", + _PROBLEMATIC_EXPANSIONS, + _REPLACEMENT_EXPANSIONS, ) url_suffix = url_suffix.replace( _PROBLEMATIC_EXPANSIONS, @@ -622,9 +629,12 @@ class OnyxConfluence: current_limit // 2, _MINIMUM_PAGINATION_LIMIT ) logger.warning( - f"Confluence returned {raw_response.status_code}. " - f"Reducing limit from {old_limit} to {current_limit} " - f"and retrying." + "Confluence returned %s. " + "Reducing limit from %s to %s " + "and retrying.", + raw_response.status_code, + old_limit, + current_limit, ) url_suffix = update_param_in_path( url_suffix, "limit", str(current_limit) @@ -653,18 +663,26 @@ class OnyxConfluence: continue logger.exception( - f"Error in confluence call to {url_suffix} " - f"after reducing limit to {current_limit}.\n" - f"Raw Response Text: {raw_response.text}\n" - f"Error: {e}\n" + "Error in confluence call to %s " + "after reducing limit to %s.\n" + "Raw Response Text: %s\n" + "Error: %s\n", + url_suffix, + current_limit, + raw_response.text, + e, ) raise logger.exception( - f"Error in confluence call to {url_suffix} \n" - f"Raw Response Text: {raw_response.text} \n" - f"Full Response: {raw_response.__dict__} \n" - f"Error: {e} \n" + "Error in confluence call to %s \n" + "Raw Response Text: %s \n" + "Full Response: %s \n" + "Error: %s \n", + url_suffix, + raw_response.text, + raw_response.__dict__, + e, ) raise @@ -672,7 +690,8 @@ class OnyxConfluence: next_response = raw_response.json() except Exception as e: logger.exception( - f"Failed to parse response as JSON. Response: {raw_response.__dict__}" + "Failed to parse response as JSON. Response: %s", + raw_response.__dict__, ) raise e @@ -734,7 +753,8 @@ class OnyxConfluence: # stop paginating. if url_suffix and not results: logger.info( - f"No results found for call '{old_url_suffix}' despite next link being present. Stopping pagination." + "No results found for call '%s' despite next link being present. Stopping pagination.", + old_url_suffix, ) break @@ -771,7 +791,7 @@ class OnyxConfluence: cql_url, limit=limit, next_page_callback=next_page_callback ) except Exception as e: - logger.exception(f"Error in paginated_page_retrieval: {e}") + logger.exception("Error in paginated_page_retrieval: %s", e) raise e def cql_paginate_all_expansions( @@ -953,10 +973,12 @@ class OnyxConfluence: response=e.response, ) from e raise - logger.debug(f"jsonrpc response: {response}") + logger.debug("jsonrpc response: %s", response) if not response.get("result"): logger.warning( - f"No jsonrpc response for space permissions for space {space_key}\nResponse: {response}" + "No jsonrpc response for space permissions for space %s\nResponse: %s", + space_key, + response, ) return response.get("result", []) @@ -999,14 +1021,20 @@ def get_user_email_from_username__server( except HTTPError as e: status_code = e.response.status_code if e.response is not None else "N/A" logger.warning( - f"Failed to get confluence email for {user_name}: HTTP {status_code} - {e}" + "Failed to get confluence email for %s: HTTP %s - %s", + user_name, + status_code, + e, ) # For now, we'll just return None and log a warning. This means # we will keep retrying to get the email every group sync. email = None except Exception as e: logger.warning( - f"Failed to get confluence email for {user_name}: {type(e).__name__} - {e}" + "Failed to get confluence email for %s: %s - %s", + user_name, + type(e).__name__, + e, ) email = None _USER_EMAIL_CACHE[user_name] = email @@ -1083,7 +1111,8 @@ def extract_text_from_confluence_html( ) if not user_id: logger.warning( - f"ri:userkey not found in ri:user element. Found attrs: {user.attrs}" + "ri:userkey not found in ri:user element. Found attrs: %s", + user.attrs, ) continue # Include @ sign for tagging, more clear for LLM @@ -1097,7 +1126,8 @@ def extract_text_from_confluence_html( page_data = html_page_reference.find("ri:page") if not page_data: logger.warning( - f"Skipping retrieval of {html_page_reference} because because page data is missing" + "Skipping retrieval of %s because because page data is missing", + html_page_reference, ) continue @@ -1105,13 +1135,14 @@ def extract_text_from_confluence_html( if not page_title: # only fetch pages that have a title logger.warning( - f"Skipping retrieval of {html_page_reference} because it has no title" + "Skipping retrieval of %s because it has no title", + html_page_reference, ) continue if page_title in fetched_titles: # prevent recursive fetching of pages - logger.debug(f"Skipping {page_title} because it has already been fetched") + logger.debug("Skipping %s because it has already been fetched", page_title) continue fetched_titles.add(page_title) @@ -1131,7 +1162,9 @@ def extract_text_from_confluence_html( break except Exception as e: logger.warning( - f"Error getting page contents for object {confluence_object}: {e}" + "Error getting page contents for object %s: %s", + confluence_object, + e, ) continue @@ -1153,7 +1186,7 @@ def extract_text_from_confluence_html( text_from_link = html_link_body.text html_link_body.replaceWith(f"(LINK TEXT: {text_from_link})") except Exception as e: - logger.warning(f"Error processing ac:link-body: {e}") + logger.warning("Error processing ac:link-body: %s", e) for html_attachment in soup.findAll("ri:attachment"): # This extracts the text from inline attachments in the page so they can be @@ -1163,7 +1196,7 @@ def extract_text_from_confluence_html( f"{sanitize_attachment_title(html_attachment.attrs['ri:filename'])}" ) # to be replaced later except Exception as e: - logger.warning(f"Error processing ac:attachment: {e}") + logger.warning("Error processing ac:attachment: %s", e) return format_document_soup(soup) diff --git a/backend/onyx/connectors/confluence/utils.py b/backend/onyx/connectors/confluence/utils.py index f9992309f0..a84edf3ced 100644 --- a/backend/onyx/connectors/confluence/utils.py +++ b/backend/onyx/connectors/confluence/utils.py @@ -145,9 +145,10 @@ def process_attachment( else: if attachment_size > CONFLUENCE_CONNECTOR_ATTACHMENT_SIZE_THRESHOLD: logger.warning( - f"Skipping {attachment_link} due to size. " - f"size={attachment_size} " - f"threshold={CONFLUENCE_CONNECTOR_ATTACHMENT_SIZE_THRESHOLD}" + "Skipping %s due to size. size=%s threshold=%s", + attachment_link, + attachment_size, + CONFLUENCE_CONNECTOR_ATTACHMENT_SIZE_THRESHOLD, ) return AttachmentProcessingResult( text=None, @@ -156,14 +157,19 @@ def process_attachment( ) logger.info( - f"Downloading attachment: title={attachment['title']} length={attachment_size} link={attachment_link}" + "Downloading attachment: title=%s length=%s link=%s", + attachment["title"], + attachment_size, + attachment_link, ) # Download the attachment resp: requests.Response = confluence_client._session.get(attachment_link) if resp.status_code != 200: logger.warning( - f"Failed to fetch {attachment_link} with status code {resp.status_code}" + "Failed to fetch %s with status code %s", + attachment_link, + resp.status_code, ) return AttachmentProcessingResult( text=None, @@ -226,7 +232,7 @@ def _process_image_attachment( media_type=media_type, file_origin=FileOrigin.CONNECTOR, ) - logger.info(f"Stored image attachment with file name: {file_name}") + logger.info("Stored image attachment with file name: %s", file_name) # Return empty text but include the file_name for later processing return AttachmentProcessingResult(text="", file_name=file_name, error=None) @@ -252,14 +258,16 @@ def convert_attachment_to_content( # Quick check for unsupported types: if media_type.startswith("video/") or media_type == "application/gliffy+json": logger.warning( - f"Skipping unsupported attachment type: '{media_type}' for {attachment['title']}" + "Skipping unsupported attachment type: '%s' for %s", + media_type, + attachment["title"], ) return None result = process_attachment(confluence_client, attachment, page_id, allow_images) if result.error is not None: logger.warning( - f"Attachment {attachment['title']} encountered error: {result.error}" + "Attachment %s encountered error: %s", attachment["title"], result.error ) return None @@ -367,7 +375,8 @@ def handle_confluence_rate_limit(confluence_call: F) -> F: except requests.HTTPError as e: delay_until = _handle_http_error(e, attempt, MAX_RETRIES) logger.warning( - f"HTTPError in confluence call. Retrying in {delay_until} seconds..." + "HTTPError in confluence call. Retrying in %s seconds...", + delay_until, ) while time.monotonic() < delay_until: # in the future, check a signal here to exit @@ -403,8 +412,8 @@ def _handle_http_error(e: requests.HTTPError, attempt: int, max_retries: int) -> FORBIDDEN_RETRY_DELAY = 10 if attempt < FORBIDDEN_MAX_RETRY_ATTEMPTS: logger.warning( - "403 error. This sometimes happens when we hit " - f"Confluence rate limits. Retrying in {FORBIDDEN_RETRY_DELAY} seconds..." + "403 error. This sometimes happens when we hit Confluence rate limits. Retrying in %s seconds...", + FORBIDDEN_RETRY_DELAY, ) return FORBIDDEN_RETRY_DELAY @@ -416,8 +425,10 @@ def _handle_http_error(e: requests.HTTPError, attempt: int, max_retries: int) -> delay = min(STARTING_DELAY * (BACKOFF**attempt), MAX_DELAY) logger.warning( - f"Server error {e.response.status_code}. " - f"Retrying in {delay} seconds (attempt {attempt + 1})..." + "Server error %s. Retrying in %s seconds (attempt %s)...", + e.response.status_code, + delay, + attempt + 1, ) return math.ceil(time.monotonic() + delay) @@ -435,7 +446,9 @@ def _handle_http_error(e: requests.HTTPError, attempt: int, max_retries: int) -> retry_after = int(retry_after_header) if retry_after > MAX_DELAY: logger.warning( - f"Clamping retry_after from {retry_after} to {MAX_DELAY} seconds..." + "Clamping retry_after from %s to %s seconds...", + retry_after, + MAX_DELAY, ) retry_after = MAX_DELAY if retry_after < MIN_DELAY: @@ -445,7 +458,7 @@ def _handle_http_error(e: requests.HTTPError, attempt: int, max_retries: int) -> if retry_after is not None: logger.warning( - f"Rate limiting with retry header. Retrying after {retry_after} seconds..." + "Rate limiting with retry header. Retrying after %s seconds...", retry_after ) delay = retry_after else: diff --git a/backend/onyx/connectors/connector_runner.py b/backend/onyx/connectors/connector_runner.py index be8b4bf6e9..220de41e25 100644 --- a/backend/onyx/connectors/connector_runner.py +++ b/backend/onyx/connectors/connector_runner.py @@ -213,7 +213,8 @@ class ConnectorRunner(Generic[CT]): yield None, None, None, next_checkpoint logger.debug( - f"Connector took {time.monotonic() - start} seconds to get to the next checkpoint." + "Connector took %s seconds to get to the next checkpoint.", + time.monotonic() - start, ) else: @@ -264,7 +265,9 @@ class ConnectorRunner(Generic[CT]): f"{key}: {value}" for key, value in local_vars.items() ) logger.error( - f"Error in connector. type: {exc_type};\nlocal_vars below -> \n{local_vars_str[:1024]}" + "Error in connector. type: %s;\nlocal_vars below -> \n%s", + exc_type, + local_vars_str[:1024], ) raise diff --git a/backend/onyx/connectors/cross_connector_utils/miscellaneous_utils.py b/backend/onyx/connectors/cross_connector_utils/miscellaneous_utils.py index a29883648b..1727ae683c 100644 --- a/backend/onyx/connectors/cross_connector_utils/miscellaneous_utils.py +++ b/backend/onyx/connectors/cross_connector_utils/miscellaneous_utils.py @@ -127,7 +127,7 @@ def _parse_document_source(connector_type: Any) -> DocumentSource | None: return connector_type if not isinstance(connector_type, str): - logger.warning(f"Invalid connector_type type: {type(connector_type).__name__}") + logger.warning("Invalid connector_type type: %s", type(connector_type).__name__) return None normalized = re.sub(r"[\s\-]+", "_", connector_type.strip().lower()) @@ -135,7 +135,9 @@ def _parse_document_source(connector_type: Any) -> DocumentSource | None: return DocumentSource(normalized) except ValueError: logger.warning( - f"Invalid connector_type value: '{connector_type}' (normalized: '{normalized}')" + "Invalid connector_type value: '%s' (normalized: '%s')", + connector_type, + normalized, ) return None diff --git a/backend/onyx/connectors/cross_connector_utils/rate_limit_wrapper.py b/backend/onyx/connectors/cross_connector_utils/rate_limit_wrapper.py index fe8c1cb1a5..fecfd0e91c 100644 --- a/backend/onyx/connectors/cross_connector_utils/rate_limit_wrapper.py +++ b/backend/onyx/connectors/cross_connector_utils/rate_limit_wrapper.py @@ -57,7 +57,9 @@ class _RateLimitDecorator: while len(self.call_history) == self.max_calls: sleep_time = self.sleep_time * (self.sleep_backoff**sleep_cnt) logger.notice( - f"Rate limit exceeded for function {func.__name__}. Waiting {sleep_time} seconds before retrying." + "Rate limit exceeded for function %s. Waiting %s seconds before retrying.", + func.__name__, + sleep_time, ) time.sleep(sleep_time) sleep_cnt += 1 diff --git a/backend/onyx/connectors/cross_connector_utils/tabular_section_utils.py b/backend/onyx/connectors/cross_connector_utils/tabular_section_utils.py index 7807309282..731b9706ab 100644 --- a/backend/onyx/connectors/cross_connector_utils/tabular_section_utils.py +++ b/backend/onyx/connectors/cross_connector_utils/tabular_section_utils.py @@ -67,7 +67,7 @@ def tabular_file_to_sections( try: text = file_io_to_text(file).strip() except Exception: - logger.exception(f"Failure decoding {file_name}") + logger.exception("Failure decoding %s", file_name) raise if not text: diff --git a/backend/onyx/connectors/discord/connector.py b/backend/onyx/connectors/discord/connector.py index 169754788b..34fa048c98 100644 --- a/backend/onyx/connectors/discord/connector.py +++ b/backend/onyx/connectors/discord/connector.py @@ -109,7 +109,7 @@ async def _fetch_filtered_channels( continue filtered_channels.append(channel) - logger.info(f"Found {len(filtered_channels)} channels for the authenticated user") + logger.info("Found %s channels for the authenticated user", len(filtered_channels)) return filtered_channels diff --git a/backend/onyx/connectors/dropbox/connector.py b/backend/onyx/connectors/dropbox/connector.py index 6c40966001..96e7575be1 100644 --- a/backend/onyx/connectors/dropbox/connector.py +++ b/backend/onyx/connectors/dropbox/connector.py @@ -59,7 +59,7 @@ class DropboxConnector(LoadConnector, PollConnector): ) return link_metadata.url except ApiError as err: - logger.exception(f"Failed to create a shared link for {path}: {err}") + logger.exception("Failed to create a shared link for %s: %s", path, err) return "" def _yield_files_recursive( @@ -117,7 +117,9 @@ class DropboxConnector(LoadConnector, PollConnector): ) except Exception as e: logger.exception( - f"Error decoding file {entry.path_display} as utf-8 error occurred: {e}" + "Error decoding file %s as utf-8 error occurred: %s", + entry.path_display, + e, ) elif isinstance(entry, FolderMetadata): diff --git a/backend/onyx/connectors/drupal_wiki/connector.py b/backend/onyx/connectors/drupal_wiki/connector.py index 9111da210c..40c856b6f9 100644 --- a/backend/onyx/connectors/drupal_wiki/connector.py +++ b/backend/onyx/connectors/drupal_wiki/connector.py @@ -129,7 +129,7 @@ class DrupalWikiConnector( self._api_token: str | None = None # set by load_credentials def set_allow_images(self, value: bool) -> None: - logger.info(f"Setting allow_images to {value}.") + logger.info("Setting allow_images to %s.", value) self.allow_images = value def _get_page_attachments(self, page_id: int) -> list[dict[str, Any]]: @@ -144,16 +144,16 @@ class DrupalWikiConnector( """ url = f"{self.base_url}/api/rest/scope/api/attachment" params = {"pageId": str(page_id)} - logger.debug(f"Fetching attachments for page {page_id} from {url}") + logger.debug("Fetching attachments for page %s from %s", page_id, url) try: response = rate_limited_get(url, headers=self.headers, params=params) response.raise_for_status() attachments = response.json() - logger.info(f"Found {len(attachments)} attachments for page {page_id}") + logger.info("Found %s attachments for page %s", len(attachments), page_id) return attachments except Exception as e: - logger.warning(f"Failed to fetch attachments for page {page_id}: {e}") + logger.warning("Failed to fetch attachments for page %s: %s", page_id, e) return [] def _download_attachment(self, attachment_id: int) -> bytes: @@ -167,7 +167,7 @@ class DrupalWikiConnector( Raw bytes of the attachment. """ url = f"{self.base_url}/api/rest/scope/api/attachment/{attachment_id}/download" - logger.info(f"Downloading attachment {attachment_id} from {url}") + logger.info("Downloading attachment %s from %s", attachment_id, url) # Use headers without Accept for binary downloads download_headers = {"Authorization": f"Bearer {self._api_token}"} @@ -197,7 +197,7 @@ class DrupalWikiConnector( if file_extension in OnyxFileExtensions.ALL_ALLOWED_EXTENSIONS: return True - logger.warning(f"Unsupported file type: {file_extension} for {file_name}") + logger.warning("Unsupported file type: %s for %s", file_extension, file_name) return False def _get_media_type_from_filename(self, filename: str) -> str: @@ -256,7 +256,8 @@ class DrupalWikiConnector( if media_type.startswith("image/"): if not self.allow_images: logger.info( - f"Skipping image attachment {file_name} because allow_images is False", + "Skipping image attachment %s because allow_images is False", + file_name, ) return [], None @@ -272,7 +273,9 @@ class DrupalWikiConnector( file_origin=FileOrigin.CONNECTOR, ) sections.append(image_section) - logger.debug(f"Stored image attachment with file name: {file_name}") + logger.debug( + "Stored image attachment with file name: %s", file_name + ) except Exception as e: return [], f"Image storage failed: {e}" @@ -291,7 +294,7 @@ class DrupalWikiConnector( ) except Exception: logger.exception( - f"Failed to extract tabular sections from {file_name}" + "Failed to extract tabular sections from %s", file_name ) if not sections: return [], f"No content extracted from tabular file {file_name}" @@ -311,7 +314,9 @@ class DrupalWikiConnector( media_for_image = get_image_type_from_bytes(image_data) except ValueError: logger.warning( - f"Unable to determine media type for embedded image {image_name} on attachment {file_name}" + "Unable to determine media type for embedded image %s on attachment %s", + image_name, + file_name, ) image_counter += 1 @@ -332,7 +337,10 @@ class DrupalWikiConnector( sections.append(image_section) except Exception as err: logger.warning( - f"Failed to store embedded image {image_name or image_counter} for attachment {file_name}: {err}" + "Failed to store embedded image %s for attachment %s: %s", + image_name or image_counter, + file_name, + err, ) extraction_result = extract_text_and_images( @@ -346,7 +354,7 @@ class DrupalWikiConnector( if text_content: sections.insert(0, TextSection(text=text_content, link=download_url)) logger.info( - f"Extracted {len(text_content)} characters from {file_name}" + "Extracted %s characters from %s", len(text_content), file_name ) elif not sections: return [], f"No text extracted for {file_name}" @@ -355,7 +363,10 @@ class DrupalWikiConnector( except Exception as e: logger.error( - f"Failed to process attachment {attachment.get('name', 'unknown')} on page {page_id}: {e}" + "Failed to process attachment %s on page %s: %s", + attachment.get("name", "unknown"), + page_id, + e, ) return [], f"Failed to process attachment: {e}" @@ -403,13 +414,13 @@ class DrupalWikiConnector( while has_more and len(all_space_ids) > last_num_ids: last_num_ids = len(all_space_ids) params = {"size": size, "page": page} - logger.debug(f"Fetching spaces from {url} (page={page}, size={size})") + logger.debug("Fetching spaces from %s (page=%s, size=%s)", url, page, size) response = rate_limited_get(url, headers=self.headers, params=params) response.raise_for_status() resp_json = response.json() space_response = DrupalWikiSpaceResponse.model_validate(resp_json) - logger.info(f"Fetched {len(space_response.content)} spaces from {page}") + logger.info("Fetched %s spaces from %s", len(space_response.content), page) # Collect ids into the set to deduplicate for space in space_response.content: all_space_ids.add(space.id) @@ -421,7 +432,7 @@ class DrupalWikiConnector( # Return a deterministic, sorted list of ids space_id_list = list(sorted(all_space_ids)) - logger.debug(f"Total spaces fetched: {len(space_id_list)}") + logger.debug("Total spaces fetched: %s", len(space_id_list)) return space_id_list def _get_pages_for_space( @@ -455,7 +466,12 @@ class DrupalWikiConnector( params["modifiedAfter"] = int(modified_after) logger.debug( - f"Fetching pages for space {space_id} from {url} ({page=}, {size=}, {modified_after=})" + "Fetching pages for space %s from %s (page=%r, size=%r, modified_after=%r)", + space_id, + url, + page, + size, + modified_after, ) response = rate_limited_get(url, headers=self.headers, params=params) response.raise_for_status() @@ -464,11 +480,14 @@ class DrupalWikiConnector( try: page_response = DrupalWikiPageResponse.model_validate(resp_json) except Exception as e: - logger.error(f"Failed to validate Drupal Wiki page response: {e}") + logger.error("Failed to validate Drupal Wiki page response: %s", e) raise ConnectorValidationError(f"Invalid API response format: {e}") logger.info( - f"Fetched {len(page_response.content)} pages in space {space_id} (page={page})" + "Fetched %s pages in space %s (page=%s)", + len(page_response.content), + space_id, + page, ) # Pydantic should automatically parse content items as DrupalWikiPage objects @@ -480,7 +499,7 @@ class DrupalWikiConnector( page += 1 - logger.debug(f"Total pages fetched for space {space_id}: {len(all_pages)}") + logger.debug("Total pages fetched for space %s: %s", space_id, len(all_pages)) return all_pages def _get_page_content(self, page_id: int) -> DrupalWikiPage: @@ -530,7 +549,9 @@ class DrupalWikiConnector( attachments = self._get_page_attachments(page.id) for attachment in attachments: logger.info( - f"Processing attachment: {attachment.get('name', 'Unknown')} (ID: {attachment['id']})" + "Processing attachment: %s (ID: %s)", + attachment.get("name", "Unknown"), + attachment["id"], ) # Use downloadUrl from API; fallback to page URL raw_download = attachment.get("downloadUrl") @@ -548,14 +569,18 @@ class DrupalWikiConnector( ) if error: logger.warning( - f"Error processing attachment {attachment.get('name', 'Unknown')}: {error}" + "Error processing attachment %s: %s", + attachment.get("name", "Unknown"), + error, ) continue if attachment_sections: sections.extend(attachment_sections) logger.debug( - f"Added {len(attachment_sections)} section(s) for attachment {attachment.get('name', 'Unknown')}" + "Added %s section(s) for attachment %s", + len(attachment_sections), + attachment.get("name", "Unknown"), ) # Create metadata @@ -575,7 +600,7 @@ class DrupalWikiConnector( doc_updated_at=datetime_from_utc_timestamp(page.lastModified), ) except Exception as e: - logger.error(f"Error processing page {page.id}: {e}") + logger.error("Error processing page %s: %s", page.id, e) return ConnectorFailure( failed_document=DocumentFailure( document_id=str(page.id), @@ -609,7 +634,7 @@ class DrupalWikiConnector( # Initialize page_ids from self.pages if not already set if not checkpoint.page_ids and self.pages: - logger.info(f"Initializing page_ids from self.pages: {self.pages}") + logger.info("Initializing page_ids from self.pages: %s", self.pages) checkpoint.page_ids = [int(page_id.strip()) for page_id in self.pages] # Ensure spaces is not None @@ -618,7 +643,7 @@ class DrupalWikiConnector( while checkpoint.current_page_id_index < len(checkpoint.page_ids): page_id = checkpoint.page_ids[checkpoint.current_page_id_index] - logger.debug(f"Processing page ID: {page_id}") + logger.debug("Processing page ID: %s", page_id) try: # Get the page content directly @@ -626,7 +651,7 @@ class DrupalWikiConnector( # Skip pages outside the time range if not self._is_page_in_time_range(page.lastModified, start, end): - logger.info(f"Skipping page {page_id} - outside time range") + logger.info("Skipping page %s - outside time range", page_id) checkpoint.current_page_id_index += 1 continue @@ -635,7 +660,7 @@ class DrupalWikiConnector( yield doc_or_failure except Exception as e: - logger.error(f"Error processing page ID {page_id}: {e}") + logger.error("Error processing page ID %s: %s", page_id, e) yield ConnectorFailure( failed_document=DocumentFailure( document_id=str(page_id), @@ -667,17 +692,17 @@ class DrupalWikiConnector( all_space_ids = self._get_space_ids() # checkpoint.spaces expects a list of ints; assign returned list checkpoint.spaces = all_space_ids - logger.info(f"Found {len(checkpoint.spaces)} spaces to process") + logger.info("Found %s spaces to process", len(checkpoint.spaces)) # Otherwise, use provided spaces if checkpoint is empty elif not checkpoint.spaces: - logger.info(f"Using provided spaces: {self.spaces}") + logger.info("Using provided spaces: %s", self.spaces) # Use provided spaces checkpoint.spaces = [int(space_id.strip()) for space_id in self.spaces] # Process spaces from the checkpoint while checkpoint.current_space_index < len(checkpoint.spaces): space_id = checkpoint.spaces[checkpoint.current_space_index] - logger.debug(f"Processing space ID: {space_id}") + logger.debug("Processing space ID: %s", space_id) # Get pages for the current space, filtered by start time if provided pages = self._get_pages_for_space(space_id, modified_after=start) @@ -685,13 +710,13 @@ class DrupalWikiConnector( # Process pages from the checkpoint while checkpoint.current_page_index < len(pages): page = pages[checkpoint.current_page_index] - logger.debug(f"Processing page: {page.title} (ID: {page.id})") + logger.debug("Processing page: %s (ID: %s)", page.title, page.id) # For space-based pages, we already filtered by modifiedAfter in the API call # Only need to check the end time boundary if end and page.lastModified >= end: logger.info( - f"Skipping page {page.id} - outside time range (after end)" + "Skipping page %s - outside time range (after end)", page.id ) checkpoint.current_page_index += 1 continue @@ -767,12 +792,14 @@ class DrupalWikiConnector( """ slim_docs: list[SlimDocument | HierarchyNode] = [] logger.info( - f"Starting retrieve_all_slim_docs with include_all_spaces={self.include_all_spaces}, spaces={self.spaces}" + "Starting retrieve_all_slim_docs with include_all_spaces=%s, spaces=%s", + self.include_all_spaces, + self.spaces, ) # Process specific page IDs if provided if self.pages: - logger.info(f"Processing specific pages: {self.pages}") + logger.info("Processing specific pages: %s", self.pages) for page_id in self.pages: try: # Get the page content directly @@ -782,7 +809,7 @@ class DrupalWikiConnector( if not self._is_page_in_time_range( page_content.lastModified, start, end ): - logger.info(f"Skipping page {page_id} - outside time range") + logger.info("Skipping page %s - outside time range", page_id) continue # Create slim document for the page @@ -794,7 +821,7 @@ class DrupalWikiConnector( id=page_url, ) ) - logger.debug(f"Added slim document for page {page_content.id}") + logger.debug("Added slim document for page %s", page_content.id) # Process attachments for this page attachments = self._get_page_attachments(page_content.id) @@ -807,13 +834,14 @@ class DrupalWikiConnector( ) ) logger.debug( - f"Added slim document for attachment {attachment['id']}" + "Added slim document for attachment %s", + attachment["id"], ) # Yield batch if it reaches the batch size if len(slim_docs) >= self.batch_size: logger.debug( - f"Yielding batch of {len(slim_docs)} slim documents" + "Yielding batch of %s slim documents", len(slim_docs) ) yield slim_docs slim_docs = [] @@ -825,7 +853,7 @@ class DrupalWikiConnector( except Exception as e: logger.error( - f"Error processing page ID {page_id} for slim documents: {e}" + "Error processing page ID %s for slim documents: %s", page_id, e ) # Process spaces if include_all_spaces is True or spaces are provided @@ -838,25 +866,25 @@ class DrupalWikiConnector( # Fetch all spaces all_space_ids = self._get_space_ids() spaces_to_process = all_space_ids - logger.info(f"Found {len(spaces_to_process)} spaces to process") + logger.info("Found %s spaces to process", len(spaces_to_process)) else: - logger.info(f"Using provided spaces: {self.spaces}") + logger.info("Using provided spaces: %s", self.spaces) # Use provided spaces spaces_to_process = [int(space_id.strip()) for space_id in self.spaces] # Process each space for space_id in spaces_to_process: - logger.info(f"Processing space ID: {space_id}") + logger.info("Processing space ID: %s", space_id) # Get pages for the current space, filtered by start time if provided pages = self._get_pages_for_space(space_id, modified_after=start) # Process each page for page in pages: - logger.debug(f"Processing page: {page.title} (ID: {page.id})") + logger.debug("Processing page: %s (ID: %s)", page.title, page.id) # Skip pages outside the time range if end and page.lastModified >= end: logger.info( - f"Skipping page {page.id} - outside time range (after end)" + "Skipping page %s - outside time range (after end)", page.id ) continue @@ -867,7 +895,7 @@ class DrupalWikiConnector( id=page_url, ) ) - logger.info(f"Added slim document for page {page.id}") + logger.info("Added slim document for page %s", page.id) # Process attachments for this page attachments = self._get_page_attachments(page.id) @@ -880,13 +908,14 @@ class DrupalWikiConnector( ) ) logger.info( - f"Added slim document for attachment {attachment['id']}" + "Added slim document for attachment %s", + attachment["id"], ) # Yield batch if it reaches the batch size if len(slim_docs) >= self.batch_size: logger.info( - f"Yielding batch of {len(slim_docs)} slim documents" + "Yielding batch of %s slim documents", len(slim_docs) ) yield slim_docs slim_docs = [] @@ -898,7 +927,7 @@ class DrupalWikiConnector( # Yield remaining documents if slim_docs: - logger.debug(f"Yielding final batch of {len(slim_docs)} slim documents") + logger.debug("Yielding final batch of %s slim documents", len(slim_docs)) yield slim_docs def validate_connector_settings(self) -> None: diff --git a/backend/onyx/connectors/egnyte/connector.py b/backend/onyx/connectors/egnyte/connector.py index b1f648a18b..123345c519 100644 --- a/backend/onyx/connectors/egnyte/connector.py +++ b/backend/onyx/connectors/egnyte/connector.py @@ -71,7 +71,7 @@ def _process_egnyte_file( # Explicitly excluding image extensions here. TODO: consider allowing images if extension not in OnyxFileExtensions.TEXT_AND_DOCUMENT_EXTENSIONS: - logger.warning(f"Skipping file '{file_name}' with extension '{extension}'") + logger.warning("Skipping file '%s' with extension '%s'", file_name, extension) return None # Extract text content based on file type @@ -283,7 +283,7 @@ class EgnyteConnector(LoadConnector, PollConnector, OAuthConnector): # Iterate through yielded files and filter them for file in self._get_files_list(self.folder_path): if not self._should_index_file(file, start_time, end_time): - logger.debug(f"Skipping file '{file['path']}'.") + logger.debug("Skipping file '%s'.", file["path"]) continue try: @@ -302,7 +302,9 @@ class EgnyteConnector(LoadConnector, PollConnector, OAuthConnector): if not response.ok: logger.error( - f"Failed to fetch file content: {file['path']} (status code: {response.status_code})" + "Failed to fetch file content: %s (status code: %s)", + file["path"], + response.status_code, ) continue @@ -331,7 +333,7 @@ class EgnyteConnector(LoadConnector, PollConnector, OAuthConnector): current_batch = [] except Exception: - logger.exception(f"Failed to process file {file['path']}") + logger.exception("Failed to process file %s", file["path"]) continue if current_batch: diff --git a/backend/onyx/connectors/file/connector.py b/backend/onyx/connectors/file/connector.py index 0e6e6482c7..22ff71ba3a 100644 --- a/backend/onyx/connectors/file/connector.py +++ b/backend/onyx/connectors/file/connector.py @@ -73,7 +73,7 @@ def _create_image_section( ) return section, stored_file_name except Exception as e: - logger.error(f"Failed to store image {display_name}: {e}") + logger.error("Failed to store image %s: %s", display_name, e) raise e @@ -98,7 +98,7 @@ def _process_file( if extension not in OnyxFileExtensions.ALL_ALLOWED_EXTENSIONS: logger.warning( - f"Skipping file '{file_name}' with unrecognized extension '{extension}'" + "Skipping file '%s' with unrecognized extension '%s'", file_name, extension ) return [] @@ -121,7 +121,7 @@ def _process_file( # Read the image data image_data = file.read() if not image_data: - logger.warning(f"Empty image file: {file_name}") + logger.warning("Empty image file: %s", file_name) return [] # Create an ImageSection for the image @@ -147,7 +147,7 @@ def _process_file( ) ] except Exception as e: - logger.error(f"Failed to process image file {file_name}: {e}") + logger.error("Failed to process image file %s: %s", file_name, e) return [] # 2) Otherwise: text-based approach. Possibly with embedded images. @@ -165,7 +165,9 @@ def _process_file( # If so, we should add it to any metadata processed so far if extraction_result.metadata: logger.debug( - f"Found file-specific metadata for {file_name}: {extraction_result.metadata}" + "Found file-specific metadata for %s: %s", + file_name, + extraction_result.metadata, ) onyx_metadata, more_custom_tags = process_onyx_metadata( extraction_result.metadata @@ -218,13 +220,13 @@ def _process_file( ) ) except Exception as e: - logger.error(f"Failed to process tabular file {file_name}: {e}") + logger.error("Failed to process tabular file %s: %s", file_name, e) return [] if not sections: - logger.warning(f"No content extracted from tabular file {file_name}") + logger.warning("No content extracted from tabular file %s", file_name) return [] elif extraction_result.text_content.strip(): - logger.debug(f"Creating TextSection for {file_name} with link: {link}") + logger.debug("Creating TextSection for %s with link: %s", file_name, link) sections.append( TextSection(link=link, text=extraction_result.text_content.strip()) ) @@ -245,11 +247,14 @@ def _process_file( ) sections.append(image_section) logger.debug( - f"Created ImageSection for embedded image {idx} in {file_name}, stored as: {stored_file_name}" + "Created ImageSection for embedded image %s in %s, stored as: %s", + idx, + file_name, + stored_file_name, ) except Exception as e: logger.warning( - f"Failed to process embedded image {idx} in {file_name}: {e}" + "Failed to process embedded image %s in %s: %s", idx, file_name, e ) return [ @@ -319,7 +324,7 @@ class LocalFileConnector(LoadConnector): else: zip_metadata = loaded_metadata except Exception as e: - logger.warning(f"Failed to load metadata from file store: {e}") + logger.warning("Failed to load metadata from file store: %s", e) elif self._zip_metadata_deprecated: logger.warning( "Using deprecated inline zip_metadata dict. Re-upload files to use the new file store format." @@ -333,7 +338,9 @@ class LocalFileConnector(LoadConnector): file_record = file_store.read_file_record(file_id=file_id) if not file_record: # typically an unsupported extension - logger.warning(f"No file record found for '{file_id}' in PG; skipping.") + logger.warning( + "No file record found for '%s' in PG; skipping.", file_id + ) continue metadata = zip_metadata.get( diff --git a/backend/onyx/connectors/fireflies/connector.py b/backend/onyx/connectors/fireflies/connector.py index 25347cf0c2..0b06e77e61 100644 --- a/backend/onyx/connectors/fireflies/connector.py +++ b/backend/onyx/connectors/fireflies/connector.py @@ -190,8 +190,10 @@ class FirefliesConnector(PollConnector, LoadConnector): if response.status_code < 500 or attempt == _FIREFLIES_MAX_RETRIES - 1: break logger.warning( - f"Fireflies returned {response.status_code} on attempt " - f"{attempt + 1}/{_FIREFLIES_MAX_RETRIES}, retrying" + "Fireflies returned %s on attempt %s/%s, retrying", + response.status_code, + attempt + 1, + _FIREFLIES_MAX_RETRIES, ) time.sleep(_FIREFLIES_RETRY_BASE_DELAY_SECONDS * (2**attempt)) diff --git a/backend/onyx/connectors/freshdesk/connector.py b/backend/onyx/connectors/freshdesk/connector.py index f86baea1df..7c390e5ef9 100644 --- a/backend/onyx/connectors/freshdesk/connector.py +++ b/backend/onyx/connectors/freshdesk/connector.py @@ -233,7 +233,9 @@ class FreshdeskConnector(PollConnector, LoadConnector): tickets = json.loads(response.content) logger.info( - f"Fetched {len(tickets)} tickets from Freshdesk API (Page {params['page']})" + "Fetched %s tickets from Freshdesk API (Page %s)", + len(tickets), + params["page"], ) yield tickets diff --git a/backend/onyx/connectors/gitbook/connector.py b/backend/onyx/connectors/gitbook/connector.py index 3c795ce5ba..b4480d46b3 100644 --- a/backend/onyx/connectors/gitbook/connector.py +++ b/backend/onyx/connectors/gitbook/connector.py @@ -235,9 +235,10 @@ class GitbookConnector(LoadConnector, PollConnector): pages: list[dict[str, Any]] = content.get("pages", []) current_batch: list[Document | HierarchyNode] = [] - logger.info(f"Found {len(pages)} root pages.") + logger.info("Found %s root pages.", len(pages)) logger.info( - f"First 20 Page Ids: {[page.get('id', 'Unknown') for page in pages[:20]]}" + "First 20 Page Ids: %s", + [page.get("id", "Unknown") for page in pages[:20]], ) while pages: @@ -268,7 +269,7 @@ class GitbookConnector(LoadConnector, PollConnector): yield current_batch except requests.RequestException as e: - logger.error(f"Error fetching GitBook content: {str(e)}") + logger.error("Error fetching GitBook content: %s", str(e)) raise def load_from_state(self) -> GenerateDocumentsOutput: diff --git a/backend/onyx/connectors/github/connector.py b/backend/onyx/connectors/github/connector.py index ada0ede3c9..dd00712774 100644 --- a/backend/onyx/connectors/github/connector.py +++ b/backend/onyx/connectors/github/connector.py @@ -140,11 +140,13 @@ def _paginate_until_error( if num_objs % CURSOR_LOG_FREQUENCY == 0: logger.info( - f"Retrieved {num_objs} objects with current cursor url: {get_nextUrl(pag_list, nextUrl_key)}" + "Retrieved %s objects with current cursor url: %s", + num_objs, + get_nextUrl(pag_list, nextUrl_key), ) except Exception as e: - logger.exception(f"Error during cursor-based pagination: {e}") + logger.exception("Error during cursor-based pagination: %s", e) if num_objs - prev_num_objs > 0: raise @@ -230,7 +232,7 @@ def _get_userinfo(user: NamedUser) -> dict[str, str]: try: return cast(str | None, getattr(user, attr_name)) except GithubException: - logger.debug(f"Error getting {attr_name} for user") + logger.debug("Error getting %s for user", attr_name) return None return { @@ -504,7 +506,10 @@ class GithubConnector( repos.append(repo) except GithubException as e: logger.warning( - f"Could not fetch repo {self.repo_owner}/{repo_name}: {e}" + "Could not fetch repo %s/%s: %s", + self.repo_owner, + repo_name, + e, ) return repos @@ -609,7 +614,7 @@ class GithubConnector( repo, self.github_client ) if self.include_prs and checkpoint.stage == GithubConnectorStage.PRS: - logger.info(f"Fetching PRs for repo: {repo.name}") + logger.info("Fetching PRs for repo: %s", repo.name) pr_batch = _get_batch_rate_limited( self._pull_requests_func(repo), @@ -676,7 +681,7 @@ class GithubConnector( # In offset mode, while indexing without time constraints, the pr batch # will be empty when we're done. used_cursor = checkpoint.cursor_url is not None - logger.info(f"Fetched {num_prs} PRs for repo: {repo.name}") + logger.info("Fetched %s PRs for repo: %s", num_prs, repo.name) if num_prs > 0 and not done_with_prs and not used_cursor: return checkpoint @@ -692,7 +697,7 @@ class GithubConnector( checkpoint.stage = GithubConnectorStage.ISSUES if self.include_issues and checkpoint.stage == GithubConnectorStage.ISSUES: - logger.info(f"Fetching issues for repo: {repo.name}") + logger.info("Fetching issues for repo: %s", repo.name) issue_batch = list( _get_batch_rate_limited( @@ -704,7 +709,7 @@ class GithubConnector( self.github_client, ) ) - logger.info(f"Fetched {len(issue_batch)} issues for repo: {repo.name}") + logger.info("Fetched %s issues for repo: %s", len(issue_batch), repo.name) checkpoint.curr_page += 1 done_with_issues = False num_issues = 0 @@ -753,7 +758,7 @@ class GithubConnector( ) continue - logger.info(f"Fetched {num_issues} issues for repo: {repo.name}") + logger.info("Fetched %s issues for repo: %s", num_issues, repo.name) # if we found any issues on the page, and we're not done, return the checkpoint. # don't return if we're using cursor-based pagination to avoid infinite loops if num_issues > 0 and not done_with_issues and not checkpoint.cursor_url: @@ -778,7 +783,9 @@ class GithubConnector( if checkpoint.cached_repo_ids: logger.info( - f"{len(checkpoint.cached_repo_ids)} repos remaining (IDs: {checkpoint.cached_repo_ids})" + "%s repos remaining (IDs: %s)", + len(checkpoint.cached_repo_ids), + checkpoint.cached_repo_ids, ) else: logger.info("No more repos remaining") @@ -920,7 +927,9 @@ class GithubConnector( f"{self.repo_owner}/{repo_name}" ) logger.info( - f"Successfully accessed repository: {self.repo_owner}/{repo_name}" + "Successfully accessed repository: %s/%s", + self.repo_owner, + repo_name, ) test_repo.get_contents("") valid_repos = True diff --git a/backend/onyx/connectors/github/rate_limit_utils.py b/backend/onyx/connectors/github/rate_limit_utils.py index fdb7a5c22e..c58f6c7c8e 100644 --- a/backend/onyx/connectors/github/rate_limit_utils.py +++ b/backend/onyx/connectors/github/rate_limit_utils.py @@ -21,5 +21,7 @@ def sleep_after_rate_limit_exception(github_client: Github) -> None: tzinfo=timezone.utc ) - datetime.now(tz=timezone.utc) sleep_time += timedelta(minutes=1) # add an extra minute just to be safe - logger.notice(f"Ran into Github rate-limit. Sleeping {sleep_time.seconds} seconds.") + logger.notice( + "Ran into Github rate-limit. Sleeping %s seconds.", sleep_time.seconds + ) time.sleep(sleep_time.total_seconds()) diff --git a/backend/onyx/connectors/github/utils.py b/backend/onyx/connectors/github/utils.py index e5029b3a7e..90e31b1500 100644 --- a/backend/onyx/connectors/github/utils.py +++ b/backend/onyx/connectors/github/utils.py @@ -57,7 +57,7 @@ def deserialize_repository( except Exception as e: # If all else fails, re-fetch the repo directly logger.warning( - f"Failed to deserialize repository: {e}. Attempting to re-fetch." + "Failed to deserialize repository: %s. Attempting to re-fetch.", e ) repo_id = cached_repo.id return github_client.get_repo(repo_id) diff --git a/backend/onyx/connectors/gmail/connector.py b/backend/onyx/connectors/gmail/connector.py index 147ba28771..1e827baa41 100644 --- a/backend/onyx/connectors/gmail/connector.py +++ b/backend/onyx/connectors/gmail/connector.py @@ -443,7 +443,9 @@ class GmailConnector( query = _build_time_range_query(time_range_start, time_range_end) slim_doc_batch: list[SlimDocument | HierarchyNode] = [] logger.info( - f"Fetching {'slim' if is_slim else 'full'} threads for user: {user_email}" + "Fetching %s threads for user: %s", + "slim" if is_slim else "full", + user_email, ) gmail_service = get_gmail_service(self.creds, user_email) try: diff --git a/backend/onyx/connectors/gong/connector.py b/backend/onyx/connectors/gong/connector.py index 46191f7484..a3f28b6fd3 100644 --- a/backend/onyx/connectors/gong/connector.py +++ b/backend/onyx/connectors/gong/connector.py @@ -183,7 +183,7 @@ class GongConnector(CheckpointedConnector[GongConnectorCheckpoint]): # detect it before raise_for_status so callers can restart the workspace. if cursor and "cursor has expired" in response.text.lower(): raise _CursorExpiredError(response.text) - logger.error(f"Error fetching transcripts: {response.text}") + logger.error("Error fetching transcripts: %s", response.text) response.raise_for_status() data = response.json() @@ -248,7 +248,7 @@ class GongConnector(CheckpointedConnector[GongConnectorCheckpoint]): for workspace in self.workspaces: workspace_id = workspace_map.get(workspace) if not workspace_id: - logger.error(f"Invalid Gong workspace: {workspace}") + logger.error("Invalid Gong workspace: %s", workspace) continue resolved.append(workspace_id) @@ -304,12 +304,15 @@ class GongConnector(CheckpointedConnector[GongConnectorCheckpoint]): call_time_str = call_metadata["started"] call_title = call_metadata["title"] logger.info( - f"Indexing Gong call id {call_id} from {call_time_str.split('T', 1)[0]}: {call_title}" + "Indexing Gong call id %s from %s: %s", + call_id, + call_time_str.split("T", 1)[0], + call_title, ) call_parties = cast(list[dict] | None, call_details.get("parties")) if call_parties is None: - logger.error(f"Couldn't get parties for Call ID: {call_id}") + logger.error("Couldn't get parties for Call ID: %s", call_id) call_parties = [] id_to_name_map = self._parse_parties(call_parties) @@ -395,9 +398,8 @@ class GongConnector(CheckpointedConnector[GongConnectorCheckpoint]): if newly_stashed: logger.warning( - f"Gong call details not yet available (race condition); " - f"deferring to next checkpoint invocation: " - f"call_ids={newly_stashed}" + "Gong call details not yet available (race condition); deferring to next checkpoint invocation: call_ids=%s", + newly_stashed, ) # First attempt on any newly-stashed transcripts counts as attempt #1. # pending_call_details_attempts is guaranteed 0 here because @@ -467,8 +469,11 @@ class GongConnector(CheckpointedConnector[GongConnectorCheckpoint]): start, end ) logger.info( - f"Fetching Gong calls between {start_time} and {end_time} " - f"(workspace {checkpoint.workspace_index + 1}/{len(workspace_ids)})" + "Fetching Gong calls between %s and %s (workspace %s/%s)", + start_time, + end_time, + checkpoint.workspace_index + 1, + len(workspace_ids), ) workspace_id = workspace_ids[checkpoint.workspace_index] @@ -488,9 +493,9 @@ class GongConnector(CheckpointedConnector[GongConnectorCheckpoint]): # Document upserts are idempotent (keyed by call_id) so # reprocessing is safe. logger.warning( - f"Gong pagination cursor expired for workspace " - f"{checkpoint.workspace_index + 1}/{len(workspace_ids)}; " - f"restarting workspace from beginning of time range." + "Gong pagination cursor expired for workspace %s/%s; restarting workspace from beginning of time range.", + checkpoint.workspace_index + 1, + len(workspace_ids), ) checkpoint.cursor = None checkpoint.has_more = True @@ -560,17 +565,17 @@ class GongConnector(CheckpointedConnector[GongConnectorCheckpoint]): checkpoint.pending_call_details_attempts += 1 logger.warning( - f"Gong call details still missing after " - f"{checkpoint.pending_call_details_attempts}/" - f"{self.MAX_CALL_DETAILS_ATTEMPTS} attempts: " - f"missing_call_ids={list(checkpoint.pending_transcripts.keys())}" + "Gong call details still missing after %s/%s attempts: missing_call_ids=%s", + checkpoint.pending_call_details_attempts, + self.MAX_CALL_DETAILS_ATTEMPTS, + list(checkpoint.pending_transcripts.keys()), ) if checkpoint.pending_call_details_attempts >= self.MAX_CALL_DETAILS_ATTEMPTS: logger.error( - f"Giving up on missing Gong call details after " - f"{self.MAX_CALL_DETAILS_ATTEMPTS} attempts: " - f"missing_call_ids={list(checkpoint.pending_transcripts.keys())}" + "Giving up on missing Gong call details after %s attempts: missing_call_ids=%s", + self.MAX_CALL_DETAILS_ATTEMPTS, + list(checkpoint.pending_transcripts.keys()), ) for call_id in list(checkpoint.pending_transcripts.keys()): yield ConnectorFailure( diff --git a/backend/onyx/connectors/google_drive/connector.py b/backend/onyx/connectors/google_drive/connector.py index 04c06f6945..d5537118c7 100644 --- a/backend/onyx/connectors/google_drive/connector.py +++ b/backend/onyx/connectors/google_drive/connector.py @@ -126,7 +126,7 @@ def _clean_requested_drive_ids( filtered_folder_ids = requested_folder_ids - all_drive_ids_available if invalid_requested_drive_ids: logger.warning( - f"Some shared drive IDs were not found. IDs: {invalid_requested_drive_ids}" + "Some shared drive IDs were not found. IDs: %s", invalid_requested_drive_ids ) logger.warning("Checking for folder access instead...") filtered_folder_ids.update(invalid_requested_drive_ids) @@ -696,7 +696,9 @@ class GoogleDriveConnector( ) if failed_ids and folder_id in failed_ids: logger.debug( - f"Skipping folder {folder_id} using {email} (previously confirmed no parents)" + "Skipping folder %s using %s (previously confirmed no parents)", + folder_id, + email, ) continue @@ -704,10 +706,10 @@ class GoogleDriveConnector( folder = get_folder_metadata(service, folder_id, field_type) if not folder: - logger.debug(f"Failed to fetch folder {folder_id} using {email}") + logger.debug("Failed to fetch folder %s using %s", folder_id, email) continue - logger.debug(f"Successfully fetched folder {folder_id} using {email}") + logger.debug("Successfully fetched folder %s using %s", folder_id, email) # If this folder has parents, use it if folder.get("parents"): @@ -722,17 +724,22 @@ class GoogleDriveConnector( if best_folder is None: best_folder = folder logger.debug( - f"Folder {folder_id} has no parents when fetched by {email}, will try admin to check for parent access" + "Folder %s has no parents when fetched by %s, will try admin to check for parent access", + folder_id, + email, ) if best_folder: logger.debug( - f"Successfully fetched folder {folder_id} but no parents found" + "Successfully fetched folder %s but no parents found", folder_id ) return best_folder logger.debug( - f"All attempts failed to fetch folder {folder_id} (tried {retriever_email} and {self.primary_admin_email})" + "All attempts failed to fetch folder %s (tried %s and %s)", + folder_id, + retriever_email, + self.primary_admin_email, ) return None @@ -752,7 +759,9 @@ class GoogleDriveConnector( drive_service = get_drive_service(self.creds, user_email) is_service_account = isinstance(self.creds, ServiceAccountCredentials) logger.info( - f"Getting all drives for user {user_email} with service account: {is_service_account}" + "Getting all drives for user %s with service account: %s", + user_email, + is_service_account, ) all_drive_ids: set[str] = set() for drive in execute_paginated_retrieval( @@ -807,7 +816,7 @@ class GoogleDriveConnector( completion.processed_drive_ids.add(drive_id) return drive_id elif status == DriveIdStatus.IN_PROGRESS: - logger.debug(f"Drive id in progress: {drive_id}") + logger.debug("Drive id in progress: %s", drive_id) future_work = drive_id if future_work: @@ -855,9 +864,11 @@ class GoogleDriveConnector( if curr_stage.stage == DriveRetrievalStage.MY_DRIVE_FILES: if self.include_my_drives or user_email in self._requested_my_drive_emails: logger.info( - f"Getting all files in my drive as '{user_email}. Resuming: {resuming}. " - f"Stage completed until: {curr_stage.completed_until}. " - f"Next page token: {curr_stage.next_page_token}" + "Getting all files in my drive as '%s. Resuming: %s. Stage completed until: %s. Next page token: %s", + user_email, + resuming, + curr_stage.completed_until, + curr_stage.next_page_token, ) for file_or_token in add_retrieval_info( @@ -876,7 +887,7 @@ class GoogleDriveConnector( DriveRetrievalStage.MY_DRIVE_FILES, ): if isinstance(file_or_token, str): - logger.debug(f"Done with max num pages for user {user_email}") + logger.debug("Done with max num pages for user %s", user_email) checkpoint.completion_map[user_email].next_page_token = ( file_or_token ) @@ -928,7 +939,10 @@ class GoogleDriveConnector( drive_id = get_new_drive_id(user_email) if drive_id: logger.info( - f"Getting files in shared drive '{drive_id}' as '{user_email}. Resuming: {resuming}" + "Getting files in shared drive '%s' as '%s. Resuming: %s", + drive_id, + user_email, + resuming, ) curr_stage.completed_until = 0 curr_stage.current_folder_or_drive_id = drive_id @@ -975,9 +989,8 @@ class GoogleDriveConnector( folder_id = curr_stage.current_folder_or_drive_id if folder_id is None: logger.warning( - f"folder id not set in checkpoint for user {user_email}. " - "This happens occasionally when the connector is interrupted " - "and resumed." + "folder id not set in checkpoint for user %s. This happens occasionally when the connector is interrupted and resumed.", + user_email, ) else: resume_start = curr_stage.completed_until @@ -1003,7 +1016,9 @@ class GoogleDriveConnector( if num_completed_folders >= FOLDERS_PER_CHECKPOINT: return # resume from this folder on the next run - logger.info(f"Getting files in folder '{folder_id}' as '{user_email}'") + logger.info( + "Getting files in folder '%s' as '%s'", folder_id, user_email + ) yield from _yield_from_folder_crawl(folder_id, start) num_completed_folders += 1 @@ -1019,11 +1034,11 @@ class GoogleDriveConnector( start: SecondsSinceUnixEpoch | None = None, end: SecondsSinceUnixEpoch | None = None, ) -> Iterator[RetrievedDriveFile]: - logger.info(f"Impersonating user {user_email}") + logger.info("Impersonating user %s", user_email) curr_stage = checkpoint.completion_map[user_email] resuming = True if curr_stage.stage == DriveRetrievalStage.START: - logger.info(f"Setting stage to {DriveRetrievalStage.MY_DRIVE_FILES.value}") + logger.info("Setting stage to %s", DriveRetrievalStage.MY_DRIVE_FILES.value) curr_stage.stage = DriveRetrievalStage.MY_DRIVE_FILES resuming = False drive_service = get_drive_service(self.creds, user_email) @@ -1034,7 +1049,7 @@ class GoogleDriveConnector( # validate that the user has access to the drive APIs by performing a simple # request and checking for a 401 try: - logger.debug(f"Getting root folder id for user {user_email}") + logger.debug("Getting root folder id for user %s", user_email) # default is ~17mins of retries, don't do that here for cases so we don't # waste 17mins everytime we run into a user without access to drive APIs retry_builder(tries=3, delay=1)(get_root_folder_id)(drive_service) @@ -1043,7 +1058,7 @@ class GoogleDriveConnector( # fail gracefully, let the other impersonations continue # one user without access shouldn't block the entire connector logger.warning( - f"User '{user_email}' does not have access to the drive APIs." + "User '%s' does not have access to the drive APIs.", user_email ) # mark this user as done so we don't try to retrieve anything for them # again @@ -1053,12 +1068,14 @@ class GoogleDriveConnector( except RefreshError as e: if is_user_removed(): logger.warning( - f"User '{user_email}' confirmed removed from workspace, skipping." + "User '%s' confirmed removed from workspace, skipping.", user_email ) curr_stage.stage = DriveRetrievalStage.DONE return logger.warning( - f"User '{user_email}' impersonation failed at validation gate. Error: {e}" + "User '%s' impersonation failed at validation gate. Error: %s", + user_email, + e, ) curr_stage.stage = DriveRetrievalStage.DONE yield RetrievedDriveFile( @@ -1085,12 +1102,12 @@ class GoogleDriveConnector( except RefreshError as e: if is_user_removed(): logger.warning( - f"User '{user_email}' removed mid-run, skipping remaining files." + "User '%s' removed mid-run, skipping remaining files.", user_email ) curr_stage.stage = DriveRetrievalStage.DONE else: logger.warning( - f"User '{user_email}' impersonation failed mid-run. Error: {e}" + "User '%s' impersonation failed mid-run. Error: %s", user_email, e ) curr_stage.stage = DriveRetrievalStage.DONE yield RetrievedDriveFile( @@ -1144,12 +1161,12 @@ class GoogleDriveConnector( # we've found all users and drives, now time to actually start # fetching stuff - logger.info(f"Found {len(all_org_emails)} users to impersonate") - logger.debug(f"Users: {all_org_emails}") - logger.info(f"Found {len(sorted_drive_ids)} drives to retrieve") - logger.debug(f"Drives: {sorted_drive_ids}") - logger.info(f"Found {len(sorted_folder_ids)} folders to retrieve") - logger.debug(f"Folders: {sorted_folder_ids}") + logger.info("Found %s users to impersonate", len(all_org_emails)) + logger.debug("Users: %s", all_org_emails) + logger.info("Found %s drives to retrieve", len(sorted_drive_ids)) + logger.debug("Drives: %s", sorted_drive_ids) + logger.info("Found %s folders to retrieve", len(sorted_folder_ids)) + logger.debug("Folders: %s", sorted_folder_ids) drive_id_getter = self.make_drive_id_getter(sorted_drive_ids, checkpoint) @@ -1160,7 +1177,7 @@ class GoogleDriveConnector( if stage_completion.stage != DriveRetrievalStage.DONE ] - logger.debug(f"Non-completed users remaining: {len(non_completed_org_emails)}") + logger.debug("Non-completed users remaining: %s", len(non_completed_org_emails)) # don't process too many emails before returning a checkpoint. This is # to resolve the case where there are a ton of emails that don't have access @@ -1204,7 +1221,7 @@ class GoogleDriveConnector( ) - self._retrieved_folder_and_drive_ids if remaining_folders: logger.warning( - f"Some folders/drives were not retrieved. IDs: {remaining_folders}" + "Some folders/drives were not retrieved. IDs: %s", remaining_folders ) if any( checkpoint.completion_map[user_email].stage != DriveRetrievalStage.DONE @@ -1263,11 +1280,11 @@ class GoogleDriveConnector( return logger.info( - f"Getting shared files/my drive files for OAuth " - f"with include_files_shared_with_me={self.include_files_shared_with_me}, " - f"include_my_drives={self.include_my_drives}, " - f"include_shared_drives={self.include_shared_drives}." - f"Using '{self.primary_admin_email}' as the account." + "Getting shared files/my drive files for OAuth with include_files_shared_with_me=%s, include_my_drives=%s, include_shared_drives=%s.Using '%s' as the account.", + self.include_files_shared_with_me, + self.include_my_drives, + self.include_shared_drives, + self.primary_admin_email, ) yield from add_retrieval_info( get_all_files_for_oauth( @@ -1343,11 +1360,13 @@ class GoogleDriveConnector( for drive_id in drive_ids_to_retrieve: if drive_id in self._retrieved_folder_and_drive_ids: logger.info( - f"Skipping drive '{drive_id}' as it has already been retrieved" + "Skipping drive '%s' as it has already been retrieved", drive_id ) continue logger.info( - f"Getting files in shared drive '{drive_id}' as '{self.primary_admin_email}'" + "Getting files in shared drive '%s' as '%s'", + drive_id, + self.primary_admin_email, ) for file_or_token in _yield_from_drive(drive_id, start): if isinstance(file_or_token, str): @@ -1417,7 +1436,9 @@ class GoogleDriveConnector( # instead, the traversed_parent_ids are used to determine what we have left to retrieve for folder_id in remaining_folders: logger.info( - f"Getting files in folder '{folder_id}' as '{self.primary_admin_email}'" + "Getting files in folder '%s' as '%s'", + folder_id, + self.primary_admin_email, ) yield from _yield_from_folder_crawl(folder_id, start) @@ -1426,7 +1447,7 @@ class GoogleDriveConnector( ) - self._retrieved_folder_and_drive_ids if remaining_folders: logger.warning( - f"Some folders/drives were not retrieved. IDs: {remaining_folders}" + "Some folders/drives were not retrieved. IDs: %s", remaining_folders ) def _checkpointed_retrieval( @@ -1485,8 +1506,9 @@ class GoogleDriveConnector( continue logger.debug( - f"Updating checkpoint for file: {drive_file.get('name')}. " - f"Seen: {document_id in checkpoint.all_retrieved_file_ids}" + "Updating checkpoint for file: %s. Seen: %s", + drive_file.get("name"), + document_id in checkpoint.all_retrieved_file_ids, ) if document_id in checkpoint.all_retrieved_file_ids: continue @@ -1656,7 +1678,7 @@ class GoogleDriveConnector( add_prefix=True, ) if new_ancestors: - logger.debug(f"Yielding {len(new_ancestors)} new hierarchy nodes") + logger.debug("Yielding %s new hierarchy nodes", len(new_ancestors)) yield from new_ancestors func_with_args = [ @@ -1674,7 +1696,7 @@ class GoogleDriveConnector( results: list[Document | ConnectorFailure] = [ r for r in raw_results if r is not None ] - logger.debug(f"batch has {len(results)} docs or failures") + logger.debug("batch has %s docs or failures", len(results)) yield from results checkpoint.retrieved_folder_and_drive_ids = self._retrieved_folder_and_drive_ids @@ -1700,8 +1722,8 @@ class GoogleDriveConnector( ) except Exception as e: logger.exception( - f"Error extracting document: " - f"{retrieved_file.drive_file.get('name')} from Google Drive" + "Error extracting document: %s from Google Drive", + retrieved_file.drive_file.get("name"), ) return ConnectorFailure( failed_entity=EntityFailure( @@ -1730,8 +1752,9 @@ class GoogleDriveConnector( ) logger.info( - f"Loading from checkpoint with completion stage: {checkpoint.completion_stage}," - f"num retrieved ids: {len(checkpoint.all_retrieved_file_ids)}" + "Loading from checkpoint with completion stage: %s,num retrieved ids: %s", + checkpoint.completion_stage, + len(checkpoint.all_retrieved_file_ids), ) checkpoint = copy.deepcopy(checkpoint) self._retrieved_folder_and_drive_ids = checkpoint.retrieved_folder_and_drive_ids @@ -1757,7 +1780,7 @@ class GoogleDriveConnector( checkpoint.retrieved_folder_and_drive_ids = self._retrieved_folder_and_drive_ids logger.info( - f"num drive files retrieved: {len(checkpoint.all_retrieved_file_ids)}" + "num drive files retrieved: %s", len(checkpoint.all_retrieved_file_ids) ) if checkpoint.completion_stage == DriveRetrievalStage.DONE: checkpoint.has_more = False @@ -1796,7 +1819,7 @@ class GoogleDriveConnector( "Credentials missing, should not call this method before calling load_credentials" ) - logger.info(f"Resolving {len(errors)} errors") + logger.info("Resolving %s errors", len(errors)) doc_ids = [ failure.failed_document.document_id for failure in errors diff --git a/backend/onyx/connectors/google_drive/doc_conversion.py b/backend/onyx/connectors/google_drive/doc_conversion.py index ad576c97b2..9b9d6cbb4d 100644 --- a/backend/onyx/connectors/google_drive/doc_conversion.py +++ b/backend/onyx/connectors/google_drive/doc_conversion.py @@ -91,7 +91,7 @@ def _get_folder_info( _folder_cache[folder_id] = (folder_name, parent_id) return folder_name, parent_id except HttpError as e: - logger.warning(f"Failed to get folder info for {folder_id}: {e}") + logger.warning("Failed to get folder info for %s: %s", folder_id, e) _folder_cache[folder_id] = ("Unknown", None) return "Unknown", None @@ -112,7 +112,7 @@ def _get_drive_name(service: GoogleDriveService, drive_id: str) -> str: _folder_cache[cache_key] = (drive_name, None) return drive_name except HttpError as e: - logger.warning(f"Failed to get drive name for {drive_id}: {e}") + logger.warning("Failed to get drive name for %s: %s", drive_id, e) _folder_cache[cache_key] = (f"Shared Drive {drive_id}", None) return f"Shared Drive {drive_id}" @@ -289,13 +289,15 @@ def _download_request(request: Any, file_id: str, size_threshold: int) -> bytes: ) if download_progress.resumable_progress > size_threshold: logger.warning( - f"File {file_id} exceeds size threshold of {size_threshold}. Skipping2." + "File %s exceeds size threshold of %s. Skipping2.", + file_id, + size_threshold, ) return bytes() response = response_bytes.getvalue() if not response: - logger.warning(f"Failed to download {file_id}") + logger.warning("Failed to download %s", file_id) return bytes() return response @@ -362,7 +364,7 @@ def _download_and_extract_sections_basic( ) sections.append(section) except Exception as e: - logger.error(f"Failed to process image {file_name}: {e}") + logger.error("Failed to process image %s: %s", file_name, e) return FileExtractionResult(sections=sections) # For Google Docs, Sheets, and Slides, export via the Drive API @@ -373,7 +375,7 @@ def _download_and_extract_sections_basic( ) response = _download_request(request, file_id, size_threshold) if not response: - logger.warning(f"Failed to export {file_name} as {export_mime_type}") + logger.warning("Failed to export %s as %s", file_name, export_mime_type) return FileExtractionResult(sections=[]) if export_mime_type in OnyxMimeTypes.TABULAR_MIME_TYPES: @@ -408,7 +410,7 @@ def _download_and_extract_sections_basic( text = response_call().decode("utf-8") return FileExtractionResult(sections=[TextSection(link=link, text=text)]) except UnicodeDecodeError as e: - logger.warning(f"Failed to extract text from {file_name}: {e}") + logger.warning("Failed to extract text from %s: %s", file_name, e) return FileExtractionResult(sections=[]) elif ( @@ -467,20 +469,20 @@ def _download_and_extract_sections_basic( ) pdf_sections.append(section) except Exception as e: - logger.error(f"Failed to process PDF images in {file_name}: {e}") + logger.error("Failed to process PDF images in %s: %s", file_name, e) return FileExtractionResult(sections=pdf_sections) # Final attempt at extracting text file_ext = get_file_ext(file.get("name", "")) if file_ext not in OnyxFileExtensions.ALL_ALLOWED_EXTENSIONS: - logger.warning(f"Skipping file {file.get('name')} due to extension.") + logger.warning("Skipping file %s due to extension.", file.get("name")) return FileExtractionResult(sections=[]) try: text = extract_file_text(io.BytesIO(response_call()), file_name) return FileExtractionResult(sections=[TextSection(link=link, text=text)]) except Exception as e: - logger.warning(f"Failed to extract text from {file_name}: {e}") + logger.warning("Failed to extract text from %s: %s", file_name, e) return FileExtractionResult(sections=[]) @@ -522,7 +524,8 @@ def align_basic_advanced( ).strip() if heading_key == "": logger.warning( - f"Cannot match heading: {heading}, its link will come from the following section" + "Cannot match heading: %s, its link will come from the following section", + heading, ) continue heading_offset = heading.find(heading_key) @@ -537,7 +540,9 @@ def align_basic_advanced( ) if heading_start < 0: logger.warning( - f"Heading key {heading_key} from heading {heading} not found in basic text" + "Heading key %s from heading %s not found in basic text", + heading_key, + heading, ) heading_start = prev_start continue @@ -666,9 +671,11 @@ def convert_drive_item_to_document( # This SHOULD happen very rarely, and we don't want to break the indexing process when # a high volume of 403s occurs early. We leave a verbose log to help investigate. logger.error( - f"Skipping file id: {file.get('id')} name: {file.get('name')} due to 403 error." - f"Attempted to retrieve with {retriever_emails}," - f"got the following errors: {first_error.failure_message}" + "Skipping file id: %s name: %s due to 403 error.Attempted to retrieve with %s,got the following errors: %s", + file.get("id"), + file.get("name"), + retriever_emails, + first_error.failure_message, ) return None return first_error @@ -711,18 +718,20 @@ def _convert_drive_item_to_document( try: size_int = int(size_str) except ValueError: - logger.warning(f"Parsing string to int failed: size_str={size_str}") + logger.warning("Parsing string to int failed: size_str=%s", size_str) else: if size_int > size_threshold: logger.warning( - f"{file.get('name')} exceeds size threshold of {size_threshold}. Skipping." + "%s exceeds size threshold of %s. Skipping.", + file.get("name"), + size_threshold, ) return None # If it's a Google Doc, we might do advanced parsing if file.get("mimeType") == GDriveMimeType.DOC.value: try: - logger.debug(f"starting advanced parsing for {file.get('name')}") + logger.debug("starting advanced parsing for %s", file.get("name")) # get_document_sections is the advanced approach for Google Docs doc_sections = get_document_sections( docs_service=_get_docs_service(), @@ -734,7 +743,8 @@ def _convert_drive_item_to_document( ) if any(SMART_CHIP_CHAR in section.text for section in doc_sections): logger.debug( - f"found smart chips in {file.get('name')}, aligning with basic sections" + "found smart chips in %s, aligning with basic sections", + file.get("name"), ) basic_extraction = _download_and_extract_sections_basic( file, @@ -750,7 +760,8 @@ def _convert_drive_item_to_document( except Exception as e: logger.warning( - f"Error in advanced parsing: {e}. Falling back to basic extraction." + "Error in advanced parsing: %s. Falling back to basic extraction.", + e, ) # Not Google Doc, attempt basic extraction else: @@ -766,7 +777,7 @@ def _convert_drive_item_to_document( # If we still don't have any sections, skip this file if not sections: - logger.warning(f"No content extracted from {file.get('name')}. Skipping.") + logger.warning("No content extracted from %s. Skipping.", file.get("name")) return None doc_id = onyx_document_id_from_drive_file(file) @@ -830,7 +841,7 @@ def _convert_drive_item_to_document( try: doc_id = onyx_document_id_from_drive_file(file) except Exception as e2: - logger.warning(f"Error getting document id from file: {e2}") + logger.warning("Error getting document id from file: %s", e2) file_name = file.get("name") error_str = ( @@ -838,9 +849,9 @@ def _convert_drive_item_to_document( ) if isinstance(e, HttpError) and e.status_code == 403: logger.warning( - f"Uncommon permissions error while downloading file. User " - f"{retriever_email} was able to see file {file_name} " - "but cannot download it." + "Uncommon permissions error while downloading file. User %s was able to see file %s but cannot download it.", + retriever_email, + file_name, ) logger.warning(error_str) diff --git a/backend/onyx/connectors/google_drive/file_retrieval.py b/backend/onyx/connectors/google_drive/file_retrieval.py index f9b65cabe0..292eb250d5 100644 --- a/backend/onyx/connectors/google_drive/file_retrieval.py +++ b/backend/onyx/connectors/google_drive/file_retrieval.py @@ -146,11 +146,11 @@ def get_folder_metadata( ) except HttpError as e: if e.resp.status in (403, 404): - logger.debug(f"Cannot access folder {folder_id}: {e}") + logger.debug("Cannot access folder %s: %s", folder_id, e) else: raise e except RefreshError: - logger.debug(f"Cannot access folder {folder_id}: impersonation failed") + logger.debug("Cannot access folder %s: impersonation failed", folder_id) return None @@ -179,11 +179,11 @@ def get_shared_drive_name( return drive.get("name") except HttpError as e: if e.resp.status in (403, 404): - logger.debug(f"Cannot access drive {drive_id}: {e}") + logger.debug("Cannot access drive %s: %s", drive_id, e) else: raise except RefreshError: - logger.debug(f"Cannot access drive {drive_id}: impersonation failed") + logger.debug("Cannot access drive %s: impersonation failed", drive_id) return None @@ -308,7 +308,7 @@ def crawl_folders_for_files( start=start, end=end, ): - logger.info(f"Found file: {file['name']}, user email: {user_email}") + logger.info("Found file: %s, user email: %s", file["name"], user_email) found_files = True yield RetrievedDriveFile( drive_file=file, @@ -327,9 +327,9 @@ def crawl_folders_for_files( if isinstance(e, HttpError) and e.status_code == 403: # don't yield an error here because this is expected behavior # when a user doesn't have access to a folder - logger.debug(f"Error getting files in parent {parent_id}: {e}") + logger.debug("Error getting files in parent %s: %s", parent_id, e) else: - logger.error(f"Error getting files in parent {parent_id}: {e}") + logger.error("Error getting files in parent %s: %s", parent_id, e) yield RetrievedDriveFile( drive_file=file, user_email=user_email, @@ -338,7 +338,7 @@ def crawl_folders_for_files( error=e, ) else: - logger.info(f"Skipping subfolder files since already traversed: {parent_id}") + logger.info("Skipping subfolder files since already traversed: %s", parent_id) for subfolder in _get_folders_in_parent( service=service, @@ -370,7 +370,7 @@ def get_files_in_shared_drive( ) -> Iterator[GoogleDriveFileType | str]: kwargs = {ORDER_BY_KEY: GoogleFields.MODIFIED_TIME.value} if page_token: - logger.info(f"Using page token: {page_token}") + logger.info("Using page token: %s", page_token) kwargs[PAGE_TOKEN_KEY] = page_token if cache_folders: @@ -432,7 +432,7 @@ def get_all_files_in_my_drive_and_shared( ) -> Iterator[GoogleDriveFileType | str]: kwargs = {ORDER_BY_KEY: GoogleFields.MODIFIED_TIME.value} if page_token: - logger.info(f"Using page token: {page_token}") + logger.info("Using page token: %s", page_token) kwargs[PAGE_TOKEN_KEY] = page_token if cache_folders: @@ -487,7 +487,7 @@ def get_all_files_for_oauth( ) -> Iterator[GoogleDriveFileType | str]: kwargs = {ORDER_BY_KEY: GoogleFields.MODIFIED_TIME.value} if page_token: - logger.info(f"Using page token: {page_token}") + logger.info("Using page token: %s", page_token) kwargs[PAGE_TOKEN_KEY] = page_token should_get_all = ( @@ -615,7 +615,7 @@ def _get_files_by_web_view_links_batch( exception: Exception | None, ) -> None: if exception: - logger.warning(f"Error retrieving file {request_id}: {exception}") + logger.warning("Error retrieving file %s: %s", request_id, exception) result.errors[request_id] = exception else: result.files[request_id] = response @@ -637,7 +637,7 @@ def _get_files_by_web_view_links_batch( ) batch.add(request, request_id=web_view_link) except ValueError as e: - logger.warning(f"Failed to extract file ID from {web_view_link}: {e}") + logger.warning("Failed to extract file ID from %s: %s", web_view_link, e) result.errors[web_view_link] = e batch.execute() diff --git a/backend/onyx/connectors/google_site/connector.py b/backend/onyx/connectors/google_site/connector.py index d9ea461a1d..260fc2f83e 100644 --- a/backend/onyx/connectors/google_site/connector.py +++ b/backend/onyx/connectors/google_site/connector.py @@ -96,7 +96,7 @@ class GoogleSitesConnector(LoadConnector): + "This page will not have a working link.\n\n" + f"# of broken links so far - {count}" ) - logger.info(f"Path to page: {path}") + logger.info("Path to page: %s", path) # cleanup the hidden `Skip to main content` and `Skip to navigation` that # appears at the top of every page for div in soup.find_all("div", attrs={"data-is-touch-wrapper": "true"}): diff --git a/backend/onyx/connectors/google_utils/google_utils.py b/backend/onyx/connectors/google_utils/google_utils.py index b66c2b8879..8cff3196fc 100644 --- a/backend/onyx/connectors/google_utils/google_utils.py +++ b/backend/onyx/connectors/google_utils/google_utils.py @@ -96,14 +96,18 @@ def _execute_with_retry(request: Any) -> Any: ) else: logger.error( - f"No Retry-After header or timestamp found in error message: {error}" + "No Retry-After header or timestamp found in error message: %s", + error, ) sleep_time = 60 sleep_time += 3 # Add a buffer to be safe logger.info( - f"Rate limit exceeded. Attempt {attempt}/{max_attempts}. Sleeping for {sleep_time} seconds." + "Rate limit exceeded. Attempt %s/%s. Sleeping for %s seconds.", + attempt, + max_attempts, + sleep_time, ) time.sleep(sleep_time) @@ -146,7 +150,8 @@ def _execute_single_retrieval( and "pageToken" in str(e) ): logger.warning( - f"Invalid page token: {request_kwargs['pageToken']}, retrying from start of request" + "Invalid page token: %s, retrying from start of request", + request_kwargs["pageToken"], ) request_kwargs.pop("pageToken") return _execute_single_retrieval( @@ -154,13 +159,13 @@ def _execute_single_retrieval( continue_on_404_or_403, **request_kwargs, ) - logger.error(f"Error executing request: {e}") + logger.error("Error executing request: %s", e) raise e elif _is_rate_limit_error(e): results = _execute_with_retry(retrieval_function(**request_kwargs)) elif e.resp.status == 404 or e.resp.status == 403: if continue_on_404_or_403: - logger.debug(f"Error executing request: {e}") + logger.debug("Error executing request: %s", e) results = {} else: raise e diff --git a/backend/onyx/connectors/highspot/client.py b/backend/onyx/connectors/highspot/client.py index de72b12ac4..1bc40353cf 100644 --- a/backend/onyx/connectors/highspot/client.py +++ b/backend/onyx/connectors/highspot/client.py @@ -141,7 +141,7 @@ class HighspotClient: request_headers.update(headers) try: - logger.debug(f"Making {method} request to {url}") + logger.debug("Making %s request to %s", method, url) response = self.session.request( method=method, url=url, @@ -200,13 +200,15 @@ class HighspotClient: params = {"right": "view", "start": current_offset, "limit": PAGE_SIZE} response = self._make_request("GET", "spots", params=params) found_spots = response.get("collection", []) - logger.info(f"Received {len(found_spots)} spots at offset {current_offset}") + logger.info( + "Received %s spots at offset %s", len(found_spots), current_offset + ) all_spots.extend(found_spots) if len(found_spots) < PAGE_SIZE: has_more = False else: current_offset += PAGE_SIZE - logger.info(f"Total spots retrieved: {len(all_spots)}") + logger.info("Total spots retrieved: %s", len(all_spots)) return all_spots def get_spot(self, spot_id: str) -> Dict[str, Any]: diff --git a/backend/onyx/connectors/highspot/connector.py b/backend/onyx/connectors/highspot/connector.py index a5edd503d6..dd3ff84eee 100644 --- a/backend/onyx/connectors/highspot/connector.py +++ b/backend/onyx/connectors/highspot/connector.py @@ -152,14 +152,16 @@ class HighspotConnector(LoadConnector, PollConnector, SlimConnectorWithPermSync) while has_more: logger.info( - f"Retrieving items from spot {spot.name}, offset {offset}" + "Retrieving items from spot %s, offset %s", + spot.name, + offset, ) response = self.client.get_spot_items( spot_id=spot.id, offset=offset, page_size=self.batch_size ) items = response.get("collection", []) logger.info( - f"Received {len(items)} items from spot {spot.name}" + "Received %s items from spot %s", len(items), spot.name ) if not items: has_more = False @@ -175,7 +177,7 @@ class HighspotConnector(LoadConnector, PollConnector, SlimConnectorWithPermSync) item_details = self.client.get_item(item_id) if not item_details: logger.warning( - f"Item {item_id} details not found, skipping" + "Item %s details not found, skipping", item_id ) continue # Apply time filter if specified @@ -197,7 +199,9 @@ class HighspotConnector(LoadConnector, PollConnector, SlimConnectorWithPermSync) except (ValueError, TypeError): # Skip if date cannot be parsed logger.warning( - f"Invalid date format for item {item_id}: {updated_at}" + "Invalid date format for item %s: %s", + item_id, + updated_at, ) continue @@ -250,7 +254,7 @@ class HighspotConnector(LoadConnector, PollConnector, SlimConnectorWithPermSync) else item_id # ty: ignore[possibly-unresolved-reference] ) logger.error( - f"Error retrieving item {item_id}: {str(e)}" + "Error retrieving item %s: %s", item_id, str(e) ) except Exception as e: item_id = ( @@ -259,23 +263,23 @@ class HighspotConnector(LoadConnector, PollConnector, SlimConnectorWithPermSync) else item_id # ty: ignore[possibly-unresolved-reference] ) logger.error( - f"Unexpected error for item {item_id}: {str(e)}" + "Unexpected error for item %s: %s", item_id, str(e) ) has_more = len(items) >= self.batch_size offset += self.batch_size except (HighspotClientError, ValueError) as e: - logger.error(f"Error processing spot {spot.name}: {str(e)}") + logger.error("Error processing spot %s: %s", spot.name, str(e)) raise except Exception as e: logger.error( - f"Unexpected error processing spot {spot.name}: {str(e)}" + "Unexpected error processing spot %s: %s", spot.name, str(e) ) raise except Exception as e: - logger.error(f"Error in Highspot connector: {str(e)}") + logger.error("Error in Highspot connector: %s", str(e)) raise if doc_batch: @@ -303,7 +307,10 @@ class HighspotConnector(LoadConnector, PollConnector, SlimConnectorWithPermSync) title, description = self._extract_title_and_description(item_details) default_content = f"{title}\n{description}" logger.info( - f"Processing item {item_id} with extension {file_extension} and file name {content_name}" + "Processing item %s with extension %s and file name %s", + item_id, + file_extension, + content_name, ) try: @@ -330,23 +337,25 @@ class HighspotConnector(LoadConnector, PollConnector, SlimConnectorWithPermSync) else: logger.warning( - f"Item {item_id} has unsupported format: {file_extension}" + "Item %s has unsupported format: %s", item_id, file_extension ) return default_content except HighspotClientError as e: error_context = f"item {item_id}" if item_id else "(item id not found)" - logger.warning(f"Could not retrieve content for {error_context}: {str(e)}") + logger.warning( + "Could not retrieve content for %s: %s", error_context, str(e) + ) return default_content except ValueError as e: error_context = f"item {item_id}" if item_id else "(item id not found)" - logger.error(f"Value error for {error_context}: {str(e)}") + logger.error("Value error for %s: %s", error_context, str(e)) return default_content except Exception as e: error_context = f"item {item_id}" if item_id else "(item id not found)" logger.error( - f"Unexpected error retrieving content for {error_context}: {str(e)}" + "Unexpected error retrieving content for %s: %s", error_context, str(e) ) return default_content @@ -395,7 +404,9 @@ class HighspotConnector(LoadConnector, PollConnector, SlimConnectorWithPermSync) while has_more: logger.info( - f"Retrieving slim documents from spot {spot.name}, offset {offset}" + "Retrieving slim documents from spot %s, offset %s", + spot.name, + offset, ) response = self.client.get_spot_items( spot_id=spot.id, offset=offset, page_size=self.batch_size @@ -425,7 +436,7 @@ class HighspotConnector(LoadConnector, PollConnector, SlimConnectorWithPermSync) except (HighspotClientError, ValueError): logger.exception( - f"Error retrieving slim documents from spot {spot.name}" + "Error retrieving slim documents from spot %s", spot.name ) raise @@ -445,7 +456,7 @@ class HighspotConnector(LoadConnector, PollConnector, SlimConnectorWithPermSync) try: return self.client.health_check() except Exception as e: - logger.error(f"Failed to validate credentials: {str(e)}") + logger.error("Failed to validate credentials: %s", str(e)) return False diff --git a/backend/onyx/connectors/highspot/utils.py b/backend/onyx/connectors/highspot/utils.py index efc00dac16..d365dda875 100644 --- a/backend/onyx/connectors/highspot/utils.py +++ b/backend/onyx/connectors/highspot/utils.py @@ -38,11 +38,11 @@ def scrape_url_content( context = browser.new_context() page = context.new_page() - logger.info(f"Navigating to URL: {url}") + logger.info("Navigating to URL: %s", url) try: page.goto(url, timeout=timeout_ms) except Exception as e: - logger.error(f"Failed to navigate to {url}: {str(e)}") + logger.error("Failed to navigate to %s: %s", url, str(e)) return None if scroll_before_scraping: @@ -54,7 +54,7 @@ def scrape_url_content( try: page.wait_for_load_state("networkidle", timeout=timeout_ms) except Exception as e: - logger.warning(f"Network idle wait timed out: {str(e)}") + logger.warning("Network idle wait timed out: %s", str(e)) break new_height = page.evaluate("document.body.scrollHeight") @@ -83,12 +83,12 @@ def scrape_url_content( else: parsed_html.cleaned_text += "\n" + iframe_content except Exception as e: - logger.warning(f"Error processing iframes: {str(e)}") + logger.warning("Error processing iframes: %s", str(e)) return parsed_html.cleaned_text except Exception as e: - logger.error(f"Error scraping URL {url}: {str(e)}") + logger.error("Error scraping URL %s: %s", url, str(e)) return None finally: @@ -96,12 +96,12 @@ def scrape_url_content( try: browser.close() except Exception as e: - logger.debug(f"Error closing browser: {str(e)}") + logger.debug("Error closing browser: %s", str(e)) if playwright: try: playwright.stop() except Exception as e: - logger.debug(f"Error stopping playwright: {str(e)}") + logger.debug("Error stopping playwright: %s", str(e)) def validate_url(url: str) -> None: diff --git a/backend/onyx/connectors/hubspot/connector.py b/backend/onyx/connectors/hubspot/connector.py index 1b4aa74123..f3f0443607 100644 --- a/backend/onyx/connectors/hubspot/connector.py +++ b/backend/onyx/connectors/hubspot/connector.py @@ -153,11 +153,18 @@ class HubSpotConnector(LoadConnector, PollConnector): last_exc = e if attempt == 0: logger.warning( - f"Batch fetch of {len(chunk)} {object_type} failed, retrying: {e}" + "Batch fetch of %s %s failed, retrying: %s", + len(chunk), + object_type, + e, ) time.sleep(1) logger.warning( - f"Failed to batch-fetch {len(chunk)} {object_type} {chunk} after retry: {last_exc}" + "Failed to batch-fetch %s %s %s after retry: %s", + len(chunk), + object_type, + chunk, + last_exc, ) return [] @@ -299,9 +306,8 @@ class HubSpotConnector(LoadConnector, PollConnector): ) except (ValueError, TypeError): logger.error( - f"HubSpot search limit reached but last modified timestamp " - f"has unrecognized format ({last_ts_ms!r}); records after " - "the 10,000th may be missing." + "HubSpot search limit reached but last modified timestamp has unrecognized format (%r); records after the 10,000th may be missing.", + last_ts_ms, ) return if next_start <= start: @@ -490,7 +496,10 @@ class HubSpotConnector(LoadConnector, PollConnector): except Exception as e: logger.warning( - f"Failed to get associations from {from_object_type} to {to_object_type}: {e}" + "Failed to get associations from %s to %s: %s", + from_object_type, + to_object_type, + e, ) return [] @@ -529,7 +538,9 @@ class HubSpotConnector(LoadConnector, PollConnector): return associated_notes except Exception as e: - logger.warning(f"Failed to get notes for {object_type} {object_id}: {e}") + logger.warning( + "Failed to get notes for %s %s: %s", object_type, object_id, e + ) return [] def _create_object_section( diff --git a/backend/onyx/connectors/imap/connector.py b/backend/onyx/connectors/imap/connector.py index 64b9927310..0e93abc12d 100644 --- a/backend/onyx/connectors/imap/connector.py +++ b/backend/onyx/connectors/imap/connector.py @@ -188,7 +188,9 @@ class ImapConnector( for email_id in current_todos: email_msg = _fetch_email(mail_client=mail_client, email_id=email_id) if not email_msg: - logger.warning(f"Failed to fetch message {email_id=}; skipping") + logger.warning( + "Failed to fetch message email_id=%r; skipping", email_id + ) continue email_headers = EmailHeaders.from_email_msg(email_msg=email_msg) @@ -261,7 +263,9 @@ def _fetch_all_mailboxes_for_email_account(mail_client: imaplib.IMAP4_SSL) -> li mailboxes_str = mailboxes_raw else: logger.warning( - f"Expected the mailbox data to be of type str, instead got {type(mailboxes_raw)=} {mailboxes_raw}; skipping" + "Expected the mailbox data to be of type str, instead got type(mailboxes_raw)=%r %s; skipping", + type(mailboxes_raw), + mailboxes_raw, ) continue @@ -275,7 +279,8 @@ def _fetch_all_mailboxes_for_email_account(mail_client: imaplib.IMAP4_SSL) -> li match = re.match(r'\([^)]*\)\s+"([^"]+)"\s+"?(.+?)"?$', mailboxes_str) if not match: logger.warning( - f"Invalid mailbox-data formatting structure: {mailboxes_str=}; skipping" + "Invalid mailbox-data formatting structure: mailboxes_str=%r; skipping", + mailboxes_str, ) continue @@ -392,8 +397,9 @@ def _parse_email_body( raw_payload = part.get_payload(decode=True) if not isinstance(raw_payload, bytes): logger.warning( - "Payload section from email was expected to be an array of bytes, instead got " - f"{type(raw_payload)=}, {raw_payload=}" + "Payload section from email was expected to be an array of bytes, instead got type(raw_payload)=%r, raw_payload=%r", + type(raw_payload), + raw_payload, ) continue body = raw_payload.decode(charset) @@ -404,7 +410,8 @@ def _parse_email_body( if not body: logger.warning( - f"Email with {email_headers.id=} has an empty body; returning an empty string" + "Email with email_headers.id=%r has an empty body; returning an empty string", + email_headers.id, ) return "" diff --git a/backend/onyx/connectors/jira/connector.py b/backend/onyx/connectors/jira/connector.py index 4a5c2e5c03..268ce65049 100644 --- a/backend/onyx/connectors/jira/connector.py +++ b/backend/onyx/connectors/jira/connector.py @@ -270,15 +270,17 @@ def _bulk_fetch_batch( except requests.exceptions.JSONDecodeError: if len(issue_ids) <= 1: logger.exception( - f"Jira bulk-fetch response for issue(s) {issue_ids} could not " - f"be decoded as JSON (response too large or truncated)." + "Jira bulk-fetch response for issue(s) %s could not be decoded as JSON (response too large or truncated).", + issue_ids, ) raise mid = len(issue_ids) // 2 logger.warning( - f"Jira bulk-fetch JSON decode failed for batch of {len(issue_ids)} issues. " - f"Splitting into sub-batches of {mid} and {len(issue_ids) - mid}." + "Jira bulk-fetch JSON decode failed for batch of %s issues. Splitting into sub-batches of %s and %s.", + len(issue_ids), + mid, + len(issue_ids) - mid, ) left = _bulk_fetch_batch(jira_client, issue_ids[:mid], fields) right = _bulk_fetch_batch(jira_client, issue_ids[mid:], fields) @@ -297,7 +299,7 @@ def bulk_fetch_issues( try: raw_issues.extend(_bulk_fetch_batch(jira_client, list(batch), fields)) except Exception as e: - logger.error(f"Error fetching issues: {e}") + logger.error("Error fetching issues: %s", e) raise return [ @@ -358,7 +360,10 @@ def _perform_jql_search_v2( Unfortunately, jira server/data center will forever use the v2 APIs that are now deprecated. """ logger.debug( - f"Fetching Jira issues with JQL: {jql}, starting at {start}, max results: {max_results}" + "Fetching Jira issues with JQL: %s, starting at %s, max results: %s", + jql, + start, + max_results, ) try: issues = jira_client.search_issues( @@ -388,8 +393,10 @@ def process_jira_issue( if labels_to_skip: if any(label in issue.fields.labels for label in labels_to_skip): logger.info( - f"Skipping {issue.key} because it has a label to skip. Found " - f"labels: {issue.fields.labels}. Labels to skip: {labels_to_skip}." + "Skipping %s because it has a label to skip. Found labels: %s. Labels to skip: %s.", + issue.key, + issue.fields.labels, + labels_to_skip, ) return None @@ -409,7 +416,9 @@ def process_jira_issue( # Check ticket size if len(ticket_content.encode("utf-8")) > JIRA_CONNECTOR_MAX_TICKET_SIZE: logger.info( - f"Skipping {issue.key} because it exceeds the maximum size of {JIRA_CONNECTOR_MAX_TICKET_SIZE} bytes." + "Skipping %s because it exceeds the maximum size of %s bytes.", + issue.key, + JIRA_CONNECTOR_MAX_TICKET_SIZE, ) return None @@ -475,7 +484,7 @@ def process_jira_issue( metadata_dict[_FIELD_PROJECT_NAME] = project.name metadata_dict[_FIELD_PROJECT] = project.key else: - logger.error(f"Project should exist but does not for {issue.key}") + logger.error("Project should exist but does not for %s", issue.key) return Document( id=page_url, @@ -1025,7 +1034,7 @@ class JiraConnector( ConnectorValidationError: For other HTTP errors with extracted error messages """ status_code = getattr(e, "status_code", None) - logger.error(f"Jira API error during validation: {e}") + logger.error("Jira API error during validation: %s", e) # Handle specific status codes with appropriate exceptions if status_code == 401: diff --git a/backend/onyx/connectors/jira/utils.py b/backend/onyx/connectors/jira/utils.py index 9c83b6983f..16e2ffb231 100644 --- a/backend/onyx/connectors/jira/utils.py +++ b/backend/onyx/connectors/jira/utils.py @@ -145,7 +145,7 @@ def get_comment_strs( comment_strs.append(body_text) except Exception as e: - logger.error(f"Failed to process comment due to an error: {e}") + logger.error("Failed to process comment due to an error: %s", e) continue return comment_strs @@ -182,7 +182,7 @@ class CustomFieldExtractor: else: return str(value) except Exception as e: - logger.error(f"Error processing custom field value {value}: {e}") + logger.error("Error processing custom field value %s: %s", value, e) return "" @staticmethod diff --git a/backend/onyx/connectors/linear/connector.py b/backend/onyx/connectors/linear/connector.py index cd4830c981..cd99dc337d 100644 --- a/backend/onyx/connectors/linear/connector.py +++ b/backend/onyx/connectors/linear/connector.py @@ -67,7 +67,7 @@ def _make_query(request_body: dict[str, Any], api_key: str) -> requests.Response if i == _NUM_RETRIES - 1: raise e - logger.warning(f"A Linear GraphQL error occurred: {e}. Retrying...") + logger.warning("A Linear GraphQL error occurred: %s. Retrying...", e) raise RuntimeError( "Unexpected execution when querying Linear. This should never happen." @@ -306,11 +306,11 @@ class LinearConnector(LoadConnector, PollConnector, OAuthConnector): "after": endCursor, }, } - logger.debug(f"Requesting issues from Linear with query: {graphql_query}") + logger.debug("Requesting issues from Linear with query: %s", graphql_query) response = _make_query(graphql_query, self.linear_api_key) response_json = response.json() - logger.debug(f"Raw response from Linear: {response_json}") + logger.debug("Raw response from Linear: %s", response_json) edges = response_json["data"]["issues"]["edges"] documents: list[Document | HierarchyNode] = [] diff --git a/backend/onyx/connectors/loopio/connector.py b/backend/onyx/connectors/loopio/connector.py index 560fe00252..7ee4960836 100644 --- a/backend/onyx/connectors/loopio/connector.py +++ b/backend/onyx/connectors/loopio/connector.py @@ -65,7 +65,9 @@ class LoopioConnector(LoadConnector, PollConnector): ) if response.status_code == 400: logger.error( - f"Loopio API returned 400 for {resource} with params {params}", + "Loopio API returned 400 for %s with params %s", + resource, + params, ) logger.error(response.text) response.raise_for_status() @@ -123,14 +125,15 @@ class LoopioConnector(LoadConnector, PollConnector): answer_text = entry.get("answer", {}).get("text", "") if not answer_text: logger.warning( - f"The Library entry {entry['id']} has no answer text. Skipping." + "The Library entry %s has no answer text. Skipping.", + entry["id"], ) continue try: answer = parse_html_page_basic(answer_text) except Exception as e: - logger.error(f"Error parsing HTML for entry {entry['id']}: {e}") + logger.error("Error parsing HTML for entry %s: %s", entry["id"], e) continue questions = [ diff --git a/backend/onyx/connectors/mediawiki/wiki.py b/backend/onyx/connectors/mediawiki/wiki.py index 647ce8bf2b..68aac5db09 100644 --- a/backend/onyx/connectors/mediawiki/wiki.py +++ b/backend/onyx/connectors/mediawiki/wiki.py @@ -200,7 +200,7 @@ class MediaWikiConnector(LoadConnector, PollConnector): ) for page in all_pages: logger.info( - f"MediaWikiConnector: title='{page.title()}' url={page.full_url()}" + "MediaWikiConnector: title='%s' url=%s", page.title(), page.full_url() ) doc_batch.append( get_doc_from_page(page, self.site, self.document_source_type) diff --git a/backend/onyx/connectors/notion/connector.py b/backend/onyx/connectors/notion/connector.py index f0d608eeca..44b7d1f4bb 100644 --- a/backend/onyx/connectors/notion/connector.py +++ b/backend/onyx/connectors/notion/connector.py @@ -186,7 +186,7 @@ class NotionConnector(LoadConnector, PollConnector): self, block_id: str, cursor: str | None = None ) -> dict[str, Any] | None: """Fetch all child blocks via the Notion API.""" - logger.debug(f"Fetching children of block with ID '{block_id}'") + logger.debug("Fetching children of block with ID '%s'", block_id) block_url = f"https://api.notion.com/v1/blocks/{block_id}/children" query_params = None if not cursor else {"start_cursor": cursor} res = rl_requests.get( @@ -202,13 +202,17 @@ class NotionConnector(LoadConnector, PollConnector): # this happens when a page is not shared with the integration # in this case, we should just ignore the page logger.error( - f"Unable to access block with ID '{block_id}'. " - f"This is likely due to the block not being shared " - f"with the Onyx integration. Exact exception:\n\n{e}" + "Unable to access block with ID '%s'. " + "This is likely due to the block not being shared " + "with the Onyx integration. Exact exception:\n\n%s", + block_id, + e, ) else: logger.exception( - f"Error fetching blocks with status code {res.status_code}: {res.json()}" + "Error fetching blocks with status code %s: %s", + res.status_code, + res.json(), ) # This can occasionally happen, the reason is unknown and cannot be reproduced on our internal Notion @@ -219,7 +223,7 @@ class NotionConnector(LoadConnector, PollConnector): @retry(tries=3, delay=1, backoff=2) def _fetch_page(self, page_id: str) -> NotionPage: """Fetch a page from its ID via the Notion API, retry with database if page fetch fails.""" - logger.debug(f"Fetching page for ID '{page_id}'") + logger.debug("Fetching page for ID '%s'", page_id) page_url = f"https://api.notion.com/v1/pages/{page_id}" res = rl_requests.get( page_url, @@ -230,7 +234,9 @@ class NotionConnector(LoadConnector, PollConnector): res.raise_for_status() except Exception as e: logger.warning( - f"Failed to fetch page, trying database for ID '{page_id}'. Exception: {e}" + "Failed to fetch page, trying database for ID '%s'. Exception: %s", + page_id, + e, ) # Try fetching as a database if page fetch fails, this happens if the page is set to a wiki # it becomes a database from the notion perspective @@ -244,7 +250,7 @@ class NotionConnector(LoadConnector, PollConnector): Note: As of API 2025-09-03, database objects no longer include `properties` (schema moved to individual data sources). """ - logger.debug(f"Fetching database for ID '{database_id}' as a page") + logger.debug("Fetching database for ID '%s' as a page", database_id) database_url = f"https://api.notion.com/v1/databases/{database_id}" res = rl_requests.get( database_url, @@ -254,7 +260,7 @@ class NotionConnector(LoadConnector, PollConnector): try: res.raise_for_status() except Exception as e: - logger.exception(f"Error fetching database as page - {res.json()}") + logger.exception("Error fetching database as page - %s", res.json()) raise e db_data = res.json() database_name = db_data.get("title") @@ -271,7 +277,7 @@ class NotionConnector(LoadConnector, PollConnector): self, database_id: str ) -> list[NotionDataSource]: """Fetch the list of data sources for a database.""" - logger.debug(f"Fetching data sources for database '{database_id}'") + logger.debug("Fetching data sources for database '%s'", database_id) res = rl_requests.get( f"https://api.notion.com/v1/databases/{database_id}", headers=self.headers, @@ -282,12 +288,14 @@ class NotionConnector(LoadConnector, PollConnector): except Exception as e: if res.status_code in (403, 404): logger.error( - f"Unable to access database with ID '{database_id}'. " - f"This is likely due to the database not being shared " - f"with the Onyx integration. Exact exception:\n{e}" + "Unable to access database with ID '%s'. " + "This is likely due to the database not being shared " + "with the Onyx integration. Exact exception:\n%s", + database_id, + e, ) return [] - logger.exception(f"Error fetching database - {res.json()}") + logger.exception("Error fetching database - %s", res.json()) raise e db_data = res.json() @@ -303,7 +311,7 @@ class NotionConnector(LoadConnector, PollConnector): self, data_source_id: str, cursor: str | None = None ) -> dict[str, Any]: """Query a data source via POST /v1/data_sources/{id}/query.""" - logger.debug(f"Querying data source '{data_source_id}'") + logger.debug("Querying data source '%s'", data_source_id) url = f"https://api.notion.com/v1/data_sources/{data_source_id}/query" body = None if not cursor else {"start_cursor": cursor} res = rl_requests.post( @@ -317,12 +325,14 @@ class NotionConnector(LoadConnector, PollConnector): except Exception as e: if res.status_code in (403, 404): logger.error( - f"Unable to access data source with ID '{data_source_id}'. " - f"This is likely due to it not being shared " - f"with the Onyx integration. Exact exception:\n{e}" + "Unable to access data source with ID '%s'. " + "This is likely due to it not being shared " + "with the Onyx integration. Exact exception:\n%s", + data_source_id, + e, ) return {"results": [], "next_cursor": None} - logger.exception(f"Error querying data source - {res.json()}") + logger.exception("Error querying data source - %s", res.json()) raise e return res.json() @@ -499,7 +509,7 @@ class NotionConnector(LoadConnector, PollConnector): logger.debug("Skipping Notion object id field property") return None - logger.debug(f"Unreadable property from innermost prop: {sub_inner_dict}") + logger.debug("Unreadable property from innermost prop: %s", sub_inner_dict) return None result = "" @@ -512,7 +522,7 @@ class NotionConnector(LoadConnector, PollConnector): except Exception as e: # This is not a critical failure, these properties are not the actual contents of the page # more similar to metadata - logger.warning(f"Error recursing properties for {prop_name}: {e}") + logger.warning("Error recursing properties for %s: %s", prop_name, e) continue # Not a perfect way to format Notion database tables but there's no perfect representation # since this must be represented as plaintext @@ -555,8 +565,9 @@ class NotionConnector(LoadConnector, PollConnector): data_sources = self._fetch_data_sources_for_database(database_id) if not data_sources: logger.warning( - f"Database '{database_id}' returned zero data sources — " - f"no pages will be indexed from this database." + "Database '%s' returned zero data sources — " + "no pages will be indexed from this database.", + database_id, ) for ds in data_sources: self._data_source_to_database_map[ds.id] = database_id @@ -578,12 +589,16 @@ class NotionConnector(LoadConnector, PollConnector): if obj_type == "page": logger.debug( - f"Found page with ID '{obj_id}' in database '{database_id}'" + "Found page with ID '%s' in database '%s'", + obj_id, + database_id, ) result_pages.append(result["id"]) elif obj_type == "database": logger.debug( - f"Found database with ID '{obj_id}' in database '{database_id}'" + "Found database with ID '%s' in database '%s'", + obj_id, + database_id, ) nested_db_title = result.get("title", []) nested_db_name = None @@ -640,7 +655,9 @@ class NotionConnector(LoadConnector, PollConnector): for result in data["results"]: logger.debug( - f"Found child block for block with ID '{base_block_id}': {result}" + "Found child block for block with ID '%s': %s", + base_block_id, + result, ) result_block_id = result["id"] result_type = result["type"] @@ -648,25 +665,32 @@ class NotionConnector(LoadConnector, PollConnector): if result_type == "ai_block": logger.warning( - f"Skipping 'ai_block' ('{result_block_id}') for base block '{base_block_id}': " - f"Notion API does not currently support reading AI blocks (as of 24/02/09) " - f"(discussion: https://github.com/onyx-dot-app/onyx/issues/1053)" + "Skipping 'ai_block' ('%s') for base block '%s': " + "Notion API does not currently support reading AI blocks (as of 24/02/09) " + "(discussion: https://github.com/onyx-dot-app/onyx/issues/1053)", + result_block_id, + base_block_id, ) continue if result_type == "unsupported": logger.warning( - f"Skipping unsupported block type '{result_type}' " - f"('{result_block_id}') for base block '{base_block_id}': " - f"(discussion: https://github.com/onyx-dot-app/onyx/issues/1230)" + "Skipping unsupported block type '%s' " + "('%s') for base block '%s': " + "(discussion: https://github.com/onyx-dot-app/onyx/issues/1230)", + result_type, + result_block_id, + base_block_id, ) continue if result_type == "external_object_instance_page": logger.warning( - f"Skipping 'external_object_instance_page' ('{result_block_id}') for base block '{base_block_id}': " - f"Notion API does not currently support reading external blocks (as of 24/07/03) " - f"(discussion: https://github.com/onyx-dot-app/onyx/issues/1761)" + "Skipping 'external_object_instance_page' ('%s') for base block '%s': " + "Notion API does not currently support reading external blocks (as of 24/07/03) " + "(discussion: https://github.com/onyx-dot-app/onyx/issues/1761)", + result_block_id, + base_block_id, ) continue @@ -700,9 +724,9 @@ class NotionConnector(LoadConnector, PollConnector): child_pages.append(result_block_id) self._child_page_parent_map[result_block_id] = page_id else: - logger.debug(f"Entering sub-block: {result_block_id}") + logger.debug("Entering sub-block: %s", result_block_id) sub_output = self._read_blocks(result_block_id, page_id) - logger.debug(f"Finished sub-block: {result_block_id}") + logger.debug("Finished sub-block: %s", result_block_id) result_blocks.extend(sub_output.blocks) child_pages.extend(sub_output.child_page_ids) hierarchy_nodes.extend(sub_output.hierarchy_nodes) @@ -772,10 +796,10 @@ class NotionConnector(LoadConnector, PollConnector): all_child_page_ids: list[str] = [] for page in pages: if page.id in self.indexed_pages: - logger.debug(f"Already indexed page with ID '{page.id}'. Skipping.") + logger.debug("Already indexed page with ID '%s'. Skipping.", page.id) continue - logger.info(f"Reading page with ID '{page.id}', with url {page.url}") + logger.info("Reading page with ID '%s', with url %s", page.id, page.url) block_output = self._read_blocks(page.id) all_child_page_ids.extend(block_output.child_page_ids) @@ -807,11 +831,12 @@ class NotionConnector(LoadConnector, PollConnector): if not block_output.blocks: if not raw_page_title: logger.warning( - f"No blocks OR title found for page with ID '{page.id}'. Skipping." + "No blocks OR title found for page with ID '%s'. Skipping.", + page.id, ) continue - logger.debug(f"No blocks found for page with ID '{page.id}'") + logger.debug("No blocks found for page with ID '%s'", page.id) """ Something like: @@ -872,7 +897,7 @@ class NotionConnector(LoadConnector, PollConnector): def _search_notion(self, query_dict: dict[str, Any]) -> NotionSearchResponse: """Search for pages from a Notion database. Includes some small number of retries to handle misc, flakey failures.""" - logger.debug(f"Searching for pages in Notion with query_dict: {query_dict}") + logger.debug("Searching for pages in Notion with query_dict: %s", query_dict) res = rl_requests.post( "https://api.notion.com/v1/search", headers=self.headers, @@ -926,8 +951,10 @@ class NotionConnector(LoadConnector, PollConnector): ) except requests.exceptions.RequestException as e: logger.warning( - f"Could not fetch database '{db_id}', " - f"defaulting to workspace root. Error: {e}" + "Could not fetch database '%s', " + "defaulting to workspace root. Error: %s", + db_id, + e, ) db_name = f"Database {db_id}" parent_raw_id = self.workspace_id @@ -988,7 +1015,8 @@ class NotionConnector(LoadConnector, PollConnector): yield [workspace_node] logger.info( - f"Recursively loading pages from Notion based on root page with ID: {self.root_page_id}" + "Recursively loading pages from Notion based on root page with ID: %s", + self.root_page_id, ) pages = [self._fetch_page(page_id=self.root_page_id)] yield from batch_generator(self._read_pages(pages), self.batch_size) diff --git a/backend/onyx/connectors/productboard/connector.py b/backend/onyx/connectors/productboard/connector.py index 6110d1753b..3b83499216 100644 --- a/backend/onyx/connectors/productboard/connector.py +++ b/backend/onyx/connectors/productboard/connector.py @@ -218,7 +218,7 @@ class ProductboardConnector(PollConnector): ): return True else: - logger.debug(f"Unable to find updated_at for document '{document.id}'") + logger.debug("Unable to find updated_at for document '%s'", document.id) return False diff --git a/backend/onyx/connectors/salesforce/connector.py b/backend/onyx/connectors/salesforce/connector.py index 10a8a1f71c..5cf94777d4 100644 --- a/backend/onyx/connectors/salesforce/connector.py +++ b/backend/onyx/connectors/salesforce/connector.py @@ -306,11 +306,14 @@ class SalesforceConnector(LoadConnector, PollConnector, SlimConnectorWithPermSyn num_csvs += 1 num_bytes += file_size logger.info( - f"CSV download: object_type={object_type} path={csv_path} bytes={file_size}" + "CSV download: object_type=%s path=%s bytes=%s", + object_type, + csv_path, + file_size, ) logger.info( - f"CSV download total: total_csvs={num_csvs} total_bytes={num_bytes}" + "CSV download total: total_csvs=%s total_bytes=%s", num_csvs, num_bytes ) @staticmethod @@ -339,12 +342,14 @@ class SalesforceConnector(LoadConnector, PollConnector, SlimConnectorWithPermSyn # This takes 10-70 minutes first time (idk why the range is so big) total_types = len(object_type_to_csv_path) - logger.info(f"Starting to process {total_types} object types") + logger.info("Starting to process %s object types", total_types) for i, (object_type, csv_paths) in enumerate( object_type_to_csv_path.items(), 1 ): - logger.info(f"Processing object type {object_type} ({i}/{total_types})") + logger.info( + "Processing object type %s (%s/%s)", object_type, i, total_types + ) # If path is None, it means it failed to fetch the csv if csv_paths is None: continue @@ -354,10 +359,11 @@ class SalesforceConnector(LoadConnector, PollConnector, SlimConnectorWithPermSyn num_records = 0 logger.debug( - f"Processing CSV: object_type={object_type} " - f"csv={csv_path} " - f"len={Path(csv_path).stat().st_size} " - f"records={num_records}" + "Processing CSV: object_type=%s csv=%s len=%s records=%s", + object_type, + csv_path, + Path(csv_path).stat().st_size, + num_records, ) with open(csv_path, "r", newline="", encoding="utf-8") as f: @@ -376,15 +382,16 @@ class SalesforceConnector(LoadConnector, PollConnector, SlimConnectorWithPermSyn sf_db.flush() logger.debug( - f"Added {len(new_ids)} new/updated records for {object_type}" + "Added %s new/updated records for %s", len(new_ids), object_type ) logger.info( - f"Processed CSV: object_type={object_type} " - f"csv={csv_path} " - f"len={Path(csv_path).stat().st_size} " - f"records={num_records} " - f"db_len={sf_db.file_size}" + "Processed CSV: object_type=%s csv=%s len=%s records=%s db_len=%s", + object_type, + csv_path, + Path(csv_path).stat().st_size, + num_records, + sf_db.file_size, ) os.remove(csv_path) @@ -469,10 +476,11 @@ class SalesforceConnector(LoadConnector, PollConnector, SlimConnectorWithPermSyn processed = examined_ids - 1 if now - last_log_time > SalesforceConnector.LOG_INTERVAL: logger.info( - f"Processing stats: {type_to_processed} " - f"file_size={sf_db.file_size} " - f"processed={processed} " - f"remaining={len(changed_ids_to_type) - processed}" + "Processing stats: %s file_size=%s processed=%s remaining=%s", + type_to_processed, + sf_db.file_size, + processed, + len(changed_ids_to_type) - processed, ) last_log_time = now @@ -481,7 +489,7 @@ class SalesforceConnector(LoadConnector, PollConnector, SlimConnectorWithPermSyn parent_object = sf_db.get_record(parent_id, parent_type) if not parent_object: logger.warning( - f"Failed to get parent object {parent_id} for {parent_type}" + "Failed to get parent object %s for %s", parent_id, parent_type ) continue @@ -556,12 +564,14 @@ class SalesforceConnector(LoadConnector, PollConnector, SlimConnectorWithPermSyn ) total_types = len(object_type_to_csv_paths) - logger.info(f"Starting to process {total_types} object types") + logger.info("Starting to process %s object types", total_types) for i, (object_type, csv_paths) in enumerate( object_type_to_csv_paths.items(), 1 ): - logger.info(f"Processing object type {object_type} ({i}/{total_types})") + logger.info( + "Processing object type %s (%s/%s)", object_type, i, total_types + ) # If path is None, it means it failed to fetch the csv if csv_paths is None: continue @@ -575,10 +585,11 @@ class SalesforceConnector(LoadConnector, PollConnector, SlimConnectorWithPermSyn num_records += 1 logger.debug( - f"Processing CSV: object_type={object_type} " - f"csv={csv_path} " - f"len={Path(csv_path).stat().st_size} " - f"records={num_records}" + "Processing CSV: object_type=%s csv=%s len=%s records=%s", + object_type, + csv_path, + Path(csv_path).stat().st_size, + num_records, ) new_ids = sf_db.update_from_csv( @@ -591,15 +602,16 @@ class SalesforceConnector(LoadConnector, PollConnector, SlimConnectorWithPermSyn sf_db.flush() logger.debug( - f"Added {len(new_ids)} new/updated records for {object_type}" + "Added %s new/updated records for %s", len(new_ids), object_type ) logger.info( - f"Processed CSV: object_type={object_type} " - f"csv={csv_path} " - f"len={Path(csv_path).stat().st_size} " - f"records={num_records} " - f"db_len={sf_db.file_size}" + "Processed CSV: object_type=%s csv=%s len=%s records=%s db_len=%s", + object_type, + csv_path, + Path(csv_path).stat().st_size, + num_records, + sf_db.file_size, ) os.remove(csv_path) @@ -607,9 +619,9 @@ class SalesforceConnector(LoadConnector, PollConnector, SlimConnectorWithPermSyn gc.collect() - logger.info(f"Found {len(changed_ids_to_type)} total updated records") + logger.info("Found %s total updated records", len(changed_ids_to_type)) logger.info( - f"Starting to process parent objects of types: {ctx.parent_types}" + "Starting to process parent objects of types: %s", ctx.parent_types ) # Step 3 - extract and index docs @@ -629,13 +641,13 @@ class SalesforceConnector(LoadConnector, PollConnector, SlimConnectorWithPermSyn raise finally: logger.info( - f"Final processing stats: " - f"examined={examined_ids} " - f"parents_changed={parents_changed} " - f"remaining={len(changed_ids_to_type) - examined_ids}" + "Final processing stats: examined=%s parents_changed=%s remaining=%s", + examined_ids, + parents_changed, + len(changed_ids_to_type) - examined_ids, ) - logger.info(f"Top level object types processed: {type_to_processed}") + logger.info("Top level object types processed: %s", type_to_processed) sf_db.close() @@ -673,9 +685,9 @@ class SalesforceConnector(LoadConnector, PollConnector, SlimConnectorWithPermSyn ) gc.collect() - logger.info(f"Found {len(changed_ids_to_type)} total updated records") + logger.info("Found %s total updated records", len(changed_ids_to_type)) logger.info( - f"Starting to process parent objects of types: {ctx.parent_types}" + "Starting to process parent objects of types: %s", ctx.parent_types ) # Step 3 - extract and index docs @@ -850,9 +862,10 @@ class SalesforceConnector(LoadConnector, PollConnector, SlimConnectorWithPermSyn processed = num_examined if now - last_log_time > SalesforceConnector.LOG_INTERVAL: logger.info( - f"Processing stats: {type_to_processed} " - f"processed={processed} " - f"remaining={len(changed_ids_to_type) - processed}" + "Processing stats: %s processed=%s remaining=%s", + type_to_processed, + processed, + len(changed_ids_to_type) - processed, ) last_log_time = now @@ -862,13 +875,13 @@ class SalesforceConnector(LoadConnector, PollConnector, SlimConnectorWithPermSyn raise finally: logger.info( - f"Final processing stats: " - f"processed={processed} " - f"remaining={len(changed_ids_to_type) - processed} " - f"parents_changed={parents_changed}" + "Final processing stats: processed=%s remaining=%s parents_changed=%s", + processed, + len(changed_ids_to_type) - processed, + parents_changed, ) - logger.info(f"Top level object types processed: {type_to_processed}") + logger.info("Top level object types processed: %s", type_to_processed) sf_db.close() @@ -927,9 +940,11 @@ class SalesforceConnector(LoadConnector, PollConnector, SlimConnectorWithPermSyn # 'is_custom': sobject['custom'] # } - logger.info(f"Describe: num_prefixes={len(prefix_to_type)}") + logger.info("Describe: num_prefixes=%s", len(prefix_to_type)) - logger.info(f"Parent object types: num={len(parent_types)} list={parent_types}") + logger.info( + "Parent object types: num=%s list=%s", len(parent_types), parent_types + ) for parent_type in parent_types: # parent_onyx_sf_type = OnyxSalesforceType(parent_type, sf_client) @@ -954,17 +969,19 @@ class SalesforceConnector(LoadConnector, PollConnector, SlimConnectorWithPermSyn # Use only the specified fields type_to_queryable_fields[parent_type] = field_set - logger.info(f"Using custom fields for {parent_type}: {field_set}") + logger.info("Using custom fields for %s: %s", parent_type, field_set) else: # Use all queryable fields type_to_queryable_fields[parent_type] = ( sf_client.get_queryable_fields_by_type(parent_type) ) - logger.info(f"Using all fields for {parent_type}") + logger.info("Using all fields for %s", parent_type) child_types_all = sf_client.get_children_of_sf_type(parent_type) - logger.debug(f"Found {len(child_types_all)} child types for {parent_type}") - logger.debug(f"child types: {child_types_all}") + logger.debug( + "Found %s child types for %s", len(child_types_all), parent_type + ) + logger.debug("child types: %s", child_types_all) child_types_working = child_types_all.copy() if associations_config is not None: @@ -975,7 +992,7 @@ class SalesforceConnector(LoadConnector, PollConnector, SlimConnectorWithPermSyn for k in associations_config: if k not in child_types_working: any_not_found = True - logger.warning(f"Association {k} not found in {parent_type}") + logger.warning("Association %s not found in %s", k, parent_type) if any_not_found: queryable_fields = sf_client.get_queryable_fields_by_type( parent_type @@ -1035,11 +1052,14 @@ class SalesforceConnector(LoadConnector, PollConnector, SlimConnectorWithPermSyn child_types.update(child_types_working.keys()) logger.info( - f"Child object types: parent={parent_type} num={len(child_types_working)} list={child_types_working.keys()}" + "Child object types: parent=%s num=%s list=%s", + parent_type, + len(child_types_working), + child_types_working.keys(), ) logger.info( - f"Final child object types: num={len(child_types)} list={child_types}" + "Final child object types: num=%s list=%s", len(child_types), child_types ) all_types: set[str] = set(parent_types) @@ -1049,7 +1069,7 @@ class SalesforceConnector(LoadConnector, PollConnector, SlimConnectorWithPermSyn all_types.add(USER_OBJECT_TYPE) # Always add User for permissioning purposes all_types.add(ACCOUNT_OBJECT_TYPE) # Always add Account for reference purposes - logger.info(f"All object types: num={len(all_types)} list={all_types}") + logger.info("All object types: num=%s list=%s", len(all_types), all_types) # Ensure User and Account have queryable fields if they weren't already processed essential_types = [USER_OBJECT_TYPE, ACCOUNT_OBJECT_TYPE] diff --git a/backend/onyx/connectors/salesforce/doc_conversion.py b/backend/onyx/connectors/salesforce/doc_conversion.py index 2fe3317dfb..86543cff5c 100644 --- a/backend/onyx/connectors/salesforce/doc_conversion.py +++ b/backend/onyx/connectors/salesforce/doc_conversion.py @@ -132,10 +132,10 @@ def _extract_primary_owner( ) -> BasicExpertInfo | None: object_dict = sf_object.data if not (last_modified_by_id := object_dict.get("LastModifiedById")): - logger.warning(f"No LastModifiedById found for {sf_object.id}") + logger.warning("No LastModifiedById found for %s", sf_object.id) return None if not (last_modified_by := sf_db.get_record(last_modified_by_id)): - logger.warning(f"No LastModifiedBy found for {last_modified_by_id}") + logger.warning("No LastModifiedBy found for %s", last_modified_by_id) return None user_data = last_modified_by.data @@ -153,7 +153,7 @@ def _extract_primary_owner( and expert_info.email is None and expert_info.display_name is None ): - logger.warning(f"No identifying information found for user {user_data}") + logger.warning("No identifying information found for user %s", user_data) return None return expert_info diff --git a/backend/onyx/connectors/salesforce/onyx_salesforce.py b/backend/onyx/connectors/salesforce/onyx_salesforce.py index e1d0c2c959..209967ee4e 100644 --- a/backend/onyx/connectors/salesforce/onyx_salesforce.py +++ b/backend/onyx/connectors/salesforce/onyx_salesforce.py @@ -78,7 +78,7 @@ class OnyxSalesforce(Salesforce): except SalesforceRefusedRequest as e: if is_salesforce_rate_limit_error(e): logger.warning( - f"Salesforce rate limit exceeded for query: {query[:100]}..." + "Salesforce rate limit exceeded for query: %s...", query[:100] ) # Add additional delay for rate limit errors time.sleep(5) @@ -99,7 +99,7 @@ class OnyxSalesforce(Salesforce): except SalesforceRefusedRequest as e: if is_salesforce_rate_limit_error(e): logger.warning( - f"Salesforce rate limit exceeded for query_all: {query[:100]}..." + "Salesforce rate limit exceeded for query_all: %s...", query[:100] ) # Add additional delay for rate limit errors time.sleep(5) @@ -206,7 +206,7 @@ class OnyxSalesforce(Salesforce): try: result = self.safe_query(query) except Exception: - logger.exception(f"Query failed: {query=}") + logger.exception("Query failed: query=%r", query) else: for child_record_key, child_result in result["records"][0].items(): if child_record_key == "attributes": @@ -256,7 +256,7 @@ class OnyxSalesforce(Salesforce): except SalesforceRefusedRequest as e: if is_salesforce_rate_limit_error(e): logger.warning( - f"Salesforce rate limit exceeded for describe_type: {name}" + "Salesforce rate limit exceeded for describe_type: %s", name ) # Add additional delay for rate limit errors time.sleep(3) @@ -303,14 +303,23 @@ class OnyxSalesforce(Salesforce): valid, reason = self._is_valid_child_object(child_relationship) if not valid: logger.debug( - f"{index}/{len_relationships} - Invalid child object: " - f"parent={sf_type} child={child_name} child_field_backreference={child_relationship['field']} {reason=}" + "%s/%s - Invalid child object: parent=%s child=%s child_field_backreference=%s reason=%r", + index, + len_relationships, + sf_type, + child_name, + child_relationship["field"], + reason, ) continue logger.debug( - f"{index}/{len_relationships} - Found valid child object: " - f"parent={sf_type} child={child_name} child_field_backreference={child_relationship['field']}" + "%s/%s - Found valid child object: parent=%s child=%s child_field_backreference=%s", + index, + len_relationships, + sf_type, + child_name, + child_relationship["field"], ) name = child_name diff --git a/backend/onyx/connectors/salesforce/salesforce_calls.py b/backend/onyx/connectors/salesforce/salesforce_calls.py index b4e5e3d497..27bb20df29 100644 --- a/backend/onyx/connectors/salesforce/salesforce_calls.py +++ b/backend/onyx/connectors/salesforce/salesforce_calls.py @@ -99,7 +99,7 @@ def _object_type_has_api_data( except SalesforceRefusedRequest as e: if is_salesforce_rate_limit_error(e): logger.warning( - f"Salesforce rate limit exceeded for object type check: {sf_type}" + "Salesforce rate limit exceeded for object type check: %s", sf_type ) # Add additional delay for rate limit errors time.sleep(3) @@ -107,7 +107,7 @@ def _object_type_has_api_data( except Exception as e: if "OPERATION_TOO_LARGE" not in str(e): - logger.warning(f"Object type {sf_type} doesn't support query: {e}") + logger.warning("Object type %s doesn't support query: %s", sf_type, e) return False return True @@ -147,9 +147,9 @@ def _bulk_retrieve_from_salesforce( if not bulk_2_type: return sf_type, None - logger.info(f"Downloading {sf_type}") + logger.info("Downloading %s", sf_type) - logger.debug(f"Query: {query}") + logger.debug("Query: %s", query) try: # This downloads the file to a file in the target path with a random name @@ -170,16 +170,16 @@ def _bulk_retrieve_from_salesforce( all_download_paths.append(new_file_path) except Exception as e: logger.error( - f"Failed to download salesforce csv for object type {sf_type}: {e}" + "Failed to download salesforce csv for object type %s: %s", sf_type, e ) - logger.warning(f"Exceptioning query for object type {sf_type}: {query}") + logger.warning("Exceptioning query for object type %s: %s", sf_type, query) return sf_type, None finally: bulk_2_handler = None bulk_2_type = None gc.collect() - logger.info(f"Downloaded {sf_type} to {all_download_paths}") + logger.info("Downloaded %s to %s", sf_type, all_download_paths) return sf_type, all_download_paths @@ -215,26 +215,32 @@ def fetch_all_csvs_in_parallel( ) if time_filter_temp is None: logger.warning( - f"Object type not filterable: type={sf_type} fields={queryable_fields}" + "Object type not filterable: type=%s fields=%s", + sf_type, + queryable_fields, ) time_filter = "" else: logger.info( - f"Object type filterable: type={sf_type} filter={time_filter_temp}" + "Object type filterable: type=%s filter=%s", + sf_type, + time_filter_temp, ) time_filter = time_filter_temp break if not _object_type_has_api_data(sf_client, sf_type, time_filter): - logger.warning(f"Object type skipped (no data available): type={sf_type}") + logger.warning("Object type skipped (no data available): type=%s", sf_type) continue query = _make_time_filtered_query(queryable_fields, sf_type, time_filter) type_to_query[sf_type] = query logger.info( - f"Object types to query: initial={len(all_types_to_filter)} queryable={len(type_to_query)}" + "Object types to query: initial=%s queryable=%s", + len(all_types_to_filter), + len(type_to_query), ) # Run the bulk retrieve in parallel diff --git a/backend/onyx/connectors/salesforce/shelve_stuff/shelve_functions.py b/backend/onyx/connectors/salesforce/shelve_stuff/shelve_functions.py index c57000b405..02062d07e4 100644 --- a/backend/onyx/connectors/salesforce/shelve_stuff/shelve_functions.py +++ b/backend/onyx/connectors/salesforce/shelve_stuff/shelve_functions.py @@ -70,8 +70,8 @@ def _update_relationship_shelves( parent_to_child_db.sync() except Exception as e: - logger.error(f"Error updating relationship shelves: {e}") - logger.error(f"Child ID: {child_id}, Parent IDs: {parent_ids}") + logger.error("Error updating relationship shelves: %s", e) + logger.error("Child ID: %s, Parent IDs: %s", child_id, parent_ids) raise @@ -134,7 +134,7 @@ def get_type_from_id(object_id: str) -> str | None: # Look up the object type from the ID-to-type mapping with shelve.open(get_id_type_shelf_path()) as id_type_db: if object_id not in id_type_db: - logger.warning(f"Object ID {object_id} not found in ID-to-type mapping") + logger.warning("Object ID %s not found in ID-to-type mapping", object_id) return None return id_type_db[object_id] @@ -153,7 +153,7 @@ def get_record( shelf_path = get_object_shelf_path(object_type) with shelve.open(shelf_path) as db: if object_id not in db: - logger.warning(f"Object ID {object_id} not found in {shelf_path}") + logger.warning("Object ID %s not found in %s", object_id, shelf_path) return None data = db[object_id] return SalesforceObject( diff --git a/backend/onyx/connectors/salesforce/sqlite_functions.py b/backend/onyx/connectors/salesforce/sqlite_functions.py index 370a1527b1..7c46263def 100644 --- a/backend/onyx/connectors/salesforce/sqlite_functions.py +++ b/backend/onyx/connectors/salesforce/sqlite_functions.py @@ -114,13 +114,13 @@ class OnyxSalesforceSQLite: if SQLITE_DISK_IO_ERROR not in str(e): raise - logger.warning(f"SQLite disk I/O error detected, attempting recovery: {e}") + logger.warning("SQLite disk I/O error detected, attempting recovery: %s", e) self._recover_from_corruption() self._apply_schema_impl() def _recover_from_corruption(self) -> None: """Recover from SQLite corruption by removing all database files and reconnecting.""" - logger.info(f"Removing corrupted SQLite files: {self.filename}") + logger.info("Removing corrupted SQLite files: %s", self.filename) # Close existing connection self.close() @@ -146,7 +146,7 @@ class OnyxSalesforceSQLite: if self._existing_db: file_path = Path(self.filename) file_size = file_path.stat().st_size - logger.info(f"init_db - found existing sqlite db: len={file_size}") + logger.info("init_db - found existing sqlite db: len=%s", file_size) else: # NOTE(rkuo): why is this only if the db doesn't exist? @@ -248,7 +248,10 @@ class OnyxSalesforceSQLite: ) elapsed = time.monotonic() - start - logger.info(f"init_db - create tables and indices: elapsed={elapsed:.2f}") + logger.info( + "init_db - create tables and indices: elapsed=%s", + format(elapsed, ".2f"), + ) # Analyze tables to help query planner # NOTE(rkuo): skip ANALYZE - it takes too long and we likely don't have @@ -265,13 +268,17 @@ class OnyxSalesforceSQLite: start = time.monotonic() cursor.execute("SELECT COUNT(*) FROM user_email_map") elapsed = time.monotonic() - start - logger.info(f"init_db - count user_email_map: elapsed={elapsed:.2f}") + logger.info( + "init_db - count user_email_map: elapsed=%s", format(elapsed, ".2f") + ) start = time.monotonic() if cursor.fetchone()[0] == 0: OnyxSalesforceSQLite._update_user_email_map(cursor) elapsed = time.monotonic() - start - logger.info(f"init_db - update_user_email_map: elapsed={elapsed:.2f}") + logger.info( + "init_db - update_user_email_map: elapsed=%s", format(elapsed, ".2f") + ) def get_user_id_by_email(self, email: str) -> str | None: """Get the Salesforce User ID for a given email address. @@ -322,10 +329,11 @@ class OnyxSalesforceSQLite: else: cache_bytes = abs(cache_pages * 1024) logger.info( - f"SQLite stats: sqlite_version={sqlite3.sqlite_version} " - f"cache_pages={cache_pages} " - f"page_size={page_size} " - f"cache_bytes={cache_bytes}" + "SQLite stats: sqlite_version=%s cache_pages=%s page_size=%s cache_bytes=%s", + sqlite3.sqlite_version, + cache_pages, + page_size, + cache_bytes, ) # get_changed_parent_ids_by_type_2 replaces this @@ -464,7 +472,11 @@ class OnyxSalesforceSQLite: ] for field_name, _ in parent_relationship_fields.items(): if field_name not in sf_object.data: - logger.warning(f"{field_name=} not in data for {changed_type=}!") + logger.warning( + "field_name=%r not in data for changed_type=%r!", + field_name, + changed_type, + ) continue parent_id = cast(str, sf_object.data[field_name]) @@ -472,7 +484,8 @@ class OnyxSalesforceSQLite: if parent_id_prefix not in prefix_to_type: logger.warning( - f"Could not lookup type for prefix: {parent_id_prefix=}" + "Could not lookup type for prefix: parent_id_prefix=%r", + parent_id_prefix, ) continue @@ -573,7 +586,10 @@ class OnyxSalesforceSQLite: for row in reader: if ID_FIELD not in row: logger.warning( - f"Row {row} does not have an {ID_FIELD} field in {csv_download_path}" + "Row %s does not have an %s field in %s", + row, + ID_FIELD, + csv_download_path, ) continue @@ -640,7 +656,7 @@ class OnyxSalesforceSQLite: ) result = cursor.fetchone() if not result: - logger.warning(f"Object ID {object_id} not found") + logger.warning("Object ID %s not found", object_id) return None return result[0] @@ -673,7 +689,7 @@ class OnyxSalesforceSQLite: ) result = cursor.fetchall() if not result: - logger.warning(f"Object ID {object_id} not found") + logger.warning("Object ID %s not found", object_id) return None data = json.loads(result[0][0]) @@ -772,7 +788,9 @@ class OnyxSalesforceSQLite: except Exception: logger.exception( - f"Error updating relationship tables: child_id={child_id} parent_ids={parent_ids}" + "Error updating relationship tables: child_id=%s parent_ids=%s", + child_id, + parent_ids, ) raise @@ -800,10 +818,10 @@ class OnyxSalesforceSQLite: of the user if possible.""" object_dict: dict[str, Any] = sf_object.data if not (last_modified_by_id := object_dict.get("LastModifiedById")): - logger.warning(f"No LastModifiedById found for {sf_object.id}") + logger.warning("No LastModifiedById found for %s", sf_object.id) return None if not (last_modified_by := self.get_record(last_modified_by_id)): - logger.warning(f"No LastModifiedBy found for {last_modified_by_id}") + logger.warning("No LastModifiedBy found for %s", last_modified_by_id) return None try: diff --git a/backend/onyx/connectors/sharepoint/connector.py b/backend/onyx/connectors/sharepoint/connector.py index d2b30c4b6d..cd4fc2a318 100644 --- a/backend/onyx/connectors/sharepoint/connector.py +++ b/backend/onyx/connectors/sharepoint/connector.py @@ -269,7 +269,10 @@ def sleep_and_retry( # 429 / 503 — rate limit or transient error. Back off and retry. if status in (429, 503) and attempt < max_retries: logger.warning( - f"Rate limit exceeded on {method_name}, attempt {attempt + 1}/{max_retries + 1}, sleeping and retrying" + "Rate limit exceeded on %s, attempt %s/%s, sleeping and retrying", + method_name, + attempt + 1, + max_retries + 1, ) retry_after = ( e.response.headers.get( # ty: ignore[unresolved-attribute] @@ -282,7 +285,7 @@ def sleep_and_retry( # Exponential backoff: 2^attempt * 5 seconds sleep_time = min(30, (2**attempt) * 5) - logger.info(f"Sleeping for {sleep_time} seconds before retry") + logger.info("Sleeping for %s seconds before retry", sleep_time) time.sleep(sleep_time) continue @@ -294,7 +297,7 @@ def sleep_and_retry( # caller intentionally handles. if e.response is not None: logger.warning( - f"SharePoint request failed for {method_name}: status={status}, " + "SharePoint request failed for %s: status=%s, ", method_name, status ) raise e @@ -338,7 +341,7 @@ def _log_and_raise_for_status(response: requests.Response) -> None: try: response.raise_for_status() except Exception: - logger.error(f"HTTP request failed: {response.text}") + logger.error("HTTP request failed: %s", response.text) raise @@ -383,7 +386,7 @@ def load_certificate_from_pfx(pfx_data: bytes, password: str) -> CertificateData thumbprint=certificate.fingerprint(hashes.SHA1()).hex(), ) except Exception as e: - logger.error(f"Error loading certificate: {e}") + logger.error("Error loading certificate: %s", e) return None @@ -483,7 +486,9 @@ def _download_with_cap(url: str, timeout: int, cap: int) -> bytes: content_len = int(cl_header) if content_len > cap: logger.warning( - f"Content-Length {content_len} exceeds cap {cap}; skipping download." + "Content-Length %s exceeds cap %s; skipping download.", + content_len, + cap, ) raise SizeCapExceeded("pre_download") @@ -496,7 +501,7 @@ def _download_with_cap(url: str, timeout: int, cap: int) -> bytes: if buf.tell() > cap: # Avoid keeping a large partial buffer; close and signal caller to skip. logger.warning( - f"Streaming download exceeded cap {cap} bytes; aborting early." + "Streaming download exceeded cap %s bytes; aborting early.", cap ) raise SizeCapExceeded("during_download") @@ -552,7 +557,9 @@ def _convert_driveitem_to_document_with_permissions( mime_type = driveitem.mime_type if not mime_type or mime_type in OnyxMimeTypes.EXCLUDED_IMAGE_TYPES: logger.debug( - f"Skipping malformed or excluded mime type {mime_type} for {driveitem.name}" + "Skipping malformed or excluded mime type %s for %s", + mime_type, + driveitem.name, ) return None @@ -564,7 +571,10 @@ def _convert_driveitem_to_document_with_permissions( if file_size is not None and file_size > SHAREPOINT_CONNECTOR_SIZE_THRESHOLD: logger.warning( - f"Skipping '{driveitem.name}' over size threshold ({file_size} > {SHAREPOINT_CONNECTOR_SIZE_THRESHOLD} bytes)." + "Skipping '%s' over size threshold (%s > %s bytes).", + driveitem.name, + file_size, + SHAREPOINT_CONNECTOR_SIZE_THRESHOLD, ) return None @@ -578,12 +588,16 @@ def _convert_driveitem_to_document_with_permissions( SHAREPOINT_CONNECTOR_SIZE_THRESHOLD, ) except SizeCapExceeded as e: - logger.warning(f"Skipping '{driveitem.name}' exceeded size cap: {str(e)}") + logger.warning( + "Skipping '%s' exceeded size cap: %s", driveitem.name, str(e) + ) return None except requests.RequestException as e: status = e.response.status_code if e.response is not None else -1 logger.warning( - f"Failed to download via downloadUrl for '{driveitem.name}' (status={status}); falling back to Graph API." + "Failed to download via downloadUrl for '%s' (status=%s); falling back to Graph API.", + driveitem.name, + status, ) # Fallback: download via Graph API /content endpoint @@ -598,12 +612,13 @@ def _convert_driveitem_to_document_with_permissions( ) except SizeCapExceeded: logger.warning( - f"Skipping '{driveitem.name}' exceeded size cap during Graph API download." + "Skipping '%s' exceeded size cap during Graph API download.", + driveitem.name, ) return None except Exception as e: logger.warning( - f"Failed to download via Graph API for '{driveitem.name}': {e}" + "Failed to download via Graph API for '%s': %s", driveitem.name, e ) return _create_document_failure( driveitem, f"Failed to download via graph api: {e}", e @@ -616,7 +631,8 @@ def _convert_driveitem_to_document_with_permissions( if not content_bytes: logger.warning( - f"Zero-length content for '{driveitem.name}'. Skipping text/image extraction." + "Zero-length content for '%s'. Skipping text/image extraction.", + driveitem.name, ) elif file_ext in OnyxFileExtensions.IMAGE_EXTENSIONS: image_section, _ = store_image_and_create_section( @@ -649,7 +665,7 @@ def _convert_driveitem_to_document_with_permissions( ) except Exception as e: logger.warning( - f"Failed to extract tabular sections for '{driveitem.name}': {e}" + "Failed to extract tabular sections for '%s': %s", driveitem.name, e ) else: extraction_result = extract_text_and_images( @@ -665,7 +681,7 @@ def _convert_driveitem_to_document_with_permissions( ) if include_permissions and ctx is not None: - logger.info(f"Getting external access for {driveitem.name}") + logger.info("Getting external access for %s", driveitem.name) sdk_item = driveitem.to_sdk_driveitem(graph_client) external_access = get_sharepoint_external_access( ctx=ctx, @@ -961,10 +977,11 @@ class SharepointConnector( self.sharepoint_domain_suffix = resolved_env.sharepoint_domain_suffix if sharepoint_domain_suffix != resolved_env.sharepoint_domain_suffix: logger.warning( - f"Configured sharepoint_domain_suffix '{sharepoint_domain_suffix}' " - f"differs from the expected suffix '{resolved_env.sharepoint_domain_suffix}' " - f"for the {resolved_env.environment} environment. " - f"Using '{resolved_env.sharepoint_domain_suffix}'." + "Configured sharepoint_domain_suffix '%s' differs from the expected suffix '%s' for the %s environment. Using '%s'.", + sharepoint_domain_suffix, + resolved_env.sharepoint_domain_suffix, + resolved_env.environment, + resolved_env.sharepoint_domain_suffix, ) def validate_connector_settings(self) -> None: @@ -1017,7 +1034,7 @@ class SharepointConnector( raise except Exception as e: logger.warning( - f"RoleAssignments permission probe failed (non-blocking): {e}" + "RoleAssignments permission probe failed (non-blocking): %s", e ) def _extract_tenant_domain_from_sites(self) -> str | None: @@ -1036,7 +1053,7 @@ class SharepointConnector( tenant = hostname.split(".")[0] if tenant: return tenant - logger.warning(f"No tenant domain found from {len(self.sites)} sites") + logger.warning("No tenant domain found from %s sites", len(self.sites)) return None def _resolve_tenant_domain_from_root_site(self) -> str: @@ -1130,12 +1147,13 @@ class SharepointConnector( try: parsed = urlsplit(url) except ValueError: - logger.warning(f"Sharepoint URL '{url}' could not be parsed") + logger.warning("Sharepoint URL '%s' could not be parsed", url) return None, [] if not parsed.scheme or not parsed.netloc: logger.warning( - f"Sharepoint URL '{url}' is not a valid absolute URL (missing scheme or host)" + "Sharepoint URL '%s' is not a valid absolute URL (missing scheme or host)", + url, ) return None, [] @@ -1159,7 +1177,8 @@ class SharepointConnector( if site_type_index is None or len(parts) <= site_type_index + 1: logger.warning( - f"Site URL '{url}' is not a valid Sharepoint URL (must contain /sites/ or /teams/)" + "Site URL '%s' is not a valid Sharepoint URL (must contain /sites/ or /teams/)", + url, ) continue @@ -1200,7 +1219,7 @@ class SharepointConnector( """ site = self.graph_client.sites.get_by_url(site_descriptor.url) drives = site.drives.get().execute_query() - logger.info(f"Found drives: {[d.name for d in drives]}") + logger.info("Found drives: %s", [d.name for d in drives]) matched = [ d @@ -1212,12 +1231,12 @@ class SharepointConnector( ) ] if not matched: - logger.warning(f"Drive '{drive_name}' not found") + logger.warning("Drive '%s' not found", drive_name) return None drive = matched[0] drive_web_url: str | None = drive.web_url - logger.info(f"Found drive: {drive.name} (web_url: {drive_web_url})") + logger.info("Found drive: %s (web_url: %s)", drive.name, drive_web_url) return cast(str, drive.id), drive_web_url def _get_drive_items_for_drive_id( @@ -1263,7 +1282,9 @@ class SharepointConnector( ): raise e - logger.warning(f"Failed to process site: {site_descriptor.url} - {err_str}") + logger.warning( + "Failed to process site: %s - %s", site_descriptor.url, err_str + ) def _fetch_driveitems( self, @@ -1279,7 +1300,7 @@ class SharepointConnector( try: site = self.graph_client.sites.get_by_url(site_descriptor.url) drives = site.drives.get().execute_query() - logger.debug(f"Found drives: {[d.name for d in drives]}") + logger.debug("Found drives: %s", [d.name for d in drives]) if site_descriptor.drive_name: drives = [ @@ -1293,7 +1314,7 @@ class SharepointConnector( ) ] if not drives: - logger.warning(f"Drive '{site_descriptor.drive_name}' not found") + logger.warning("Drive '%s' not found", site_descriptor.drive_name) return for drive in drives: @@ -1323,7 +1344,9 @@ class SharepointConnector( yield item, drive_name or "", drive_web_url except Exception as e: - logger.warning(f"Failed to process drive '{drive.name}': {str(e)}") + logger.warning( + "Failed to process drive '%s': %s", drive.name, str(e) + ) except Exception as e: err_str = str(e) @@ -1334,7 +1357,7 @@ class SharepointConnector( ): raise e - logger.warning(f"Failed to process site: {err_str}") + logger.warning("Failed to process site: %s", err_str) def _handle_paginated_sites( self, sites: SitesWithRoot @@ -1364,7 +1387,7 @@ class SharepointConnector( result = [] for sd in site_descriptors: if _is_site_excluded(sd.url, self.excluded_sites): - logger.info(f"Excluding site by denylist: {sd.url}") + logger.info("Excluding site by denylist: %s", sd.url) continue result.append(sd) return result @@ -1416,7 +1439,7 @@ class SharepointConnector( data = self._graph_api_get_json(page_url, params) except HTTPError as e: if e.response is not None and e.response.status_code == 404: - logger.warning(f"Site page not found: {page_url}") + logger.warning("Site page not found: %s", page_url) break if ( e.response is not None @@ -1424,9 +1447,8 @@ class SharepointConnector( and _is_graph_invalid_request(e.response) ): logger.warning( - f"$expand=canvasLayout on the LIST endpoint returned 400 " - f"for site {site_descriptor.url}. Falling back to " - f"per-page expansion." + "$expand=canvasLayout on the LIST endpoint returned 400 for site %s. Falling back to per-page expansion.", + site_descriptor.url, ) yield from self._fetch_site_pages_individually( site_pages_base, start, end, skip_ids=yielded_ids @@ -1447,7 +1469,7 @@ class SharepointConnector( page_url = data.get("@odata.nextLink") - logger.debug(f"Yielded {total_yielded} site pages for {site_descriptor.url}") + logger.debug("Yielded %s site pages for %s", total_yielded, site_descriptor.url) def _fetch_site_pages_individually( self, @@ -1502,7 +1524,7 @@ class SharepointConnector( page_url = data.get("@odata.nextLink") logger.debug( - f"Yielded {total_yielded} site pages (per-page expansion fallback)" + "Yielded %s site pages (per-page expansion fallback)", total_yielded ) def _try_expand_single_page( @@ -1526,7 +1548,9 @@ class SharepointConnector( ): page_name = fallback_page.get("name", page_id) logger.warning( - f"$expand=canvasLayout failed for page '{page_name}' ({page_id}). Indexing metadata only." + "$expand=canvasLayout failed for page '%s' (%s). Indexing metadata only.", + page_name, + page_id, ) return fallback_page raise @@ -1574,7 +1598,11 @@ class SharepointConnector( ) wait = min(retry_after, 60) logger.warning( - f"Graph API {response.status_code} on attempt {attempt + 1}, retrying in {wait}s: {url}" + "Graph API %s on attempt %s, retrying in %ss: %s", + response.status_code, + attempt + 1, + wait, + url, ) time.sleep(wait) # Re-acquire token in case it expired during a long traversal @@ -1587,7 +1615,10 @@ class SharepointConnector( if attempt < GRAPH_API_MAX_RETRIES: wait = min(2**attempt, 60) logger.warning( - f"Graph API connection error on attempt {attempt + 1}, retrying in {wait}s: {url}" + "Graph API connection error on attempt %s, retrying in %ss: %s", + attempt + 1, + wait, + url, ) time.sleep(wait) continue @@ -1856,7 +1887,9 @@ class SharepointConnector( end=end, ): if self._is_driveitem_excluded(driveitem): - logger.debug(f"Excluding by path denylist: {driveitem.web_url}") + logger.debug( + "Excluding by path denylist: %s", driveitem.web_url + ) continue if drive_web_url: @@ -1887,7 +1920,7 @@ class SharepointConnector( ) try: - logger.debug(f"Processing: {driveitem.web_url}") + logger.debug("Processing: %s", driveitem.web_url) if include_permissions: ctx = self._create_rest_client_context(site_descriptor.url) doc_batch.append( @@ -1911,7 +1944,7 @@ class SharepointConnector( ) ) except Exception as e: - logger.warning(f"Failed to process driveitem: {str(e)}") + logger.warning("Failed to process driveitem: %s", str(e)) if len(doc_batch) >= SLIM_BATCH_SIZE: yield doc_batch @@ -1924,7 +1957,8 @@ class SharepointConnector( ) for site_page in site_pages: logger.debug( - f"Processing site page: {site_page.get('webUrl', site_page.get('name', 'Unknown'))}" + "Processing site page: %s", + site_page.get("webUrl", site_page.get("name", "Unknown")), ) try: if include_permissions: @@ -1951,8 +1985,9 @@ class SharepointConnector( ) except Exception as e: logger.warning( - f"Failed to process site page " - f"{site_page.get('webUrl', site_page.get('name', 'Unknown'))}: {e}" + "Failed to process site page %s: %s", + site_page.get("webUrl", site_page.get("name", "Unknown")), + e, ) if len(doc_batch) >= SLIM_BATCH_SIZE: yield doc_batch @@ -1991,7 +2026,7 @@ class SharepointConnector( if certificate_data is None: raise RuntimeError("Failed to load certificate") - logger.info(f"Creating MSAL app with authority url {authority_url}") + logger.info("Creating MSAL app with authority url %s", authority_url) self.msal_app = msal.ConfidentialClientApplication( authority=authority_url, client_id=sp_client_id, @@ -2042,7 +2077,7 @@ class SharepointConnector( return drive_names except Exception as e: - logger.warning(f"Failed to fetch drives for site '{site_url}': {e}") + logger.warning("Failed to fetch drives for site '%s': %s", site_url, e) return [] def _build_folder_url( @@ -2237,7 +2272,7 @@ class SharepointConnector( return checkpoint logger.info( - f"Found {len(checkpoint.cached_site_descriptors)} sites to process" + "Found %s sites to process", len(checkpoint.cached_site_descriptors) ) # Set first site and return to allow checkpoint persistence if checkpoint.cached_site_descriptors: @@ -2245,7 +2280,7 @@ class SharepointConnector( checkpoint.cached_site_descriptors.popleft() ) logger.info( - f"Starting with site: {checkpoint.current_site_descriptor.url}" + "Starting with site: %s", checkpoint.current_site_descriptor.url ) # Yield site hierarchy node for the first site yield from self._yield_site_hierarchy_node( @@ -2262,14 +2297,16 @@ class SharepointConnector( return checkpoint logger.info( - f"Initializing drives for site: {checkpoint.current_site_descriptor.url}" + "Initializing drives for site: %s", + checkpoint.current_site_descriptor.url, ) try: # If the user explicitly specified drive(s) for this site, honour that if checkpoint.current_site_descriptor.drive_name: logger.info( - f"Using explicitly specified drive: {checkpoint.current_site_descriptor.drive_name}" + "Using explicitly specified drive: %s", + checkpoint.current_site_descriptor.drive_name, ) checkpoint.cached_drive_names = deque( [checkpoint.current_site_descriptor.drive_name] @@ -2282,16 +2319,21 @@ class SharepointConnector( if not checkpoint.cached_drive_names: logger.warning( - f"No accessible drives found for site: {checkpoint.current_site_descriptor.url}" + "No accessible drives found for site: %s", + checkpoint.current_site_descriptor.url, ) else: logger.info( - f"Found {len(checkpoint.cached_drive_names)} drives: {list(checkpoint.cached_drive_names)}" + "Found %s drives: %s", + len(checkpoint.cached_drive_names), + list(checkpoint.cached_drive_names), ) except Exception as e: logger.error( - f"Failed to initialize drives for site: {checkpoint.current_site_descriptor.url}: {e}" + "Failed to initialize drives for site: %s: %s", + checkpoint.current_site_descriptor.url, + e, ) # Yield a ConnectorFailure for site-level access failures start_dt = datetime.fromtimestamp(start, tz=timezone.utc) @@ -2334,9 +2376,11 @@ class SharepointConnector( site_descriptor = checkpoint.current_site_descriptor logger.info( - f"Processing drive '{checkpoint.current_drive_name}' in site: {site_descriptor.url}" + "Processing drive '%s' in site: %s", + checkpoint.current_drive_name, + site_descriptor.url, ) - logger.debug(f"Time range: {start_dt} to {end_dt}") + logger.debug("Time range: %s to %s", start_dt, end_dt) current_drive_name = checkpoint.current_drive_name if current_drive_name is None: @@ -2345,11 +2389,11 @@ class SharepointConnector( try: logger.info( - f"Fetching drive items for drive name: {current_drive_name}" + "Fetching drive items for drive name: %s", current_drive_name ) result = self._resolve_drive(site_descriptor, current_drive_name) if result is None: - logger.warning(f"Drive '{current_drive_name}' not found, skipping") + logger.warning("Drive '%s' not found, skipping", current_drive_name) self._clear_drive_checkpoint_state(checkpoint) return checkpoint @@ -2358,7 +2402,10 @@ class SharepointConnector( checkpoint.current_drive_web_url = drive_web_url except Exception as e: logger.error( - f"Failed to retrieve items from drive '{current_drive_name}' in site: {site_descriptor.url}: {e}" + "Failed to retrieve items from drive '%s' in site: %s: %s", + current_drive_name, + site_descriptor.url, + e, ) yield _create_entity_failure( f"{site_descriptor.url}|{current_drive_name}", @@ -2419,7 +2466,9 @@ class SharepointConnector( ) except Exception as e: logger.error( - f"Failed to fetch delta page for drive '{current_drive_name}': {e}" + "Failed to fetch delta page for drive '%s': %s", + current_drive_name, + e, ) yield _create_entity_failure( f"{site_descriptor.url}|{current_drive_name}", @@ -2448,19 +2497,22 @@ class SharepointConnector( item_count += 1 if self._is_driveitem_excluded(driveitem): - logger.debug(f"Excluding by path denylist: {driveitem.web_url}") + logger.debug("Excluding by path denylist: %s", driveitem.web_url) continue if driveitem.id and driveitem.id in checkpoint.seen_document_ids: logger.debug( - f"Skipping duplicate document {driveitem.id} ({driveitem.name})" + "Skipping duplicate document %s (%s)", + driveitem.id, + driveitem.name, ) continue driveitem_extension = get_file_ext(driveitem.name) if driveitem_extension not in OnyxFileExtensions.ALL_ALLOWED_EXTENSIONS: logger.warning( - f"Skipping {driveitem.web_url} as it is not a supported file type" + "Skipping %s as it is not a supported file type", + driveitem.web_url, ) continue @@ -2521,19 +2573,22 @@ class SharepointConnector( yield doc_or_failure else: logger.warning( - f"Skipping {driveitem.web_url} as it is empty and not a PDF or image" + "Skipping %s as it is empty and not a PDF or image", + driveitem.web_url, ) elif isinstance(doc_or_failure, ConnectorFailure): yield doc_or_failure except Exception as e: logger.warning( - f"Failed to process driveitem {driveitem.web_url}: {e}" + "Failed to process driveitem %s: %s", driveitem.web_url, e ) yield _create_document_failure( driveitem, f"Failed to process: {str(e)}", e ) - logger.info(f"Processed {item_count} items in drive '{current_drive_name}'") + logger.info( + "Processed %s items in drive '%s'", item_count, current_drive_name + ) if has_more_delta_pages: return checkpoint @@ -2544,7 +2599,8 @@ class SharepointConnector( # If we have more drives in current site, continue with current site if checkpoint.cached_drive_names and len(checkpoint.cached_drive_names) > 0: logger.debug( - f"Continuing with {len(checkpoint.cached_drive_names)} remaining drives in current site" + "Continuing with %s remaining drives in current site", + len(checkpoint.cached_drive_names), ) return checkpoint @@ -2554,7 +2610,8 @@ class SharepointConnector( and checkpoint.current_site_descriptor is not None ): logger.info( - f"Processing site pages for site: {checkpoint.current_site_descriptor.url}" + "Processing site pages for site: %s", + checkpoint.current_site_descriptor.url, ) checkpoint.process_site_pages = True return checkpoint @@ -2573,7 +2630,8 @@ class SharepointConnector( ) for site_page in site_pages: logger.debug( - f"Processing site page: {site_page.get('webUrl', site_page.get('name', 'Unknown'))}" + "Processing site page: %s", + site_page.get("webUrl", site_page.get("name", "Unknown")), ) client_ctx: ClientContext | None = None if include_permissions: @@ -2591,7 +2649,7 @@ class SharepointConnector( ) ) logger.info( - f"Finished processing site pages for site: {site_descriptor.url}" + "Finished processing site pages for site: %s", site_descriptor.url ) # If no more drives, move to next site if available @@ -2610,10 +2668,13 @@ class SharepointConnector( checkpoint.cached_drive_names = None # Reset for new site checkpoint.process_site_pages = False logger.info( - f"Finished site '{current_site}', moving to next site: {checkpoint.current_site_descriptor.url}" + "Finished site '%s', moving to next site: %s", + current_site, + checkpoint.current_site_descriptor.url, ) logger.info( - f"Remaining sites to process: {len(checkpoint.cached_site_descriptors) + 1}" + "Remaining sites to process: %s", + len(checkpoint.cached_site_descriptors) + 1, ) # Yield site hierarchy node for the new site yield from self._yield_site_hierarchy_node( @@ -2628,7 +2689,7 @@ class SharepointConnector( else "unknown" ) logger.info( - f"SharePoint processing complete. Finished last site: {current_site}" + "SharePoint processing complete. Finished last site: %s", current_site ) checkpoint.has_more = False return checkpoint diff --git a/backend/onyx/connectors/slack/connector.py b/backend/onyx/connectors/slack/connector.py index dc5eeba233..5b98f96052 100644 --- a/backend/onyx/connectors/slack/connector.py +++ b/backend/onyx/connectors/slack/connector.py @@ -157,7 +157,7 @@ def get_channel_messages( channel=channel["id"], is_private=channel["is_private"], ) - logger.info(f"Successfully joined '{channel['name']}'") + logger.info("Successfully joined '%s'", channel["name"]) for result in make_paginated_slack_api_call( client.conversations_history, @@ -419,12 +419,12 @@ def _get_messages( ) except SlackApiError as e: if e.response["error"] == "is_archived": - logger.warning(f"Channel {channel['name']} is archived. Skipping.") + logger.warning("Channel %s is archived. Skipping.", channel["name"]) return [], False - logger.exception(f"Error joining channel {channel['name']}") + logger.exception("Error joining channel %s", channel["name"]) raise - logger.info(f"Successfully joined '{channel['name']}'") + logger.info("Successfully joined '%s'", channel["name"]) response = client.conversations_history( channel=channel["id"], @@ -624,7 +624,7 @@ def _process_message( failure=None, ) except Exception as e: - logger.exception(f"Error processing message {message['ts']}") + logger.exception("Error processing message %s", message["ts"]) return ProcessedSlackMessage( doc=None, thread_or_message_ts=thread_or_message_ts, @@ -844,7 +844,7 @@ class SlackConnector( auth_response = self.client.auth_test() self._workspace_url = auth_response.get("url") except Exception as e: - logger.warning(f"Failed to get workspace URL from auth_test: {e}") + logger.warning("Failed to get workspace URL from auth_test: %s", e) self._workspace_url = None def retrieve_all_slim_docs_perm_sync( @@ -901,7 +901,9 @@ class SlackConnector( raw_channels, self.channels, self.channel_regex_enabled ) logger.info( - f"Channels - initial checkpoint: all={len(raw_channels)} post_filtering={len(filtered_channels)}" + "Channels - initial checkpoint: all=%s post_filtering=%s", + len(raw_channels), + len(filtered_channels), ) checkpoint.channel_ids = [c["id"] for c in filtered_channels] @@ -928,10 +930,10 @@ class SlackConnector( num_channels_remaining += 1 logger.info( - f"Channels - current status: " - f"processed={len(final_channel_ids) - num_channels_remaining} " - f"remaining={num_channels_remaining} " - f"total={len(final_channel_ids)}" + "Channels - current status: processed=%s remaining=%s total=%s", + len(final_channel_ids) - num_channels_remaining, + num_channels_remaining, + len(final_channel_ids), ) channel = checkpoint.current_channel @@ -966,7 +968,10 @@ class SlackConnector( ) logger.debug( - f"Getting messages for channel {channel} within range {oldest} - {latest}" + "Getting messages for channel %s within range %s - %s", + channel, + oldest, + latest, ) message_batch, has_more_in_channel = _get_messages( @@ -974,7 +979,11 @@ class SlackConnector( ) logger.info( - f"Retrieved messages: {len(message_batch)=} {channel=} {oldest=} {latest=}" + "Retrieved messages: len(message_batch)=%r channel=%r oldest=%r latest=%r", + len(message_batch), + channel, + oldest, + latest, ) # message_batch[0] is the newest message (Slack returns newest to oldest) @@ -1061,7 +1070,10 @@ class SlackConnector( ) logger.info( - f"Current channel processing stats: {range_start=} range_end={end} percent_complete={range_percent_complete=:.2f}" + "Current channel processing stats: range_start=%r range_end=%s percent_complete=range_percent_complete=%s", + range_start, + end, + format(range_percent_complete, ".2f"), ) checkpoint.seen_thread_ts = list(seen_thread_ts) @@ -1117,14 +1129,14 @@ class SlackConnector( channels_processed / len(final_channel_ids) * 100.0 ) logger.info( - f"All channels processing stats: " - f"processed={len(final_channel_ids) - num_channels_remaining} " - f"remaining={num_channels_remaining} " - f"total={len(final_channel_ids)} " - f"percent_complete={channels_percent_complete:.2f}" + "All channels processing stats: processed=%s remaining=%s total=%s percent_complete=%s", + len(final_channel_ids) - num_channels_remaining, + num_channels_remaining, + len(final_channel_ids), + format(channels_percent_complete, ".2f"), ) except Exception as e: - logger.exception(f"Error processing channel {channel['name']}") + logger.exception("Error processing channel %s", channel["name"]) yield ConnectorFailure( failed_entity=EntityFailure( entity_id=channel["id"], @@ -1225,8 +1237,8 @@ class SlackConnector( # Handle rate limiting specifically retry_after = int(e.response.headers.get("Retry-After", 1)) logger.warning( - f"Slack API rate limited during validation. Retry suggested after {retry_after} seconds. " - "Proceeding with validation, but be aware that connector operations might be throttled." + "Slack API rate limited during validation. Retry suggested after %s seconds. Proceeding with validation, but be aware that connector operations might be throttled.", + retry_after, ) # Continue validation without failing - the connector is likely valid but just rate limited return diff --git a/backend/onyx/connectors/slack/onyx_retry_handler.py b/backend/onyx/connectors/slack/onyx_retry_handler.py index ee70252a53..3b05b5c5ab 100644 --- a/backend/onyx/connectors/slack/onyx_retry_handler.py +++ b/backend/onyx/connectors/slack/onyx_retry_handler.py @@ -132,10 +132,10 @@ class OnyxRedisSlackRetryHandler(RetryHandler): self._redis.set(self._delay_key, "1", px=ttl_ms_new) logger.warning( - f"OnyxRedisSlackRetryHandler.prepare_for_next_attempt setting delay: " - f"current_attempt={state.current_attempt} " - f"retry-after={retry_after_value} " - f"{ttl_ms_new=}" + "OnyxRedisSlackRetryHandler.prepare_for_next_attempt setting delay: current_attempt=%s retry-after=%s ttl_ms_new=%r", + state.current_attempt, + retry_after_value, + ttl_ms_new, ) state.increment_current_attempt() diff --git a/backend/onyx/connectors/slack/onyx_slack_web_client.py b/backend/onyx/connectors/slack/onyx_slack_web_client.py index b6c4b940b2..2b28a7c180 100644 --- a/backend/onyx/connectors/slack/onyx_slack_web_client.py +++ b/backend/onyx/connectors/slack/onyx_slack_web_client.py @@ -100,7 +100,9 @@ class OnyxSlackWebClient(WebClient): if delay_ms > 0: logger.warning( - f"OnyxSlackWebClient._perform_urllib_http_request_internal delay: {delay_ms=} {self.num_requests=}" + "OnyxSlackWebClient._perform_urllib_http_request_internal delay: delay_ms=%r self.num_requests=%r", + delay_ms, + self.num_requests, ) time.sleep(delay_ms / 1000.0) diff --git a/backend/onyx/connectors/slack/utils.py b/backend/onyx/connectors/slack/utils.py index 4c9a0fe758..4e606d2292 100644 --- a/backend/onyx/connectors/slack/utils.py +++ b/backend/onyx/connectors/slack/utils.py @@ -206,7 +206,7 @@ class SlackTextCleaner: # message indexing path continues with the id in place of # the display name (ONYX-BACKEND-H6FN). logger.warning( - f"Error fetching data for user {user_id}: {e.response['error']}" + "Error fetching data for user %s: %s", user_id, e.response["error"] ) self._id_to_name_map[user_id] = user_id @@ -231,7 +231,9 @@ class SlackTextCleaner: # only fires on unexpected errors (e.g. malformed response); # still defensive, but not actionable per-message — warn. logger.warning( - f"Unable to replace user ID with username for user_id '{user_id}': {e}" + "Unable to replace user ID with username for user_id '%s': %s", + user_id, + e, ) return message diff --git a/backend/onyx/connectors/teams/connector.py b/backend/onyx/connectors/teams/connector.py index 87739e6944..822a5107c3 100644 --- a/backend/onyx/connectors/teams/connector.py +++ b/backend/onyx/connectors/teams/connector.py @@ -132,8 +132,9 @@ class TeamsConnector( try: # For validation, do a lightweight check instead of full team search logger.info( - f"Requested team count: {len(self.requested_team_list) if self.requested_team_list else 0}, " - f"Has special chars: {has_special_chars}" + "Requested team count: %s, Has special chars: %s", + len(self.requested_team_list) if self.requested_team_list else 0, + has_special_chars, ) validation_query = self.graph_client.teams.get().top(1) @@ -252,7 +253,9 @@ class TeamsConnector( yield channel_doc logger.info( - f"Processed team with id {todo_team_id}; {len(todos)} team(s) left to process" + "Processed team with id %s; %s team(s) left to process", + todo_team_id, + len(todos), ) return TeamsCheckpoint( @@ -288,7 +291,7 @@ class TeamsConnector( for team in teams: if not team.id: logger.warning( - f"Expected a team with an id, instead got no id: {team=}" + "Expected a team with an id, instead got no id: team=%r", team ) continue @@ -299,7 +302,8 @@ class TeamsConnector( for channel in channels: if not channel.id: logger.warning( - f"Expected a channel with an id, instead got no id: {channel=}" + "Expected a channel with an id, instead got no id: channel=%r", + channel, ) continue @@ -414,7 +418,7 @@ def _construct_semantic_identifier(channel: Channel, top_message: Message) -> st user_display_name if user_display_name else "Unknown User" ) else: - logger.warning(f"Message {top_message=} has no `from.user` field") + logger.warning("Message top_message=%r has no `from.user` field", top_message) top_message_user_name = "Unknown User" top_message_content = top_message.body.content or "" @@ -427,7 +431,9 @@ def _construct_semantic_identifier(channel: Channel, top_message: Message) -> st except Exception: logger.exception( - f"Error parsing snippet for message {top_message.id} with url {top_message.web_url}" + "Error parsing snippet for message %s with url %s", + top_message.id, + top_message.web_url, ) snippet = "" @@ -533,9 +539,8 @@ def _collect_all_teams( if problematic_names and not safe_names: # ALL requested teams have special characters - cannot use OData filtering logger.info( - f"All requested team names contain special characters (&, (, )) which require " - f"client-side filtering. Using basic /teams endpoint with pagination. " - f"Teams: {problematic_names}" + "All requested team names contain special characters (&, (, )) which require client-side filtering. Using basic /teams endpoint with pagination. Teams: %s", + problematic_names, ) # Use unfiltered query with pagination limit to avoid fetching too many teams use_client_side_filtering = True @@ -543,14 +548,15 @@ def _collect_all_teams( elif problematic_names and safe_names: # Mixed scenario - need to fetch more teams to find the problematic ones logger.info( - f"Mixed team types: will use client-side filtering for all. " - f"Safe names: {safe_names}, Special char names: {problematic_names}" + "Mixed team types: will use client-side filtering for all. Safe names: %s, Special char names: %s", + safe_names, + problematic_names, ) use_client_side_filtering = True odata_filter = None elif safe_names: # All names are safe - use OData filtering - logger.info(f"Using OData filtering for all requested teams: {safe_names}") + logger.info("Using OData filtering for all requested teams: %s", safe_names) use_client_side_filtering = False odata_filter = _build_simple_odata_filter(safe_names) else: @@ -584,7 +590,7 @@ def _collect_all_teams( # If OData filter fails, fall back to client-side filtering if not use_client_side_filtering and odata_filter: logger.warning( - f"OData filter failed: {e}. Falling back to client-side filtering." + "OData filter failed: %s. Falling back to client-side filtering.", e ) use_client_side_filtering = True odata_filter = None @@ -593,7 +599,7 @@ def _collect_all_teams( page_count = 0 continue # If client-side approach also fails, re-raise - logger.error(f"Teams query failed: {e}") + logger.error("Teams query failed: %s", e) raise filtered_teams = ( @@ -614,18 +620,21 @@ def _collect_all_teams( # Log progress every 10 pages to avoid excessive logging if page_count % 10 == 0: logger.info( - f"Searched {page_count} pages, found {len(found_team_names)} matching teams so far" + "Searched %s pages, found %s matching teams so far", + page_count, + len(found_team_names), ) # Stop if we found all requested teams or hit the page limit if requested_set.issubset(found_team_names): - logger.info(f"Found all requested teams after {page_count} pages") + logger.info("Found all requested teams after %s pages", page_count) break elif page_count >= max_pages: logger.warning( - f"Reached maximum page limit ({max_pages}) while searching for teams. " - f"Found: {found_team_names & requested_set}, " - f"Missing: {requested_set - found_team_names}" + "Reached maximum page limit (%s) while searching for teams. Found: %s, Missing: %s", + max_pages, + found_team_names & requested_set, + requested_set - found_team_names, ) break diff --git a/backend/onyx/connectors/teams/utils.py b/backend/onyx/connectors/teams/utils.py index ec6d91eeaa..0f1edea649 100644 --- a/backend/onyx/connectors/teams/utils.py +++ b/backend/onyx/connectors/teams/utils.py @@ -94,14 +94,14 @@ def _get_or_fetch_email( user_id = member.properties.get("userId") if not user_id: - logger.warning(f"No user-id found for this member; {member=}") + logger.warning("No user-id found for this member; member=%r", member) return None json_data = _retry(graph_client=graph_client, request_url=f"users/{user_id}") email = json_data.get("userPrincipalName") if not isinstance(email, str): - logger.warning(f"Expected email to be of type str, instead got {email=}") + logger.warning("Expected email to be of type str, instead got email=%r", email) return None return email @@ -172,12 +172,14 @@ def fetch_expert_infos( expert_infos = [] for member in members: if not member.display_name: - logger.warning(f"Failed to grab the display-name of {member=}; skipping") + logger.warning( + "Failed to grab the display-name of member=%r; skipping", member + ) continue email = _get_or_fetch_email(graph_client=graph_client, member=member) if not email: - logger.warning(f"Failed to grab the email of {member=}; skipping") + logger.warning("Failed to grab the email of member=%r; skipping", member) continue expert_infos.append( diff --git a/backend/onyx/connectors/testrail/connector.py b/backend/onyx/connectors/testrail/connector.py index 55fb3ab27c..6abcb58eb1 100644 --- a/backend/onyx/connectors/testrail/connector.py +++ b/backend/onyx/connectors/testrail/connector.py @@ -205,7 +205,7 @@ class TestRailConnector(LoadConnector, PollConnector): fields = self._api_get("get_case_fields") return fields if isinstance(fields, list) else [] except Exception as e: - logger.warning(f"Failed to fetch case fields from TestRail: {e}") + logger.warning("Failed to fetch case fields from TestRail: %s", e) return [] def _parse_items_string(self, items_str: str) -> dict[str, str]: @@ -263,7 +263,7 @@ class TestRailConnector(LoadConnector, PollConnector): ) except Exception as e: - logger.warning(f"Failed to build field maps from TestRail: {e}") + logger.warning("Failed to build field maps from TestRail: %s", e) return field_labels, value_maps @@ -454,7 +454,9 @@ class TestRailConnector(LoadConnector, PollConnector): full_text = "\n".join(text_lines) if len(full_text) > self.skip_doc_absolute_chars: logger.warning( - f"Skipping TestRail case {case_id} due to excessive size: {len(full_text)} chars" + "Skipping TestRail case %s due to excessive size: %s chars", + case_id, + len(full_text), ) return None diff --git a/backend/onyx/connectors/web/connector.py b/backend/onyx/connectors/web/connector.py index 302a25a3aa..fef47aa5b7 100644 --- a/backend/onyx/connectors/web/connector.py +++ b/backend/onyx/connectors/web/connector.py @@ -184,7 +184,7 @@ def check_internet_connection(url: str) -> None: # around this. if status_code == 403: logger.warning( - f"Received 403 Forbidden for {url}, will retry with browser automation" + "Received 403 Forbidden for %s, will retry with browser automation", url ) return @@ -438,10 +438,12 @@ def _handle_cookies(context: BrowserContext, url: str) -> None: try: context.add_cookies([cookie]) # ty: ignore[invalid-argument-type] except Exception as e: - logger.debug(f"Failed to add cookie {cookie['name']} for {domain}: {e}") + logger.debug( + "Failed to add cookie %s for %s: %s", cookie["name"], domain, e + ) except Exception: logger.exception( - f"Unexpected error while handling cookies for Web Connector with URL {url}" + "Unexpected error while handling cookies for Web Connector with URL %s", url ) @@ -600,12 +602,15 @@ class WebConnector(LoadConnector, SlimConnector): initial_url = final_url if initial_url in session_ctx.visited_links: logger.info( - f"{index}: {initial_url} redirected to {final_url} - already indexed" + "%s: %s redirected to %s - already indexed", + index, + initial_url, + final_url, ) page.close() return result - logger.info(f"{index}: {initial_url} redirected to {final_url}") + logger.info("%s: %s redirected to %s", index, initial_url, final_url) session_ctx.visited_links.add(initial_url) # If we got here, the request was successful @@ -640,7 +645,10 @@ class WebConnector(LoadConnector, SlimConnector): scroll_attempts += 1 except Exception as scroll_err: logger.warning( - f"{index}: auto-scroll skipped for {initial_url}: {scroll_err}" + "%s: auto-scroll skipped for %s: %s", + index, + initial_url, + scroll_err, ) content = page.content() @@ -677,7 +685,7 @@ class WebConnector(LoadConnector, SlimConnector): the code below can extract text from within these iframes. """ logger.debug( - f"{index}: Length of cleaned text {len(parsed_html.cleaned_text)}" + "%s: Length of cleaned text %s", index, len(parsed_html.cleaned_text) ) if JAVASCRIPT_DISABLED_MESSAGE in parsed_html.cleaned_text: iframe_count = page.frame_locator("iframe").locator("html").count() @@ -698,7 +706,7 @@ class WebConnector(LoadConnector, SlimConnector): hashed_text = hash((parsed_html.title, parsed_html.cleaned_text)) if hashed_text in session_ctx.content_hashes: logger.info( - f"{index}: Skipping duplicate title + content for {initial_url}" + "%s: Skipping duplicate title + content for %s", index, initial_url ) return result @@ -755,7 +763,7 @@ class WebConnector(LoadConnector, SlimConnector): index = len(session_ctx.visited_links) logger.info( - f"{index}: {'Slim-visiting' if slim else 'Visiting'} {initial_url}" + "%s: %s %s", index, "Slim-visiting" if slim else "Visiting", initial_url ) # Add retry mechanism with exponential backoff @@ -766,7 +774,11 @@ class WebConnector(LoadConnector, SlimConnector): # Add a random delay between retries (exponential backoff) delay = min(2**retry_count + random.uniform(0, 1), 10) logger.info( - f"Retry {retry_count}/{self.MAX_RETRIES} for {initial_url} after {delay:.2f}s delay" + "Retry %s/%s for %s after %ss delay", + retry_count, + self.MAX_RETRIES, + initial_url, + format(delay, ".2f"), ) time.sleep(delay) diff --git a/backend/onyx/connectors/xenforo/connector.py b/backend/onyx/connectors/xenforo/connector.py index 09c9e01ac2..159d0d110f 100644 --- a/backend/onyx/connectors/xenforo/connector.py +++ b/backend/onyx/connectors/xenforo/connector.py @@ -172,7 +172,9 @@ class XenforoConnector(LoadConnector): # Get all pages on thread_list_page for pre_count, thread_list_page in enumerate(pages, start=1): logger.info( - f"Getting pages from thread_list_page.. Current: {pre_count}/{len(pages)}\r" + "Getting pages from thread_list_page.. Current: %s/%s\r", + pre_count, + len(pages), ) all_threads += self.get_threads(thread_list_page) # If the URL contains "threads/", add the thread to the list. @@ -183,13 +185,17 @@ class XenforoConnector(LoadConnector): for thread_count, thread_url in enumerate(all_threads, start=1): soup = self.requestsite(thread_url) if soup is None: - logger.error(f"Failed to load page: {self.base_url}") + logger.error("Failed to load page: %s", self.base_url) continue pages = get_pages(soup, thread_url) # Getting all pages for all threads for page_index, page in enumerate(pages, start=1): logger.info( - f"Progress: Page {page_index}/{len(pages)} - Thread {thread_count}/{len(all_threads)}\r" + "Progress: Page %s/%s - Thread %s/%s\r", + page_index, + len(pages), + thread_count, + len(all_threads), ) soup_page = self.requestsite(page) doc_batch.extend( @@ -225,13 +231,16 @@ class XenforoConnector(LoadConnector): ) if response.status_code != 200: logger.error( - f"<{url}> Request Error: {response.status_code} - {response.reason}" + "<%s> Request Error: %s - %s", + url, + response.status_code, + response.reason, ) return BeautifulSoup(response.text, "html.parser") except TimeoutError: logger.error("Timed out Error.") except Exception as e: - logger.error(f"Error on {url}") + logger.error("Error on %s", url) logger.exception(e) return BeautifulSoup("", "html.parser") diff --git a/backend/onyx/connectors/zulip/connector.py b/backend/onyx/connectors/zulip/connector.py index 9eee0b6b1b..2c66dacb68 100644 --- a/backend/onyx/connectors/zulip/connector.py +++ b/backend/onyx/connectors/zulip/connector.py @@ -97,7 +97,7 @@ class ZulipConnector(LoadConnector, PollConnector): narrow_link = f"{self.base_url}#narrow/stream/{stream_operand}/topic/{topic_operand}/near/{m.id}" return narrow_link except Exception as e: - logger.error(f"Error generating Zulip message link: {e}") + logger.error("Error generating Zulip message link: %s", e) # Fallback to a basic link that at least includes the base URL return f"{self.base_url}#narrow/id/{m.id}" @@ -105,7 +105,7 @@ class ZulipConnector(LoadConnector, PollConnector): if self.client is None: raise ConnectorMissingCredentialError("Zulip") - logger.info(f"Fetching messages starting with anchor={anchor}") + logger.info("Fetching messages starting with anchor=%s", anchor) request = build_search_narrow( limit=INDEX_BATCH_SIZE, anchor=anchor, apply_md=False ) @@ -135,7 +135,9 @@ class ZulipConnector(LoadConnector, PollConnector): doc_time = edit_time if edit_time is not None else post_time except (ValueError, TypeError) as e: - logger.warning(f"Failed to parse timestamp for message {message.id}: {e}") + logger.warning( + "Failed to parse timestamp for message %s: %s", message.id, e + ) post_time = None edit_time = None doc_time = None diff --git a/backend/onyx/connectors/zulip/utils.py b/backend/onyx/connectors/zulip/utils.py index e189ed3154..76252d267c 100644 --- a/backend/onyx/connectors/zulip/utils.py +++ b/backend/onyx/connectors/zulip/utils.py @@ -37,7 +37,7 @@ def __call_with_retry(fun: Callable, *args: Any, **kwargs: Any) -> Dict[str, Any if result.get("result") == "error": if result.get("code") == "RATE_LIMIT_HIT": retry_after = float(result["retry-after"]) + 1 - logger.warning(f"Rate limit hit, retrying after {retry_after} seconds") + logger.warning("Rate limit hit, retrying after %s seconds", retry_after) time.sleep(retry_after) return __call_with_retry(fun, *args) return result diff --git a/backend/onyx/context/search/federated/slack_search.py b/backend/onyx/context/search/federated/slack_search.py index 3c0c3ba7e7..3dea4d7bb4 100644 --- a/backend/onyx/context/search/federated/slack_search.py +++ b/backend/onyx/context/search/federated/slack_search.py @@ -80,26 +80,26 @@ def fetch_and_cache_channel_metadata( try: cached = redis_client.get(cache_key) if cached: - logger.debug(f"Channel metadata cache HIT for team {team_id}") + logger.debug("Channel metadata cache HIT for team %s", team_id) cached_str: str = ( cached.decode("utf-8") if isinstance(cached, bytes) else str(cached) ) cached_data = cast(dict[str, ChannelMetadata], json.loads(cached_str)) - logger.debug(f"Loaded {len(cached_data)} channels from cache") + logger.debug("Loaded %s channels from cache", len(cached_data)) if not include_private: filtered: dict[str, ChannelMetadata] = { k: v for k, v in cached_data.items() if v.get("type") != ChannelType.PRIVATE_CHANNEL.value } - logger.debug(f"Filtered to {len(filtered)} channels (exclude private)") + logger.debug("Filtered to %s channels (exclude private)", len(filtered)) return filtered return cached_data except Exception as e: - logger.warning(f"Error reading from channel metadata cache: {e}") + logger.warning("Error reading from channel metadata cache: %s", e) # Cache miss - fetch from Slack API with retry logic - logger.debug(f"Channel metadata cache MISS for team {team_id} - fetching from API") + logger.debug("Channel metadata cache MISS for team %s - fetching from API", team_id) slack_client = WebClient(token=access_token) channel_metadata: dict[str, ChannelMetadata] = {} @@ -149,7 +149,7 @@ def fetch_and_cache_channel_metadata( if not cursor: break - logger.info(f"Fetched {channel_count} channels for team {team_id}") + logger.info("Fetched %s channels for team %s", channel_count, team_id) # Cache the results try: @@ -159,10 +159,14 @@ def fetch_and_cache_channel_metadata( ex=CHANNEL_METADATA_CACHE_TTL, ) logger.info( - f"Cached {channel_count} channels for team {team_id} (TTL: {CHANNEL_METADATA_CACHE_TTL}s, key: {cache_key})" + "Cached %s channels for team %s (TTL: %ss, key: %s)", + channel_count, + team_id, + CHANNEL_METADATA_CACHE_TTL, + cache_key, ) except Exception as e: - logger.warning(f"Error caching channel metadata: {e}") + logger.warning("Error caching channel metadata: %s", e) return channel_metadata @@ -189,8 +193,10 @@ def fetch_and_cache_channel_metadata( # Remove the problematic channel type and retry available_channel_types.remove(missing_channel_type) logger.warning( - f"Missing scope '{needed_scope}' for channel type '{missing_channel_type}'. " - f"Continuing with reduced channel types: {available_channel_types}" + "Missing scope '%s' for channel type '%s'. Continuing with reduced channel types: %s", + needed_scope, + missing_channel_type, + available_channel_types, ) # Don't count this as a retry attempt, just try again with fewer types if available_channel_types: # Only continue if we have types left @@ -198,27 +204,35 @@ def fetch_and_cache_channel_metadata( # Otherwise fall through to retry logic else: logger.error( - f"Missing scope '{needed_scope}' but could not map to channel type or already removed. " - f"Response: {e.response}" + "Missing scope '%s' but could not map to channel type or already removed. Response: %s", + needed_scope, + e.response, ) # For other errors, use retry logic if attempt < CHANNEL_METADATA_MAX_RETRIES - 1: retry_delay = CHANNEL_METADATA_RETRY_DELAY * (2**attempt) logger.warning( - f"Failed to fetch channel metadata (attempt {attempt + 1}/{CHANNEL_METADATA_MAX_RETRIES}): {e}. " - f"Retrying in {retry_delay}s..." + "Failed to fetch channel metadata (attempt %s/%s): %s. Retrying in %ss...", + attempt + 1, + CHANNEL_METADATA_MAX_RETRIES, + e, + retry_delay, ) time.sleep(retry_delay) else: logger.error( - f"Failed to fetch channel metadata after {CHANNEL_METADATA_MAX_RETRIES} attempts: {e}" + "Failed to fetch channel metadata after %s attempts: %s", + CHANNEL_METADATA_MAX_RETRIES, + e, ) # If we have some channel metadata despite errors, return it with a warning if channel_metadata: logger.warning( - f"Returning partial channel metadata ({len(channel_metadata)} channels) despite errors. Last error: {last_exception}" + "Returning partial channel metadata (%s channels) despite errors. Last error: %s", + len(channel_metadata), + last_exception, ) return channel_metadata @@ -262,7 +276,7 @@ def get_cached_user_profile( # Empty string means user was not found previously return cached_str if cached_str else None except Exception as e: - logger.debug(f"Error reading user profile cache: {e}") + logger.debug("Error reading user profile cache: %s", e) # Cache miss - fetch from Slack API slack_client = WebClient(token=access_token) @@ -280,7 +294,7 @@ def get_cached_user_profile( ex=USER_PROFILE_CACHE_TTL, ) except Exception as e: - logger.debug(f"Error caching user profile: {e}") + logger.debug("Error caching user profile: %s", e) return name @@ -288,14 +302,15 @@ def get_cached_user_profile( error_str = str(e) if "user_not_found" in error_str: logger.debug( - f"User {user_id} not found in Slack workspace (likely deleted/deactivated)" + "User %s not found in Slack workspace (likely deleted/deactivated)", + user_id, ) elif "ratelimited" in error_str: # Don't cache rate limit errors - we'll retry later - logger.debug(f"Rate limited fetching user {user_id}, will retry later") + logger.debug("Rate limited fetching user %s, will retry later", user_id) return None else: - logger.warning(f"Could not fetch profile for user {user_id}: {e}") + logger.warning("Could not fetch profile for user %s: %s", user_id, e) # Cache negative result to avoid repeated lookups for missing users try: @@ -405,7 +420,9 @@ def _should_skip_channel( return True except Exception as e: logger.warning( - f"Could not determine channel type for {channel_id}, filtering out: {e}" + "Could not determine channel type for %s, filtering out: %s", + channel_id, + e, ) return True return False @@ -439,13 +456,18 @@ def _fetch_thread_from_url( messages: list[dict[str, Any]] = response.get("messages", []) except SlackApiError as e: logger.warning( - f"Failed to fetch thread from URL (channel={channel_id}, ts={thread_ts}): {e}" + "Failed to fetch thread from URL (channel=%s, ts=%s): %s", + channel_id, + thread_ts, + e, ) return SlackQueryResult(messages=[], filtered_channels=[]) if not messages: logger.warning( - f"No messages found for URL override (channel={channel_id}, ts={thread_ts})" + "No messages found for URL override (channel=%s, ts=%s)", + channel_id, + thread_ts, ) return SlackQueryResult(messages=[], filtered_channels=[]) @@ -502,7 +524,10 @@ def _fetch_thread_from_url( ) logger.info( - f"URL override: fetched thread from channel={channel_id}, ts={thread_ts}, {len(messages)} messages" + "URL override: fetched thread from channel=%s, ts=%s, %s messages", + channel_id, + thread_ts, + len(messages), ) return SlackQueryResult(messages=[slack_message], filtered_channels=[]) @@ -536,7 +561,7 @@ def query_slack( # Add channel filter to query final_query = f"{query_string} {channel_filter}" - logger.info(f"Final query to slack: {final_query}") + logger.info("Final query to slack: %s", final_query) # Detect if query asks for most recent results sort_by_time = is_recency_query(query_string) @@ -560,16 +585,18 @@ def query_slack( messages: dict[str, Any] = response.get("messages", {}) matches: list[dict[str, Any]] = messages.get("matches", []) - logger.info(f"Slack search found {len(matches)} messages") + logger.info("Slack search found %s messages", len(matches)) except SlackApiError as slack_error: - logger.error(f"Slack API error in search_messages: {slack_error}") + logger.error("Slack API error in search_messages: %s", slack_error) logger.error( - f"Slack API error details: status={slack_error.response.status_code}, error={slack_error.response.get('error')}" + "Slack API error details: status=%s, error=%s", + slack_error.response.status_code, + slack_error.response.get("error"), ) if "not_allowed_token_type" in str(slack_error): # Log token type prefix token_prefix = access_token[:4] if len(access_token) >= 4 else "unknown" - logger.error(f"TOKEN TYPE ERROR: access_token type: {token_prefix}...") + logger.error("TOKEN TYPE ERROR: access_token type: %s...", token_prefix) return SlackQueryResult(messages=[], filtered_channels=[]) # convert matches to slack messages @@ -765,15 +792,17 @@ def _fetch_thread_context( # Check for rate limit error specifically if e.response and e.response.status_code == 429: logger.warning( - f"Slack rate limit hit while fetching thread context for {channel_id}/{thread_id}" + "Slack rate limit hit while fetching thread context for %s/%s", + channel_id, + thread_id, ) return ThreadContextResult.rate_limited(message.text) # For other Slack errors, log and return original text - logger.error(f"Slack API error in thread context fetch: {e}") + logger.error("Slack API error in thread context fetch: %s", e) return ThreadContextResult.error(message.text) except Exception as e: # Network errors, timeouts, etc - treat as recoverable error - logger.error(f"Unexpected error in thread context fetch: {e}") + logger.error("Unexpected error in thread context fetch: %s", e) return ThreadContextResult.error(message.text) # If empty response or single message (not a thread), return original text @@ -804,7 +833,7 @@ def _build_thread_text( if not replies: return thread_text - logger.debug(f"Thread {messages[0].get('ts')}: {len(replies)} replies included") + logger.debug("Thread %s: %s replies included", messages[0].get("ts"), len(replies)) thread_text += "\n\nReplies:" for msg in replies: @@ -829,10 +858,10 @@ def _build_thread_text( except SlackApiError as e: if "user_not_found" in str(e): logger.debug( - f"User {userid} not found (likely deleted/deactivated)" + "User %s not found (likely deleted/deactivated)", userid ) else: - logger.warning(f"Could not fetch profile for user {userid}: {e}") + logger.warning("Could not fetch profile for user %s: %s", userid, e) continue if not user_name: continue @@ -878,8 +907,11 @@ def fetch_thread_contexts_with_rate_limit_handling( messages_without_context = [] logger.info( - f"Fetching thread context for {len(messages_for_context)} of {len(slack_messages)} messages " - f"(batch_size={batch_size}, max={max_messages or 'unlimited'})" + "Fetching thread context for %s of %s messages (batch_size=%s, max=%s)", + len(messages_for_context), + len(slack_messages), + batch_size, + max_messages or "unlimited", ) results: list[str] = [] @@ -896,9 +928,11 @@ def fetch_thread_contexts_with_rate_limit_handling( remaining = messages_for_context[i:] skipped_batches = total_batches - rate_limit_batch logger.warning( - f"Slack rate limit: skipping {len(remaining)} remaining messages " - f"({skipped_batches} of {total_batches} batches). " - f"Successfully enriched {len(results)} messages before rate limit." + "Slack rate limit: skipping %s remaining messages (%s of %s batches). Successfully enriched %s messages before rate limit.", + len(remaining), + skipped_batches, + total_batches, + len(results), ) results.extend([msg.text for msg in remaining]) break @@ -925,7 +959,7 @@ def fetch_thread_contexts_with_rate_limit_handling( for j, result in enumerate(batch_results): if result is None: # Unexpected exception (shouldn't happen) - use original text, stop - logger.error(f"Unexpected None result for message {j} in batch") + logger.error("Unexpected None result for message %s in batch", j) results.append(batch[j].text) rate_limited = True rate_limit_batch = current_batch @@ -940,8 +974,9 @@ def fetch_thread_contexts_with_rate_limit_handling( if rate_limited: logger.warning( - f"Slack rate limit (429) hit at batch {current_batch}/{total_batches} " - f"while fetching thread context. Stopping further API calls." + "Slack rate limit (429) hit at batch %s/%s while fetching thread context. Stopping further API calls.", + current_batch, + total_batches, ) # Add original text for messages we didn't fetch context for @@ -1002,7 +1037,7 @@ def slack_retrieval( if not entities: logger.debug("No entity configuration found, using defaults") else: - logger.debug(f"Using entity configuration: {entities}") + logger.debug("Using entity configuration: %s", entities) # Extract limit from entity config if not explicitly provided query_limit = limit @@ -1011,9 +1046,11 @@ def slack_retrieval( parsed_entities = SlackEntities(**entities) if limit is None: query_limit = parsed_entities.max_messages_per_query - logger.debug(f"Using max_messages_per_query from config: {query_limit}") + logger.debug( + "Using max_messages_per_query from config: %s", query_limit + ) except Exception as e: - logger.warning(f"Error parsing entities for limit: {e}") + logger.warning("Error parsing entities for limit: %s", e) if limit is None: query_limit = 100 # Fallback default elif limit is None: @@ -1059,7 +1096,8 @@ def slack_retrieval( if channel_type == ChannelType.PRIVATE_CHANNEL: allowed_private_channel = slack_event_context.channel_id logger.debug( - f"Private channel context: will only allow messages from {allowed_private_channel} + public channels" + "Private channel context: will only allow messages from %s + public channels", + allowed_private_channel, ) # Build search tasks — direct thread fetches + keyword searches diff --git a/backend/onyx/context/search/federated/slack_search_utils.py b/backend/onyx/context/search/federated/slack_search_utils.py index 66c7e543cb..a81bf51ac1 100644 --- a/backend/onyx/context/search/federated/slack_search_utils.py +++ b/backend/onyx/context/search/federated/slack_search_utils.py @@ -214,37 +214,44 @@ def extract_date_range_from_query( data = json.loads(response_clean) if not isinstance(data, dict): logger.debug( - f"LLM date extraction returned non-dict response for query: " - f"'{query}', using default: {default_search_days} days" + "LLM date extraction returned non-dict response for query: '%s', using default: %s days", + query, + default_search_days, ) return default_search_days days_back = data.get("days_back") if days_back is None: logger.debug( - f"LLM date extraction returned null for query: '{query}', using default: {default_search_days} days" + "LLM date extraction returned null for query: '%s', using default: %s days", + query, + default_search_days, ) return default_search_days if not isinstance(days_back, (int, float)): logger.debug( - f"LLM date extraction returned non-numeric days_back for " - f"query: '{query}', using default: {default_search_days} days" + "LLM date extraction returned non-numeric days_back for query: '%s', using default: %s days", + query, + default_search_days, ) return default_search_days except json.JSONDecodeError: logger.debug( - f"Failed to parse LLM date extraction response for query: '{query}' " - f"(response: '{response_clean}'), " - f"using default: {default_search_days} days" + "Failed to parse LLM date extraction response for query: '%s' (response: '%s'), using default: %s days", + query, + response_clean, + default_search_days, ) return default_search_days return min(int(days_back), default_search_days) except Exception as e: - logger.warning(f"Error extracting date range with LLM for query '{query}': {e}") + logger.warning( + "Error extracting date range with LLM for query '%s': %s", query, e + ) return default_search_days @@ -625,7 +632,7 @@ def expand_query_with_llm(query_text: str, llm: LLM) -> list[str]: # Log if we filtered out garbage if len(raw_queries) != len(rephrased_queries): filtered_out = set(raw_queries) - set(rephrased_queries) - logger.warning(f"Filtered out non-keyword LLM responses: {filtered_out}") + logger.warning("Filtered out non-keyword LLM responses: %s", filtered_out) # If no queries generated, use empty query if not rephrased_queries: @@ -633,12 +640,14 @@ def expand_query_with_llm(query_text: str, llm: LLM) -> list[str]: return [""] logger.debug( - f"Expanded query into {len(rephrased_queries)} queries: {rephrased_queries}" + "Expanded query into %s queries: %s", + len(rephrased_queries), + rephrased_queries, ) return rephrased_queries[:MAX_SLACK_QUERY_EXPANSIONS] except Exception as e: - logger.error(f"Error expanding query: {e}") + logger.error("Error expanding query: %s", e) return [query_text] @@ -681,7 +690,7 @@ def build_slack_queries( parsed_entities = SlackEntities(**entities) default_search_days = parsed_entities.default_search_days except ValidationError as e: - logger.warning(f"Invalid entities in build_slack_queries: {e}") + logger.warning("Invalid entities in build_slack_queries: %s", e) days_back = extract_date_range_from_query( query=query.query, @@ -705,7 +714,7 @@ def build_slack_queries( url_fetches.append( DirectThreadFetch(channel_id=channel_id, thread_ts=thread_ts) ) - logger.info(f"Detected Slack URL: channel={channel_id}, ts={thread_ts}") + logger.info("Detected Slack URL: channel=%s, ts=%s", channel_id, thread_ts) # ALWAYS extract channel references from the query (not just for recency queries) channel_references = extract_channel_references_from_query(query.query) @@ -718,7 +727,7 @@ def build_slack_queries( channel_references, entities, available_channels ) logger.info( - f"Detected and validated channel references: {channel_references}" + "Detected and validated channel references: %s", channel_references ) # If valid channels detected, use ONLY those channels with NO keywords @@ -728,7 +737,7 @@ def build_slack_queries( ] except ValueError as e: # If validation fails, log the error and continue with normal flow - logger.warning(f"Channel reference validation failed: {e}") + logger.warning("Channel reference validation failed: %s", e) channel_references = set() # use llm to generate slack queries (use original query to use same keywords as the user) diff --git a/backend/onyx/context/search/retrieval/search_runner.py b/backend/onyx/context/search/retrieval/search_runner.py index 555824bf1b..6ca527e938 100644 --- a/backend/onyx/context/search/retrieval/search_runner.py +++ b/backend/onyx/context/search/retrieval/search_runner.py @@ -173,7 +173,9 @@ def search_chunks( if not top_chunks: logger.debug( - f"Search returned no results for query: {query_request.query} with filters: {query_request.filters}." + "Search returned no results for query: %s with filters: %s.", + query_request.query, + query_request.filters, ) return top_chunks diff --git a/backend/onyx/db/chat.py b/backend/onyx/db/chat.py index 5aa2fc24c7..1c76c0deb6 100644 --- a/backend/onyx/db/chat.py +++ b/backend/onyx/db/chat.py @@ -416,7 +416,8 @@ def get_chat_message( if expected_user_id != user_id: logger.error( - f"User {user_id} tried to fetch a chat message that does not belong to them" + "User %s tried to fetch a chat message that does not belong to them", + user_id, ) raise ValueError("Chat message does not belong to user") diff --git a/backend/onyx/db/connector.py b/backend/onyx/db/connector.py index f5d4a524cf..e8ce0d7130 100644 --- a/backend/onyx/db/connector.py +++ b/backend/onyx/db/connector.py @@ -400,5 +400,5 @@ def get_kg_enabled_connectors(db_session: Session) -> list[KGConnectorData]: return connector_results except Exception as e: - logger.error(f"Error fetching unprocessed connector IDs: {str(e)}") + logger.error("Error fetching unprocessed connector IDs: %s", str(e)) raise e diff --git a/backend/onyx/db/connector_credential_pair.py b/backend/onyx/db/connector_credential_pair.py index 13bf5dccf2..e602da9aca 100644 --- a/backend/onyx/db/connector_credential_pair.py +++ b/backend/onyx/db/connector_credential_pair.py @@ -397,7 +397,8 @@ def update_connector_credential_pair_from_id( ) if not cc_pair: logger.warning( - f"Attempted to update pair for Connector Credential Pair '{cc_pair_id}' but it does not exist" + "Attempted to update pair for Connector Credential Pair '%s' but it does not exist", + cc_pair_id, ) return @@ -425,7 +426,9 @@ def update_connector_credential_pair( ) if not cc_pair: logger.warning( - f"Attempted to update pair for connector id {connector_id} and credential id {credential_id}" + "Attempted to update pair for connector id %s and credential id %s", + connector_id, + credential_id, ) return diff --git a/backend/onyx/db/credentials.py b/backend/onyx/db/credentials.py index 86392f9789..4ac361566f 100644 --- a/backend/onyx/db/credentials.py +++ b/backend/onyx/db/credentials.py @@ -386,7 +386,7 @@ def _delete_credential_internal( if associated_connectors or associated_doc_cc_pairs: if force: logger.warning( - f"Force deleting credential {credential_id} and its associated records" + "Force deleting credential %s and its associated records", credential_id ) # Delete DocumentByConnectorCredentialPair records first @@ -406,9 +406,9 @@ def _delete_credential_internal( ) if force: - logger.warning(f"Force deleting credential {credential_id}") + logger.warning("Force deleting credential %s", credential_id) else: - logger.notice(f"Deleting credential {credential_id}") + logger.notice("Deleting credential %s", credential_id) _cleanup_credential__user_group_relationships__no_commit(db_session, credential_id) db_session.delete(credential) diff --git a/backend/onyx/db/discord_bot.py b/backend/onyx/db/discord_bot.py index b65f7c9909..6c20efb737 100644 --- a/backend/onyx/db/discord_bot.py +++ b/backend/onyx/db/discord_bot.py @@ -100,7 +100,8 @@ def get_or_create_discord_service_api_key( # Database only stores the hash, so we must regenerate to get the raw key. # This is safe since the Discord bot is the only consumer of this key. logger.debug( - f"Found existing Discord service API key for tenant {tenant_id} that isn't in cache, regenerating to update cache" + "Found existing Discord service API key for tenant %s that isn't in cache, regenerating to update cache", + tenant_id, ) new_api_key = generate_api_key(tenant_id) existing.hashed_api_key = hash_api_key(new_api_key) @@ -109,7 +110,7 @@ def get_or_create_discord_service_api_key( return new_api_key # Create new API key - logger.info(f"Creating Discord service API key for tenant {tenant_id}") + logger.info("Creating Discord service API key for tenant %s", tenant_id) api_key_args = APIKeyArgs( name=DISCORD_SERVICE_API_KEY_NAME, role=UserRole.LIMITED, # Limited role is sufficient for chat requests diff --git a/backend/onyx/db/document.py b/backend/onyx/db/document.py index 346159bb9d..e7e5692869 100644 --- a/backend/onyx/db/document.py +++ b/backend/onyx/db/document.py @@ -1120,7 +1120,9 @@ def prepare_to_modify_documents( break except OperationalError as e: logger.warning( - f"Failed to acquire locks for documents on attempt {i}, retrying. Error: {e}" + "Failed to acquire locks for documents on attempt %s, retrying. Error: %s", + i, + e, ) time.sleep(retry_delay) diff --git a/backend/onyx/db/document_set.py b/backend/onyx/db/document_set.py index 4629c3ee23..fcea055e6c 100644 --- a/backend/onyx/db/document_set.py +++ b/backend/onyx/db/document_set.py @@ -307,7 +307,7 @@ def insert_document_set( db_session.commit() except Exception as e: db_session.rollback() - logger.error(f"Error creating document set: {e}") + logger.error("Error creating document set: %s", e) raise return new_document_set_row, ds_cc_pairs diff --git a/backend/onyx/db/engine/sql_engine.py b/backend/onyx/db/engine/sql_engine.py index 0bf18a1285..11ab5f946d 100644 --- a/backend/onyx/db/engine/sql_engine.py +++ b/backend/onyx/db/engine/sql_engine.py @@ -108,7 +108,9 @@ if LOG_POSTGRES_LATENCY: total_time = time.time() - conn.info["query_start_time"] if total_time > 0.1: logger.debug( - f"Query Complete: {statement}\n\nTotal Time: {total_time:.4f} seconds" + "Query Complete: %s\n\nTotal Time: %s seconds", + statement, + format(total_time, ".4f"), ) @@ -128,17 +130,21 @@ if LOG_POSTGRES_CONN_COUNTS: pool_size = connection_proxy._pool.size() logger.debug( "Connection Checkout\n" - f"Active Connections: {active_connections};\n" - f"Idle: {idle_connections};\n" - f"Pool Size: {pool_size};\n" - f"Total connection checkouts: {checkout_count}" + "Active Connections: %s;\n" + "Idle: %s;\n" + "Pool Size: %s;\n" + "Total connection checkouts: %s", + active_connections, + idle_connections, + pool_size, + checkout_count, ) @event.listens_for(Engine, "checkin") def log_checkin(dbapi_connection, connection_record): # noqa: ARG001 global checkin_count checkin_count += 1 - logger.debug(f"Total connection checkins: {checkin_count}") + logger.debug("Total connection checkins: %s", checkin_count) class SqlEngine: @@ -201,7 +207,7 @@ class SqlEngine: # any passed in kwargs override the defaults final_engine_kwargs.update(extra_engine_kwargs) - logger.info(f"Creating engine with kwargs: {final_engine_kwargs}") + logger.info("Creating engine with kwargs: %s", final_engine_kwargs) # echo=True here for inspecting all emitted db queries engine = create_engine(connection_string, **final_engine_kwargs) @@ -261,7 +267,7 @@ class SqlEngine: # any passed in kwargs override the defaults final_engine_kwargs.update(extra_engine_kwargs) - logger.info(f"Creating engine with kwargs: {final_engine_kwargs}") + logger.info("Creating engine with kwargs: %s", final_engine_kwargs) # echo=True here for inspecting all emitted db queries engine = create_engine(connection_string, **final_engine_kwargs) diff --git a/backend/onyx/db/federated.py b/backend/onyx/db/federated.py index 08f5628ecf..6f12545e27 100644 --- a/backend/onyx/db/federated.py +++ b/backend/onyx/db/federated.py @@ -56,7 +56,7 @@ def validate_federated_connector_credentials( get_federated_connector(source, credentials) return True except Exception as e: - logger.error(f"Error validating credentials for source {source}: {e}") + logger.error("Error validating credentials for source %s: %s", source, e) return False diff --git a/backend/onyx/db/hierarchy.py b/backend/onyx/db/hierarchy.py index 0f0f05cc62..560f572e06 100644 --- a/backend/onyx/db/hierarchy.py +++ b/backend/onyx/db/hierarchy.py @@ -126,7 +126,10 @@ def ensure_source_node_exists( db_session.commit() logger.info( - f"Created SOURCE hierarchy node for {source.value}: id={source_node.id}, display_name={display_name}" + "Created SOURCE hierarchy node for %s: id=%s, display_name=%s", + source.value, + source_node.id, + display_name, ) return source_node @@ -154,7 +157,9 @@ def resolve_parent_hierarchy_node_id( # Parent not found - fall back to SOURCE node logger.warning( - f"Parent hierarchy node not found: raw_id={raw_parent_id}, source={source}. Falling back to SOURCE node." + "Parent hierarchy node not found: raw_id=%s, source=%s. Falling back to SOURCE node.", + raw_parent_id, + source, ) source_node = get_source_hierarchy_node(db_session, source) return source_node.id if source_node else None @@ -392,7 +397,9 @@ def link_hierarchy_nodes_to_documents( if nodes_to_update: logger.debug( - f"Linked {len(nodes_to_update)} hierarchy nodes to documents for source {source.value}" + "Linked %s hierarchy nodes to documents for source %s", + len(nodes_to_update), + source.value, ) return len(nodes_to_update) @@ -606,7 +613,9 @@ def update_hierarchy_node_permissions( if not existing_node: logger.warning( - f"Hierarchy node not found for permission update: raw_node_id={raw_node_id}, source={source}" + "Hierarchy node not found for permission update: raw_node_id=%s, source=%s", + raw_node_id, + source, ) return False diff --git a/backend/onyx/db/image_generation.py b/backend/onyx/db/image_generation.py index 5cd50a2b6f..748b717262 100644 --- a/backend/onyx/db/image_generation.py +++ b/backend/onyx/db/image_generation.py @@ -233,13 +233,13 @@ def create_default_image_gen_config_from_api_key( db_session.commit() - logger.info(f"Created default image generation config: {image_provider_id}") + logger.info("Created default image generation config: %s", image_provider_id) return config except Exception: db_session.rollback() logger.exception( - f"Failed to create default image generation config {image_provider_id}" + "Failed to create default image generation config %s", image_provider_id ) return None diff --git a/backend/onyx/db/index_attempt_metrics.py b/backend/onyx/db/index_attempt_metrics.py index b82309e9b9..38a1c98984 100644 --- a/backend/onyx/db/index_attempt_metrics.py +++ b/backend/onyx/db/index_attempt_metrics.py @@ -190,8 +190,9 @@ def safe_record_single_event( ) except Exception: logger.exception( - f"Failed to record stage event {stage.value} " - f"for attempt {index_attempt_id}" + "Failed to record stage event %s for attempt %s", + stage.value, + index_attempt_id, ) @@ -260,8 +261,9 @@ def time_stage( ) except Exception: logger.exception( - f"Failed to record stage event {stage.value} " - f"for attempt {index_attempt_id}" + "Failed to record stage event %s for attempt %s", + stage.value, + index_attempt_id, ) @@ -353,8 +355,9 @@ class StageEventBuffer: ) except Exception: logger.exception( - f"Failed to flush StageEventBuffer for " - f"{self.stage.value} / attempt {self.index_attempt_id}" + "Failed to flush StageEventBuffer for %s / attempt %s", + self.stage.value, + self.index_attempt_id, ) def _reset(self) -> None: diff --git a/backend/onyx/db/indexing_coordination.py b/backend/onyx/db/indexing_coordination.py index 6579eae84b..6010a96ee1 100644 --- a/backend/onyx/db/indexing_coordination.py +++ b/backend/onyx/db/indexing_coordination.py @@ -65,10 +65,10 @@ class IndexingCoordination: if existing_attempt: logger.info( - f"Indexing already in progress: " - f"cc_pair={cc_pair_id} " - f"search_settings={search_settings_id} " - f"existing_attempt={existing_attempt[0].id}" + "Indexing already in progress: cc_pair=%s search_settings=%s existing_attempt=%s", + cc_pair_id, + search_settings_id, + existing_attempt[0].id, ) return None @@ -82,21 +82,21 @@ class IndexingCoordination: ) logger.info( - f"Created Index Attempt: " - f"cc_pair={cc_pair_id} " - f"search_settings={search_settings_id} " - f"attempt_id={attempt_id} " - f"celery_task_id={celery_task_id}" + "Created Index Attempt: cc_pair=%s search_settings=%s attempt_id=%s celery_task_id=%s", + cc_pair_id, + search_settings_id, + attempt_id, + celery_task_id, ) return attempt_id except SQLAlchemyError as e: logger.info( - f"Failed to create index attempt (likely race condition): " - f"cc_pair={cc_pair_id} " - f"search_settings={search_settings_id} " - f"error={str(e)}" + "Failed to create index attempt (likely race condition): cc_pair=%s search_settings=%s error=%s", + cc_pair_id, + search_settings_id, + str(e), ) db_session.rollback() return None @@ -127,7 +127,7 @@ class IndexingCoordination: attempt.cancellation_requested = True db_session.commit() - logger.info(f"Requested cancellation for attempt {index_attempt_id}") + logger.info("Requested cancellation for attempt %s", index_attempt_id) @staticmethod def set_total_batches( @@ -145,7 +145,9 @@ class IndexingCoordination: db_session.commit() logger.info( - f"Set total batches: attempt={index_attempt_id} total={total_batches}" + "Set total batches: attempt=%s total=%s", + index_attempt_id, + total_batches, ) @staticmethod @@ -183,11 +185,11 @@ class IndexingCoordination: db_session.commit() logger.info( - f"Updated batch completion: " - f"attempt={index_attempt_id} " - f"completed={attempt.completed_batches} " - f"total={attempt.total_batches} " - f"docs={total_docs_indexed} " + "Updated batch completion: attempt=%s completed=%s total=%s docs=%s ", + index_attempt_id, + attempt.completed_batches, + attempt.total_batches, + total_docs_indexed, ) return attempt.completed_batches, attempt.total_batches @@ -195,7 +197,7 @@ class IndexingCoordination: except Exception: db_session.rollback() logger.exception( - f"Failed to update batch completion for attempt {index_attempt_id}" + "Failed to update batch completion for attempt %s", index_attempt_id ) raise @@ -276,7 +278,7 @@ class IndexingCoordination: attempt = get_index_attempt(db_session, index_attempt_id) if not attempt: - logger.error(f"Index attempt {index_attempt_id} not found in database") + logger.error("Index attempt %s not found in database", index_attempt_id) return False current_time = get_db_current_time(db_session) diff --git a/backend/onyx/db/kg_config.py b/backend/onyx/db/kg_config.py index e01c5199ac..2d5266f034 100644 --- a/backend/onyx/db/kg_config.py +++ b/backend/onyx/db/kg_config.py @@ -21,10 +21,10 @@ def get_kg_config_settings() -> KGConfigSettings: return KGConfigSettings.model_validate(stored_config or {}) except KvKeyNotFoundError: # Default to empty kg config if no config have been set yet - logger.debug(f"No kg config found in KV store for key: {KV_KG_CONFIG_KEY}") + logger.debug("No kg config found in KV store for key: %s", KV_KG_CONFIG_KEY) return KGConfigSettings() except Exception as e: - logger.error(f"Error loading kg config from KV store: {str(e)}") + logger.error("Error loading kg config from KV store: %s", str(e)) return KGConfigSettings() diff --git a/backend/onyx/db/mcp.py b/backend/onyx/db/mcp.py index 46b495f5dd..571ea8535b 100644 --- a/backend/onyx/db/mcp.py +++ b/backend/onyx/db/mcp.py @@ -167,12 +167,14 @@ def delete_mcp_server(server_id: int, db_session: Session) -> None: # Count tools that will be deleted tools_count = db_session.query(Tool).filter(Tool.mcp_server_id == server_id).count() - logger.info(f"Deleting MCP server {server_id} with {tools_count} associated tools") + logger.info( + "Deleting MCP server %s with %s associated tools", server_id, tools_count + ) db_session.delete(server) db_session.commit() - logger.info(f"Successfully deleted MCP server {server_id} and its tools") + logger.info("Successfully deleted MCP server %s and its tools", server_id) def get_all_mcp_tools_for_server(server_id: int, db_session: Session) -> list[Tool]: diff --git a/backend/onyx/db/models.py b/backend/onyx/db/models.py index 6b8b679eb9..73764a2b27 100644 --- a/backend/onyx/db/models.py +++ b/backend/onyx/db/models.py @@ -273,7 +273,7 @@ class NullFilteredString(TypeDecorator): dialect: Dialect, # noqa: ARG002 ) -> str | None: if value is not None and "\x00" in value: - logger.warning(f"NUL characters found in value: {value}") + logger.warning("NUL characters found in value: %s", value) return value.replace("\x00", "") return value diff --git a/backend/onyx/db/pat.py b/backend/onyx/db/pat.py index 855086b9bf..c7abd53a73 100644 --- a/backend/onyx/db/pat.py +++ b/backend/onyx/db/pat.py @@ -79,7 +79,7 @@ def _schedule_pat_last_used_update(hashed_token: str, now: datetime) -> None: ) await session.commit() except Exception as e: - logger.warning(f"Failed to update last_used_at for PAT: {e}") + logger.warning("Failed to update last_used_at for PAT: %s", e) asyncio.create_task(_update()) diff --git a/backend/onyx/db/projects.py b/backend/onyx/db/projects.py index fe6ed96f42..7ace2c7fcc 100644 --- a/backend/onyx/db/projects.py +++ b/backend/onyx/db/projects.py @@ -143,7 +143,7 @@ def upload_files_to_user_files_with_indexing( tenant_id = get_current_tenant_id() for rejected_file in rejected_files: logger.warning( - f"File {rejected_file.filename} rejected for {rejected_file.reason}" + "File %s rejected for %s", rejected_file.filename, rejected_file.reason ) if DISABLE_VECTOR_DB and background_tasks is not None: @@ -151,7 +151,9 @@ def upload_files_to_user_files_with_indexing( background_tasks.add_task(drain_processing_loop, tenant_id) for user_file in indexable_files: - logger.info(f"Queued in-process processing for user_file_id={user_file.id}") + logger.info( + "Queued in-process processing for user_file_id=%s", user_file.id + ) else: from onyx.background.celery.versioned_apps.client import app as client_app @@ -164,7 +166,9 @@ def upload_files_to_user_files_with_indexing( expires=CELERY_USER_FILE_PROCESSING_TASK_EXPIRES, ) logger.info( - f"Triggered indexing for user_file_id={user_file.id} with task_id={task.id}" + "Triggered indexing for user_file_id=%s with task_id=%s", + user_file.id, + task.id, ) return CategorizedFilesResult( diff --git a/backend/onyx/db/release_notes.py b/backend/onyx/db/release_notes.py index 36e03e334e..79c56f891c 100644 --- a/backend/onyx/db/release_notes.py +++ b/backend/onyx/db/release_notes.py @@ -91,7 +91,10 @@ def create_release_notifications_for_versions( total_created += created_count logger.debug( - f"Created {created_count} release notes notifications (version {entry.version}, {len(user_ids)} eligible users)" + "Created %s release notes notifications (version %s, %s eligible users)", + created_count, + entry.version, + len(user_ids), ) return total_created diff --git a/backend/onyx/db/rotate_encryption_key.py b/backend/onyx/db/rotate_encryption_key.py index 37a79cc079..8a50a66e80 100644 --- a/backend/onyx/db/rotate_encryption_key.py +++ b/backend/onyx/db/rotate_encryption_key.py @@ -128,7 +128,11 @@ def rotate_encryption_key( except (ValueError, UnicodeDecodeError) as e: pk_vals = [row[i] for i in range(len(pk_names))] logger.warning( - f"Could not decrypt/parse {table_name}.{col_name} row {pk_vals} — skipping: {e}" + "Could not decrypt/parse %s.%s row %s — skipping: %s", + table_name, + col_name, + pk_vals, + e, ) continue @@ -152,7 +156,11 @@ def rotate_encryption_key( if reencrypted > 0: totals[f"{table_name}.{col_name}"] = reencrypted logger.info( - f"{'[DRY RUN] Would re-encrypt' if dry_run else 'Re-encrypted'} {reencrypted} value(s) in {table_name}.{col_name}" + "%s %s value(s) in %s.%s", + "[DRY RUN] Would re-encrypt" if dry_run else "Re-encrypted", + reencrypted, + table_name, + col_name, ) return totals diff --git a/backend/onyx/db/seeding/chat_history_seeding.py b/backend/onyx/db/seeding/chat_history_seeding.py index a53285633a..6f081897cc 100644 --- a/backend/onyx/db/seeding/chat_history_seeding.py +++ b/backend/onyx/db/seeding/chat_history_seeding.py @@ -32,16 +32,16 @@ def seed_chat_history( persona_id: optional persona/assistant to associate with sessions """ with get_session_with_current_tenant() as db_session: - logger.info(f"Seeding {num_sessions} sessions.") + logger.info("Seeding %s sessions.", num_sessions) for y in range(0, num_sessions): create_chat_session(db_session, f"pytest_session_{y}", user_id, persona_id) # randomize all session times - logger.info(f"Seeding {num_messages} messages per session.") + logger.info("Seeding %s messages per session.", num_messages) rows = db_session.query(ChatSession).all() for x in range(0, len(rows)): if x % 1024 == 0: - logger.info(f"Seeded messages for {x} sessions so far.") + logger.info("Seeded messages for %s sessions so far.", x) row = rows[x] row.time_created = datetime.now(tz=timezone.utc) - timedelta( @@ -89,4 +89,4 @@ def seed_chat_history( db_session.commit() - logger.info(f"Seeded messages for {len(rows)} sessions. Finished.") + logger.info("Seeded messages for %s sessions. Finished.", len(rows)) diff --git a/backend/onyx/db/swap_index.py b/backend/onyx/db/swap_index.py index 8b692b5559..4f11ab51d5 100644 --- a/backend/onyx/db/swap_index.py +++ b/backend/onyx/db/swap_index.py @@ -91,7 +91,7 @@ def _perform_index_swap( contextual_rag_llm_name=new_search_settings.contextual_rag_llm_name, ) except ValueError as e: - logger.error(f"Model not found, defaulting to no contextual model: {e}") + logger.error("Model not found, defaulting to no contextual model: %s", e) update_no_default_contextual_rag_provider( db_session=db_session, ) @@ -111,7 +111,10 @@ def _perform_index_swap( for x in range(VESPA_NUM_ATTEMPTS_ON_STARTUP): try: logger.notice( - f"Document index {document_index.__class__.__name__} swap (attempt {x + 1}/{VESPA_NUM_ATTEMPTS_ON_STARTUP})..." + "Document index %s swap (attempt %s/%s)...", + document_index.__class__.__name__, + x + 1, + VESPA_NUM_ATTEMPTS_ON_STARTUP, ) document_index.ensure_indices_exist( primary_embedding_dim=new_search_settings.final_embedding_dim, @@ -126,15 +129,17 @@ def _perform_index_swap( break except Exception: logger.exception( - f"Document index swap for {document_index.__class__.__name__} did not succeed. " - f"The document index services may not be ready yet. Retrying in {WAIT_SECONDS} seconds." + "Document index swap for %s did not succeed. The document index services may not be ready yet. Retrying in %s seconds.", + document_index.__class__.__name__, + WAIT_SECONDS, ) time.sleep(WAIT_SECONDS) if not success: logger.error( - f"Document index swap for {document_index.__class__.__name__} did not succeed. " - f"Attempt limit reached. ({VESPA_NUM_ATTEMPTS_ON_STARTUP})" + "Document index swap for %s did not succeed. Attempt limit reached. (%s)", + document_index.__class__.__name__, + VESPA_NUM_ATTEMPTS_ON_STARTUP, ) return None @@ -230,5 +235,5 @@ def check_and_perform_index_swap(db_session: Session) -> SearchSettings | None: return None # Should not reach here, but handle gracefully - logger.error(f"Unknown switchover_type: {switchover_type}") + logger.error("Unknown switchover_type: %s", switchover_type) return None diff --git a/backend/onyx/db/sync_record.py b/backend/onyx/db/sync_record.py index 5a56c407da..f60ed7d19f 100644 --- a/backend/onyx/db/sync_record.py +++ b/backend/onyx/db/sync_record.py @@ -32,8 +32,10 @@ def insert_sync_record( if existing_in_progress_sync_record is not None: logger.info( - f"Cancelling existing in-progress sync record {existing_in_progress_sync_record.id} " - f"for entity_id={entity_id} sync_type={sync_type}" + "Cancelling existing in-progress sync record %s for entity_id=%s sync_type=%s", + existing_in_progress_sync_record.id, + entity_id, + sync_type, ) mark_sync_records_as_cancelled(db_session, entity_id, sync_type) @@ -130,10 +132,9 @@ def update_sync_record_status( sync_record = fetch_latest_sync_record(db_session, entity_id, sync_type) if sync_record is None: logger.warning( - f"No sync record found for entity_id={entity_id} " - f"sync_type={sync_type} — skipping status update. " - f"This typically means the record was never created " - f"(insert_sync_record failed silently) or was cleaned up." + "No sync record found for entity_id=%s sync_type=%s — skipping status update. This typically means the record was never created (insert_sync_record failed silently) or was cleaned up.", + entity_id, + sync_type, ) return diff --git a/backend/onyx/db/tag.py b/backend/onyx/db/tag.py index 46070dd8d6..1155791e3f 100644 --- a/backend/onyx/db/tag.py +++ b/backend/onyx/db/tag.py @@ -23,7 +23,9 @@ def check_tag_validity(tag_key: str, tag_value: str) -> bool: Additionally, extremely long tags are not really usable / useful.""" if len(tag_key) + len(tag_value) > 255: logger.error( - f"Tag with key '{tag_key}' and value '{tag_value}' is too long, cannot be used" + "Tag with key '%s' and value '%s' is too long, cannot be used", + tag_key, + tag_value, ) return False diff --git a/backend/onyx/db/user_preferences.py b/backend/onyx/db/user_preferences.py index 8487074c2a..c8847ddda5 100644 --- a/backend/onyx/db/user_preferences.py +++ b/backend/onyx/db/user_preferences.py @@ -126,7 +126,7 @@ def get_latest_access_token_for_user( ) return result.scalar_one_or_none() except Exception as e: - logger.error(f"Error fetching AccessToken: {e}") + logger.error("Error fetching AccessToken: %s", e) return None diff --git a/backend/onyx/db/users.py b/backend/onyx/db/users.py index 9a5f66da63..623265c55c 100644 --- a/backend/onyx/db/users.py +++ b/backend/onyx/db/users.py @@ -487,7 +487,9 @@ def assign_user_to_default_groups__no_commit( recompute_user_permissions__no_commit(user.id, db_session) - logger.info(f"Assigned user {user.email} to default group '{default_group.name}'") + logger.info( + "Assigned user %s to default group '%s'", user.email, default_group.name + ) def delete_user_from_db( diff --git a/backend/onyx/deep_research/dr_loop.py b/backend/onyx/deep_research/dr_loop.py index 322858e675..4716639fdb 100644 --- a/backend/onyx/deep_research/dr_loop.py +++ b/backend/onyx/deep_research/dr_loop.py @@ -439,8 +439,9 @@ def run_deep_research_llm_loop( if timed_out or is_last_cycle: if timed_out: logger.info( - f"Deep research exceeded {DEEP_RESEARCH_FORCE_REPORT_SECONDS}s " - f"(elapsed: {elapsed_seconds:.1f}s), forcing final report generation" + "Deep research exceeded %ss (elapsed: %ss), forcing final report generation", + DEEP_RESEARCH_FORCE_REPORT_SECONDS, + format(elapsed_seconds, ".1f"), ) report_turn_index = ( orchestrator_start_turn_index + cycle + reasoning_cycles @@ -632,7 +633,7 @@ def run_deep_research_llm_loop( for tool_call in tool_calls: if tool_call.tool_name != RESEARCH_AGENT_TOOL_NAME: logger.warning( - f"Unexpected tool call: {tool_call.tool_name}" + "Unexpected tool call: %s", tool_call.tool_name ) continue @@ -735,7 +736,8 @@ def run_deep_research_llm_loop( # synthetic failure response so the invariant holds and the LLM # knows the call failed. logger.error( - f"Research agent call at tab_index {tab_index} failed; emitting synthetic failure response" + "Research agent call at tab_index %s failed; emitting synthetic failure response", + tab_index, ) failed_tool_call = research_agent_calls[tab_index] failure_message = "Research agent call failed. Try a different approach or continue without this result." diff --git a/backend/onyx/document_index/opensearch/client.py b/backend/onyx/document_index/opensearch/client.py index bc2ebe45a4..8e1ee0ac8d 100644 --- a/backend/onyx/document_index/opensearch/client.py +++ b/backend/onyx/document_index/opensearch/client.py @@ -139,7 +139,10 @@ class OpenSearchClient(AbstractContextManager): timeout: int = DEFAULT_OPENSEARCH_CLIENT_TIMEOUT_S, ): logger.debug( - f"Creating OpenSearch client with host {host}, port {port} and timeout {timeout} seconds." + "Creating OpenSearch client with host %s, port %s and timeout %s seconds.", + host, + port, + timeout, ) self._client = OpenSearch( hosts=[{"host": host, "port": port}], @@ -220,7 +223,7 @@ class OpenSearchClient(AbstractContextManager): logger.info("Successfully put cluster settings.") return True else: - logger.error(f"Failed to put cluster settings: {response}.") + logger.error("Failed to put cluster settings: %s.", response) return False @log_function_time(print_only=True, debug_only=True) @@ -321,7 +324,8 @@ class OpenSearchIndexClient(OpenSearchClient): self._index_name = index_name self._emit_metrics = emit_metrics logger.debug( - f"OpenSearch client created successfully for index {self._index_name}." + "OpenSearch client created successfully for index %s.", + self._index_name, ) @log_function_time(print_only=True, debug_only=True, include_args=True) @@ -342,7 +346,7 @@ class OpenSearchIndexClient(OpenSearchClient): "mappings": mappings, "settings": settings, } - logger.debug(f"Creating index {self._index_name}.") + logger.debug("Creating index %s.", self._index_name) response = self._client.indices.create(index=self._index_name, body=body) if not response.get("acknowledged", False): raise RuntimeError(f"Failed to create index {self._index_name}.") @@ -351,7 +355,7 @@ class OpenSearchIndexClient(OpenSearchClient): raise RuntimeError( f"OpenSearch responded with index name {response_index} when creating index {self._index_name}." ) - logger.debug(f"Index {self._index_name} created successfully.") + logger.debug("Index %s created successfully.", self._index_name) @log_function_time(print_only=True, debug_only=True) def delete_index(self) -> bool: @@ -365,15 +369,16 @@ class OpenSearchIndexClient(OpenSearchClient): """ if not self._client.indices.exists(index=self._index_name): logger.warning( - f"Tried to delete index {self._index_name} but it does not exist." + "Tried to delete index %s but it does not exist.", + self._index_name, ) return False - logger.info(f"Deleting index {self._index_name}.") + logger.info("Deleting index %s.", self._index_name) response = self._client.indices.delete(index=self._index_name) if not response.get("acknowledged", False): raise RuntimeError(f"Failed to delete index {self._index_name}.") - logger.info(f"Index {self._index_name} deleted successfully.") + logger.info("Index %s deleted successfully.", self._index_name) return True @log_function_time(print_only=True, debug_only=True) @@ -408,7 +413,7 @@ class OpenSearchIndexClient(OpenSearchClient): Exception: There was an error updating the mappings, such as attempting to change the type of an existing field. """ - logger.debug(f"Putting mappings for index {self._index_name}.") + logger.debug("Putting mappings for index %s.", self._index_name) response = self._client.indices.put_mapping( index=self._index_name, body=mappings ) @@ -416,7 +421,7 @@ class OpenSearchIndexClient(OpenSearchClient): raise RuntimeError( f"Failed to put the mapping update for index {self._index_name}." ) - logger.debug(f"Successfully put mappings for index {self._index_name}.") + logger.debug("Successfully put mappings for index %s.", self._index_name) @log_function_time(print_only=True, debug_only=True, include_args=True) def validate_index(self, expected_mappings: dict[str, Any]) -> bool: @@ -444,10 +449,11 @@ class OpenSearchIndexClient(OpenSearchClient): exists_response = self.index_exists() if not exists_response: logger.warning( - f"Tried to validate index {self._index_name} but it does not exist." + "Tried to validate index %s but it does not exist.", + self._index_name, ) return False - logger.debug(f"Validating index {self._index_name}.") + logger.debug("Validating index %s.", self._index_name) get_result = self._client.indices.get(index=self._index_name) index_info: dict[str, Any] = get_result.get(self._index_name, {}) @@ -469,7 +475,9 @@ class OpenSearchIndexClient(OpenSearchClient): for property in expected_mapping_properties: if property not in index_mapping_properties: logger.warning( - f'The field "{property}" was not found in the index {self._index_name}.' + 'The field "%s" was not found in the index %s.', + property, + self._index_name, ) return False @@ -483,12 +491,16 @@ class OpenSearchIndexClient(OpenSearchClient): index_property_type = index_mapping_properties[property].get("type", "") if expected_property_type != index_property_type: logger.warning( - f'The field "{property}" in the index {self._index_name} has type {index_property_type} ' - f"but the expected type is {expected_property_type}." + 'The field "%s" in the index %s has type %s ' + "but the expected type is %s.", + property, + self._index_name, + index_property_type, + expected_property_type, ) return False - logger.debug(f"Index {self._index_name} validated successfully.") + logger.debug("Index %s validated successfully.", self._index_name) return True @log_function_time(print_only=True, debug_only=True, include_args=True) @@ -509,7 +521,7 @@ class OpenSearchIndexClient(OpenSearchClient): Raises: Exception: There was an error updating the settings of the index. """ - logger.debug(f"Updating settings of index {self._index_name}.") + logger.debug("Updating settings of index %s.", self._index_name) params = { "timeout": timeout, } @@ -520,7 +532,7 @@ class OpenSearchIndexClient(OpenSearchClient): raise RuntimeError( f"Failed to update settings of index {self._index_name}." ) - logger.debug(f"Settings of index {self._index_name} updated successfully.") + logger.debug("Settings of index %s updated successfully.", self._index_name) @log_function_time(print_only=True, debug_only=True) def get_settings( @@ -550,7 +562,7 @@ class OpenSearchIndexClient(OpenSearchClient): Raises: Exception: There was an error getting the settings of the index. """ - logger.debug(f"Getting settings of index {self._index_name}.") + logger.debug("Getting settings of index %s.", self._index_name) params = { "include_defaults": str(include_defaults).lower(), "flat_settings": str(flat_settings).lower(), @@ -572,14 +584,14 @@ class OpenSearchIndexClient(OpenSearchClient): Raises: Exception: There was an error opening the index. """ - logger.debug(f"Opening index {self._index_name}.") + logger.debug("Opening index %s.", self._index_name) params = { "timeout": timeout, } response = self._client.indices.open(index=self._index_name, params=params) if not response.get("acknowledged", False): raise RuntimeError(f"Failed to open index {self._index_name}.") - logger.debug(f"Index {self._index_name} opened successfully.") + logger.debug("Index %s opened successfully.", self._index_name) @log_function_time(print_only=True, debug_only=True) def close_index(self, timeout: float = DEFAULT_INDEX_SETTINGS_TIMEOUT_S) -> None: @@ -588,14 +600,14 @@ class OpenSearchIndexClient(OpenSearchClient): Raises: Exception: There was an error closing the index. """ - logger.debug(f"Closing index {self._index_name}.") + logger.debug("Closing index %s.", self._index_name) params = { "timeout": timeout, } response = self._client.indices.close(index=self._index_name, params=params) if not response.get("acknowledged", False): raise RuntimeError(f"Failed to close index {self._index_name}.") - logger.debug(f"Index {self._index_name} closed successfully.") + logger.debug("Index %s closed successfully.", self._index_name) @log_function_time( print_only=True, @@ -629,8 +641,10 @@ class OpenSearchIndexClient(OpenSearchClient): update_if_exists is False. """ logger.debug( - f"Trying to index document ID {document.document_id} for tenant {tenant_state.tenant_id}. " - f"update_if_exists={update_if_exists}." + "Trying to index document ID %s for tenant %s. " "update_if_exists=%s.", + document.document_id, + tenant_state.tenant_id, + update_if_exists, ) document_chunk_id: str = get_opensearch_doc_chunk_id( tenant_state=tenant_state, @@ -671,7 +685,7 @@ class OpenSearchIndexClient(OpenSearchClient): raise RuntimeError( f'Unknown OpenSearch indexing result: "{result_string}".' ) - logger.debug(f"Successfully indexed {document_chunk_id}.") + logger.debug("Successfully indexed %s.", document_chunk_id) @log_function_time( print_only=True, @@ -716,7 +730,10 @@ class OpenSearchIndexClient(OpenSearchClient): if not documents: return logger.debug( - f"Bulk indexing {len(documents)} documents for tenant {tenant_state.tenant_id}. update_if_exists={update_if_exists}." + "Bulk indexing %s documents for tenant %s. update_if_exists=%s.", + len(documents), + tenant_state.tenant_id, + update_if_exists, ) data = [] for document in documents: @@ -745,7 +762,7 @@ class OpenSearchIndexClient(OpenSearchClient): f"OpenSearch reported no errors during bulk index but the number of successful operations " f"({success}) does not match the number of documents ({len(documents)})." ) - logger.debug(f"Successfully bulk indexed {len(documents)} documents.") + logger.debug("Successfully bulk indexed %s documents.", len(documents)) @log_function_time(print_only=True, debug_only=True, include_args=True) def delete_document(self, document_chunk_id: str) -> bool: @@ -763,13 +780,17 @@ class OpenSearchIndexClient(OpenSearchClient): """ try: logger.debug( - f"Trying to delete document chunk {document_chunk_id} from index {self._index_name}." + "Trying to delete document chunk %s from index %s.", + document_chunk_id, + self._index_name, ) result = self._client.delete(index=self._index_name, id=document_chunk_id) except TransportError as e: if e.status_code == 404: logger.debug( - f"Document chunk {document_chunk_id} not found in index {self._index_name}." + "Document chunk %s not found in index %s.", + document_chunk_id, + self._index_name, ) return False else: @@ -779,12 +800,16 @@ class OpenSearchIndexClient(OpenSearchClient): match result_string: case "deleted": logger.debug( - f"Successfully deleted document chunk {document_chunk_id} from index {self._index_name}." + "Successfully deleted document chunk %s from index %s.", + document_chunk_id, + self._index_name, ) return True case "not_found": logger.debug( - f"Document chunk {document_chunk_id} not found in index {self._index_name}." + "Document chunk %s not found in index %s.", + document_chunk_id, + self._index_name, ) return False case _: @@ -806,7 +831,8 @@ class OpenSearchIndexClient(OpenSearchClient): The number of documents deleted. """ logger.debug( - f"Trying to delete documents by query for index {self._index_name}." + "Trying to delete documents by query for index %s.", + self._index_name, ) result = self._client.delete_by_query(index=self._index_name, body=query_body) if result.get("timed_out", False): @@ -827,7 +853,9 @@ class OpenSearchIndexClient(OpenSearchClient): ) logger.debug( - f"Successfully deleted {num_deleted} documents by query for index {self._index_name}." + "Successfully deleted %s documents by query for index %s.", + num_deleted, + self._index_name, ) return num_deleted @@ -854,7 +882,9 @@ class OpenSearchIndexClient(OpenSearchClient): Exception: There was an error updating the document. """ logger.debug( - f"Trying to update document chunk {document_chunk_id} for index {self._index_name}." + "Trying to update document chunk %s for index %s.", + document_chunk_id, + self._index_name, ) update_body: dict[str, Any] = {"doc": properties_to_update} result = self._client.update( @@ -875,12 +905,15 @@ class OpenSearchIndexClient(OpenSearchClient): # Sanity check. case "updated": logger.debug( - f"Successfully updated document chunk {document_chunk_id} for index {self._index_name}." + "Successfully updated document chunk %s for index %s.", + document_chunk_id, + self._index_name, ) return case "noop": logger.warning( - f'OpenSearch reported a no-op when trying to update document with ID "{document_chunk_id}".' + 'OpenSearch reported a no-op when trying to update document with ID "%s".', + document_chunk_id, ) return case _: @@ -906,7 +939,9 @@ class OpenSearchIndexClient(OpenSearchClient): The document chunk. """ logger.debug( - f"Trying to get document chunk {document_chunk_id} from index {self._index_name}." + "Trying to get document chunk %s from index %s.", + document_chunk_id, + self._index_name, ) result = self._client.get(index=self._index_name, id=document_chunk_id) found_result: bool = result.get("found", False) @@ -922,7 +957,9 @@ class OpenSearchIndexClient(OpenSearchClient): ) logger.debug( - f"Successfully got document chunk {document_chunk_id} from index {self._index_name}." + "Successfully got document chunk %s from index %s.", + document_chunk_id, + self._index_name, ) return DocumentChunk.model_validate(document_chunk_source) @@ -959,7 +996,9 @@ class OpenSearchIndexClient(OpenSearchClient): List of search hits that match the search request. """ logger.debug( - f"Trying to search index {self._index_name} with search pipeline {search_pipeline_id}." + "Trying to search index %s with search pipeline %s.", + self._index_name, + search_pipeline_id, ) result: dict[str, Any] params = {"phase_took": "true"} @@ -1014,7 +1053,9 @@ class OpenSearchIndexClient(OpenSearchClient): ) search_hits.append(search_hit) logger.debug( - f"Successfully searched index {self._index_name} and got {len(search_hits)} hits." + "Successfully searched index %s and got %s hits.", + self._index_name, + len(search_hits), ) return search_hits @@ -1050,7 +1091,8 @@ class OpenSearchIndexClient(OpenSearchClient): List of document chunk IDs that match the search request. """ logger.debug( - f"Trying to search for document chunk IDs in index {self._index_name}." + "Trying to search for document chunk IDs in index %s.", + self._index_name, ) if "_source" not in body or body["_source"] is not False: logger.warning( @@ -1099,7 +1141,9 @@ class OpenSearchIndexClient(OpenSearchClient): ) document_chunk_ids.append(document_chunk_id) logger.debug( - f"Successfully searched for document chunk IDs in index {self._index_name} and got {len(document_chunk_ids)} hits." + "Successfully searched for document chunk IDs in index %s and got %s hits.", + self._index_name, + len(document_chunk_ids), ) return document_chunk_ids @@ -1176,11 +1220,17 @@ class OpenSearchIndexClient(OpenSearchClient): """ if time_took and time_took > CLIENT_THRESHOLD_TO_LOG_SLOW_SEARCH_MS: logger.warning( - f"OpenSearch client warning: Search for index {self._index_name} took {time_took} milliseconds.\n" - f"Body: {get_new_body_without_vectors(body)}\n" - f"Search pipeline ID: {search_pipeline_id}\n" - f"Phase took: {phase_took}\n" - f"Profile: {json.dumps(profile, indent=2)}\n" + "OpenSearch client warning: Search for index %s took %s milliseconds.\n" + "Body: %s\n" + "Search pipeline ID: %s\n" + "Phase took: %s\n" + "Profile: %s\n", + self._index_name, + time_took, + get_new_body_without_vectors(body), + search_pipeline_id, + phase_took, + json.dumps(profile, indent=2), ) if timed_out: error_str = f"OpenSearch client error: Search timed out for index {self._index_name}." @@ -1234,10 +1284,13 @@ def wait_for_opensearch_with_timeout( time_elapsed = time.monotonic() - time_start if time_elapsed > wait_limit_s: logger.info( - f"[OpenSearch] Readiness probe did not succeed within the timeout ({wait_limit_s} seconds)." + "[OpenSearch] Readiness probe did not succeed within the timeout (%s seconds).", + wait_limit_s, ) return False logger.info( - f"[OpenSearch] Readiness probe ongoing. elapsed={time_elapsed:.1f} timeout={wait_limit_s:.1f}" + "[OpenSearch] Readiness probe ongoing. elapsed=%s timeout=%s", + format(time_elapsed, ".1f"), + format(wait_limit_s, ".1f"), ) time.sleep(wait_interval_s) diff --git a/backend/onyx/document_index/opensearch/opensearch_document_index.py b/backend/onyx/document_index/opensearch/opensearch_document_index.py index 2e529428c5..90b17b9df8 100644 --- a/backend/onyx/document_index/opensearch/opensearch_document_index.py +++ b/backend/onyx/document_index/opensearch/opensearch_document_index.py @@ -442,7 +442,8 @@ class OpenSearchOldDocumentIndex(OldDocumentIndex): """ if fields is None and user_fields is None: logger.warning( - f"Tried to update document {doc_id} with no updated fields or user fields." + "Tried to update document %s with no updated fields or user fields.", + doc_id, ) return @@ -481,20 +482,19 @@ class OpenSearchOldDocumentIndex(OldDocumentIndex): self._secondary_real_index.update([update_request]) except NotFoundError: logger.exception( - f"Tried to update document {doc_id} but at least one of its chunks was not found in OpenSearch. " - "This is likely due to it not having been indexed yet. Skipping update for now..." + "Tried to update document %s but at least one of its chunks was not found in OpenSearch. This is likely due to it not having been indexed yet. Skipping update for now...", + doc_id, ) return except ChunkCountNotFoundError: logger.warning( - f"Tried to update document {doc_id} but its chunk count is not known. We tolerate this for now " - "but this will not be an acceptable state once OpenSearch is the primary document index and the " - "indexing/updating race condition is fixed." + "Tried to update document %s but its chunk count is not known. We tolerate this for now but this will not be an acceptable state once OpenSearch is the primary document index and the indexing/updating race condition is fixed.", + doc_id, ) return except ChunkCountZeroError: logger.warning( - f"Tried to update document {doc_id} but its chunk count was 0." + "Tried to update document %s but its chunk count was 0.", doc_id ) return @@ -647,8 +647,9 @@ class OpenSearchDocumentIndex(DocumentIndex): search pipelines. """ logger.debug( - f"[OpenSearchDocumentIndex] Verifying and creating index {self._index_name} if " - f"necessary, with embedding dimension {embedding_dim}." + "[OpenSearchDocumentIndex] Verifying and creating index %s if necessary, with embedding dimension %s.", + self._index_name, + embedding_dim, ) with redis_shared_lock( @@ -678,8 +679,9 @@ class OpenSearchDocumentIndex(DocumentIndex): self._client.put_mapping(expected_mappings) except Exception as e: logger.error( - f"Failed to update mappings for index {self._index_name}. This likely means a " - f"field type was changed which requires reindexing. Error: {e}" + "Failed to update mappings for index %s. This likely means a field type was changed which requires reindexing. Error: %s", + self._index_name, + e, ) raise @@ -716,8 +718,10 @@ class OpenSearchDocumentIndex(DocumentIndex): for cc in indexing_metadata.doc_id_to_chunk_cnt_diff.values() ) logger.debug( - f"[OpenSearchDocumentIndex] Indexing {total_chunks} chunks from {len(indexing_metadata.doc_id_to_chunk_cnt_diff)} " - f"documents for index {self._index_name}." + "[OpenSearchDocumentIndex] Indexing %s chunks from %s documents for index %s.", + total_chunks, + len(indexing_metadata.doc_id_to_chunk_cnt_diff), + self._index_name, ) document_indexing_results: list[DocumentInsertionRecord] = [] @@ -778,7 +782,8 @@ class OpenSearchDocumentIndex(DocumentIndex): # Refresh the index and try one more time. We do not refresh # after every delete because this may become expensive. logger.warning( - f"Failed to bulk index documents: {e}. Refreshing index and trying again." + "Failed to bulk index documents: %s. Refreshing index and trying again.", + e, ) self._client.refresh_index() self._client.bulk_index_documents( @@ -834,7 +839,9 @@ class OpenSearchDocumentIndex(DocumentIndex): The number of chunks successfully deleted. """ logger.debug( - f"[OpenSearchDocumentIndex] Deleting document {document_id} from index {self._index_name}." + "[OpenSearchDocumentIndex] Deleting document %s from index %s.", + document_id, + self._index_name, ) query_body = DocumentQuery.delete_from_document_id_query( document_id=document_id, @@ -870,7 +877,9 @@ class OpenSearchDocumentIndex(DocumentIndex): specified documents. """ logger.debug( - f"[OpenSearchDocumentIndex] Updating {len(update_requests)} chunks for index {self._index_name}." + "[OpenSearchDocumentIndex] Updating %s chunks for index %s.", + len(update_requests), + self._index_name, ) for update_request in update_requests: properties_to_update: dict[str, Any] = dict() @@ -908,8 +917,8 @@ class OpenSearchDocumentIndex(DocumentIndex): else: update_string = f"document {update_request.document_ids[0]}" logger.warning( - f"[OpenSearchDocumentIndex] Tried to update {update_string} " - "with no specified update fields. This will be a no-op." + "[OpenSearchDocumentIndex] Tried to update %s with no specified update fields. This will be a no-op.", + update_string, ) continue @@ -962,7 +971,9 @@ class OpenSearchDocumentIndex(DocumentIndex): chunk IDs vs querying for matching document chunks. """ logger.debug( - f"[OpenSearchDocumentIndex] Retrieving {len(chunk_requests)} chunks for index {self._index_name}." + "[OpenSearchDocumentIndex] Retrieving %s chunks for index %s.", + len(chunk_requests), + self._index_name, ) results: list[InferenceChunk] = [] for chunk_request in chunk_requests: @@ -1012,7 +1023,9 @@ class OpenSearchDocumentIndex(DocumentIndex): # TODO(andrei): There is some duplicated logic in this function with # others in this file. logger.debug( - f"[OpenSearchDocumentIndex] Hybrid retrieving {num_to_retrieve} chunks for index {self._index_name}." + "[OpenSearchDocumentIndex] Hybrid retrieving %s chunks for index %s.", + num_to_retrieve, + self._index_name, ) # TODO(andrei): This could be better, the caller should just make this # decision when passing in the query param. See the above comment in the @@ -1062,7 +1075,9 @@ class OpenSearchDocumentIndex(DocumentIndex): # TODO(andrei): There is some duplicated logic in this function with # others in this file. logger.debug( - f"[OpenSearchDocumentIndex] Keyword retrieving {num_to_retrieve} chunks for index {self._index_name}." + "[OpenSearchDocumentIndex] Keyword retrieving %s chunks for index %s.", + num_to_retrieve, + self._index_name, ) query_body = DocumentQuery.get_keyword_search_query( query_text=query, @@ -1104,7 +1119,9 @@ class OpenSearchDocumentIndex(DocumentIndex): # TODO(andrei): There is some duplicated logic in this function with # others in this file. logger.debug( - f"[OpenSearchDocumentIndex] Semantic retrieving {num_to_retrieve} chunks for index {self._index_name}." + "[OpenSearchDocumentIndex] Semantic retrieving %s chunks for index %s.", + num_to_retrieve, + self._index_name, ) query_body = DocumentQuery.get_semantic_search_query( query_embedding=query_embedding, @@ -1144,7 +1161,9 @@ class OpenSearchDocumentIndex(DocumentIndex): dirty: bool | None = None, # noqa: ARG002 ) -> list[InferenceChunk]: logger.debug( - f"[OpenSearchDocumentIndex] Randomly retrieving {num_to_retrieve} chunks for index {self._index_name}." + "[OpenSearchDocumentIndex] Randomly retrieving %s chunks for index %s.", + num_to_retrieve, + self._index_name, ) query_body = DocumentQuery.get_random_search_query( tenant_state=self._tenant_state, @@ -1175,7 +1194,9 @@ class OpenSearchDocumentIndex(DocumentIndex): complete. """ logger.debug( - f"[OpenSearchDocumentIndex] Indexing {len(chunks)} raw chunks for index {self._index_name}." + "[OpenSearchDocumentIndex] Indexing %s raw chunks for index %s.", + len(chunks), + self._index_name, ) # Do not raise if the document already exists, just update. This is # because the document may already have been indexed during the diff --git a/backend/onyx/document_index/vespa/chunk_retrieval.py b/backend/onyx/document_index/vespa/chunk_retrieval.py index b496fb1716..cc8bba0f39 100644 --- a/backend/onyx/document_index/vespa/chunk_retrieval.py +++ b/backend/onyx/document_index/vespa/chunk_retrieval.py @@ -125,7 +125,8 @@ def _vespa_hit_to_inference_chunk( semantic_identifier = fields.get(SEMANTIC_IDENTIFIER, "") if not semantic_identifier: logger.error( - f"Chunk with blurb: {fields.get(BLURB, 'Unknown')[:50]}... has no Semantic Identifier" + "Chunk with blurb: %s... has no Semantic Identifier", + fields.get(BLURB, "Unknown")[:50], ) source_links = fields.get(SOURCE_LINKS, {}) @@ -239,11 +240,16 @@ def get_chunks_via_visit_api( except httpx.HTTPError as e: error_base = "Failed to query Vespa" logger.error( - f"{error_base}:\n" - f"Request URL: {e.request.url}\n" - f"Request Headers: {e.request.headers}\n" - f"Request Payload: {params}\n" - f"Exception: {str(e)}" + "%s:\n" + "Request URL: %s\n" + "Request Headers: %s\n" + "Request Payload: %s\n" + "Exception: %s", + error_base, + e.request.url, + e.request.headers, + params, + str(e), ) raise httpx.HTTPError(error_base) from e @@ -266,9 +272,11 @@ def get_chunks_via_visit_api( document_tenant_id = document["fields"].get(TENANT_ID) if document_tenant_id != filters.tenant_id: logger.error( - f"Skipping document {document['document_id']} because " - f"it does not belong to tenant {filters.tenant_id}. " - "This should never happen." + "Skipping document %s because " + "it does not belong to tenant %s. " + "This should never happen.", + document["document_id"], + filters.tenant_id, ) continue @@ -317,7 +325,9 @@ def get_all_chunks_paginated( ) -> tuple[list[dict], str | None]: if continuation_token == FINISHED_VISITING_SLICE_CONTINUATION_TOKEN: logger.debug( - f"Slice {slice_id} has finished visiting. Returning empty list and {FINISHED_VISITING_SLICE_CONTINUATION_TOKEN}." + "Slice %s has finished visiting. Returning empty list and %s.", + slice_id, + FINISHED_VISITING_SLICE_CONTINUATION_TOKEN, ) return [], FINISHED_VISITING_SLICE_CONTINUATION_TOKEN @@ -362,7 +372,10 @@ def get_all_chunks_paginated( f"{continuation_token} in {time.monotonic() - start_time:.3f} seconds." ) logger.exception( - f"Request URL: {e.request.url}\nRequest Headers: {e.request.headers}\nRequest Payload: {params}\n" + "Request URL: %s\nRequest Headers: %s\nRequest Payload: %s", + e.request.url, + e.request.headers, + params, ) error_message = ( response.json().get("message") if response else "No response" @@ -382,7 +395,10 @@ def get_all_chunks_paginated( chunks = [chunk["fields"] for chunk in response_data.get("documents", [])] if next_continuation_token == FINISHED_VISITING_SLICE_CONTINUATION_TOKEN: logger.debug( - f"Slice {slice_id} has finished visiting. Returning {len(chunks)} chunks and {next_continuation_token}." + "Slice %s has finished visiting. Returning %s chunks and %s.", + slice_id, + len(chunks), + next_continuation_token, ) return chunks, next_continuation_token @@ -423,8 +439,10 @@ def get_all_chunks_paginated( raise RuntimeError(f"Slice {i} is not in the continuation token map.") if parallel_result is None: logger.error( - f"Failed to get chunks for slice {i} of {total_slices}. " - "The continuation token for this slice will not be updated." + "Failed to get chunks for slice %s of %s. " + "The continuation token for this slice will not be updated.", + i, + total_slices, ) continue chunks.extend(parallel_result[0]) @@ -522,14 +540,14 @@ def query_vespa( # Log each detail on its own line so log collectors capture them # as separate entries rather than truncating a single multiline msg logger.error( - f"Failed to query Vespa | " - f"status={status_code} | " - f"yql_length={yql_length} | " - f"exception={str(e)}" + "Failed to query Vespa | " "status=%s | " "yql_length=%s | " "exception=%s", + status_code, + yql_length, + str(e), ) if response_text: - logger.error(f"Vespa error response: {response_text[:1000]}") - logger.error(f"Vespa request URL: {e.request.url}") + logger.error("Vespa error response: %s", response_text[:1000]) + logger.error("Vespa request URL: %s", e.request.url) # Re-raise with diagnostics so callers see what actually went wrong raise httpx.HTTPError( @@ -544,17 +562,19 @@ def query_vespa( if not hits: logger.warning( - f"No hits found for YQL Query: {query_params.get('yql', 'No YQL Query')}" + "No hits found for YQL Query: %s", + query_params.get("yql", "No YQL Query"), ) - logger.debug(f"Vespa Response: {response.text}") + logger.debug("Vespa Response: %s", response.text) for hit in hits: if hit["fields"].get(CONTENT) is None: identifier = hit["fields"].get("documentid") or hit["id"] logger.error( - f"Vespa Index with Vespa ID {identifier} has no contents. " - f"This is invalid because the vector is not meaningful and keywordsearch cannot " - f"fetch this document" + "Vespa Index with Vespa ID %s has no contents. " + "This is invalid because the vector is not meaningful and keywordsearch cannot " + "fetch this document", + identifier, ) filtered_hits = [hit for hit in hits if hit["fields"].get(CONTENT) is not None] @@ -567,11 +587,13 @@ def query_vespa( set([chunk.document_id for chunk in inference_chunks]) ) logger.info( - f"Retrieved {num_retrieved_inference_chunks} inference chunks for {num_retrieved_document_ids} documents" + "Retrieved %s inference chunks for %s documents", + num_retrieved_inference_chunks, + num_retrieved_document_ids, ) except Exception as e: # Debug logging only, should not fail the retrieval - logger.error(f"Error logging retrieval statistics: {e}") + logger.error("Error logging retrieval statistics: %s", e) # Good Debugging Spot return inference_chunks @@ -657,7 +679,7 @@ def batch_search_api_retrieval( ) if uncapped_requests: - logger.debug(f"Retrieving {len(uncapped_requests)} uncapped requests") + logger.debug("Retrieving %s uncapped requests", len(uncapped_requests)) retrieved_chunks.extend( parallel_visit_api_retrieval( index_name, uncapped_requests, filters, get_large_chunks diff --git a/backend/onyx/document_index/vespa/deletion.py b/backend/onyx/document_index/vespa/deletion.py index 4d1682fca7..34647e425f 100644 --- a/backend/onyx/document_index/vespa/deletion.py +++ b/backend/onyx/document_index/vespa/deletion.py @@ -29,7 +29,7 @@ def _delete_vespa_chunk( f"{DOCUMENT_ID_ENDPOINT.format(index_name=index_name)}/{doc_chunk_id}", ) except httpx.HTTPStatusError as e: - logger.error(f"Failed to delete chunk, details: {e.response.text}") + logger.error("Failed to delete chunk, details: %s", e.response.text) raise diff --git a/backend/onyx/document_index/vespa/index.py b/backend/onyx/document_index/vespa/index.py index d213aa55e9..a4dd506dd3 100644 --- a/backend/onyx/document_index/vespa/index.py +++ b/backend/onyx/document_index/vespa/index.py @@ -274,7 +274,7 @@ class VespaIndex(DocumentIndex): jinja_env = jinja2.Environment() deploy_url = f"{VESPA_APPLICATION_ENDPOINT}/tenant/default/prepareandactivate" - logger.notice(f"Deploying Vespa application package to {deploy_url}") + logger.notice("Deploying Vespa application package to %s", deploy_url) vespa_schema_path = os.path.join( os.getcwd(), "onyx", "document_index", "vespa", "app_config" @@ -359,7 +359,7 @@ class VespaIndex(DocumentIndex): response = requests.post(deploy_url, headers=headers, data=zip_file) if response.status_code != 200: logger.error( - f"Failed to prepare Vespa Onyx Index. Response: {response.text}" + "Failed to prepare Vespa Onyx Index. Response: %s", response.text ) raise RuntimeError( f"Failed to prepare Vespa Onyx Index. Response: {response.text}" @@ -375,7 +375,7 @@ class VespaIndex(DocumentIndex): raise ValueError("Multi-tenant is not enabled") deploy_url = f"{VESPA_APPLICATION_ENDPOINT}/tenant/default/prepareandactivate" - logger.info(f"Deploying Vespa application package to {deploy_url}") + logger.info("Deploying Vespa application package to %s", deploy_url) vespa_schema_path = os.path.join( os.getcwd(), "onyx", "document_index", "vespa", "app_config" @@ -437,7 +437,9 @@ class VespaIndex(DocumentIndex): embedding_dim = embedding_dims[i] embedding_precision = embedding_precisions[i] logger.info( - f"Creating index: {index_name} with embedding dimension: {embedding_dim}" + "Creating index: %s with embedding dimension: %s", + index_name, + embedding_dim, ) schema = schema_template.render( @@ -535,7 +537,9 @@ class VespaIndex(DocumentIndex): update: _VespaUpdateRequest, http_client: httpx.Client ) -> httpx.Response: logger.debug( - f"Updating with request to {update.url} with body {update.update_request}" + "Updating with request to %s with body %s", + update.url, + update.update_request, ) return http_client.put( update.url, @@ -669,7 +673,8 @@ class VespaIndex(DocumentIndex): """ if fields is None and user_fields is None: logger.warning( - f"Tried to update document {doc_id} with no updated fields or user fields." + "Tried to update document %s with no updated fields or user fields.", + doc_id, ) return @@ -930,7 +935,7 @@ class VespaIndex(DocumentIndex): int: The number of documents deleted. """ logger.info( - f"Deleting entries with tenant_id: {tenant_id} from index: {index_name}" + "Deleting entries with tenant_id: %s from index: %s", tenant_id, index_name ) # Step 1: Retrieve all document IDs with the given tenant_id @@ -938,7 +943,9 @@ class VespaIndex(DocumentIndex): if not document_ids: logger.info( - f"No documents found with tenant_id: {tenant_id} in index: {index_name}" + "No documents found with tenant_id: %s in index: %s", + tenant_id, + index_name, ) return 0 @@ -972,7 +979,9 @@ class VespaIndex(DocumentIndex): document_ids = [] logger.debug( - f"Starting document ID retrieval for tenant_id: {tenant_id} in index: {index_name}" + "Starting document ID retrieval for tenant_id: %s in index: %s", + tenant_id, + index_name, ) while True: @@ -989,7 +998,9 @@ class VespaIndex(DocumentIndex): url = f"{VESPA_APPLICATION_ENDPOINT}/search/" logger.debug( - f"Querying for document IDs with tenant_id: {tenant_id}, offset: {offset}" + "Querying for document IDs with tenant_id: %s, offset: %s", + tenant_id, + offset, ) with get_vespa_http_client() as http_client: @@ -1010,7 +1021,7 @@ class VespaIndex(DocumentIndex): offset += limit # Move to the next page logger.debug( - f"Retrieved {len(document_ids)} document IDs for tenant_id: {tenant_id}" + "Retrieved %s document IDs for tenant_id: %s", len(document_ids), tenant_id ) return document_ids @@ -1036,7 +1047,7 @@ class VespaIndex(DocumentIndex): def _delete_document( delete_request: "_VespaDeleteRequest", http_client: httpx.Client ) -> None: - logger.debug(f"Deleting document with ID {delete_request.document_id}") + logger.debug("Deleting document with ID %s", delete_request.document_id) response = http_client.delete( delete_request.url, headers={"Content-Type": "application/json"}, @@ -1044,7 +1055,7 @@ class VespaIndex(DocumentIndex): ) response.raise_for_status() - logger.debug(f"Starting batch deletion for {len(delete_requests)} documents") + logger.debug("Starting batch deletion for %s documents", len(delete_requests)) with concurrent.futures.ThreadPoolExecutor(max_workers=NUM_THREADS) as executor: with get_vespa_http_client() as http_client: @@ -1066,9 +1077,9 @@ class VespaIndex(DocumentIndex): doc_id = future_to_document_id[future] try: future.result() - logger.debug(f"Successfully deleted document: {doc_id}") + logger.debug("Successfully deleted document: %s", doc_id) except httpx.HTTPError as e: - logger.error(f"Failed to delete document {doc_id}: {e}") + logger.error("Failed to delete document %s: %s", doc_id, e) # Optionally, implement retry logic or error handling here logger.info("Batch deletion completed") diff --git a/backend/onyx/document_index/vespa/indexing_utils.py b/backend/onyx/document_index/vespa/indexing_utils.py index cad9a717ef..6ea46f8e7c 100644 --- a/backend/onyx/document_index/vespa/indexing_utils.py +++ b/backend/onyx/document_index/vespa/indexing_utils.py @@ -80,7 +80,7 @@ def _does_doc_chunk_exist( return False if doc_fetch_response.status_code != 200: - logger.debug(f"Failed to check for document with URL {doc_url}") + logger.debug("Failed to check for document with URL %s", doc_url) raise RuntimeError( f"Unexpected fetch document by ID value from Vespa: " f"error={doc_fetch_response.status_code} " @@ -227,7 +227,7 @@ def _index_vespa_chunk( if chunk.tenant_id: vespa_document_fields[TENANT_ID] = chunk.tenant_id vespa_url = f"{DOCUMENT_ID_ENDPOINT.format(index_name=index_name)}/{vespa_chunk_id}" - logger.debug(f'Indexing to URL "{vespa_url}"') + logger.debug('Indexing to URL "%s"', vespa_url) # Retry logic with exponential backoff for rate limiting for attempt in range(INDEXING_MAX_RETRIES): @@ -246,10 +246,12 @@ def _index_vespa_chunk( INDEXING_BASE_DELAY * (2**attempt), INDEXING_MAX_DELAY ) * random.uniform(0.5, 1.0) logger.warning( - f"Rate limited while indexing document '{document.id}' " - f"(attempt {attempt + 1}/{INDEXING_MAX_RETRIES}). " - f"Vespa response: '{e.response.text}'. " - f"Backing off for {delay:.2f} seconds." + "Rate limited while indexing document '%s' (attempt %s/%s). Vespa response: '%s'. Backing off for %s seconds.", + document.id, + attempt + 1, + INDEXING_MAX_RETRIES, + e.response.text, + format(delay, ".2f"), ) time.sleep(delay) continue @@ -259,7 +261,9 @@ def _index_vespa_chunk( ) from e elif e.response.status_code == HTTPStatus.INSUFFICIENT_STORAGE: logger.error( - f"Failed to index document: '{document.id}'. Got response: '{e.response.text}'" + "Failed to index document: '%s'. Got response: '%s'", + document.id, + e.response.text, ) logger.error( "NOTE: HTTP Status 507 Insufficient Storage usually means " @@ -277,21 +281,29 @@ def _index_vespa_chunk( ): # Non-retryable errors - fail immediately logger.error( - f"Non-retryable HTTP {e.response.status_code} error for document '{document.id}'" + "Non-retryable HTTP %s error for document '%s'", + e.response.status_code, + document.id, ) raise # Retry other errors with shorter backoff if attempt < INDEXING_MAX_RETRIES - 1: delay = INDEXING_BASE_DELAY * (1.5**attempt) logger.warning( - f"HTTP error {e.response.status_code} while indexing document '{document.id}' " - f"(attempt {attempt + 1}/{INDEXING_MAX_RETRIES}). Retrying in {delay:.2f} seconds." + "HTTP error %s while indexing document '%s' (attempt %s/%s). Retrying in %s seconds.", + e.response.status_code, + document.id, + attempt + 1, + INDEXING_MAX_RETRIES, + format(delay, ".2f"), ) time.sleep(delay) continue else: logger.exception( - f"Failed to index document: '{document.id}'. Got response: '{e.response.text}'" + "Failed to index document: '%s'. Got response: '%s'", + document.id, + e.response.text, ) raise except Exception as e: @@ -299,14 +311,17 @@ def _index_vespa_chunk( if attempt < INDEXING_MAX_RETRIES - 1: delay = INDEXING_BASE_DELAY * (1.5**attempt) logger.warning( - f"Error while indexing document '{document.id}' " - f"(attempt {attempt + 1}/{INDEXING_MAX_RETRIES}): {str(e)}. " - f"Retrying in {delay:.2f} seconds." + "Error while indexing document '%s' (attempt %s/%s): %s. Retrying in %s seconds.", + document.id, + attempt + 1, + INDEXING_MAX_RETRIES, + str(e), + format(delay, ".2f"), ) time.sleep(delay) continue else: - logger.exception(f"Failed to index document: '{document.id}'") + logger.exception("Failed to index document: '%s'", document.id) raise diff --git a/backend/onyx/document_index/vespa/shared_utils/utils.py b/backend/onyx/document_index/vespa/shared_utils/utils.py index 370c3bd297..38399eea62 100644 --- a/backend/onyx/document_index/vespa/shared_utils/utils.py +++ b/backend/onyx/document_index/vespa/shared_utils/utils.py @@ -90,18 +90,23 @@ def wait_for_vespa_with_timeout(wait_interval: int = 5, wait_limit: int = 60) -> return True except Exception as e: logger.warning( - f"Vespa: Readiness probe failed trying to connect to {url}. Exception: {e}" + "Vespa: Readiness probe failed trying to connect to %s. Exception: %s", + url, + e, ) time_elapsed = time.monotonic() - time_start if time_elapsed > wait_limit: logger.info( - f"Vespa: Readiness probe did not succeed within the timeout ({wait_limit} seconds)." + "Vespa: Readiness probe did not succeed within the timeout (%s seconds).", + wait_limit, ) return False logger.info( - f"Vespa: Readiness probe ongoing. elapsed={time_elapsed:.1f} timeout={wait_limit:.1f}" + "Vespa: Readiness probe ongoing. elapsed=%s timeout=%s", + format(time_elapsed, ".1f"), + format(wait_limit, ".1f"), ) time.sleep(wait_interval) diff --git a/backend/onyx/document_index/vespa/shared_utils/vespa_request_builders.py b/backend/onyx/document_index/vespa/shared_utils/vespa_request_builders.py index 9f44e87ad5..ea7f369871 100644 --- a/backend/onyx/document_index/vespa/shared_utils/vespa_request_builders.py +++ b/backend/onyx/document_index/vespa/shared_utils/vespa_request_builders.py @@ -155,7 +155,7 @@ def build_vespa_filters( try: pid = int(persona_id) except Exception: - logger.warning(f"Invalid persona ID: {persona_id}") + logger.warning("Invalid persona ID: %s", persona_id) return "" return f'({PERSONAS} contains "{pid}")' diff --git a/backend/onyx/document_index/vespa/vespa_document_index.py b/backend/onyx/document_index/vespa/vespa_document_index.py index 3162085eef..cfcf2f7366 100644 --- a/backend/onyx/document_index/vespa/vespa_document_index.py +++ b/backend/onyx/document_index/vespa/vespa_document_index.py @@ -264,8 +264,11 @@ def _update_single_chunk( resp.raise_for_status() except httpx.HTTPStatusError as e: logger.error( - f"Failed to update doc chunk {doc_chunk_id} (doc_id={doc_id}). " - f"Code: {e.response.status_code}. Details: {e.response.text}" + "Failed to update doc chunk %s (doc_id=%s). Code: %s. Details: %s", + doc_chunk_id, + doc_id, + e.response.status_code, + e.response.text, ) # Re-raise so the @retry decorator will catch and retry, unless the # status code is < 5xx, in which case wrap the exception in something @@ -523,7 +526,7 @@ class VespaDocumentIndex(DocumentIndex): ) logger.info( - f"Updated {len(doc_chunk_ids)} chunks for document {doc_id}." + "Updated %s chunks for document %s.", len(doc_chunk_ids), doc_id ) def id_based_retrieval( @@ -594,9 +597,9 @@ class VespaDocumentIndex(DocumentIndex): f"hybrid_search_{query_type.value}_base_{len(query_embedding)}" ) - logger.info(f"Selected ranking profile: {ranking_profile}") + logger.info("Selected ranking profile: %s", ranking_profile) - logger.debug(f"Query YQL: {yql}") + logger.debug("Query YQL: %s", yql) # In this interface we do not pass in hybrid alpha. Tracing the codepath # of the legacy Vespa interface, it so happens that KEYWORD always diff --git a/backend/onyx/error_handling/exceptions.py b/backend/onyx/error_handling/exceptions.py index 357c82c26b..3fab19f8fb 100644 --- a/backend/onyx/error_handling/exceptions.py +++ b/backend/onyx/error_handling/exceptions.py @@ -63,9 +63,9 @@ def log_onyx_error(exc: OnyxError) -> None: detail = exc.detail status_code = exc.status_code if status_code >= 500: - logger.error(f"OnyxError {exc.error_code.code}: {detail}") + logger.error("OnyxError %s: %s", exc.error_code.code, detail) elif status_code >= 400: - logger.warning(f"OnyxError {exc.error_code.code}: {detail}") + logger.warning("OnyxError %s: %s", exc.error_code.code, detail) def onyx_error_to_json_response(exc: OnyxError) -> JSONResponse: diff --git a/backend/onyx/evals/eval.py b/backend/onyx/evals/eval.py index b5223bc122..497a6115a8 100644 --- a/backend/onyx/evals/eval.py +++ b/backend/onyx/evals/eval.py @@ -249,8 +249,11 @@ def _get_answer_with_tools( ) logger.info( - f"Eval completed. Tools called: {result.tools_called}.\n" - f"Assertion passed: {assertion_passed}. Details: {assertion_details}\n" + "Eval completed. Tools called: %s.\n" + "Assertion passed: %s. Details: %s", + result.tools_called, + assertion_passed, + assertion_details, ) return EvalToolResult( @@ -333,7 +336,10 @@ def _get_multi_turn_answer_with_tools( # Process each turn sequentially for turn_idx, msg in enumerate(messages): logger.info( - f"Processing turn {turn_idx + 1}/{len(messages)}: {msg.message[:50]}..." + "Processing turn %s/%s: %s...", + turn_idx + 1, + len(messages), + msg.message[:50], ) # Handle per-turn tool forcing @@ -403,8 +409,12 @@ def _get_multi_turn_answer_with_tools( ) logger.info( - f"Turn {turn_idx + 1} completed. Tools called: {result.tools_called}.\n" - f"Assertion passed: {assertion_passed}. Details: {assertion_details}\n" + "Turn %s completed. Tools called: %s.\n" + "Assertion passed: %s. Details: %s", + turn_idx + 1, + result.tools_called, + assertion_passed, + assertion_details, ) turn_results.append( diff --git a/backend/onyx/evals/providers/local.py b/backend/onyx/evals/providers/local.py index b3bb9f55b7..0b9a5170fb 100644 --- a/backend/onyx/evals/providers/local.py +++ b/backend/onyx/evals/providers/local.py @@ -172,7 +172,7 @@ class LocalEvalProvider(EvalProvider): except Exception as e: print(f" {RED}ERROR:{RESET} {e}") failed[0] += 1 - logger.exception(f"Error running eval for input: {message}") + logger.exception("Error running eval for input: %s", message) def _run_multi_turn_eval( self, @@ -232,4 +232,4 @@ class LocalEvalProvider(EvalProvider): except Exception as e: print(f" {RED}ERROR:{RESET} {e}") failed[0] += 1 - logger.exception(f"Error running multi-turn eval: {first_msg}") + logger.exception("Error running multi-turn eval: %s", first_msg) diff --git a/backend/onyx/federated_connectors/federated_retrieval.py b/backend/onyx/federated_connectors/federated_retrieval.py index e109833900..82141368c3 100644 --- a/backend/onyx/federated_connectors/federated_retrieval.py +++ b/backend/onyx/federated_connectors/federated_retrieval.py @@ -71,28 +71,29 @@ def get_federated_retrieval_functions( mapping.federated_connector.config or {} ) logger.debug( - f"Found Slack federated connector config: {slack_federated_connector_config}" + "Found Slack federated connector config: %s", + slack_federated_connector_config, ) break if slack_federated_connector_config is None: logger.debug( - f"Skipping Slack federated search: document sets {document_set_names} " - "are not associated with any Slack federated connector" + "Skipping Slack federated search: document sets %s are not associated with any Slack federated connector", + document_set_names, ) # Return empty list - no Slack federated search for this context return [] try: slack_bots = fetch_slack_bots(db_session) - logger.debug(f"Found {len(slack_bots)} Slack bots") + logger.debug("Found %s Slack bots", len(slack_bots)) # First try to find a bot with user token tenant_slack_bot = next( (bot for bot in slack_bots if bot.enabled and bot.user_token), None ) if tenant_slack_bot: - logger.debug(f"Selected bot with user_token: {tenant_slack_bot.name}") + logger.debug("Selected bot with user_token: %s", tenant_slack_bot.name) else: # Fall back to any enabled bot without user token tenant_slack_bot = next( @@ -100,7 +101,8 @@ def get_federated_retrieval_functions( ) if tenant_slack_bot: logger.debug( - f"Selected bot without user_token: {tenant_slack_bot.name} (limited functionality)" + "Selected bot without user_token: %s (limited functionality)", + tenant_slack_bot.name, ) else: logger.warning("No enabled Slack bots found") @@ -121,7 +123,8 @@ def get_federated_retrieval_functions( ) if not tenant_slack_bot.user_token: logger.warning( - f"Using bot_token for Slack search (limited functionality): {tenant_slack_bot.name}" + "Using bot_token for Slack search (limited functionality): %s", + tenant_slack_bot.name, ) # For bot context, we don't need real OAuth credentials @@ -147,7 +150,8 @@ def get_federated_retrieval_functions( # Use connector config for channel filtering (guaranteed to exist at this point) connector_entities = slack_federated_connector_config logger.debug( - f"Using Slack federated connector entities for bot context: {connector_entities}" + "Using Slack federated connector entities for bot context: %s", + connector_entities, ) def create_slack_retrieval_function( @@ -182,12 +186,13 @@ def get_federated_retrieval_functions( ) ) logger.debug( - f"Added Slack federated search for bot, returning {len(federated_retrieval_infos_slack)} retrieval functions" + "Added Slack federated search for bot, returning %s retrieval functions", + len(federated_retrieval_infos_slack), ) return federated_retrieval_infos_slack except Exception as e: - logger.warning(f"Could not setup Slack bot federated search: {e}") + logger.warning("Could not setup Slack bot federated search: %s", e) # Fall through to regular federated connector logic if user_id is None: diff --git a/backend/onyx/federated_connectors/oauth_utils.py b/backend/onyx/federated_connectors/oauth_utils.py index d5117ed519..6eddc19f71 100644 --- a/backend/onyx/federated_connectors/oauth_utils.py +++ b/backend/onyx/federated_connectors/oauth_utils.py @@ -84,7 +84,10 @@ def generate_oauth_state( cache.set(cache_key, json.dumps(session.to_dict()), ex=ttl) logger.info( - f"Generated OAuth state for federated_connector_id={federated_connector_id}, user_id={user_id}, state={state_b64}" + "Generated OAuth state for federated_connector_id=%s, user_id=%s, state=%s", + federated_connector_id, + user_id, + state_b64, ) return state_b64 diff --git a/backend/onyx/federated_connectors/slack/federated_connector.py b/backend/onyx/federated_connectors/slack/federated_connector.py index d185ce54a4..6d18020577 100644 --- a/backend/onyx/federated_connectors/slack/federated_connector.py +++ b/backend/onyx/federated_connectors/slack/federated_connector.py @@ -57,10 +57,10 @@ class SlackFederatedConnector(FederatedConnector): SlackEntities(**entities) return True except ValidationError as e: - logger.warning(f"Validation error for Slack entities: {e}") + logger.warning("Validation error for Slack entities: %s", e) return False except Exception as e: - logger.error(f"Error validating Slack entities: {e}") + logger.error("Error validating Slack entities: %s", e) return False @classmethod @@ -290,7 +290,7 @@ class SlackFederatedConnector(FederatedConnector): Returns: Search results in SlackSearchResponse format """ - logger.debug(f"Slack federated search called with entities: {entities}") + logger.debug("Slack federated search called with entities: %s", entities) # Get team_id from Slack API for caching and filtering team_id = None @@ -304,9 +304,9 @@ class SlackFederatedConnector(FederatedConnector): auth_response.data ) team_id = auth_data.get("team_id") - logger.debug(f"Slack team_id: {team_id}") + logger.debug("Slack team_id: %s", team_id) except Exception as e: - logger.warning(f"Could not fetch team_id from Slack API: {e}") + logger.warning("Could not fetch team_id from Slack API: %s", e) with get_session_with_current_tenant() as db_session: return slack_retrieval( diff --git a/backend/onyx/file_processing/extract_file_text.py b/backend/onyx/file_processing/extract_file_text.py index 6d793a47a5..ce2f4da62c 100644 --- a/backend/onyx/file_processing/extract_file_text.py +++ b/backend/onyx/file_processing/extract_file_text.py @@ -446,7 +446,9 @@ def read_docx_file( UnsupportedFormatException, ) as e: logger.warning( - f"Failed to extract docx {file_name or 'docx file'}: {e}. Attempting to read as text file." + "Failed to extract docx %s: %s. Attempting to read as text file.", + file_name or "docx file", + e, ) # May be an invalid docx, but still a valid text file @@ -631,7 +633,9 @@ def xlsx_sheet_extraction(file: IO[Any], file_name: str = "") -> list[tuple[str, except Exception as e: if any(s in str(e) for s in KNOWN_OPENPYXL_BUGS): logger.warning( - f"Failed to extract text from {file_name or 'xlsx file'}. This happens due to a bug in openpyxl. {e}" + "Failed to extract text from %s. This happens due to a bug in openpyxl. %s", + file_name or "xlsx file", + e, ) return [] raise @@ -669,7 +673,8 @@ def eml_to_text(file: IO[Any]) -> str: raw_file = text_file.detach() except Exception as detach_error: logger.warning( - f"Failed to detach TextIOWrapper for EML upload, using original file: {detach_error}" + "Failed to detach TextIOWrapper for EML upload, using original file: %s", + detach_error, ) raw_file = file try: @@ -686,7 +691,7 @@ def eml_to_text(file: IO[Any]) -> str: elif isinstance(payload, list): text_content.extend(item for item in payload if isinstance(item, str)) else: - logger.warning(f"Unexpected payload type: {type(payload)}") + logger.warning("Unexpected payload type: %s", type(payload)) return TEXT_SECTION_SEPARATOR.join(text_content) @@ -735,7 +740,8 @@ def extract_file_text( return unstructured_to_text(file, file_name) except Exception as unstructured_error: logger.error( - f"Failed to process with Unstructured: {str(unstructured_error)}. Falling back to normal processing." + "Failed to process with Unstructured: %s. Falling back to normal processing.", + str(unstructured_error), ) if extension is None: extension = get_file_ext(file_name) @@ -757,7 +763,7 @@ def extract_file_text( raise RuntimeError( f"Failed to process file {file_name or 'Unknown'}: {str(e)}" ) from e - logger.warning(f"Failed to process file {file_name or 'Unknown'}: {str(e)}") + logger.warning("Failed to process file %s: %s", file_name or "Unknown", str(e)) return "" @@ -814,7 +820,7 @@ def extract_text_and_images( ) # Clean up any temporary objects and force garbage collection unreachable = gc.collect() - logger.info(f"Unreachable objects: {unreachable}") + logger.info("Unreachable objects: %s", unreachable) return res @@ -836,7 +842,8 @@ def _extract_text_and_images( ) except Exception as e: logger.error( - f"Failed to process with Unstructured: {str(e)}. Falling back to normal processing." + "Failed to process with Unstructured: %s. Falling back to normal processing.", + str(e), ) file.seek(0) # Reset file pointer just in case @@ -913,7 +920,7 @@ def _extract_text_and_images( return ExtractionResult(text_content="", embedded_images=[], metadata={}) except Exception as e: - logger.exception(f"Failed to extract text/images from {file_name}: {e}") + logger.exception("Failed to extract text/images from %s: %s", file_name, e) return ExtractionResult(text_content="", embedded_images=[], metadata={}) diff --git a/backend/onyx/file_processing/html_utils.py b/backend/onyx/file_processing/html_utils.py index f9376a2802..0371df4c24 100644 --- a/backend/onyx/file_processing/html_utils.py +++ b/backend/onyx/file_processing/html_utils.py @@ -212,7 +212,7 @@ def web_html_cleanup( if not page_text: raise ValueError("Empty content returned by trafilatura.") except Exception as e: - logger.info(f"Trafilatura parsing failed: {e}. Falling back on bs4.") + logger.info("Trafilatura parsing failed: %s. Falling back on bs4.", e) page_text = format_document_soup(soup) else: page_text = format_document_soup(soup) diff --git a/backend/onyx/file_processing/image_utils.py b/backend/onyx/file_processing/image_utils.py index 4e90c8490b..c31331b737 100644 --- a/backend/onyx/file_processing/image_utils.py +++ b/backend/onyx/file_processing/image_utils.py @@ -90,7 +90,7 @@ def store_image_and_create_section( file_id=file_id, ) except Exception as e: - logger.error(f"Failed to store image: {e}") + logger.error("Failed to store image: %s", e) raise e # Create an ImageSection with empty text (will be filled by LLM later in the pipeline) diff --git a/backend/onyx/file_processing/password_validation.py b/backend/onyx/file_processing/password_validation.py index 53a66db84c..cea56afff3 100644 --- a/backend/onyx/file_processing/password_validation.py +++ b/backend/onyx/file_processing/password_validation.py @@ -90,7 +90,7 @@ def is_file_password_protected( if extension not in extension_to_function: logger.warning( - f"Extension={extension} can be password protected, but no function found" + "Extension=%s can be password protected, but no function found", extension ) return False diff --git a/backend/onyx/file_processing/unstructured.py b/backend/onyx/file_processing/unstructured.py index 411b746445..84f7db3e16 100644 --- a/backend/onyx/file_processing/unstructured.py +++ b/backend/onyx/file_processing/unstructured.py @@ -49,7 +49,9 @@ def _sdk_partition_request( ) return request except Exception as e: - logger.error(f"Error creating partition request for file {file_name}: {str(e)}") + logger.error( + "Error creating partition request for file %s: %s", file_name, str(e) + ) raise @@ -57,7 +59,7 @@ def unstructured_to_text(file: IO[Any], file_name: str) -> str: from unstructured.staging.base import dict_to_elements from unstructured_client import UnstructuredClient - logger.debug(f"Starting to read file: {file_name}") + logger.debug("Starting to read file: %s", file_name) req = _sdk_partition_request(file, file_name, strategy="fast") unstructured_client = UnstructuredClient(api_key_auth=get_unstructured_api_key()) diff --git a/backend/onyx/file_store/document_batch_storage.py b/backend/onyx/file_store/document_batch_storage.py index 1d873c600b..9d3702a157 100644 --- a/backend/onyx/file_store/document_batch_storage.py +++ b/backend/onyx/file_store/document_batch_storage.py @@ -125,7 +125,7 @@ class DocumentBatchStorage(ABC): if converted_keys: doc_id = doc_dict.get("id", "unknown") logger.warning( - f"Normalized legacy metadata for document {doc_id}: {converted_keys}" + "Normalized legacy metadata for document %s: %s", doc_id, converted_keys ) doc_dict["metadata"] = normalized_metadata @@ -167,10 +167,13 @@ class FileStoreDocumentBatchStorage(DocumentBatchStorage): ) logger.debug( - f"Stored batch {batch_num} with {len(documents)} documents to FileStore as {file_name}" + "Stored batch %s with %s documents to FileStore as %s", + batch_num, + len(documents), + file_name, ) except Exception as e: - logger.error(f"Failed to store batch {batch_num}: {e}") + logger.error("Failed to store batch %s: %s", batch_num, e) raise def get_batch(self, batch_num: int) -> list[Document] | None: @@ -184,7 +187,7 @@ class FileStoreDocumentBatchStorage(DocumentBatchStorage): file_type="application/json", ): logger.warning( - f"Batch {batch_num} not found in FileStore with name {file_name}" + "Batch %s not found in FileStore with name %s", batch_num, file_name ) return None @@ -193,23 +196,27 @@ class FileStoreDocumentBatchStorage(DocumentBatchStorage): documents = self._deserialize_documents(data) logger.debug( - f"Retrieved batch {batch_num} with {len(documents)} documents from FileStore" + "Retrieved batch %s with %s documents from FileStore", + batch_num, + len(documents), ) return documents except Exception as e: - logger.error(f"Failed to retrieve batch {batch_num}: {e}") + logger.error("Failed to retrieve batch %s: %s", batch_num, e) raise def delete_batch_by_name(self, batch_file_name: str) -> None: """Delete a specific batch from FileStore.""" self.file_store.delete_file(batch_file_name) - logger.debug(f"Deleted batch {batch_file_name} from FileStore") + logger.debug("Deleted batch %s from FileStore", batch_file_name) def delete_batch_by_num(self, batch_num: int) -> None: """Delete a specific batch from FileStore.""" batch_file_name = self._get_batch_file_name(batch_num) self.delete_batch_by_name(batch_file_name) - logger.debug(f"Deleted batch num {batch_num} {batch_file_name} from FileStore") + logger.debug( + "Deleted batch num %s %s from FileStore", batch_num, batch_file_name + ) def cleanup_all_batches(self) -> None: """Clean up all batches for this index attempt.""" @@ -235,7 +242,7 @@ class FileStoreDocumentBatchStorage(DocumentBatchStorage): path_info = self.extract_path_info(batch_file_name) if path_info is None: logger.warning( - f"Could not extract path info from batch file: {batch_file_name}" + "Could not extract path info from batch file: %s", batch_file_name ) continue new_batch_file_name = self._get_batch_file_name(path_info.batch_num) @@ -255,7 +262,7 @@ class FileStoreDocumentBatchStorage(DocumentBatchStorage): batch_num=int(batch_num.split(".")[0]), # remove .json ) except Exception as e: - logger.error(f"Failed to extract path info from {path}: {e}") + logger.error("Failed to extract path info from %s: %s", path, e) return None diff --git a/backend/onyx/file_store/file_store.py b/backend/onyx/file_store/file_store.py index f485396317..439da5d29f 100644 --- a/backend/onyx/file_store/file_store.py +++ b/backend/onyx/file_store/file_store.py @@ -231,7 +231,7 @@ class S3BackedFileStore(FileStore): self._s3_client = boto3.client(**client_kwargs) except Exception as e: - logger.error(f"Failed to initialize S3 client: {e}") + logger.error("Failed to initialize S3 client: %s", e) raise RuntimeError(f"Failed to initialize S3 client: {e}") return self._s3_client @@ -255,7 +255,7 @@ class S3BackedFileStore(FileStore): # Log if truncation occurred (when the key is exactly at the limit) if len(s3_key) == 1024: - logger.info(f"File name was too long and was truncated: {file_name}") + logger.info("File name was too long and was truncated: %s", file_name) return s3_key @@ -267,12 +267,12 @@ class S3BackedFileStore(FileStore): # Check if bucket exists try: s3_client.head_bucket(Bucket=bucket_name) - logger.info(f"S3 bucket '{bucket_name}' already exists") + logger.info("S3 bucket '%s' already exists", bucket_name) except ClientError as e: error_code = e.response["Error"]["Code"] if error_code == "404": # Bucket doesn't exist, create it - logger.info(f"Creating S3 bucket '{bucket_name}'") + logger.info("Creating S3 bucket '%s'", bucket_name) # For AWS S3, we need to handle region-specific bucket creation region = ( @@ -291,18 +291,18 @@ class S3BackedFileStore(FileStore): # For us-east-1 or MinIO/other S3-compatible services s3_client.create_bucket(Bucket=bucket_name) - logger.info(f"Successfully created S3 bucket '{bucket_name}'") + logger.info("Successfully created S3 bucket '%s'", bucket_name) elif error_code == "403": # Bucket exists but we don't have permission to access it logger.warning( - f"S3 bucket '{bucket_name}' exists but access is forbidden" + "S3 bucket '%s' exists but access is forbidden", bucket_name ) raise RuntimeError( f"Access denied to S3 bucket '{bucket_name}'. Check credentials and permissions." ) else: # Some other error occurred - logger.error(f"Failed to check S3 bucket '{bucket_name}': {e}") + logger.error("Failed to check S3 bucket '%s': %s", bucket_name, e) raise RuntimeError(f"Failed to check S3 bucket '{bucket_name}': {e}") def has_file( @@ -405,7 +405,7 @@ class S3BackedFileStore(FileStore): Bucket=file_record.bucket_name, Key=file_record.object_key ) except ClientError: - logger.error(f"Failed to read file {file_id} from S3") + logger.error("Failed to read file %s from S3", file_id) raise # FIX: Stream file content instead of loading entire file into memory @@ -451,7 +451,7 @@ class S3BackedFileStore(FileStore): ) return response.get("ContentLength") except Exception as e: - logger.warning(f"Error getting file size for {file_id}: {e}") + logger.warning("Error getting file size for %s: %s", file_id, e) return None def delete_file( @@ -473,8 +473,9 @@ class S3BackedFileStore(FileStore): return if not file_record.bucket_name: logger.error( - f"File record {file_id} with key {file_record.object_key} " - "has no bucket name, cannot delete from filestore" + "File record %s with key %s has no bucket name, cannot delete from filestore", + file_id, + file_record.object_key, ) delete_filerecord_by_file_id(file_id=file_id, db_session=db_session) db_session.commit() @@ -491,8 +492,9 @@ class S3BackedFileStore(FileStore): # since the end goal (object not existing) is achieved if e.response.get("Error", {}).get("Code") == "NoSuchKey": logger.warning( - f"delete_file: File {file_id} not found in file store (key: {file_record.object_key}), " - "cleaning up database record." + "delete_file: File %s not found in file store (key: %s), cleaning up database record.", + file_id, + file_record.object_key, ) else: raise @@ -564,7 +566,10 @@ class S3BackedFileStore(FileStore): except Exception as e: db_session.rollback() logger.exception( - f"Failed to change file ID from {old_file_id} to {new_file_id}: {e}" + "Failed to change file ID from %s to %s: %s", + old_file_id, + new_file_id, + e, ) raise diff --git a/backend/onyx/file_store/postgres_file_store.py b/backend/onyx/file_store/postgres_file_store.py index 3114736d96..0bef3debdd 100644 --- a/backend/onyx/file_store/postgres_file_store.py +++ b/backend/onyx/file_store/postgres_file_store.py @@ -167,7 +167,9 @@ class PostgresBackedFileStore(FileStore): _delete_large_object(raw_conn, old_oid) except Exception: logger.warning( - f"Failed to unlink old large object {old_oid} for file {file_id}" + "Failed to unlink old large object %s for file %s", + old_oid, + file_id, ) session.commit() @@ -178,7 +180,7 @@ class PostgresBackedFileStore(FileStore): _delete_large_object(raw_conn, oid) except Exception: logger.exception( - f"Failed to delete large object {oid} for file {file_id}" + "Failed to delete large object %s for file %s", oid, file_id ) raise e @@ -219,7 +221,7 @@ class PostgresBackedFileStore(FileStore): ) return record.file_size except Exception as e: - logger.warning(f"Error getting file size for {file_id}: {e}") + logger.warning("Error getting file size for %s: %s", file_id, e) return None def delete_file( @@ -245,7 +247,9 @@ class PostgresBackedFileStore(FileStore): _delete_large_object(raw_conn, file_content.lobj_oid) except Exception: logger.warning( - f"Large object {file_content.lobj_oid} for file {file_id} not found, cleaning up records only." + "Large object %s for file %s not found, cleaning up records only.", + file_content.lobj_oid, + file_id, ) delete_file_content_by_file_id(file_id=file_id, db_session=session) @@ -309,7 +313,10 @@ class PostgresBackedFileStore(FileStore): except Exception as e: session.rollback() logger.exception( - f"Failed to change file ID from {old_file_id} to {new_file_id}: {e}" + "Failed to change file ID from %s to %s: %s", + old_file_id, + new_file_id, + e, ) raise diff --git a/backend/onyx/file_store/staging.py b/backend/onyx/file_store/staging.py index 631d5c77ee..b1141a2cea 100644 --- a/backend/onyx/file_store/staging.py +++ b/backend/onyx/file_store/staging.py @@ -85,11 +85,12 @@ def delete_files_best_effort( deleted += 1 except Exception: logger.exception( - f"[{context}] Failed to delete file_id={file_id}; will be " - "retried on next sweep." + "[%s] Failed to delete file_id=%s; will be retried on next sweep.", + context, + file_id, ) if deleted: - logger.info(f"[{context}] reaped {deleted} file(s)") + logger.info("[%s] reaped %s file(s)", context, deleted) return deleted diff --git a/backend/onyx/file_store/utils.py b/backend/onyx/file_store/utils.py index f411e0d854..6d5c715eb8 100644 --- a/backend/onyx/file_store/utils.py +++ b/backend/onyx/file_store/utils.py @@ -55,7 +55,7 @@ def store_plaintext(file_id: str, plaintext_content: str) -> bool: ) return True except Exception as e: - logger.warning(f"Failed to store plaintext for {file_id}: {e}") + logger.warning("Failed to store plaintext for %s: %s", file_id, e) return False @@ -134,7 +134,7 @@ def load_user_file(file_id: UUID, db_session: Session) -> InMemoryChatFile: status = "plaintext" return chat_file except Exception as e: - logger.warning(f"Failed to load plaintext for user file {user_file.id}: {e}") + logger.warning("Failed to load plaintext for user file %s: %s", user_file.id, e) # Fall back to original file if plaintext not available file_io = file_store.read_file(user_file.file_id, mode="b") @@ -148,7 +148,10 @@ def load_user_file(file_id: UUID, db_session: Session) -> InMemoryChatFile: return chat_file finally: logger.debug( - f"load_user_file finished: file_id={user_file.file_id} chat_file_type={chat_file_type} status={status}" + "load_user_file finished: file_id=%s chat_file_type=%s status=%s", + user_file.file_id, + chat_file_type, + status, ) @@ -333,7 +336,8 @@ def verify_user_files( user_file_ids.append(UUID(file_descriptor["user_file_id"])) except (ValueError, TypeError): logger.warning( - f"Invalid user_file_id in file descriptor: {file_descriptor['user_file_id']}" + "Invalid user_file_id in file descriptor: %s", + file_descriptor["user_file_id"], ) continue else: diff --git a/backend/onyx/indexing/adapters/user_file_indexing_adapter.py b/backend/onyx/indexing/adapters/user_file_indexing_adapter.py index 5736310354..cc49aeb9c5 100644 --- a/backend/onyx/indexing/adapters/user_file_indexing_adapter.py +++ b/backend/onyx/indexing/adapters/user_file_indexing_adapter.py @@ -96,7 +96,9 @@ class UserFileIndexingAdapter: break except OperationalError as e: logger.warning( - f"Failed to acquire locks for user files on attempt {i}, retrying. Error: {e}" + "Failed to acquire locks for user files on attempt %s, retrying. Error: %s", + i, + e, ) time.sleep(retry_delay) @@ -157,7 +159,7 @@ class UserFileIndexingAdapter: provider_type=llm.config.model_provider, ) except Exception as e: - logger.error(f"Error getting tokenizer: {e}") + logger.error("Error getting tokenizer: %s", e) llm_tokenizer = None user_file_id_to_raw_text: dict[str, str] = {} diff --git a/backend/onyx/indexing/chunker.py b/backend/onyx/indexing/chunker.py index 2b926a1b60..02455a8521 100644 --- a/backend/onyx/indexing/chunker.py +++ b/backend/onyx/indexing/chunker.py @@ -191,7 +191,7 @@ class Chunker: ) -> list[DocAwareChunk]: # Specifically for reproducing an issue with gmail if document.source == DocumentSource.GMAIL: - logger.debug(f"Chunking {document.semantic_identifier}") + logger.debug("Chunking %s", document.semantic_identifier) # Title prep title = extract_blurb( diff --git a/backend/onyx/indexing/chunking/document_chunker.py b/backend/onyx/indexing/chunking/document_chunker.py index efe545de0b..4861f57aba 100644 --- a/backend/onyx/indexing/chunking/document_chunker.py +++ b/backend/onyx/indexing/chunking/document_chunker.py @@ -87,8 +87,9 @@ class DocumentChunker: if not section_text and (not document.title or section_idx > 0): logger.warning( - f"Skipping empty or irrelevant section in doc " - f"{document.semantic_identifier}, link={section.link}" + "Skipping empty or irrelevant section in doc %s, link=%s", + document.semantic_identifier, + section.link, ) continue diff --git a/backend/onyx/indexing/chunking/tabular_section_chunker/tabular_section_chunker.py b/backend/onyx/indexing/chunking/tabular_section_chunker/tabular_section_chunker.py index 1695558ad7..a5fb4f9125 100644 --- a/backend/onyx/indexing/chunking/tabular_section_chunker/tabular_section_chunker.py +++ b/backend/onyx/indexing/chunking/tabular_section_chunker/tabular_section_chunker.py @@ -274,7 +274,7 @@ class TabularChunker(SectionChunker): if not chunk_texts: logger.warning( - f"TabularChunker: skipping unparseable section (link={section.link})" + "TabularChunker: skipping unparseable section (link=%s)", section.link ) return SectionChunkerOutput( payloads=payloads, accumulator=AccumulatorState() diff --git a/backend/onyx/indexing/embedder.py b/backend/onyx/indexing/embedder.py index 08f2ec5e3b..4a8c967aab 100644 --- a/backend/onyx/indexing/embedder.py +++ b/backend/onyx/indexing/embedder.py @@ -299,7 +299,7 @@ def embed_chunks_with_failure_handling( scope.set_tag("tenant_id", tenant_id) scope.fingerprint = ["embedding-failure", type(e).__name__] sentry_sdk.capture_exception(e) - logger.exception(f"Failed to embed chunks for document '{doc_id}'") + logger.exception("Failed to embed chunks for document '%s'", doc_id) failures.append( ConnectorFailure( failed_document=DocumentFailure( diff --git a/backend/onyx/indexing/indexing_pipeline.py b/backend/onyx/indexing/indexing_pipeline.py index 898d358ce7..48287bb0c5 100644 --- a/backend/onyx/indexing/indexing_pipeline.py +++ b/backend/onyx/indexing/indexing_pipeline.py @@ -224,7 +224,7 @@ def _embed_chunks_to_store( if not chunk_batch: continue - logger.debug(f"Embedding batch {batch_idx}: {len(chunk_batch)} chunks") + logger.debug("Embedding batch %s: %s chunks", batch_idx, len(chunk_batch)) chunks_with_embeddings, embedding_failures = embed_chunks_with_failure_handling( chunks=chunk_batch, @@ -372,7 +372,7 @@ def index_doc_batch_with_handler( scope.set_extra("document_ids", document_ids) scope.fingerprint = ["indexing-pipeline-failure", type(e).__name__] sentry_sdk.capture_exception(e) - logger.exception(f"Failed to index document batch: {document_ids}") + logger.exception("Failed to index document batch: %s", document_ids) index_pipeline_result = IndexingPipelineResult( new_docs=0, @@ -432,7 +432,7 @@ def _delete_replaced_files( try: file_store.delete_file(old_file_id, error_on_missing=False) except Exception: - logger.exception(f"Failed to delete replaced file_id={old_file_id}.") + logger.exception("Failed to delete replaced file_id=%s.", old_file_id) def index_doc_batch_prepare( @@ -469,7 +469,9 @@ def index_doc_batch_prepare( doc.id for doc in documents if doc.id not in updatable_doc_ids ] logger.info( - f"Skipping {len(skipped_doc_ids)} documents because they are up to date. Skipped doc IDs: {skipped_doc_ids}" + "Skipping %s documents because they are up to date. Skipped doc IDs: %s", + len(skipped_doc_ids), + skipped_doc_ids, ) # for all updatable docs, upsert into the DB @@ -495,7 +497,9 @@ def index_doc_batch_prepare( ) logger.info( - f"Upserted {len(updatable_docs)} changed docs out of {len(documents)} total docs into the DB" + "Upserted %s changed docs out of %s total docs into the DB", + len(updatable_docs), + len(documents), ) # for all docs, upsert the document to cc pair relationship @@ -549,7 +553,8 @@ def filter_documents(document_batch: list[Document]) -> list[Document]: # This is again verified later in the pipeline after chunking but at that point there should # already be no documents that are empty. logger.warning( - f"Skipping document with ID {document.id} as it has neither title nor content." + "Skipping document with ID %s as it has neither title nor content.", + document.id, ) continue @@ -557,7 +562,7 @@ def filter_documents(document_batch: list[Document]) -> list[Document]: # The title is explicitly empty ("" and not None) and the document is empty # so when building the chunk text representation, it will be empty and unuseable logger.warning( - f"Skipping document with ID {document.id} as the chunks will be empty." + "Skipping document with ID %s as the chunks will be empty.", document.id ) continue @@ -580,8 +585,10 @@ def filter_documents(document_batch: list[Document]) -> list[Document]: # Assumption here is that files that are that long, are generated files and not the type users # generally care for. logger.warning( - f"Skipping document with ID {document.id} as it is too long " - f"({doc_total_chars:,} chars, max={MAX_DOCUMENT_CHARS:,})" + "Skipping document with ID %s as it is too long (%s chars, max=%s)", + document.id, + format(doc_total_chars, ","), + format(MAX_DOCUMENT_CHARS, ","), ) skipped_too_long.append((document.id, doc_total_chars)) continue @@ -595,12 +602,16 @@ def filter_documents(document_batch: list[Document]) -> list[Document]: # Get the source from the first document (all in batch should be same source) source = documents[0].source.value if documents[0].source else "unknown" logger.debug( - f"Document batch filter [{source}]: {len(documents)} docs kept, {len(skipped_too_long)} skipped (too long). " - f"Total chars: {total_chars_in_batch:,}, Avg: {avg_chars:,.0f} chars/doc" + "Document batch filter [%s]: %s docs kept, %s skipped (too long). Total chars: %s, Avg: %s chars/doc", + source, + len(documents), + len(skipped_too_long), + format(total_chars_in_batch, ","), + format(avg_chars, ",.0f"), ) if skipped_too_long: logger.warning( - f"Skipped oversized documents [{source}]: {skipped_too_long[:5]}" + "Skipped oversized documents [%s]: %s", source, skipped_too_long[:5] ) # Log first 5 return documents @@ -695,7 +706,7 @@ def process_image_sections(documents: list[Document]) -> list[IndexingDocument]: file_record = file_store.read_file_record(file_id=section.image_file_id) if not file_record: logger.warning( - f"Image file {section.image_file_id} not found in FileStore" + "Image file %s not found in FileStore", section.image_file_id ) processed_section.text = "[Image could not be processed]" continue @@ -709,7 +720,7 @@ def process_image_sections(documents: list[Document]) -> list[IndexingDocument]: ) ) except Exception as e: - logger.error(f"Error reading image section: {e}") + logger.error("Error reading image section: %s", e) processed_section.text = "[Error processing image]" indexed_documents.append( @@ -854,10 +865,10 @@ def add_chunk_summaries( except LLMRateLimitError as e: # Erroring during chunker is undesirable, so we log the error and continue # TODO: for v2, add robust retry logic - logger.exception(f"Rate limit adding chunk summary: {e}", exc_info=e) + logger.exception("Rate limit adding chunk summary: %s", e, exc_info=e) chunk.chunk_context = "" except Exception as e: - logger.exception(f"Error adding chunk summary: {e}", exc_info=e) + logger.exception("Error adding chunk summary: %s", e, exc_info=e) chunk.chunk_context = "" run_functions_tuples_in_parallel( @@ -998,7 +1009,7 @@ def _apply_document_ingestion_hook( if not hook_result.sections: reason = hook_result.rejection_reason or "Document rejected by hook" logger.info( - f"Document ingestion hook dropped document doc_id={doc.id!r}: {reason}" + "Document ingestion hook dropped document doc_id=%r: %s", doc.id, reason ) return None new_sections: list[TextSection | ImageSection] = [] @@ -1011,12 +1022,13 @@ def _apply_document_ingestion_hook( new_sections.append(TextSection(text=s.text, link=s.link)) else: logger.warning( - f"Document ingestion hook returned a section with neither text nor " - f"image_file_id for doc_id={doc.id!r} — skipping section." + "Document ingestion hook returned a section with neither text nor image_file_id for doc_id=%r — skipping section.", + doc.id, ) if not new_sections: logger.info( - f"Document ingestion hook produced no valid sections for doc_id={doc.id!r} — dropping document." + "Document ingestion hook produced no valid sections for doc_id=%r — dropping document.", + doc.id, ) return None return doc.model_copy(update={"sections": new_sections}) @@ -1085,9 +1097,11 @@ def index_doc_batch( connector_id = getattr(adapter, "connector_id", None) credential_id = getattr(adapter, "credential_id", None) logger.debug( - f"Starting index_doc_batch: connector_id={connector_id}, " - f"credential_id={credential_id}, tenant_id={tenant_id}, " - f"num_docs={len(document_batch)}" + "Starting index_doc_batch: connector_id=%s, credential_id=%s, tenant_id=%s, num_docs=%s", + connector_id, + credential_id, + tenant_id, + len(document_batch), ) # Pull the attempt id off the adapter (when present) so per-stage metrics @@ -1128,7 +1142,7 @@ def index_doc_batch( } for doc in context.indexable_docs ] - logger.debug(f"Starting indexing process for documents: {doc_descriptors}") + logger.debug("Starting indexing process for documents: %s", doc_descriptors) logger.debug("Starting chunking") # NOTE: no special handling for failures here, since the chunker is not diff --git a/backend/onyx/indexing/vector_db_insertion.py b/backend/onyx/indexing/vector_db_insertion.py index 652bfe4ba2..a78d345f3e 100644 --- a/backend/onyx/indexing/vector_db_insertion.py +++ b/backend/onyx/indexing/vector_db_insertion.py @@ -64,8 +64,9 @@ def write_chunks_to_vector_db_with_backoff( # where the 60s read timeout fires when the server is slow to # finish the bulk index.) logger.warning( - f"Failed to write chunk batch to vector db. Trying individual docs. " - f"Batch error: {type(e).__name__}: {e}" + "Failed to write chunk batch to vector db. Trying individual docs. Batch error: %s: %s", + type(e).__name__, + e, ) # give some specific logging on this common failure case. @@ -106,7 +107,7 @@ def write_chunks_to_vector_db_with_backoff( scope.fingerprint = ["vector-db-write-failure", type(e).__name__] sentry_sdk.capture_exception(e) logger.exception( - f"Failed to write document chunks for '{doc_id}' to vector db" + "Failed to write document chunks for '%s' to vector db", doc_id ) # give some specific logging on this common failure case. diff --git a/backend/onyx/key_value_store/store.py b/backend/onyx/key_value_store/store.py index aca9e5a772..f22e92ec24 100644 --- a/backend/onyx/key_value_store/store.py +++ b/backend/onyx/key_value_store/store.py @@ -36,7 +36,7 @@ class PgRedisKVStore(KeyValueStore): except Exception as e: # Fallback gracefully to Postgres if Cache backend fails logger.error( - f"Failed to set value in Cache backend for key '{key}': {str(e)}" + "Failed to set value in Cache backend for key '%s': %s", key, str(e) ) encrypted_val = val if encrypt else None @@ -60,7 +60,7 @@ class PgRedisKVStore(KeyValueStore): return json.loads(cached.decode("utf-8")) except Exception as e: logger.error( - f"Failed to get value from cache for key '{key}': {str(e)}" + "Failed to get value from cache for key '%s': %s", key, str(e) ) with get_session_with_current_tenant() as db_session: @@ -83,7 +83,9 @@ class PgRedisKVStore(KeyValueStore): ex=KV_REDIS_KEY_EXPIRATION, ) except Exception as e: - logger.error(f"Failed to set value in cache for key '{key}': {str(e)}") + logger.error( + "Failed to set value in cache for key '%s': %s", key, str(e) + ) return cast(JSON_ro, value) @@ -91,7 +93,9 @@ class PgRedisKVStore(KeyValueStore): try: self._get_cache().delete(REDIS_KEY_PREFIX + key) except Exception as e: - logger.error(f"Failed to delete value from cache for key '{key}': {str(e)}") + logger.error( + "Failed to delete value from cache for key '%s': %s", key, str(e) + ) with get_session_with_current_tenant() as db_session: result = db_session.query(KVStore).filter_by(key=key).delete() diff --git a/backend/onyx/kg/clustering/clustering.py b/backend/onyx/kg/clustering/clustering.py index a712bc824b..87efa0fd1c 100644 --- a/backend/onyx/kg/clustering/clustering.py +++ b/backend/onyx/kg/clustering/clustering.py @@ -202,7 +202,7 @@ def _cluster_one_grounded_entity( # if there is a match, update the entity, otherwise create a new one with get_session_with_current_tenant() as db_session: if best_entity: - logger.debug(f"Merged {entity.name} with {best_entity.name}") + logger.debug("Merged %s with %s", entity.name, best_entity.name) update_vespa = ( best_entity.document_id is None and entity.document_id is not None ) @@ -281,7 +281,8 @@ def _transfer_one_relationship( } if len(entity_translations) != len(staging_entity_id_names): logger.error( - f"Missing entity translations for {staging_entity_id_names - entity_translations.keys()}" + "Missing entity translations for %s", + staging_entity_id_names - entity_translations.keys(), ) return @@ -312,7 +313,7 @@ def kg_clustering( This will change with deep extraction, where grounded-sourceless entities can be extracted and then need to be clustered. """ - logger.info(f"Starting kg clustering for tenant {tenant_id}") + logger.info("Starting kg clustering for tenant %s", tenant_id) kg_config_settings = get_kg_config_settings() validate_kg_settings(kg_config_settings) @@ -336,7 +337,9 @@ def kg_clustering( # NOTE: we assume every entity is transferred, as we currently only have grounded entities time_delta = time.monotonic() - start_time logger.info( - f"Finished transferring {i_batch + 1} entity batches in {time_delta:.2f}s" + "Finished transferring %s entity batches in %ss", + i_batch + 1, + format(time_delta, ".2f"), ) # Create parent-child relationships in parallel @@ -373,7 +376,9 @@ def kg_clustering( # logger.debug(f"Transferred relationship types batch {i}") time_delta = time.monotonic() - start_time logger.info( - f"Finished transferring {i_batch + 1} relationship type batches in {time_delta:.2f}s" + "Finished transferring %s relationship type batches in %ss", + i_batch + 1, + format(time_delta, ".2f"), ) # Transfer the relationships in parallel @@ -394,7 +399,9 @@ def kg_clustering( # logger.debug(f"Transferred relationships batch {i}") time_delta = time.monotonic() - start_time logger.info( - f"Finished transferring {i_batch + 1} relationship batches in {time_delta:.2f}s" + "Finished transferring %s relationship batches in %ss", + i_batch + 1, + format(time_delta, ".2f"), ) # Update vespa for each document @@ -413,14 +420,16 @@ def kg_clustering( try: update_kg_chunks_vespa_info(update_requests, index_name, tenant_id) except Exception as e: - logger.error(f"Error updating vespa for document {document.id}: {e}") + logger.error("Error updating vespa for document %s: %s", document.id, e) last_lock_time = extend_lock( lock, CELERY_GENERIC_BEAT_LOCK_TIMEOUT, last_lock_time ) # logger.debug(f"Updated vespa for documents batch {i}") time_delta = time.monotonic() - start_time logger.info( - f"Finished updating {i_batch + 1} document batches in {time_delta:.2f}s" + "Finished updating %s document batches in %ss", + i_batch + 1, + format(time_delta, ".2f"), ) # Delete the transferred objects from the staging tables @@ -431,7 +440,7 @@ def kg_clustering( ).delete(synchronize_session=False) db_session.commit() except Exception as e: - logger.error(f"Error deleting relationships: {e}") + logger.error("Error deleting relationships: %s", e) try: with get_session_with_current_tenant() as db_session: @@ -440,7 +449,7 @@ def kg_clustering( ).delete(synchronize_session=False) db_session.commit() except Exception as e: - logger.error(f"Error deleting relationship types: {e}") + logger.error("Error deleting relationship types: %s", e) try: with get_session_with_current_tenant() as db_session: @@ -449,5 +458,5 @@ def kg_clustering( ).delete(synchronize_session=False) db_session.commit() except Exception as e: - logger.error(f"Error deleting entities: {e}") + logger.error("Error deleting entities: %s", e) logger.info("Finished deleting all transferred staging entries") diff --git a/backend/onyx/kg/clustering/normalizations.py b/backend/onyx/kg/clustering/normalizations.py index d9590a0aac..a429bd207d 100644 --- a/backend/onyx/kg/clustering/normalizations.py +++ b/backend/onyx/kg/clustering/normalizations.py @@ -267,7 +267,7 @@ def normalize_entities( ) normalized_map[entity] = format_entity_id_for_models(normalized_entity) else: - logger.warning(f"No normalized entity found for {entity}") + logger.warning("No normalized entity found for %s", entity) normalized_map[entity] = format_entity_id_for_models(entity) return NormalizedEntities( @@ -308,7 +308,7 @@ def normalize_relationships( norm_target = entity_normalization_map.get(target) if norm_source is None or norm_target is None: - logger.warning(f"No normalized entities found for {raw_rel}") + logger.warning("No normalized entities found for %s", raw_rel) continue # 2. Find candidate normalized relationships @@ -325,7 +325,7 @@ def normalize_relationships( ] if not candidate_rels: - logger.warning(f"No candidate relationships found for {raw_rel}") + logger.warning("No candidate relationships found for %s", raw_rel) continue # 3. Encode and find best match diff --git a/backend/onyx/kg/extractions/extraction_processing.py b/backend/onyx/kg/extractions/extraction_processing.py index 921d31d6ab..9f91e302b4 100644 --- a/backend/onyx/kg/extractions/extraction_processing.py +++ b/backend/onyx/kg/extractions/extraction_processing.py @@ -217,7 +217,7 @@ def kg_extraction( - Update document table to set kg_extracted = True """ - logger.info(f"Starting kg extraction for tenant {tenant_id}") + logger.info("Starting kg extraction for tenant %s", tenant_id) kg_config_settings = get_kg_config_settings() validate_kg_settings(kg_config_settings) @@ -275,7 +275,9 @@ def kg_extraction( if len(unprocessed_document_batch) == 0: logger.info( - f"No unprocessed documents found for connector {connector_id}. Processed {document_batch_counter} batches." + "No unprocessed documents found for connector %s. Processed %s batches.", + connector_id, + document_batch_counter, ) break @@ -283,7 +285,7 @@ def kg_extraction( last_lock_time = extend_lock( lock, CELERY_GENERIC_BEAT_LOCK_TIMEOUT, last_lock_time ) - logger.info(f"Processing document batch {document_batch_counter}") + logger.info("Processing document batch %s", document_batch_counter) # Get the document attributes and entity types batch_metadata = _get_batch_documents_enhanced_metadata( @@ -300,7 +302,7 @@ def kg_extraction( # info for after the connector has been processed kg_stage = KGStage.SKIPPED logger.debug( - f"Document {unprocessed_document.id} is not of any entity type" + "Document %s is not of any entity type", unprocessed_document.id ) elif batch_metadata[unprocessed_document.id].skip: # info for after the connector has been processed. But no message as there may be many @@ -445,7 +447,8 @@ def kg_extraction( parts = split_entity_id(entity) if len(parts) != 2: logger.error( - f"Invalid entity {entity} in aggregated_kg_extractions.entities" + "Invalid entity %s in aggregated_kg_extractions.entities", + entity, ) continue @@ -506,14 +509,15 @@ def kg_extraction( db_session.commit() except Exception as e: - logger.error(f"Error adding entity {entity}. Error message: {e}") + logger.error("Error adding entity %s. Error message: %s", entity, e) for document_id, relationship in batch_relationships: relationship_split = split_relationship_id(relationship) if len(relationship_split) != 3: logger.error( - f"Invalid relationship {relationship} in aggregated_kg_extractions.relationships" + "Invalid relationship %s in aggregated_kg_extractions.relationships", + relationship, ) continue @@ -543,7 +547,9 @@ def kg_extraction( db_session.commit() except Exception as e: logger.error( - f"Error adding relationship type {relationship_type_id_name} to the database: {e}" + "Error adding relationship type %s to the database: %s", + relationship_type_id_name, + e, ) with get_session_with_current_tenant() as db_session: @@ -557,7 +563,9 @@ def kg_extraction( db_session.commit() except Exception as e: logger.error( - f"Error adding relationship {relationship} to the database: {e}" + "Error adding relationship %s to the database: %s", + relationship, + e, ) # Populate the Documents table with the kg information for the documents diff --git a/backend/onyx/kg/resets/reset_vespa.py b/backend/onyx/kg/resets/reset_vespa.py index 8d312bb523..25af389a03 100644 --- a/backend/onyx/kg/resets/reset_vespa.py +++ b/backend/onyx/kg/resets/reset_vespa.py @@ -71,7 +71,10 @@ def reset_vespa_kg_index( or all documents from kg grounded sources if source_name is None. """ logger.info( - f"Resetting kg vespa index {index_name} for tenant {tenant_id}, source: {source_name if source_name else 'all'}" + "Resetting kg vespa index %s for tenant %s, source: %s", + index_name, + tenant_id, + source_name if source_name else "all", ) last_lock_time = time.monotonic() @@ -121,5 +124,8 @@ def reset_vespa_kg_index( ) logger.info( - f"Finished resetting kg vespa index {index_name} for tenant {tenant_id}, source: {source_name if source_name else 'all'}" + "Finished resetting kg vespa index %s for tenant %s, source: %s", + index_name, + tenant_id, + source_name if source_name else "all", ) diff --git a/backend/onyx/kg/utils/extraction_utils.py b/backend/onyx/kg/utils/extraction_utils.py index 3fc5d56564..a33cd5cd81 100644 --- a/backend/onyx/kg/utils/extraction_utils.py +++ b/backend/onyx/kg/utils/extraction_utils.py @@ -297,7 +297,7 @@ def kg_implied_extraction( if process_results is None: continue - (implied_entity, implied_relationship, _, _) = process_results + implied_entity, implied_relationship, _, _ = process_results implied_entities.add(implied_entity) implied_relationships.add(implied_relationship) else: @@ -446,7 +446,9 @@ def kg_classify_document( classification_class=classification_class, ) except Exception as e: - logger.error(f"Failed to classify document {document_entity}. Error: {str(e)}") + logger.error( + "Failed to classify document %s. Error: %s", document_entity, str(e) + ) return None @@ -523,7 +525,10 @@ def kg_deep_extract_chunks( except Exception as e: failed_chunks = [chunk.chunk_id for chunk in chunk_batch] logger.error( - f"Failed to process chunks {failed_chunks} from document {document_entity}. Error: {str(e)}" + "Failed to process chunks %s from document %s. Error: %s", + failed_chunks, + document_entity, + str(e), ) return None diff --git a/backend/onyx/llm/cost.py b/backend/onyx/llm/cost.py index 33ecf7cc27..f94eec60e4 100644 --- a/backend/onyx/llm/cost.py +++ b/backend/onyx/llm/cost.py @@ -33,6 +33,8 @@ def calculate_llm_cost_cents( except Exception as e: # Log but don't fail - unknown models or errors shouldn't block usage logger.debug( - f"Could not calculate cost for model {model_name}: {e}. Assuming cost is 0." + "Could not calculate cost for model %s: %s. Assuming cost is 0.", + model_name, + e, ) return 0.0 diff --git a/backend/onyx/llm/litellm_singleton/config.py b/backend/onyx/llm/litellm_singleton/config.py index ebb79bae5a..0c6fc48f42 100644 --- a/backend/onyx/llm/litellm_singleton/config.py +++ b/backend/onyx/llm/litellm_singleton/config.py @@ -124,7 +124,9 @@ def load_model_metadata_enrichments() -> None: enrichments_path = Path(__file__).parent.parent / "model_metadata_enrichments.json" if not enrichments_path.exists(): - logger.warning(f"Model metadata enrichments file not found: {enrichments_path}") + logger.warning( + "Model metadata enrichments file not found: %s", enrichments_path + ) return try: @@ -140,7 +142,7 @@ def load_model_metadata_enrichments() -> None: # Model not in litellm.model_cost - add it with just our metadata litellm.model_cost[model_key] = metadata - logger.info(f"Loaded model metadata enrichments for {len(enrichments)} models") + logger.info("Loaded model metadata enrichments for %s models", len(enrichments)) # Clear the model name parser cache since enrichments are now loaded # This ensures any parsing done before enrichments were loaded gets refreshed @@ -151,7 +153,7 @@ def load_model_metadata_enrichments() -> None: except ImportError: pass # Parser not yet imported, no cache to clear except Exception as e: - logger.error(f"Failed to load model metadata enrichments: {e}") + logger.error("Failed to load model metadata enrichments: %s", e) def initialize_litellm() -> None: diff --git a/backend/onyx/llm/multi_llm.py b/backend/onyx/llm/multi_llm.py index 537c12dad7..3b68050c34 100644 --- a/backend/onyx/llm/multi_llm.py +++ b/backend/onyx/llm/multi_llm.py @@ -421,7 +421,7 @@ class LitellmLLM(LLM): db_session.commit() except Exception as e: # Log but don't fail the LLM call if tracking fails - logger.warning(f"Failed to track LLM cost: {e}") + logger.warning("Failed to track LLM cost: %s", e) def _completion( self, diff --git a/backend/onyx/llm/prompt_cache/cache_manager.py b/backend/onyx/llm/prompt_cache/cache_manager.py index 56b40024e4..fc1af6ec36 100644 --- a/backend/onyx/llm/prompt_cache/cache_manager.py +++ b/backend/onyx/llm/prompt_cache/cache_manager.py @@ -85,13 +85,15 @@ class CacheManager: self._kv_store.store(cache_key, metadata_dict, encrypt=False) logger.debug( - f"Stored cache metadata: provider={metadata.provider}, " - f"model={metadata.model_name}, cache_key={metadata.cache_key[:16]}..., " - f"tenant_id={metadata.tenant_id}" + "Stored cache metadata: provider=%s, model=%s, cache_key=%s..., tenant_id=%s", + metadata.provider, + metadata.model_name, + metadata.cache_key[:16], + metadata.tenant_id, ) except Exception as e: # Best-effort: log and continue - logger.warning(f"Failed to store cache metadata: {str(e)}") + logger.warning("Failed to store cache metadata: %s", str(e)) def retrieve_cache_metadata( self, @@ -125,14 +127,16 @@ class CacheManager: self.store_cache_metadata(metadata) logger.debug( - f"Retrieved cache metadata: provider={provider}, " - f"model={model_name}, cache_key={cache_key_hash[:16]}..., " - f"tenant_id={tenant_id}" + "Retrieved cache metadata: provider=%s, model=%s, cache_key=%s..., tenant_id=%s", + provider, + model_name, + cache_key_hash[:16], + tenant_id, ) return metadata except Exception as e: # Best-effort: log and continue - logger.debug(f"Cache metadata not found or error retrieving: {str(e)}") + logger.debug("Cache metadata not found or error retrieving: %s", str(e)) return None def delete_cache_metadata( @@ -156,11 +160,14 @@ class CacheManager: ) self._kv_store.delete(cache_key) logger.debug( - f"Deleted cache metadata for provider={provider}, model={model_name}, cache_key={cache_key_hash[:16]}..." + "Deleted cache metadata for provider=%s, model=%s, cache_key=%s...", + provider, + model_name, + cache_key_hash[:16], ) except Exception as e: # Best-effort: log and continue - logger.warning(f"Failed to delete cache metadata: {str(e)}") + logger.warning("Failed to delete cache metadata: %s", str(e)) def _make_json_serializable(obj: object) -> object: diff --git a/backend/onyx/llm/prompt_cache/processor.py b/backend/onyx/llm/prompt_cache/processor.py index c1885bc62d..b848e6789c 100644 --- a/backend/onyx/llm/prompt_cache/processor.py +++ b/backend/onyx/llm/prompt_cache/processor.py @@ -68,7 +68,8 @@ def process_with_prompt_cache( # If provider doesn't support caching, combine and return unchanged if not provider_adapter.supports_caching(): logger.debug( - f"Provider {llm_config.model_provider} does not support caching, combining messages without caching" + "Provider %s does not support caching, combining messages without caching", + llm_config.model_provider, ) # Use no-op adapter to combine messages from onyx.llm.prompt_cache.providers.noop import NoOpPromptCacheProvider @@ -105,9 +106,11 @@ def process_with_prompt_cache( ) logger.debug( - f"Processed prompt with caching: provider={llm_config.model_provider}, " - f"model={llm_config.model_name}, cache_key={cache_key_hash[:16]}..., " - f"continuation={continuation}" + "Processed prompt with caching: provider=%s, model=%s, cache_key=%s..., continuation=%s", + llm_config.model_provider, + llm_config.model_name, + cache_key_hash[:16], + continuation, ) # Create cache metadata for tracking (even for implicit caching) @@ -126,8 +129,9 @@ def process_with_prompt_cache( except Exception as e: # Best-effort: log error and fall back to no-op behavior logger.warning( - f"Error processing prompt with caching for provider={llm_config.model_provider}: {str(e)}. " - "Falling back to non-cached behavior." + "Error processing prompt with caching for provider=%s: %s. Falling back to non-cached behavior.", + llm_config.model_provider, + str(e), ) # Fall back to no-op adapter from onyx.llm.prompt_cache.providers.noop import NoOpPromptCacheProvider diff --git a/backend/onyx/llm/utils.py b/backend/onyx/llm/utils.py index 51d1b50355..0342d3ae21 100644 --- a/backend/onyx/llm/utils.py +++ b/backend/onyx/llm/utils.py @@ -421,7 +421,7 @@ def test_llm(llm: LLM) -> str | None: llm.invoke(UserMessage(content="Do not respond"), max_tokens=50) return None except Exception as e: - logger.warning(f"Failed to call LLM with the following error: {e!s}") + logger.warning("Failed to call LLM with the following error: %s", e) safe_msg, _, _ = litellm_exception_to_error_msg( e, llm, fallback_to_error_msg=False ) @@ -588,9 +588,8 @@ def get_llm_contextual_cost( ) except Exception: logger.exception( - "An unexpected error occurred while calculating cost for model " - f"{llm.config.model_name} (potentially due to malformed name). " - "Assuming cost is 0." + "An unexpected error occurred while calculating cost for model %s (potentially due to malformed name). Assuming cost is 0.", + llm.config.model_name, ) return 0 @@ -606,7 +605,7 @@ def llm_max_input_tokens( """Best effort attempt to get the max input tokens for the LLM.""" if GEN_AI_MAX_TOKENS: # This is an override, so always return this - logger.info(f"Using override GEN_AI_MAX_TOKENS: {GEN_AI_MAX_TOKENS}") + logger.info("Using override GEN_AI_MAX_TOKENS: %s", GEN_AI_MAX_TOKENS) return GEN_AI_MAX_TOKENS model_obj = find_model_obj( @@ -616,7 +615,9 @@ def llm_max_input_tokens( ) if not model_obj: logger.warning( - f"Model '{model_name}' not found in LiteLLM. Falling back to {GEN_AI_MODEL_FALLBACK_MAX_TOKENS} tokens." + "Model '%s' not found in LiteLLM. Falling back to %s tokens.", + model_name, + GEN_AI_MODEL_FALLBACK_MAX_TOKENS, ) return GEN_AI_MODEL_FALLBACK_MAX_TOKENS @@ -629,7 +630,9 @@ def llm_max_input_tokens( return max_tokens logger.warning( - f"No max tokens found for '{model_name}'. Falling back to {GEN_AI_MODEL_FALLBACK_MAX_TOKENS} tokens." + "No max tokens found for '%s'. Falling back to %s tokens.", + model_name, + GEN_AI_MODEL_FALLBACK_MAX_TOKENS, ) return GEN_AI_MODEL_FALLBACK_MAX_TOKENS @@ -648,7 +651,9 @@ def get_llm_max_output_tokens( if not model_obj: logger.warning( - f"Model '{model_name}' not found in LiteLLM. Falling back to {default_output_tokens} output tokens." + "Model '%s' not found in LiteLLM. Falling back to %s output tokens.", + model_name, + default_output_tokens, ) return default_output_tokens @@ -662,7 +667,9 @@ def get_llm_max_output_tokens( return int(max_tokens * 0.1) logger.warning( - f"No max output tokens found for '{model_name}'. Falling back to {default_output_tokens} output tokens." + "No max output tokens found for '%s'. Falling back to %s output tokens.", + model_name, + default_output_tokens, ) return default_output_tokens @@ -798,7 +805,10 @@ def model_supports_image_input(model_name: str, model_provider: str) -> bool: return True except Exception as e: logger.warning( - f"Failed to query database for {model_provider} model {model_name} image support: {e}" + "Failed to query database for %s model %s image support: %s", + model_provider, + model_name, + e, ) # Fallback to looking up the model in the litellm model_cost dict @@ -815,14 +825,16 @@ def litellm_thinks_model_supports_image_input( model_obj = find_model_obj(get_model_map(), model_provider, model_name) if not model_obj: logger.warning( - f"No litellm entry found for {model_provider}/{model_name}, this model may or may not support image input." + "No litellm entry found for %s/%s, this model may or may not support image input.", + model_provider, + model_name, ) return False # The or False here is because sometimes the dict contains the key but the value is None return model_obj.get("supports_vision", False) or False except Exception: logger.exception( - f"Failed to get model object for {model_provider}/{model_name}" + "Failed to get model object for %s/%s", model_provider, model_name ) return False @@ -842,7 +854,9 @@ def model_is_reasoning_model(model_name: str, model_provider: str) -> bool: if reasoning is not None: return reasoning logger.error( - f"Cannot find reasoning for name={model_name} and provider={model_provider}" + "Cannot find reasoning for name=%s and provider=%s", + model_name, + model_provider, ) # Fallback: try using litellm.supports_reasoning() for newer models @@ -856,13 +870,15 @@ def model_is_reasoning_model(model_name: str, model_provider: str) -> bool: return litellm.supports_reasoning(model=full_model_name) except Exception: logger.exception( - f"Failed to check if {model_provider}/{model_name} supports reasoning" + "Failed to check if %s/%s supports reasoning", + model_provider, + model_name, ) return False except Exception: logger.exception( - f"Failed to get model object for {model_provider}/{model_name}" + "Failed to get model object for %s/%s", model_provider, model_name ) return False @@ -910,7 +926,9 @@ def is_true_openai_model(model_provider: str, model_name: str) -> bool: except Exception: logger.exception( - f"Failed to determine if {model_provider}/{model_name} is a true OpenAI model" + "Failed to determine if %s/%s is a true OpenAI model", + model_provider, + model_name, ) return False diff --git a/backend/onyx/llm/well_known_providers/auto_update_service.py b/backend/onyx/llm/well_known_providers/auto_update_service.py index 92a6bd8304..7b00892de6 100644 --- a/backend/onyx/llm/well_known_providers/auto_update_service.py +++ b/backend/onyx/llm/well_known_providers/auto_update_service.py @@ -32,7 +32,7 @@ def _get_cached_last_updated_at() -> datetime | None: if value is not None: return datetime.fromisoformat(value.decode("utf-8")) except Exception as e: - logger.warning(f"Failed to get cached last_updated_at: {e}") + logger.warning("Failed to get cached last_updated_at: %s", e) return None @@ -44,7 +44,7 @@ def _set_cached_last_updated_at(updated_at: datetime) -> None: ex=_CACHE_TTL_SECONDS, ) except Exception as e: - logger.warning(f"Failed to set cached last_updated_at: {e}") + logger.warning("Failed to set cached last_updated_at: %s", e) def fetch_llm_recommendations_from_github( @@ -67,10 +67,10 @@ def fetch_llm_recommendations_from_github( data = response.json() return LLMRecommendations.model_validate(data) except httpx.HTTPError as e: - logger.error(f"Failed to fetch LLM config from GitHub: {e}") + logger.error("Failed to fetch LLM config from GitHub: %s", e) return None except Exception as e: - logger.error(f"Error parsing LLM config: {e}") + logger.error("Error parsing LLM config: %s", e) return None @@ -120,7 +120,7 @@ def sync_llm_models_from_github( if provider_type not in config.providers: logger.debug( - f"No config for provider type '{provider_type}' in GitHub config" + "No config for provider type '%s' in GitHub config", provider_type ) continue @@ -134,7 +134,7 @@ def sync_llm_models_from_github( if changes > 0: results[provider.name] = changes logger.info( - f"Applied {changes} model changes to provider '{provider.name}'" + "Applied %s model changes to provider '%s'", changes, provider.name ) _set_cached_last_updated_at(config.updated_at) @@ -146,4 +146,4 @@ def reset_cache() -> None: try: get_cache_backend().delete(_CACHE_KEY_LAST_UPDATED_AT) except Exception as e: - logger.warning(f"Failed to reset cache: {e}") + logger.warning("Failed to reset cache: %s", e) diff --git a/backend/onyx/main.py b/backend/onyx/main.py index 05ba767898..ce8da2124e 100644 --- a/backend/onyx/main.py +++ b/backend/onyx/main.py @@ -186,19 +186,19 @@ setup_uvicorn_logger(shared_file_handlers=file_handlers) def validation_exception_handler(request: Request, exc: Exception) -> JSONResponse: if not isinstance(exc, RequestValidationError): logger.error( - f"Unexpected exception type in validation_exception_handler - {type(exc)}" + "Unexpected exception type in validation_exception_handler - %s", type(exc) ) raise exc exc_str = f"{exc}".replace("\n", " ").replace(" ", " ") - logger.exception(f"{request}: {exc_str}") + logger.exception("%s: %s", request, exc_str) content = {"status_code": 422, "message": exc_str, "data": None} return JSONResponse(content=content, status_code=422) def value_error_handler(_: Request, exc: Exception) -> JSONResponse: if not isinstance(exc, ValueError): - logger.error(f"Unexpected exception type in value_error_handler - {type(exc)}") + logger.error("Unexpected exception type in value_error_handler - %s", type(exc)) raise exc try: @@ -313,7 +313,7 @@ async def lifespan(app: FastAPI) -> AsyncGenerator[None, None]: # noqa: ARG001 # Set recursion limit if SYSTEM_RECURSION_LIMIT is not None: sys.setrecursionlimit(SYSTEM_RECURSION_LIMIT) - logger.notice(f"System recursion limit set to {SYSTEM_RECURSION_LIMIT}") + logger.notice("System recursion limit set to %s", SYSTEM_RECURSION_LIMIT) SqlEngine.set_app_name(POSTGRES_WEB_APP_NAME) @@ -404,11 +404,11 @@ def log_http_error(request: Request, exc: Exception) -> JSONResponse: if isinstance(exc, BasicAuthenticationError): # For BasicAuthenticationError, just log a brief message without stack trace # (almost always spammy) - logger.debug(f"Authentication failed: {str(exc)}") + logger.debug("Authentication failed: %s", str(exc)) elif status_code == 404 and request.url.path == "/metrics": # Log 404 errors for the /metrics endpoint with debug level - logger.debug(f"404 error for /metrics endpoint: {str(exc)}") + logger.debug("404 error for /metrics endpoint: %s", str(exc)) elif status_code >= 400: error_msg = f"{str(exc)}\n" @@ -595,7 +595,7 @@ def get_application(lifespan_override: Lifespan | None = None) -> FastAPI: if "offline_access" not in oidc_scopes: oidc_scopes.append("offline_access") except Exception as e: - logger.warning(f"Error configuring OIDC scopes: {e}") + logger.warning("Error configuring OIDC scopes: %s", e) # Fall back to default scopes if there's an error oidc_scopes = BASE_SCOPES @@ -700,7 +700,10 @@ app = fetch_versioned_implementation(module="onyx.main", attribute="get_applicat if __name__ == "__main__": logger.notice( - f"Starting Onyx Backend version {__version__} on http://{APP_HOST}:{str(APP_PORT)}/" + "Starting Onyx Backend version %s on http://%s:%s/", + __version__, + APP_HOST, + str(APP_PORT), ) if global_version.is_ee_version(): diff --git a/backend/onyx/mcp_server/api.py b/backend/onyx/mcp_server/api.py index 3b1681cd20..604b1d0091 100644 --- a/backend/onyx/mcp_server/api.py +++ b/backend/onyx/mcp_server/api.py @@ -97,7 +97,7 @@ def create_mcp_fastapi_app() -> FastAPI: # Authentication is handled by FastMCP's OnyxTokenVerifier (see auth.py) if MCP_SERVER_CORS_ORIGINS: - logger.info(f"CORS origins: {MCP_SERVER_CORS_ORIGINS}") + logger.info("CORS origins: %s", MCP_SERVER_CORS_ORIGINS) app.add_middleware( CORSMiddleware, allow_origins=MCP_SERVER_CORS_ORIGINS, diff --git a/backend/onyx/mcp_server/tools/search.py b/backend/onyx/mcp_server/tools/search.py index 791fdbbdac..4e20d44c8e 100644 --- a/backend/onyx/mcp_server/tools/search.py +++ b/backend/onyx/mcp_server/tools/search.py @@ -120,8 +120,11 @@ async def search_indexed_documents( ``` """ logger.info( - f"Onyx MCP Server: document search: query='{query}', sources={source_types}, " - f"document_sets={document_set_names}, limit={limit}" + "Onyx MCP Server: document search: query='%s', sources=%s, document_sets=%s, limit=%s", + query, + source_types, + document_set_names, + limit, ) # Normalize empty list inputs to None so downstream filter construction is @@ -137,7 +140,9 @@ async def search_indexed_documents( time_cutoff_dt = datetime.fromisoformat(time_cutoff.replace("Z", "+00:00")) except ValueError as e: logger.warning( - f"Onyx MCP Server: Invalid time_cutoff format '{time_cutoff}': {e}. Continuing without time filter." + "Onyx MCP Server: Invalid time_cutoff format '%s': %s. Continuing without time filter.", + time_cutoff, + e, ) # Continue with no time_cutoff instead of returning an error time_cutoff_dt = None @@ -183,7 +188,8 @@ async def search_indexed_documents( source_type_enums.append(DocumentSource(src.lower())) except ValueError: logger.warning( - f"Onyx MCP Server: Invalid source type '{src}' - will be ignored by server" + "Onyx MCP Server: Invalid source type '%s' - will be ignored by server", + src, ) filters: BaseFilters | None = None @@ -270,7 +276,7 @@ async def search_indexed_documents( documents = documents[:limit] logger.info( - f"Onyx MCP Server: Internal search returned {len(documents)} results" + "Onyx MCP Server: Internal search returned %s results", len(documents) ) return { "documents": documents, @@ -278,7 +284,7 @@ async def search_indexed_documents( "query": query, } except Exception as e: - logger.error(f"Onyx MCP Server: Document search error: {e}", exc_info=True) + logger.error("Onyx MCP Server: Document search error: %s", e, exc_info=True) return { "error": f"Document search failed: {str(e)}", "documents": [], @@ -306,7 +312,7 @@ async def search_web( } ``` """ - logger.info(f"Onyx MCP Server: Web search: query='{query}', limit={limit}") + logger.info("Onyx MCP Server: Web search: query='%s', limit=%s", query, limit) access_token = require_access_token() @@ -328,7 +334,7 @@ async def search_web( "query": query, } except Exception as e: - logger.error(f"Onyx MCP Server: Web search error: {e}", exc_info=True) + logger.error("Onyx MCP Server: Web search error: %s", e, exc_info=True) return { "error": f"Web search failed: {str(e)}", "results": [], @@ -356,7 +362,7 @@ async def open_urls( } ``` """ - logger.info(f"Onyx MCP Server: Open URL: fetching {len(urls)} URLs") + logger.info("Onyx MCP Server: Open URL: fetching %s URLs", len(urls)) access_token = require_access_token() @@ -376,7 +382,7 @@ async def open_urls( "results": [result.model_dump(mode="json") for result in payload.results], } except Exception as e: - logger.error(f"Onyx MCP Server: URL fetch error: {e}", exc_info=True) + logger.error("Onyx MCP Server: URL fetch error: %s", e, exc_info=True) return { "error": f"URL fetch failed: {str(e)}", "results": [], diff --git a/backend/onyx/mcp_server_main.py b/backend/onyx/mcp_server_main.py index 0b8c806520..fc7b726b97 100644 --- a/backend/onyx/mcp_server_main.py +++ b/backend/onyx/mcp_server_main.py @@ -20,7 +20,7 @@ def main() -> None: set_is_ee_based_on_env_variable() setup_tracing() - logger.info(f"Starting MCP server on {MCP_SERVER_HOST}:{MCP_SERVER_PORT}") + logger.info("Starting MCP server on %s:%s", MCP_SERVER_HOST, MCP_SERVER_PORT) from onyx.mcp_server.api import mcp_app diff --git a/backend/onyx/natural_language_processing/search_nlp_models.py b/backend/onyx/natural_language_processing/search_nlp_models.py index b7ed1beace..825ae0fd02 100644 --- a/backend/onyx/natural_language_processing/search_nlp_models.py +++ b/backend/onyx/natural_language_processing/search_nlp_models.py @@ -134,7 +134,9 @@ def cleanup_embedding_thread_locals() -> None: pending = asyncio.all_tasks(loop) if pending: logger.debug( - f"Cleaning up event loop with {len(pending)} pending tasks in thread {threading.current_thread().name}" + "Cleaning up event loop with %s pending tasks in thread %s", + len(pending), + threading.current_thread().name, ) for task in pending: task.cancel() @@ -144,12 +146,12 @@ def cleanup_embedding_thread_locals() -> None: ) except Exception as e: # If gathering tasks fails, just close the loop - logger.debug(f"Error gathering tasks during cleanup: {e}") + logger.debug("Error gathering tasks during cleanup: %s", e) # Close the event loop loop.close() logger.debug( - f"Closed event loop in thread {threading.current_thread().name}" + "Closed event loop in thread %s", threading.current_thread().name ) # Clear the thread-local reference @@ -420,8 +422,10 @@ class CloudEmbedding: all_embeddings: list[Embedding] = [] logger.debug( - f"VertexAI embedding: processing {len(texts)} texts in {len(batches)} batches " - f"(batch_size={VERTEXAI_EMBEDDING_LOCAL_BATCH_SIZE})" + "VertexAI embedding: processing %s texts in %s batches (batch_size=%s)", + len(texts), + len(batches), + VERTEXAI_EMBEDDING_LOCAL_BATCH_SIZE, ) try: @@ -432,11 +436,15 @@ class CloudEmbedding: # Log progress for large batches to track memory usage patterns if batch_idx % 10 == 0 and batch_idx > 0: logger.debug( - f"VertexAI embedding progress: batch {batch_idx}/{len(batches)}, total_embeddings={len(all_embeddings)}" + "VertexAI embedding progress: batch %s/%s, total_embeddings=%s", + batch_idx, + len(batches), + len(all_embeddings), ) logger.debug( - f"VertexAI embedding completed: {len(all_embeddings)} embeddings generated" + "VertexAI embedding completed: %s embeddings generated", + len(all_embeddings), ) return all_embeddings finally: @@ -446,7 +454,7 @@ class CloudEmbedding: except asyncio.TimeoutError: logger.warning("Google GenAI client aclose() timed out after 5s") except Exception as e: - logger.warning(f"Error closing Google GenAI client: {e}") + logger.warning("Error closing Google GenAI client: %s", e) async def _embed_litellm_proxy( self, texts: list[str], model_name: str | None @@ -524,7 +532,7 @@ class CloudEmbedding: # transient provider outages). The final failure surfaces via # the RuntimeError below and is logged by the caller. logger.warning(error_string) - logger.debug(f"Exception texts: {texts}") + logger.debug("Exception texts: %s", texts) raise RuntimeError(error_string) except Exception as e: @@ -539,7 +547,7 @@ class CloudEmbedding: sanitized_api_key=self.sanitized_api_key, ) logger.warning(error_string) - logger.debug(f"Exception texts: {texts}") + logger.debug("Exception texts: %s", texts) raise RuntimeError(error_string) @@ -550,7 +558,7 @@ class CloudEmbedding: api_url: str | None = None, api_version: str | None = None, ) -> "CloudEmbedding": - logger.debug(f"Creating Embedding instance for provider: {provider}") + logger.debug("Creating Embedding instance for provider: %s", provider) return CloudEmbedding(api_key, provider, api_url, api_version) async def aclose(self) -> None: @@ -737,7 +745,10 @@ class EmbeddingModel: total_chars = sum(len(text) for text in embed_request.texts) logger.info( - f"Embedding {len(embed_request.texts)} texts with {total_chars} total characters with provider: {self.provider_type}" + "Embedding %s texts with %s total characters with provider: %s", + len(embed_request.texts), + total_chars, + self.provider_type, ) async with CloudEmbedding( @@ -763,11 +774,11 @@ class EmbeddingModel: elapsed = time.monotonic() - start_time logger.info( - f"event=embedding_provider " - f"texts={len(embed_request.texts)} " - f"chars={total_chars} " - f"provider={self.provider_type} " - f"elapsed={elapsed:.2f}" + "event=embedding_provider texts=%s chars=%s provider=%s elapsed=%s", + len(embed_request.texts), + total_chars, + self.provider_type, + format(elapsed, ".2f"), ) return EmbedResponse(embeddings=embeddings) @@ -849,7 +860,7 @@ class EmbeddingModel: text_batches = batch_list(texts, batch_size) num_of_batches = len(text_batches) - logger.debug(f"Encoding {len(texts)} texts in {num_of_batches} batches.") + logger.debug("Encoding %s texts in %s batches.", len(texts), num_of_batches) embeddings: list[Embedding] = [] @@ -958,7 +969,10 @@ class EmbeddingModel: ) logger.debug( - f"process_batch: Batch idx {batch_idx}, total num {num_of_batches}, processing time: {processing_time:.2f}s." + "process_batch: Batch idx %s, total num %s, processing time: %ss.", + batch_idx, + num_of_batches, + format(processing_time, ".2f"), ) return batch_idx, response.embeddings @@ -1255,7 +1269,10 @@ def warm_up_retry( except Exception as e: exceptions.append(e) logger.info( - f"Attempt {attempt + 1}/{tries} failed; retrying in {delay} seconds..." + "Attempt %s/%s failed; retrying in %s seconds...", + attempt + 1, + tries, + delay, ) time.sleep(delay) raise Exception(f"All retries failed: {exceptions}") @@ -1272,7 +1289,7 @@ def warm_up_bi_encoder( warm_up_str = " ".join(WARM_UP_STRINGS) - logger.debug(f"Warming up encoder model: {embedding_model.model_name}") + logger.debug("Warming up encoder model: %s", embedding_model.model_name) get_tokenizer( model_name=embedding_model.model_name, provider_type=embedding_model.provider_type, @@ -1282,17 +1299,20 @@ def warm_up_bi_encoder( try: embedding_model.encode(texts=[warm_up_str], text_type=EmbedTextType.QUERY) logger.debug( - f"Warm-up complete for encoder model: {embedding_model.model_name}" + "Warm-up complete for encoder model: %s", embedding_model.model_name ) except Exception as e: logger.warning( - f"Warm-up request failed for encoder model {embedding_model.model_name}: {e}" + "Warm-up request failed for encoder model %s: %s", + embedding_model.model_name, + e, ) if non_blocking: threading.Thread(target=_warm_up, daemon=True).start() logger.debug( - f"Started non-blocking warm-up for encoder model: {embedding_model.model_name}" + "Started non-blocking warm-up for encoder model: %s", + embedding_model.model_name, ) else: retry_encode = warm_up_retry(embedding_model.encode) @@ -1307,7 +1327,7 @@ def warm_up_cross_encoder( if SKIP_WARM_UP: return - logger.debug(f"Warming up reranking model: {rerank_model_name}") + logger.debug("Warming up reranking model: %s", rerank_model_name) reranking_model = RerankingModel( model_name=rerank_model_name, @@ -1319,16 +1339,18 @@ def warm_up_cross_encoder( def _warm_up() -> None: try: reranking_model.predict(WARM_UP_STRINGS[0], WARM_UP_STRINGS[1:]) - logger.debug(f"Warm-up complete for reranking model: {rerank_model_name}") + logger.debug("Warm-up complete for reranking model: %s", rerank_model_name) except Exception as e: logger.warning( - f"Warm-up request failed for reranking model {rerank_model_name}: {e}" + "Warm-up request failed for reranking model %s: %s", + rerank_model_name, + e, ) if non_blocking: threading.Thread(target=_warm_up, daemon=True).start() logger.debug( - f"Started non-blocking warm-up for reranking model: {rerank_model_name}" + "Started non-blocking warm-up for reranking model: %s", rerank_model_name ) else: retry_rerank = warm_up_retry(reranking_model.predict) diff --git a/backend/onyx/natural_language_processing/utils.py b/backend/onyx/natural_language_processing/utils.py index 5834341a11..c3d275b52f 100644 --- a/backend/onyx/natural_language_processing/utils.py +++ b/backend/onyx/natural_language_processing/utils.py @@ -57,7 +57,10 @@ class TiktokenTokenizer(BaseTokenizer): if len(decoded) != len(encoded): logger.warning( - f"OpenAI tokenized length {len(decoded)} does not match encoded length {len(encoded)} for string: {string}" + "OpenAI tokenized length %s does not match encoded length %s for string: %s", + len(decoded), + len(encoded), + string, ) return decoded @@ -111,7 +114,8 @@ def _check_tokenizer_cache( if not tokenizer: logger.info( - f"Falling back to default embedding model tokenizer: {DOCUMENT_ENCODER_MODEL}" + "Falling back to default embedding model tokenizer: %s", + DOCUMENT_ENCODER_MODEL, ) tokenizer = _get_default_tokenizer() @@ -129,21 +133,25 @@ def _try_initialize_tokenizer( # Try using TiktokenTokenizer first if model_provider exists try: tokenizer = TiktokenTokenizer(model_name) - logger.info(f"Initialized TiktokenTokenizer for: {model_name}") + logger.info("Initialized TiktokenTokenizer for: %s", model_name) return tokenizer except Exception as tiktoken_error: logger.debug( - f"TiktokenTokenizer not available for model {model_name}: {tiktoken_error}" + "TiktokenTokenizer not available for model %s: %s", + model_name, + tiktoken_error, ) else: # If no provider specified, try HuggingFaceTokenizer try: tokenizer = HuggingFaceTokenizer(model_name) - logger.info(f"Initialized HuggingFaceTokenizer for: {model_name}") + logger.info("Initialized HuggingFaceTokenizer for: %s", model_name) return tokenizer except Exception as hf_error: logger.warning( - f"Failed to initialize HuggingFaceTokenizer for {model_name}: {hf_error}" + "Failed to initialize HuggingFaceTokenizer for %s: %s", + model_name, + hf_error, ) # If both initializations fail, return None @@ -169,7 +177,8 @@ def get_tokenizer( provider_type = EmbeddingProvider(provider_type) except ValueError: logger.debug( - f"Invalid provider_type '{provider_type}'. Falling back to default tokenizer." + "Invalid provider_type '%s'. Falling back to default tokenizer.", + provider_type, ) return _get_default_tokenizer() return _check_tokenizer_cache(provider_type, model_name) diff --git a/backend/onyx/onyxbot/discord/api_client.py b/backend/onyx/onyxbot/discord/api_client.py index 1689485bd9..a9b4087b03 100644 --- a/backend/onyx/onyxbot/discord/api_client.py +++ b/backend/onyx/onyxbot/discord/api_client.py @@ -70,7 +70,7 @@ class OnyxAPIClient: connect=30, # 30 seconds to establish connection ) self._session = aiohttp.ClientSession(timeout=timeout) - logger.info(f"API client initialized with base URL: {self._base_url}") + logger.info("API client initialized with base URL: %s", self._base_url) async def close(self) -> None: """Close the aiohttp session. @@ -174,24 +174,26 @@ class OnyxAPIClient: response_obj = ChatFullResponse.model_validate(data) if response_obj.error_msg: - logger.warning(f"Chat API returned error: {response_obj.error_msg}") + logger.warning( + "Chat API returned error: %s", response_obj.error_msg + ) return response_obj except aiohttp.ClientConnectorError as e: - logger.error(f"Failed to connect to API: {e}") + logger.error("Failed to connect to API: %s", e) raise APIConnectionError( f"Failed to connect to API at {self._base_url}: {e}" ) from e except TimeoutError as e: - logger.error(f"API request timed out after {self._timeout}s") + logger.error("API request timed out after %ss", self._timeout) raise APITimeoutError( f"Request timed out after {self._timeout} seconds" ) from e except aiohttp.ClientError as e: - logger.error(f"HTTP client error: {e}") + logger.error("HTTP client error: %s", e) raise APIConnectionError(f"HTTP client error: {e}") from e async def health_check(self) -> bool: @@ -211,5 +213,5 @@ class OnyxAPIClient: ) as response: return response.status == 200 except Exception as e: - logger.warning(f"API server health check failed: {e}") + logger.warning("API server health check failed: %s", e) return False diff --git a/backend/onyx/onyxbot/discord/cache.py b/backend/onyx/onyxbot/discord/cache.py index b7d4402321..5f96c416ea 100644 --- a/backend/onyx/onyxbot/discord/cache.py +++ b/backend/onyx/onyxbot/discord/cache.py @@ -54,13 +54,13 @@ class DiscordCacheManager: try: guild_ids, api_key = await self._load_tenant_data(tenant_id) if not guild_ids: - logger.debug(f"No guilds found for tenant {tenant_id}") + logger.debug("No guilds found for tenant %s", tenant_id) continue if not api_key: logger.warning( - "Discord service API key missing for tenant that has registered guilds. " - f"{tenant_id} will not be handled in this refresh cycle." + "Discord service API key missing for tenant that has registered guilds. %s will not be handled in this refresh cycle.", + tenant_id, ) continue @@ -69,7 +69,7 @@ class DiscordCacheManager: new_api_keys[tenant_id] = api_key except Exception as e: - logger.warning(f"Failed to refresh tenant {tenant_id}: {e}") + logger.warning("Failed to refresh tenant %s: %s", tenant_id, e) finally: CURRENT_TENANT_ID_CONTEXTVAR.reset(context_token) @@ -78,17 +78,21 @@ class DiscordCacheManager: self._initialized = True logger.info( - f"Cache refresh complete: {len(new_guild_tenants)} guilds, {len(new_api_keys)} tenants" + "Cache refresh complete: %s guilds, %s tenants", + len(new_guild_tenants), + len(new_api_keys), ) except Exception as e: - logger.error(f"Cache refresh failed: {e}") + logger.error("Cache refresh failed: %s", e) raise CacheError(f"Failed to refresh cache: {e}") from e async def refresh_guild(self, guild_id: int, tenant_id: str) -> None: """Add a single guild to cache after registration.""" async with self._lock: - logger.info(f"Refreshing cache for guild {guild_id} (tenant: {tenant_id})") + logger.info( + "Refreshing cache for guild %s (tenant: %s)", guild_id, tenant_id + ) guild_ids, api_key = await self._load_tenant_data(tenant_id) @@ -96,9 +100,9 @@ class DiscordCacheManager: self._guild_tenants[guild_id] = tenant_id if api_key: self._api_keys[tenant_id] = api_key - logger.info(f"Cache updated for guild {guild_id}") + logger.info("Cache updated for guild %s", guild_id) else: - logger.warning(f"Guild {guild_id} not found or disabled") + logger.warning("Guild %s not found or disabled", guild_id) async def _load_tenant_data(self, tenant_id: str) -> tuple[list[int], str | None]: """Load guild IDs and provision API key if needed. diff --git a/backend/onyx/onyxbot/discord/client.py b/backend/onyx/onyxbot/discord/client.py index bb3e6a05f2..84663f70d0 100644 --- a/backend/onyx/onyxbot/discord/client.py +++ b/backend/onyx/onyxbot/discord/client.py @@ -69,7 +69,7 @@ class OnyxDiscordClient(commands.Bot): try: await self.cache.refresh_all() except Exception as e: - logger.error(f"Cache refresh failed: {e}") + logger.error("Cache refresh failed: %s", e) async def on_ready(self) -> None: """Bot connected and ready.""" @@ -79,9 +79,11 @@ class OnyxDiscordClient(commands.Bot): if not self.user: raise RuntimeError("Critical error: Discord Bot user not found") - logger.info(f"Discord Bot connected as {self.user} (ID: {self.user.id})") - logger.info(f"Connected to {len(self.guilds)} guild(s)") - logger.info(f"Cached {len(self.cache.get_all_guild_ids())} registered guild(s)") + logger.info("Discord Bot connected as %s (ID: %s)", self.user, self.user.id) + logger.info("Connected to %s guild(s)", len(self.guilds)) + logger.info( + "Cached %s registered guild(s)", len(self.cache.get_all_guild_ids()) + ) self.ready = True @@ -158,7 +160,7 @@ class OnyxDiscordClient(commands.Bot): # Get API key api_key = self.cache.get_api_key(tenant_id) if not api_key: - logger.warning(f"No API key cached for tenant {tenant_id}") + logger.warning("No API key cached for tenant %s", tenant_id) return # Check if bot should respond @@ -168,9 +170,11 @@ class OnyxDiscordClient(commands.Bot): return logger.debug( - f"Processing message: '{message.content[:50]}' in " - f"#{getattr(message.channel, 'name', 'unknown')} ({message.guild.name}), " - f"persona_id={should_respond_context.persona_id}" + "Processing message: '%s' in #%s (%s), persona_id=%s", + message.content[:50], + getattr(message.channel, "name", "unknown"), + message.guild.name, + should_respond_context.persona_id, ) # Process the message @@ -184,7 +188,7 @@ class OnyxDiscordClient(commands.Bot): ) except Exception as e: - logger.exception(f"Error processing message: {e}") + logger.exception("Error processing message: %s", e) # ----------------------------------------------------------------------------- diff --git a/backend/onyx/onyxbot/discord/handle_commands.py b/backend/onyx/onyxbot/discord/handle_commands.py index 9e7bca9f7b..eee8004cbe 100644 --- a/backend/onyx/onyxbot/discord/handle_commands.py +++ b/backend/onyx/onyxbot/discord/handle_commands.py @@ -44,29 +44,29 @@ async def handle_dm(message: discord.Message) -> None: async def _try_dm_author(message: discord.Message, content: str) -> bool: """Attempt to DM the message author. Returns True if successful.""" - logger.debug(f"Responding in Discord DM with {content}") + logger.debug("Responding in Discord DM with %s", content) try: await message.author.send(content) return True except (discord.Forbidden, discord.HTTPException) as e: # User has DMs disabled or other error - logger.warning(f"Failed to DM author {message.author.id}: {e}") + logger.warning("Failed to DM author %s: %s", message.author.id, e) except Exception as e: - logger.exception(f"Unexpected error DMing author {message.author.id}: {e}") + logger.exception("Unexpected error DMing author %s: %s", message.author.id, e) return False async def _try_delete_message(message: discord.Message) -> bool: """Attempt to delete a message. Returns True if successful.""" - logger.debug(f"Deleting potentially sensitive message {message.id}") + logger.debug("Deleting potentially sensitive message %s", message.id) try: await message.delete() return True except (discord.Forbidden, discord.HTTPException) as e: # Bot lacks permission or other error - logger.warning(f"Failed to delete message {message.id}: {e}") + logger.warning("Failed to delete message %s: %s", message.id, e) except Exception as e: - logger.exception(f"Unexpected error deleting message {message.id}: {e}") + logger.exception("Unexpected error deleting message %s: %s", message.id, e) return False @@ -77,9 +77,9 @@ async def _try_react_x(message: discord.Message) -> bool: return True except (discord.Forbidden, discord.HTTPException) as e: # Bot lacks permission or other error - logger.warning(f"Failed to react to message {message.id}: {e}") + logger.warning("Failed to react to message %s: %s", message.id, e) except Exception as e: - logger.exception(f"Unexpected error reacting to message {message.id}: {e}") + logger.exception("Unexpected error reacting to message %s: %s", message.id, e) return False @@ -107,7 +107,7 @@ async def handle_registration_command( return True guild_name = message.guild.name - logger.info(f"Registration command received: {guild_name}") + logger.info("Registration command received: %s", guild_name) try: # Parse the registration key @@ -132,18 +132,18 @@ async def handle_registration_command( ) await _register_guild(message, registration_key, cache) - logger.info(f"Registration successful: {guild_name}") + logger.info("Registration successful: %s", guild_name) await message.reply( ":white_check_mark: **Successfully registered!**\n\n" "This server is now connected to Onyx. " "I'll respond to messages based on your server and channel settings set in Onyx." ) except RegistrationError as e: - logger.debug(f"Registration failed: {guild_name}, error={e}") + logger.debug("Registration failed: %s, error=%s", guild_name, e) await _try_dm_author(message, f":x: **Registration failed.**\n\n{e}") await _try_delete_message(message) except Exception: - logger.exception(f"Registration failed unexpectedly: {guild_name}") + logger.exception("Registration failed unexpectedly: %s", guild_name) await _try_dm_author( message, ":x: **Registration failed.**\n\nAn unexpected error occurred. Please try again later.", @@ -163,7 +163,7 @@ async def _register_guild( # mypy, even though we already know that message.guild is not None raise RegistrationError("This command can only be used in a server.") - logger.info(f"Guild '{message.guild.name}' attempting to register Discord bot") + logger.info("Guild '%s' attempting to register Discord bot", message.guild.name) registration_key = registration_key.strip() # Parse tenant_id from registration key @@ -175,14 +175,14 @@ async def _register_guild( tenant_id = parsed - logger.info(f"Parsed tenant_id {tenant_id} from registration key") + logger.info("Parsed tenant_id %s from registration key", tenant_id) # Check if this guild is already registered to any tenant guild_id = message.guild.id existing_tenant = cache.get_tenant(guild_id) if existing_tenant is not None: logger.warning( - f"Guild {guild_id} is already registered to tenant {existing_tenant}" + "Guild %s is already registered to tenant %s", guild_id, existing_tenant ) raise RegistrationError( "This server is already registered.\n\nOnyxBot can only connect one Discord server to one Onyx workspace." @@ -195,7 +195,7 @@ async def _register_guild( # Collect all text channels from the guild channels = get_text_channels(guild) - logger.info(f"Found {len(channels)} text channels in guild '{guild_name}'") + logger.info("Found %s text channels in guild '%s'", len(channels), guild_name) # Validate and update in database def _sync_register() -> int: @@ -234,7 +234,7 @@ async def _register_guild( await cache.refresh_guild(guild_id, tenant_id) logger.info( - f"Guild '{guild_name}' registered with {len(channels)} channel configs" + "Guild '%s' registered with %s channel configs", guild_name, len(channels) ) finally: CURRENT_TENANT_ID_CONTEXTVAR.reset(context_token) @@ -251,7 +251,10 @@ def get_text_channels(guild: discord.Guild) -> list[DiscordChannelView]: is_private = not everyone_perms.view_channel logger.debug( - f"Found channel: #{channel.name}, type={channel.type.name}, is_private={is_private}" + "Found channel: #%s, type=%s, is_private=%s", + channel.name, + channel.type.name, + is_private, ) channels.append( @@ -263,7 +266,7 @@ def get_text_channels(guild: discord.Guild) -> list[DiscordChannelView]: ) ) - logger.debug(f"Retrieved {len(channels)} channels from guild '{guild.name}'") + logger.debug("Retrieved %s channels from guild '%s'", len(channels), guild.name) return channels @@ -292,7 +295,7 @@ async def handle_sync_channels_command( return True guild_name = message.guild.name - logger.info(f"Sync-channels command received: {guild_name}") + logger.info("Sync-channels command received: %s", guild_name) try: # Must be registered @@ -335,7 +338,11 @@ async def handle_sync_channels_command( guild_config_id, tenant_id, bot ) logger.info( - f"Sync-channels successful: {guild_name}, added={added}, removed={removed}, updated={updated}" + "Sync-channels successful: %s, added=%s, removed=%s, updated=%s", + guild_name, + added, + removed, + updated, ) await message.reply( f":white_check_mark: **Channel sync complete!**\n\n" @@ -345,11 +352,11 @@ async def handle_sync_channels_command( "New channels are disabled by default. Enable them in the Onyx admin panel." ) except SyncChannelsError as e: - logger.debug(f"Sync-channels failed: {guild_name}, error={e}") + logger.debug("Sync-channels failed: %s, error=%s", guild_name, e) await _try_dm_author(message, f":x: **Channel sync failed.**\n\n{e}") await _try_react_x(message) except Exception: - logger.exception(f"Sync-channels failed unexpectedly: {guild_name}") + logger.exception("Sync-channels failed unexpectedly: %s", guild_name) await _try_dm_author( message, ":x: **Channel sync failed.**\n\nAn unexpected error occurred. Please try again later.", @@ -406,7 +413,7 @@ async def sync_guild_channels( # Get current channels from Discord channels = get_text_channels(guild) - logger.info(f"Syncing {len(channels)} channels for guild '{guild.name}'") + logger.info("Syncing %s channels for guild '%s'", len(channels), guild.name) # Sync with database def _sync() -> tuple[int, int, int]: @@ -420,7 +427,11 @@ async def sync_guild_channels( added, removed, updated = await asyncio.to_thread(_sync) logger.info( - f"Channel sync complete for guild '{guild.name}': added={added}, removed={removed}, updated={updated}" + "Channel sync complete for guild '%s': added=%s, removed=%s, updated=%s", + guild.name, + added, + removed, + updated, ) return added, removed, updated diff --git a/backend/onyx/onyxbot/discord/handle_message.py b/backend/onyx/onyxbot/discord/handle_message.py index 89c5fde780..280bfb9033 100644 --- a/backend/onyx/onyxbot/discord/handle_message.py +++ b/backend/onyx/onyxbot/discord/handle_message.py @@ -116,7 +116,7 @@ async def check_implicit_invocation( ) if referenced_msg.author.id == bot_user.id: logger.debug( - f"Implicit invocation via reply: '{message.content[:50]}...'" + "Implicit invocation via reply: '%s...'", message.content[:50] ) return True except (discord.NotFound, discord.HTTPException): @@ -129,7 +129,9 @@ async def check_implicit_invocation( # Bot owns the thread if thread.owner_id == bot_user.id: logger.debug( - f"Implicit invocation via bot-owned thread: '{message.content[:50]}...' in #{thread.name}" + "Implicit invocation via bot-owned thread: '%s...' in #%s", + message.content[:50], + thread.name, ) return True @@ -139,7 +141,9 @@ async def check_implicit_invocation( starter = await thread.parent.fetch_message(thread.id) if starter.author.id == bot_user.id: logger.debug( - f"Implicit invocation via bot-started thread: '{message.content[:50]}...' in #{thread.name}" + "Implicit invocation via bot-started thread: '%s...' in #%s", + message.content[:50], + thread.name, ) return True except (discord.NotFound, discord.HTTPException): @@ -166,7 +170,7 @@ async def process_chat_message( await message.add_reaction(THINKING_EMOJI) except discord.DiscordException: logger.warning( - f"Failed to add thinking reaction to message: '{message.content[:50]}...'" + "Failed to add thinking reaction to message: '%s...'", message.content[:50] ) try: @@ -203,10 +207,10 @@ async def process_chat_message( pass except APIError as e: - logger.error(f"API error processing message: {e}") + logger.error("API error processing message: %s", e) await send_error_response(message, bot_user) except Exception as e: - logger.exception(f"Error processing chat message: {e}") + logger.exception("Error processing chat message: %s", e) await send_error_response(message, bot_user) @@ -298,13 +302,15 @@ async def _build_reply_chain_context( messages.reverse() # Chronological order logger.debug( - f"Built reply chain context: {len(messages)} messages in #{getattr(message.channel, 'name', 'unknown')}" + "Built reply chain context: %s messages in #%s", + len(messages), + getattr(message.channel, "name", "unknown"), ) return _format_messages_as_context(messages, bot_user) except Exception as e: - logger.warning(f"Failed to build reply chain context: {e}") + logger.warning("Failed to build reply chain context: %s", e) return None @@ -358,13 +364,13 @@ async def _build_thread_context( messages.sort(key=lambda m: m.id) # Chronological order logger.debug( - f"Built thread context: {len(messages)} messages in #{thread.name}" + "Built thread context: %s messages in #%s", len(messages), thread.name ) return _format_messages_as_context(messages, bot_user) except Exception as e: - logger.warning(f"Failed to build thread context: {e}") + logger.warning("Failed to build thread context: %s", e) return None diff --git a/backend/onyx/onyxbot/discord/utils.py b/backend/onyx/onyxbot/discord/utils.py index 79bf916f02..36e70a2cf2 100644 --- a/backend/onyx/onyxbot/discord/utils.py +++ b/backend/onyx/onyxbot/discord/utils.py @@ -35,7 +35,7 @@ def get_bot_token() -> str | None: with get_session_with_tenant(tenant_id=POSTGRES_DEFAULT_SCHEMA) as db: config = get_discord_bot_config(db) except Exception as e: - logger.error(f"Failed to get bot token from database: {e}") + logger.error("Failed to get bot token from database: %s", e) return None if config and config.bot_token: if isinstance(config.bot_token, SensitiveValue): diff --git a/backend/onyx/onyxbot/slack/handlers/handle_buttons.py b/backend/onyx/onyxbot/slack/handlers/handle_buttons.py index d5dd547ccf..b0990c23ff 100644 --- a/backend/onyx/onyxbot/slack/handlers/handle_buttons.py +++ b/backend/onyx/onyxbot/slack/handlers/handle_buttons.py @@ -238,7 +238,7 @@ def handle_publish_ephemeral_message_button( chat_message_id, None, db_session ) # is this good idea? except Exception as e: - logger.error(f"Failed to get chat message: {e}") + logger.error("Failed to get chat message: %s", e) raise e chat_message_detail = translate_db_message_to_chat_message_detail(chat_message) @@ -276,7 +276,7 @@ def handle_publish_ephemeral_message_button( delete_original=True, ) except Exception as e: - logger.error(f"Failed to send webhook: {e}") + logger.error("Failed to send webhook: %s", e) # remove handling of empheremal block and add AI feedback. all_blocks = build_slack_response_blocks( @@ -302,7 +302,7 @@ def handle_publish_ephemeral_message_button( send_as_ephemeral=False, ) except Exception as e: - logger.error(f"Failed to publish ephemeral message: {e}") + logger.error("Failed to publish ephemeral message: %s", e) raise e elif action_id == KEEP_TO_YOURSELF_ACTION_ID: @@ -353,7 +353,7 @@ def handle_publish_ephemeral_message_button( delete_original=False, ) except Exception as e: - logger.error(f"Failed to send webhook: {e}") + logger.error("Failed to send webhook: %s", e) def handle_slack_feedback( @@ -412,7 +412,7 @@ def handle_slack_feedback( feedback=feedback, ) else: - logger.error(f"Feedback type '{feedback_type}' not supported") + logger.error("Feedback type '%s' not supported", feedback_type) if get_feedback_visibility() == FeedbackVisibility.PRIVATE or feedback_type not in [ LIKE_BLOCK_ACTION_ID, diff --git a/backend/onyx/onyxbot/slack/handlers/handle_message.py b/backend/onyx/onyxbot/slack/handlers/handle_message.py index a848d83afe..84de8a3d4c 100644 --- a/backend/onyx/onyxbot/slack/handlers/handle_message.py +++ b/backend/onyx/onyxbot/slack/handlers/handle_message.py @@ -62,7 +62,7 @@ def schedule_feedback_reminder( message_ts=details.msg_to_respond, # ty: ignore[invalid-argument-type] ) except SlackApiError as e: - logger.error(f"Unable to generate the feedback reminder permalink: {e}") + logger.error("Unable to generate the feedback reminder permalink: %s", e) return None now = datetime.datetime.now() @@ -87,7 +87,7 @@ def schedule_feedback_reminder( "scheduled_message_id" ] except SlackApiError as e: - logger.error(f"Unable to generate the feedback reminder message: {e}") + logger.error("Unable to generate the feedback reminder message: %s", e) return None @@ -165,9 +165,9 @@ def handle_message( return False logger.info( - "Found slack bot config for channel. Restricting bot to use document " - f"sets: {document_set_names}, " - f"validity checks enabled: {channel_conf.get('answer_filters', 'NA')}" + "Found slack bot config for channel. Restricting bot to use document sets: %s, validity checks enabled: %s", + document_set_names, + channel_conf.get("answer_filters", "NA"), ) respond_tag_only = channel_conf.get("respond_tag_only") or False @@ -196,7 +196,7 @@ def handle_message( send_to = list(set(send_to + user_ids)) if send_to else user_ids if missing_users: - logger.warning(f"Failed to find these users/groups: {missing_users}") + logger.warning("Failed to find these users/groups: %s", missing_users) # If configured to respond to team members only, then cannot be used with a /OnyxBot command # which would just respond to the sender @@ -213,7 +213,7 @@ def handle_message( try: send_msg_ack_to_user(message_info, client) except SlackApiError as e: - logger.error(f"Was not able to react to user message due to: {e}") + logger.error("Was not able to react to user message due to: %s", e) with get_session_with_current_tenant() as db_session: if message_info.email: @@ -229,7 +229,9 @@ def handle_message( seat_result = check_seat_fn(db_session=db_session) if seat_result is not None and not seat_result.available: logger.info( - f"Blocked new Slack user {message_info.email}: {seat_result.error_message}" + "Blocked new Slack user %s: %s", + message_info.email, + seat_result.error_message, ) respond_in_thread_or_channel( client=client, @@ -257,7 +259,9 @@ def handle_message( seat_result = check_seat_fn(db_session=db_session) if seat_result is not None and not seat_result.available: logger.info( - f"Blocked inactive Slack user {message_info.email}: {seat_result.error_message}" + "Blocked inactive Slack user %s: %s", + message_info.email, + seat_result.error_message, ) respond_in_thread_or_channel( client=client, @@ -280,7 +284,7 @@ def handle_message( None, ) invalidate_license_cache_fn() - logger.info(f"Reactivated inactive Slack user {message_info.email}") + logger.info("Reactivated inactive Slack user %s", message_info.email) add_slack_user_if_not_exists(db_session, message_info.email) diff --git a/backend/onyx/onyxbot/slack/handlers/handle_regular_answer.py b/backend/onyx/onyxbot/slack/handlers/handle_regular_answer.py index b9a5208318..73a9ddb3e4 100644 --- a/backend/onyx/onyxbot/slack/handlers/handle_regular_answer.py +++ b/backend/onyx/onyxbot/slack/handlers/handle_regular_answer.py @@ -68,7 +68,7 @@ def resolve_channel_references( channel_info = get_channel_from_id(client=client, channel_id=channel_id) channel_name = channel_info.get("name") or None except Exception: - logger.warning(f"Failed to resolve channel name for ID: {channel_id}") + logger.warning("Failed to resolve channel name for ID: %s", channel_id) if not channel_name: continue @@ -197,7 +197,7 @@ def handle_regular_answer( document_set.name for document_set in persona.document_sets ] else: - logger.info(f"Using persona {persona.name} for channel config") + logger.info("Using persona %s for channel config", persona.name) document_set_names = [ document_set.name for document_set in persona.document_sets ] @@ -306,7 +306,8 @@ def handle_regular_answer( except Exception as e: logger.exception( - f"Unable to process message - did not successfully answer in {num_retries} attempts" + "Unable to process message - did not successfully answer in %s attempts", + num_retries, ) # Optionally, respond in thread with the error message, Used primarily # for debugging purposes @@ -359,7 +360,7 @@ def handle_regular_answer( and not channel_tags ): logger.error( - f"Unable to find citations to answer: '{answer.answer}' - not answering!" + "Unable to find citations to answer: '%s' - not answering!", answer.answer ) # Optionally, respond in thread with the error message # Used primarily for debugging purposes @@ -434,6 +435,7 @@ def handle_regular_answer( except Exception: logger.exception( - f"Unable to process message - could not respond in slack in {num_retries} attempts" + "Unable to process message - could not respond in slack in %s attempts", + num_retries, ) return True diff --git a/backend/onyx/onyxbot/slack/listener.py b/backend/onyx/onyxbot/slack/listener.py index 57a038ca17..a897676f2c 100644 --- a/backend/onyx/onyxbot/slack/listener.py +++ b/backend/onyx/onyxbot/slack/listener.py @@ -147,7 +147,7 @@ class SlackbotHandler: self._lock = threading.Lock() - logger.info(f"Pod ID: {self.pod_id}") + logger.info("Pod ID: %s", self.pod_id) # Set up signal handlers for graceful shutdown signal.signal(signal.SIGTERM, self.shutdown) @@ -184,10 +184,10 @@ class SlackbotHandler: len(self.tenant_ids) ) logger.debug( - f"Current active tenants with Slack bots: {len(self.tenant_ids)}" + "Current active tenants with Slack bots: %s", len(self.tenant_ids) ) except Exception as e: - logger.exception(f"Error in Slack acquisition: {e}") + logger.exception("Error in Slack acquisition: %s", e) self._shutdown_event.wait(timeout=TENANT_ACQUISITION_INTERVAL) def heartbeat_loop(self) -> None: @@ -202,9 +202,9 @@ class SlackbotHandler: tenant_ids = self.tenant_ids.copy() SlackbotHandler.send_heartbeats(self.pod_id, tenant_ids) - logger.debug(f"Sent heartbeats for {len(tenant_ids)} active tenants") + logger.debug("Sent heartbeats for %s active tenants", len(tenant_ids)) except Exception as e: - logger.exception(f"Error in heartbeat loop: {e}") + logger.exception("Error in heartbeat loop: %s", e) self._shutdown_event.wait(timeout=TENANT_HEARTBEAT_INTERVAL) def _manage_clients_per_tenant( @@ -220,7 +220,7 @@ class SlackbotHandler: # If the tokens are missing or empty, close the socket client and remove them. if not bot.bot_token or not bot.app_token: logger.debug( - f"No Slack bot tokens found for tenant={tenant_id}, bot {bot.id}" + "No Slack bot tokens found for tenant=%s, bot %s", tenant_id, bot.id ) if tenant_bot_pair in self.socket_clients: self.socket_clients[tenant_bot_pair].close() @@ -240,7 +240,9 @@ class SlackbotHandler: if not tokens_exist or tokens_changed: if tokens_exist: logger.info( - f"Slack Bot tokens changed for tenant={tenant_id}, bot {bot.id}; reconnecting" + "Slack Bot tokens changed for tenant=%s, bot %s; reconnecting", + tenant_id, + bot.id, ) else: # Warm up the model if needed @@ -266,7 +268,10 @@ class SlackbotHandler: self.socket_clients[tenant_id, bot.id] = socket_client logger.info( - f"Started SocketModeClient: {tenant_id=} {socket_client.bot_name=} {bot.id=}" + "Started SocketModeClient: tenant_id=%r socket_client.bot_name=%r bot.id=%r", + tenant_id, + socket_client.bot_name, + bot.id, ) self.tenant_ids.add(tenant_id) @@ -300,7 +305,7 @@ class SlackbotHandler: DISALLOWED_SLACK_BOT_TENANT_LIST is not None and tenant_id in DISALLOWED_SLACK_BOT_TENANT_LIST ): - logger.debug(f"Tenant {tenant_id} is disallowed; skipping.") + logger.debug("Tenant %s is disallowed; skipping.", tenant_id) continue # Already acquired in a previous loop iteration? @@ -310,7 +315,8 @@ class SlackbotHandler: # Respect max tenant limit per pod if len(self.tenant_ids) >= MAX_TENANTS_PER_POD: logger.info( - f"Max tenants per pod reached, not acquiring more: {MAX_TENANTS_PER_POD=}" + "Max tenants per pod reached, not acquiring more: MAX_TENANTS_PER_POD=%r", + MAX_TENANTS_PER_POD, ) break @@ -327,17 +333,17 @@ class SlackbotHandler: if not lock_acquired and not DEV_MODE: logger.debug( - f"Another pod holds the lock for tenant {tenant_id}, skipping." + "Another pod holds the lock for tenant %s, skipping.", tenant_id ) continue if lock_acquired: - logger.debug(f"Acquired lock for tenant {tenant_id}.") + logger.debug("Acquired lock for tenant %s.", tenant_id) self.redis_locks[tenant_id] = rlock else: # DEV_MODE will skip the lock acquisition guard logger.debug( - f"Running in DEV_MODE. Not enforcing lock for {tenant_id}." + "Running in DEV_MODE. Not enforcing lock for %s.", tenant_id ) # Now check if this tenant actually has Slack bots @@ -358,16 +364,18 @@ class SlackbotHandler: # migration has not completed yet. if isinstance(e.orig, psycopg2.errors.UndefinedTable): logger.warning( - f"Tenant {tenant_id} missing slack_bot table " - "(likely mid-provisioning); will retry next cycle." + "Tenant %s missing slack_bot table (likely mid-provisioning); will retry next cycle.", + tenant_id, ) else: logger.exception( - f"Error fetching Slack bots for tenant {tenant_id}: {e}" + "Error fetching Slack bots for tenant %s: %s", + tenant_id, + e, ) except Exception as e: logger.exception( - f"Error fetching Slack bots for tenant {tenant_id}: {e}" + "Error fetching Slack bots for tenant %s: %s", tenant_id, e ) if bots: @@ -385,7 +393,8 @@ class SlackbotHandler: rlock.release() del self.redis_locks[tenant_id] logger.debug( - f"No Slack bots for tenant {tenant_id}; lock released (if held)." + "No Slack bots for tenant %s; lock released (if held).", + tenant_id, ) finally: CURRENT_TENANT_ID_CONTEXTVAR.reset(token) @@ -395,7 +404,7 @@ class SlackbotHandler: for tenant_id in list(self.tenant_ids): if tenant_id in gated_tenants: logger.info( - f"Tenant {tenant_id} is now gated (suspended). Disconnecting." + "Tenant %s is now gated (suspended). Disconnecting.", tenant_id ) self._remove_tenant(tenant_id) if tenant_id in self.redis_locks and not DEV_MODE: @@ -406,7 +415,7 @@ class SlackbotHandler: pass except Exception as e: logger.warning( - f"Error releasing lock for gated tenant {tenant_id}: {e}" + "Error releasing lock for gated tenant %s: %s", tenant_id, e ) finally: self.redis_locks.pop(tenant_id, None) @@ -428,19 +437,21 @@ class SlackbotHandler: except ProgrammingError as e: if isinstance(e.orig, psycopg2.errors.UndefinedTable): logger.warning( - f"Tenant {tenant_id} missing slack_bot table " - "(likely mid-provisioning); will retry next cycle." + "Tenant %s missing slack_bot table (likely mid-provisioning); will retry next cycle.", + tenant_id, ) else: - logger.exception(f"Error handling tenant {tenant_id}: {e}") + logger.exception( + "Error handling tenant %s: %s", tenant_id, e + ) bots = [] except Exception as e: - logger.exception(f"Error handling tenant {tenant_id}: {e}") + logger.exception("Error handling tenant %s: %s", tenant_id, e) bots = [] if not bots: logger.info( - f"Tenant {tenant_id} no longer has Slack bots. Removing." + "Tenant %s no longer has Slack bots. Removing.", tenant_id ) self._remove_tenant(tenant_id) @@ -448,13 +459,15 @@ class SlackbotHandler: if tenant_id in self.redis_locks and not DEV_MODE: try: self.redis_locks[tenant_id].release() - logger.info(f"Released lock for tenant {tenant_id}") + logger.info("Released lock for tenant %s", tenant_id) except LockNotOwnedError: # Expected: lock expired or was stolen. pass except Exception as e: logger.warning( - f"Error releasing lock for tenant {tenant_id}: {e}" + "Error releasing lock for tenant %s: %s", + tenant_id, + e, ) finally: self.redis_locks.pop(tenant_id, None) @@ -482,7 +495,9 @@ class SlackbotHandler: del self.socket_clients[(t_id, slack_bot_id)] del self.slack_bot_tokens[(t_id, slack_bot_id)] logger.info( - f"Stopped SocketModeClient for tenant: {t_id}, app: {slack_bot_id}" + "Stopped SocketModeClient for tenant: %s, app: %s", + t_id, + slack_bot_id, ) # Remove from active set @@ -492,7 +507,7 @@ class SlackbotHandler: @staticmethod def send_heartbeats(pod_id: str, tenant_ids: set[str]) -> None: current_time = int(time.time()) - logger.debug(f"Sending heartbeats for {len(tenant_ids)} active tenants") + logger.debug("Sending heartbeats for %s active tenants", len(tenant_ids)) for tenant_id in tenant_ids: redis_client = get_redis_client(tenant_id=tenant_id) heartbeat_key = f"{OnyxRedisLocks.SLACK_BOT_HEARTBEAT_PREFIX}:{pod_id}" @@ -537,18 +552,27 @@ class SlackbotHandler: ) ): logger.warning( - f"Slack auth failed, skipping bot: {tenant_id=} {slack_bot_id=} error={e}" + "Slack auth failed, skipping bot: tenant_id=%r slack_bot_id=%r error=%s", + tenant_id, + slack_bot_id, + e, ) return None # Log other Slack API errors but continue logger.error( - f"Slack API error fetching bot info: {e} for tenant: {tenant_id}, app: {slack_bot_id}" + "Slack API error fetching bot info: %s for tenant: %s, app: %s", + e, + tenant_id, + slack_bot_id, ) except Exception as e: # Log other exceptions but continue logger.error( - f"Error fetching bot info: {e} for tenant: {tenant_id}, app: {slack_bot_id}" + "Error fetching bot info: %s for tenant: %s, app: %s", + e, + tenant_id, + slack_bot_id, ) # Append the event handler @@ -567,7 +591,10 @@ class SlackbotHandler: socket_client.connect() except SlackApiError as e: logger.warning( - f"Failed to open Slack socket connection: {tenant_id=} {slack_bot_id=} error={e}" + "Failed to open Slack socket connection: tenant_id=%r slack_bot_id=%r error=%s", + tenant_id, + slack_bot_id, + e, ) return None @@ -585,7 +612,12 @@ class SlackbotHandler: x += 1 client.close() logger.info( - f"Stopped SocketModeClient {x}/{length}: {pod_id=} {tenant_id=} {slack_bot_id=}" + "Stopped SocketModeClient %s/%s: pod_id=%r tenant_id=%r slack_bot_id=%r", + x, + length, + pod_id, + tenant_id, + slack_bot_id, ) def shutdown( @@ -605,21 +637,23 @@ class SlackbotHandler: self.heartbeat_thread.join(timeout=60.0) # Stop all socket clients - logger.info(f"Stopping {len(self.socket_clients)} socket clients") + logger.info("Stopping %s socket clients", len(self.socket_clients)) SlackbotHandler.stop_socket_clients(self.pod_id, self.socket_clients) # Release locks for all tenants we currently hold - logger.info(f"Releasing locks for {len(self.tenant_ids)} tenants") + logger.info("Releasing locks for %s tenants", len(self.tenant_ids)) for tenant_id in list(self.tenant_ids): if tenant_id in self.redis_locks: try: self.redis_locks[tenant_id].release() - logger.info(f"Released lock for tenant {tenant_id}") + logger.info("Released lock for tenant %s", tenant_id) except LockNotOwnedError: # Expected during shutdown: lock expired or was stolen. pass except Exception as e: - logger.warning(f"Error releasing lock for tenant {tenant_id}: {e}") + logger.warning( + "Error releasing lock for tenant %s: %s", tenant_id, e + ) finally: del self.redis_locks[tenant_id] @@ -655,7 +689,11 @@ def prefilter_requests(req: SocketModeRequest, client: TenantSocketModeClient) - bot_token_user_id, bot_token_bot_id = get_onyx_bot_auth_ids( tenant_id, client.web_client ) - logger.info(f"prefilter_requests: {bot_token_user_id=} {bot_token_bot_id=}") + logger.info( + "prefilter_requests: bot_token_user_id=%r bot_token_bot_id=%r", + bot_token_user_id, + bot_token_bot_id, + ) with get_session_with_current_tenant() as db_session: slack_bot = fetch_slack_bot( @@ -663,13 +701,15 @@ def prefilter_requests(req: SocketModeRequest, client: TenantSocketModeClient) - ) if not slack_bot: logger.error( - f"Slack bot with ID '{client.slack_bot_id}' not found. Skipping request." + "Slack bot with ID '%s' not found. Skipping request.", + client.slack_bot_id, ) return False if not slack_bot.enabled: logger.info( - f"Slack bot with ID '{client.slack_bot_id}' is disabled. Skipping request." + "Slack bot with ID '%s' is disabled. Skipping request.", + client.slack_bot_id, ) return False @@ -707,10 +747,10 @@ def prefilter_requests(req: SocketModeRequest, client: TenantSocketModeClient) - in _SLACK_GREETINGS_TO_IGNORE ): channel_specific_logger.error( - f"Ignoring weird Slack greeting message: '{msg}'" + "Ignoring weird Slack greeting message: '%s'", msg ) channel_specific_logger.error( - f"Weird Slack greeting message payload: '{req.payload}'" + "Weird Slack greeting message payload: '%s'", req.payload ) return False @@ -783,7 +823,8 @@ def prefilter_requests(req: SocketModeRequest, client: TenantSocketModeClient) - message_subtype = event.get("subtype") if message_subtype not in [None, "file_share", "bot_message"]: channel_specific_logger.info( - f"Ignoring message with subtype '{message_subtype}' since it is a special message type" + "Ignoring message with subtype '%s' since it is a special message type", + message_subtype, ) return False @@ -830,7 +871,9 @@ def prefilter_requests(req: SocketModeRequest, client: TenantSocketModeClient) - # Don't log Slack message content logger.debug( - f"Handling Slack request: {client.bot_name=} '{sanitize_slack_payload(req.payload)=}'" + "Handling Slack request: client.bot_name=%r 'sanitize_slack_payload(req.payload)=%r'", + client.bot_name, + sanitize_slack_payload(req.payload), ) return True @@ -864,7 +907,7 @@ def process_feedback(req: SocketModeRequest, client: TenantSocketModeClient) -> ) query_event_id, _, _ = decompose_action_id(feedback_id) - logger.info(f"Successfully handled QA feedback for event: {query_event_id}") + logger.info("Successfully handled QA feedback for event: %s", query_event_id) def build_request_details( @@ -890,7 +933,7 @@ def build_request_details( msg = remove_onyx_bot_tag(tenant_id, msg, client=client.web_client) - logger.info(f"Received Slack message: {msg}") + logger.info("Received Slack message: %s", msg) event_type = event.get("type") if event_type == "app_mention": @@ -915,8 +958,10 @@ def build_request_details( message_ts=message_ts, ) logger.info( - f"build_request_details: Capturing Slack context: " - f"channel_type={channel_type} channel_id={channel} message_ts={message_ts}" + "build_request_details: Capturing Slack context: channel_type=%s channel_id=%s message_ts=%s", + channel_type, + channel, + message_ts, ) if thread_ts != message_ts and thread_ts is not None: @@ -977,7 +1022,9 @@ def build_request_details( message_ts=None, # Slash commands don't have a message timestamp ) logger.info( - f"build_request_details: Capturing Slack context for slash command: channel_type={channel_type} channel_id={channel}" + "build_request_details: Capturing Slack context for slash command: channel_type=%s channel_id=%s", + channel_type, + channel, ) single_msg = ThreadMessage(message=msg, sender=None, role=MessageType.USER) @@ -1020,17 +1067,27 @@ def process_message( event = cast(dict[str, Any], req.payload["event"]) event_type = event.get("type") logger.info( - f"process_message start: {tenant_id=} {req.type=} {req.envelope_id=} {event_type=}" + "process_message start: tenant_id=%r req.type=%r req.envelope_id=%r event_type=%r", + tenant_id, + req.type, + req.envelope_id, + event_type, ) else: logger.info( - f"process_message start: {tenant_id=} {req.type=} {req.envelope_id=}" + "process_message start: tenant_id=%r req.type=%r req.envelope_id=%r", + tenant_id, + req.type, + req.envelope_id, ) # Throw out requests that can't or shouldn't be handled if not prefilter_requests(req, client): logger.info( - f"process_message prefiltered: {tenant_id=} {req.type=} {req.envelope_id=}" + "process_message prefiltered: tenant_id=%r req.type=%r req.envelope_id=%r", + tenant_id, + req.type, + req.envelope_id, ) return @@ -1075,7 +1132,11 @@ def process_message( apologize_for_fail(details, client) logger.info( - f"process_message finished: success={not failed} {tenant_id=} {req.type=} {req.envelope_id=}" + "process_message finished: success=%s tenant_id=%r req.type=%r req.envelope_id=%r", + not failed, + tenant_id, + req.type, + req.envelope_id, ) @@ -1185,7 +1246,7 @@ def _check_tenant_gated(client: TenantSocketModeClient, req: SocketModeRequest) "Your organization's subscription has expired. Please contact your Onyx administrator to restore access." ), ) - logger.info(f"Blocked Slack request for gated tenant {get_current_tenant_id()}") + logger.info("Blocked Slack request for gated tenant %s", get_current_tenant_id()) return True diff --git a/backend/onyx/onyxbot/slack/utils.py b/backend/onyx/onyxbot/slack/utils.py index 1a9f50f467..ff94132cc0 100644 --- a/backend/onyx/onyxbot/slack/utils.py +++ b/backend/onyx/onyxbot/slack/utils.py @@ -95,15 +95,18 @@ def get_channel_type_from_id(web_client: WebClient, channel_id: str) -> ChannelT return ChannelType.PUBLIC_CHANNEL # Public channel else: logger.warning( - f"Could not determine channel type for {channel_id}, defaulting to unknown" + "Could not determine channel type for %s, defaulting to unknown", + channel_id, ) return ChannelType.UNKNOWN else: - logger.warning(f"Invalid channel info response for {channel_id}") + logger.warning("Invalid channel info response for %s", channel_id) return ChannelType.UNKNOWN except Exception as e: logger.warning( - f"Error getting channel info for {channel_id}, defaulting to unknown: {e}" + "Error getting channel info for %s, defaulting to unknown: %s", + channel_id, + e, ) return ChannelType.UNKNOWN @@ -124,9 +127,11 @@ def check_message_limit() -> bool: _ONYX_BOT_COUNT_START_TIME = time.time() if (_ONYX_BOT_MESSAGE_COUNT + 1) > ONYX_BOT_RESPONSE_LIMIT_PER_TIME_PERIOD: logger.error( - f"OnyxBot has reached the message limit {ONYX_BOT_RESPONSE_LIMIT_PER_TIME_PERIOD}" - f" for the time period {ONYX_BOT_RESPONSE_LIMIT_TIME_PERIOD_SECONDS} seconds." - " These limits are configurable in backend/onyx/configs/onyxbot_configs.py" + "OnyxBot has reached the message limit %s" + " for the time period %s seconds." + " These limits are configurable in backend/onyx/configs/onyxbot_configs.py", + ONYX_BOT_RESPONSE_LIMIT_PER_TIME_PERIOD, + ONYX_BOT_RESPONSE_LIMIT_TIME_PERIOD_SECONDS, ) return False _ONYX_BOT_MESSAGE_COUNT += 1 @@ -142,7 +147,11 @@ def update_emote_react( ) -> None: if not message_ts: action = "remove" if remove else "add" - logger.error(f"update_emote_react - no message specified: {channel=} {action=}") + logger.error( + "update_emote_react - no message specified: channel=%s action=%s", + channel, + action, + ) return if remove: @@ -153,7 +162,7 @@ def update_emote_react( timestamp=message_ts, ) except SlackApiError as e: - logger.error(f"Failed to remove Reaction due to: {e}") + logger.error("Failed to remove Reaction due to: %s", e) return @@ -164,7 +173,7 @@ def update_emote_react( timestamp=message_ts, ) except SlackApiError as e: - logger.error(f"Was not able to react to user message due to: {e}") + logger.error("Was not able to react to user message due to: %s", e) return @@ -242,7 +251,7 @@ def respond_in_thread_or_channel( ) except Exception as e: blocks_str = str(blocks)[:1024] # truncate block logging - logger.warning(f"Failed to post message: {e} \n blocks: {blocks_str}") + logger.warning("Failed to post message: %s \n blocks: %s", e, blocks_str) logger.warning("Trying again without blocks that have urls") if not blocks: @@ -280,7 +289,9 @@ def respond_in_thread_or_channel( ) except Exception as e: blocks_str = str(blocks)[:1024] # truncate block logging - logger.warning(f"Failed to post message: {e} \n blocks: {blocks_str}") + logger.warning( + "Failed to post message: %s \n blocks: %s", e, blocks_str + ) logger.warning("Trying again without blocks that have urls") if not blocks: @@ -435,7 +446,7 @@ def get_channel_name_from_id( is_dm = any([channel_info.get("is_im"), channel_info.get("is_mpim")]) return name, is_dm except SlackApiError as e: - logger.exception(f"Couldn't fetch channel name from id: {channel_id}") + logger.exception("Couldn't fetch channel name from id: %s", channel_id) raise e @@ -451,7 +462,7 @@ def fetch_slack_user_ids_from_emails( user.data["user"]["id"] # ty: ignore[invalid-argument-type] ) except Exception: - logger.error(f"Was not able to find slack user by email: {email}") + logger.error("Was not able to find slack user by email: %s", email) failed_to_find.append(email) return user_ids, failed_to_find @@ -485,10 +496,10 @@ def fetch_user_ids_from_groups( else: failed_to_find.append(given_name) except Exception as e: - logger.error(f"Error fetching user group ids: {str(e)}") + logger.error("Error fetching user group ids: %s", e) failed_to_find.append(given_name) except Exception as e: - logger.error(f"Error fetching user groups: {str(e)}") + logger.error("Error fetching user groups: %s", e) failed_to_find = given_names return user_ids, failed_to_find @@ -520,7 +531,7 @@ def fetch_group_ids_from_names( failed_to_find.append(given_name) except Exception as e: failed_to_find = given_names - logger.error(f"Error fetching user groups: {str(e)}") + logger.error("Error fetching user groups: %s", e) return group_data, failed_to_find @@ -584,7 +595,7 @@ def read_slack_thread( # auto-detected filters blocks = reply.get("blocks") if not blocks: - logger.warning(f"OnyxBot response has no blocks: {reply}") + logger.warning("OnyxBot response has no blocks: %s", reply) continue message = blocks[0].get("text", {}).get("text") @@ -593,7 +604,7 @@ def read_slack_thread( # The first block is the auto-detected filters if message is not None and message.startswith("_Filters"): if len(blocks) < 2: - logger.warning(f"Only filter blocks found: {reply}") + logger.warning("Only filter blocks found: %s", reply) continue # This is the OnyxBot answer format, if there is a change to how we respond, # this will need to be updated to get the correct "answer" portion diff --git a/backend/onyx/prompts/prompt_utils.py b/backend/onyx/prompts/prompt_utils.py index 891a409252..0cc65f0f23 100644 --- a/backend/onyx/prompts/prompt_utils.py +++ b/backend/onyx/prompts/prompt_utils.py @@ -157,7 +157,7 @@ def get_company_context() -> str | None: ) return prompt_str except Exception as e: - logger.error(f"Error handling company awareness: {e}") + logger.error("Error handling company awareness: %s", e) return None @@ -226,7 +226,9 @@ def find_last_index(lst: list[int], max_prompt_tokens: int) -> int: if last_ind >= len(lst): logger.error( - f"Last message alone is too large! max_prompt_tokens: {max_prompt_tokens}, message_token_counts: {lst}" + "Last message alone is too large! max_prompt_tokens: %s, message_token_counts: %s", + max_prompt_tokens, + lst, ) raise ValueError("Last message alone is too large!") diff --git a/backend/onyx/redis/lock_context.py b/backend/onyx/redis/lock_context.py index 33f1f91141..9da79dde66 100644 --- a/backend/onyx/redis/lock_context.py +++ b/backend/onyx/redis/lock_context.py @@ -69,9 +69,13 @@ def redis_shared_lock( if lock.owned(): lock.release() logger.debug( - f"Redis lock {lock_name} released after {time.monotonic() - start_time:.3f} seconds." + "Redis lock %s released after %s seconds.", + lock_name, + format(time.monotonic() - start_time, ".3f"), ) else: logger.warning( - f"Redis lock {lock_name} was not owned on exit. The lock context manager took {time.monotonic() - start_time:.3f} seconds." + "Redis lock %s was not owned on exit. The lock context manager took %s seconds.", + lock_name, + format(time.monotonic() - start_time, ".3f"), ) diff --git a/backend/onyx/redis/redis_connector_doc_perm_sync.py b/backend/onyx/redis/redis_connector_doc_perm_sync.py index 9e6c1dd87a..4d5b3052f7 100644 --- a/backend/onyx/redis/redis_connector_doc_perm_sync.py +++ b/backend/onyx/redis/redis_connector_doc_perm_sync.py @@ -216,10 +216,14 @@ class RedisConnectorPermissionSync: else permissions.raw_node_id ) task_logger.warning( - f"Permissions length exceeded, skipping...: " - f"{element_id} " - f"{num_users=} {num_groups=} " - f"{permissions.external_access.MAX_NUM_ENTRIES=}" + "Permissions length exceeded, skipping...: " + "%s " + "num_users=%s num_groups=%s " + "permissions.external_access.MAX_NUM_ENTRIES=%s", + element_id, + num_users, + num_groups, + permissions.external_access.MAX_NUM_ENTRIES, ) continue @@ -251,7 +255,7 @@ class RedisConnectorPermissionSync: else permissions.raw_node_id ) task_logger.exception( - f"Failed to update permissions for element {element_id}" + "Failed to update permissions for element %s", element_id ) finally: cumulative_db_update_time += time.monotonic() - db_start diff --git a/backend/onyx/redis/redis_hierarchy.py b/backend/onyx/redis/redis_hierarchy.py index 446d43bfbd..c6708b5765 100644 --- a/backend/onyx/redis/redis_hierarchy.py +++ b/backend/onyx/redis/redis_hierarchy.py @@ -328,7 +328,8 @@ def refresh_hierarchy_cache_from_db( acquired = lock.acquire(blocking=True) if not acquired: logger.warning( - f"Could not acquire lock for hierarchy cache refresh for source {source.value} - another worker may be refreshing" + "Could not acquire lock for hierarchy cache refresh for source %s - another worker may be refreshing", + source.value, ) return @@ -336,13 +337,13 @@ def refresh_hierarchy_cache_from_db( # Always refresh from DB when called - new nodes may have been added # since the cache was last populated. The lock ensures only one worker # does the refresh at a time. - logger.info(f"Refreshing hierarchy cache for source {source.value} from DB") + logger.info("Refreshing hierarchy cache for source %s from DB", source.value) # Load all nodes for this source from DB nodes = get_all_hierarchy_nodes_for_source(db_session, source) if not nodes: - logger.warning(f"No hierarchy nodes found in DB for source {source.value}") + logger.warning("No hierarchy nodes found in DB for source %s", source.value) return # Batch insert into cache @@ -350,14 +351,14 @@ def refresh_hierarchy_cache_from_db( cache_hierarchy_nodes_batch(redis_client, source, cache_entries) logger.info( - f"Refreshed hierarchy cache for {source.value} with {len(nodes)} nodes" + "Refreshed hierarchy cache for %s with %s nodes", source.value, len(nodes) ) finally: try: lock.release() except Exception as e: - logger.warning(f"Error releasing hierarchy cache lock: {e}") + logger.warning("Error releasing hierarchy cache lock: %s", e) def _walk_ancestor_chain( @@ -379,7 +380,10 @@ def _walk_ancestor_chain( while current_id is not None and len(ancestors) < MAX_DEPTH: if current_id in visited: logger.error( - f"Cycle detected in hierarchy for source {source.value} at node {current_id}. Ancestors so far: {ancestors}" + "Cycle detected in hierarchy for source %s at node %s. Ancestors so far: %s", + source.value, + current_id, + ancestors, ) break @@ -390,7 +394,9 @@ def _walk_ancestor_chain( if not found: logger.debug( - f"Cache miss for hierarchy node {current_id} of source {source.value}, attempting refresh" + "Cache miss for hierarchy node %s of source %s, attempting refresh", + current_id, + source.value, ) refresh_hierarchy_cache_from_db(redis_client, db_session, source) parent_id, found = get_parent_id_from_cache( @@ -399,7 +405,9 @@ def _walk_ancestor_chain( if not found: logger.error( - f"Hierarchy node {current_id} not found in cache for source {source.value} even after refresh." + "Hierarchy node %s not found in cache for source %s even after refresh.", + current_id, + source.value, ) break @@ -407,8 +415,9 @@ def _walk_ancestor_chain( if len(ancestors) >= MAX_DEPTH: logger.error( - f"Hit max depth {MAX_DEPTH} traversing hierarchy for source " - f"{source.value}. Possible infinite loop or very deep hierarchy." + "Hit max depth %s traversing hierarchy for source %s. Possible infinite loop or very deep hierarchy.", + MAX_DEPTH, + source.value, ) return ancestors @@ -452,7 +461,9 @@ def get_ancestors_from_raw_id( if not found: # Cache miss - try refresh logger.debug( - f"Cache miss for raw_node_id '{parent_hierarchy_raw_node_id}' of source {source.value}, attempting refresh" + "Cache miss for raw_node_id '%s' of source %s, attempting refresh", + parent_hierarchy_raw_node_id, + source.value, ) refresh_hierarchy_cache_from_db(redis_client, db_session, source) node_id, found = get_node_id_from_raw_id( @@ -461,8 +472,9 @@ def get_ancestors_from_raw_id( if not found or node_id is None: logger.error( - f"Raw node ID '{parent_hierarchy_raw_node_id}' not found in cache " - f"for source {source.value}. Falling back to SOURCE node only." + "Raw node ID '%s' not found in cache for source %s. Falling back to SOURCE node only.", + parent_hierarchy_raw_node_id, + source.value, ) source_node_id = get_source_node_id_from_cache(redis_client, db_session, source) return [source_node_id] if source_node_id else [] @@ -507,7 +519,7 @@ def get_source_node_id_from_cache( raise ValueError(f"SOURCE node value is not a string: {value}") return int(value) - logger.error(f"SOURCE node not found for source {source.value}") + logger.error("SOURCE node not found for source %s", source.value) return None @@ -568,7 +580,9 @@ def ensure_source_node_exists( cache_hierarchy_node(redis_client, source, cache_entry) logger.info( - f"Ensured SOURCE node exists and cached for {source.value}: id={source_node.id}" + "Ensured SOURCE node exists and cached for %s: id=%s", + source.value, + source_node.id, ) return source_node.id diff --git a/backend/onyx/redis/redis_pool.py b/backend/onyx/redis/redis_pool.py index 506341dfa2..d174ac2f48 100644 --- a/backend/onyx/redis/redis_pool.py +++ b/backend/onyx/redis/redis_pool.py @@ -468,7 +468,7 @@ async def retrieve_auth_token_data(token: str) -> dict | None: token_data_str = await redis.get(redis_key) if not token_data_str: - logger.debug(f"Token key {redis_key} not found or expired in Redis") + logger.debug("Token key %s not found or expired in Redis", redis_key) return None return json.loads(token_data_str) @@ -476,7 +476,7 @@ async def retrieve_auth_token_data(token: str) -> dict | None: logger.error("Error decoding token data from Redis") return None except Exception as e: - logger.error(f"Unexpected error in retrieve_auth_token_data: {str(e)}") + logger.error("Unexpected error in retrieve_auth_token_data: %s", str(e)) raise ValueError(f"Unexpected error in retrieve_auth_token_data: {str(e)}") @@ -526,7 +526,7 @@ async def store_ws_token(token: str, user_id: str) -> None: if new_count > WS_TOKEN_RATE_LIMIT_MAX: # Over limit — decrement back since we won't use this slot await redis.decr(rate_limit_key) - logger.warning(f"WS token rate limit exceeded for user {user_id}") + logger.warning("WS token rate limit exceeded for user %s", user_id) raise WsTokenRateLimitExceeded( f"Rate limit exceeded. Maximum {WS_TOKEN_RATE_LIMIT_MAX} tokens per minute." ) @@ -564,7 +564,7 @@ async def retrieve_ws_token_data(token: str) -> dict | None: logger.error("Error decoding WS token data from Redis") return None except Exception as e: - logger.error(f"Unexpected error in retrieve_ws_token_data: {str(e)}") + logger.error("Unexpected error in retrieve_ws_token_data: %s", str(e)) return None @@ -584,11 +584,11 @@ def redis_lock_dump(lock: RedisLock, r: Redis) -> None: remote_token = None logger.warning( - f"RedisLock diagnostic: " - f"name={name} " - f"locked={locked} " - f"owned={owned} " - f"local_token={local_token} " - f"remote_token={remote_token} " - f"ttl={ttl}" + "RedisLock diagnostic: name=%s locked=%s owned=%s local_token=%s remote_token=%s ttl=%s", + name, + locked, + owned, + local_token, + remote_token, + ttl, ) diff --git a/backend/onyx/redis/redis_tenant_work_gating.py b/backend/onyx/redis/redis_tenant_work_gating.py index fdeef945a2..e018f6aeb2 100644 --- a/backend/onyx/redis/redis_tenant_work_gating.py +++ b/backend/onyx/redis/redis_tenant_work_gating.py @@ -87,7 +87,7 @@ def mark_tenant_active(tenant_id: str) -> None: # existing members, which is exactly the refresh semantics we want. _client().zadd(_SET_KEY, mapping={tenant_id: _now_ms()}) except Exception: - logger.exception(f"mark_tenant_active failed: tenant_id={tenant_id}") + logger.exception("mark_tenant_active failed: tenant_id=%s", tenant_id) def maybe_mark_tenant_active(tenant_id: str, caller: str = "unknown") -> None: @@ -109,7 +109,7 @@ def maybe_mark_tenant_active(tenant_id: str, caller: str = "unknown") -> None: mark_tenant_active(tenant_id) _marked_total.labels(caller=caller).inc() except Exception: - logger.exception(f"maybe_mark_tenant_active failed: tenant_id={tenant_id}") + logger.exception("maybe_mark_tenant_active failed: tenant_id=%s", tenant_id) def observe_active_set_size() -> int | None: diff --git a/backend/onyx/secondary_llm_flows/document_filter.py b/backend/onyx/secondary_llm_flows/document_filter.py index d4b3b71d57..1b42787f12 100644 --- a/backend/onyx/secondary_llm_flows/document_filter.py +++ b/backend/onyx/secondary_llm_flows/document_filter.py @@ -159,12 +159,13 @@ def classify_section_relevance( ) else: logger.warning( - f"Could not parse situation number from LLM response: {llm_response}" + "Could not parse situation number from LLM response: %s", + llm_response, ) classification = default_classification except Exception as e: - logger.error(f"Error calling LLM for context selection: {e}") + logger.error("Error calling LLM for context selection: %s", e) classification = default_classification # To save some effort down the line, if there is nothing surrounding, don't allow a classification of adjacent or whole doc @@ -354,7 +355,7 @@ def select_sections_for_expansion( if not section_ids: logger.warning( - f"Could not parse section IDs from LLM response: {llm_response}" + "Could not parse section IDs from LLM response: %s", llm_response ) return sections[:max_sections], None @@ -369,13 +370,17 @@ def select_sections_for_expansion( try: section_id_int = int(section_id_str) except ValueError: - logger.warning(f"Could not convert section ID to int: {section_id_str}") + logger.warning( + "Could not convert section ID to int: %s", section_id_str + ) continue # Check if in valid range if section_id_int < 0 or section_id_int >= num_sections: logger.warning( - f"Section ID {section_id_int} is out of range [0, {num_sections - 1}], skipping" + "Section ID %s is out of range [0, %s], skipping", + section_id_int, + num_sections - 1, ) continue @@ -407,9 +412,11 @@ def select_sections_for_expansion( ] logger.debug( - f"LLM selected {len(selected_sections)} valid sections from {len(sections)} total candidates. " - f"Selected document IDs: {selected_document_ids}. " - f"Document IDs with exclamation: {document_ids_with_exclamation if document_ids_with_exclamation else []}" + "LLM selected %s valid sections from %s total candidates. Selected document IDs: %s. Document IDs with exclamation: %s", + len(selected_sections), + len(sections), + selected_document_ids, + document_ids_with_exclamation if document_ids_with_exclamation else [], ) # Return document_ids if any sections had exclamation marks, otherwise None @@ -418,5 +425,5 @@ def select_sections_for_expansion( ) except Exception as e: - logger.error(f"Error calling LLM for document selection: {e}") + logger.error("Error calling LLM for document selection: %s", e) return sections[:max_sections], None diff --git a/backend/onyx/secondary_llm_flows/memory_update.py b/backend/onyx/secondary_llm_flows/memory_update.py index 6b88ee1fa3..94a67b3f0b 100644 --- a/backend/onyx/secondary_llm_flows/memory_update.py +++ b/backend/onyx/secondary_llm_flows/memory_update.py @@ -127,7 +127,7 @@ def process_memory_update( record_llm_response(span_generation, response) content = response.choice.message.content except Exception as e: - logger.warning(f"LLM invocation failed for memory update: {e}") + logger.warning("LLM invocation failed for memory update: %s", e) return (new_memory, None) # Handle empty response @@ -142,7 +142,8 @@ def process_memory_update( if not parsed_response: logger.warning( - f"Failed to parse JSON from LLM response: {content[:200]}..., defaulting to add" + "Failed to parse JSON from LLM response: %s..., defaulting to add", + content[:200], ) return (new_memory, None) @@ -171,7 +172,7 @@ def process_memory_update( try: memory_id_int = int(memory_id) except (ValueError, TypeError): - logger.warning(f"Invalid memory_id format: {memory_id}") + logger.warning("Invalid memory_id format: %s", memory_id) return (memory_text, None) # Convert from 1-indexed (LLM response) to 0-indexed (internal) @@ -180,13 +181,15 @@ def process_memory_update( # Validate index is in range if index_to_replace < 0 or index_to_replace >= len(existing_memories): logger.warning( - f"memory_id {memory_id_int} out of range (1-{len(existing_memories)}), defaulting to add" + "memory_id %s out of range (1-%s), defaulting to add", + memory_id_int, + len(existing_memories), ) return (memory_text, None) - logger.debug(f"Memory update operation: update at index {index_to_replace}") + logger.debug("Memory update operation: update at index %s", index_to_replace) return (memory_text, index_to_replace) # Unknown operation, default to add - logger.warning(f"Unknown operation '{operation}', defaulting to add") + logger.warning("Unknown operation '%s', defaulting to add", operation) return (memory_text, None) diff --git a/backend/onyx/secondary_llm_flows/source_filter.py b/backend/onyx/secondary_llm_flows/source_filter.py index 5658cb2426..9f338181a3 100644 --- a/backend/onyx/secondary_llm_flows/source_filter.py +++ b/backend/onyx/secondary_llm_flows/source_filter.py @@ -13,7 +13,7 @@ def strings_to_document_sources(source_strs: list[str]) -> list[DocumentSource]: try: sources.append(DocumentSource(s)) except ValueError: - logger.warning(f"Failed to translate {s} to a DocumentSource") + logger.warning("Failed to translate %s to a DocumentSource", s) return sources diff --git a/backend/onyx/server/auth/captcha_api.py b/backend/onyx/server/auth/captcha_api.py index 57c1185120..12ca2b3897 100644 --- a/backend/onyx/server/auth/captcha_api.py +++ b/backend/onyx/server/auth/captcha_api.py @@ -174,7 +174,8 @@ class LoginCaptchaMiddleware(BaseHTTPMiddleware): ): if _health_check_bypass_ok(request): logger.info( - f"Login captcha bypassed via health-check token client={_client_ip_for_log(request)}" + "Login captcha bypassed via health-check token client=%s", + _client_ip_for_log(request), ) else: token = request.headers.get(LOGIN_CAPTCHA_HEADER, "") diff --git a/backend/onyx/server/documents/cc_pair.py b/backend/onyx/server/documents/cc_pair.py index 992de00ed1..062e6ffd52 100644 --- a/backend/onyx/server/documents/cc_pair.py +++ b/backend/onyx/server/documents/cc_pair.py @@ -350,12 +350,13 @@ def update_cc_pair_status( if attempt.celery_task_id: client_app.control.revoke(attempt.celery_task_id) logger.info( - f"Requested cancellation for active indexing attempt {attempt.id} " - f"due to connector pause: cc_pair={cc_pair_id}" + "Requested cancellation for active indexing attempt %s due to connector pause: cc_pair=%s", + attempt.id, + cc_pair_id, ) except Exception: logger.exception( - f"Failed to request cancellation for indexing attempt {attempt.id}" + "Failed to request cancellation for indexing attempt %s", attempt.id ) else: @@ -502,10 +503,11 @@ def prune_cc_pair( ) logger.info( - f"Pruning cc_pair: cc_pair={cc_pair_id} " - f"connector={cc_pair.connector_id} " - f"credential={cc_pair.credential_id} " - f"{cc_pair.connector.name} connector." + "Pruning cc_pair: cc_pair=%s connector=%s credential=%s %s connector.", + cc_pair_id, + cc_pair.connector_id, + cc_pair.credential_id, + cc_pair.connector.name, ) payload_id = try_creating_prune_generator_task( client_app, cc_pair, db_session, r, tenant_id @@ -516,7 +518,7 @@ def prune_cc_pair( detail="Pruning task creation failed.", ) - logger.info(f"Pruning queued: cc_pair={cc_pair.id} id={payload_id}") + logger.info("Pruning queued: cc_pair=%s id=%s", cc_pair.id, payload_id) return StatusResponse( success=True, @@ -635,7 +637,8 @@ def associate_credential_to_connector( ) logger.info( - f"associate_credential_to_connector - running check_for_indexing: cc_pair={response.data}" + "associate_credential_to_connector - running check_for_indexing: cc_pair=%s", + response.data, ) return response @@ -651,14 +654,14 @@ def associate_credential_to_connector( status_code=400, detail="Connector validation error: " + str(e) ) except IntegrityError as e: - logger.error(f"IntegrityError: {e}") + logger.error("IntegrityError: %s", e) delete_connector(db_session, connector_id) db_session.commit() raise HTTPException(status_code=400, detail="Name must be unique") except Exception as e: - logger.exception(f"Unexpected error: {e}") + logger.exception("Unexpected error: %s", e) raise HTTPException(status_code=500, detail="Unexpected error") diff --git a/backend/onyx/server/documents/connector.py b/backend/onyx/server/documents/connector.py index 241d2e5698..8a614e1329 100644 --- a/backend/onyx/server/documents/connector.py +++ b/backend/onyx/server/documents/connector.py @@ -426,7 +426,7 @@ def save_zip_metadata_to_file_store( try: json.loads(metadata_bytes) except json.JSONDecodeError as e: - logger.warning(f"Unable to load {ONYX_METADATA_FILENAME}: {e}") + logger.warning("Unable to load %s: %s", ONYX_METADATA_FILENAME, e) raise HTTPException( status_code=400, detail=f"Unable to load {ONYX_METADATA_FILENAME}: {e}", @@ -441,7 +441,7 @@ def save_zip_metadata_to_file_store( ) return file_id except KeyError: - logger.info(f"No {ONYX_METADATA_FILENAME} file") + logger.info("No %s file", ONYX_METADATA_FILENAME) return None @@ -678,7 +678,7 @@ def list_connector_files( ) ) except Exception as e: - logger.warning(f"Error reading file record for {file_id}: {e}") + logger.warning("Error reading file record for %s: %s", file_id, e) # Include file with basic info even if record fetch fails files.append( ConnectorFileInfo( @@ -754,7 +754,7 @@ def update_connector_files( else: current_zip_metadata = loaded_metadata except Exception as e: - logger.warning(f"Failed to load existing metadata file: {e}") + logger.warning("Failed to load existing metadata file: %s", e) raise HTTPException( status_code=500, detail="Failed to load existing connector metadata file", @@ -785,7 +785,7 @@ def update_connector_files( else: new_zip_metadata = loaded_metadata except Exception as e: - logger.warning(f"Failed to load new metadata file: {e}") + logger.warning("Failed to load new metadata file: %s", e) # Remove specified files files_to_remove_set = set(file_ids_list) @@ -878,7 +878,9 @@ def update_connector_files( priority=OnyxCeleryPriority.HIGH, ) logger.info( - f"Marked cc_pair {cc_pair.id} for UPDATE indexing (new files) for connector {connector_id}" + "Marked cc_pair %s for UPDATE indexing (new files) for connector %s", + cc_pair.id, + connector_id, ) # If files were removed, trigger pruning immediately @@ -889,15 +891,19 @@ def update_connector_files( ) if payload_id: logger.info( - f"Triggered pruning for cc_pair {cc_pair.id} (removed files) for connector " - f"{connector_id}, payload_id={payload_id}" + "Triggered pruning for cc_pair %s (removed files) for connector %s, payload_id=%s", + cc_pair.id, + connector_id, + payload_id, ) else: logger.warning( - f"Failed to trigger pruning for cc_pair {cc_pair.id} (removed files) for connector {connector_id}" + "Failed to trigger pruning for cc_pair %s (removed files) for connector %s", + cc_pair.id, + connector_id, ) except Exception as e: - logger.error(f"Failed to trigger re-indexing after file update: {e}") + logger.error("Failed to trigger re-indexing after file update: %s", e) return FileUploadResponse( file_paths=final_file_locations, @@ -1560,7 +1566,7 @@ def create_connector_from_model( return connector_response except ValueError as e: - logger.error(f"Error creating connector: {e}") + logger.error("Error creating connector: %s", e) raise HTTPException(status_code=400, detail=str(e)) @@ -1631,7 +1637,8 @@ def create_connector_with_mock_credential( ) logger.info( - f"create_connector_with_mock_credential - running check_for_indexing: cc_pair={response.data}" + "create_connector_with_mock_credential - running check_for_indexing: cc_pair=%s", + response.data, ) mt_cloud_telemetry( @@ -1988,16 +1995,20 @@ Tenant ID: {tenant_id} text_body=email_body_text, ) logger.info( - f"Connector request email sent to hello@onyx.app for connector: {connector_name}" + "Connector request email sent to hello@onyx.app for connector: %s", + connector_name, ) except Exception as e: # Log error but don't fail the request if email fails logger.error( - f"Failed to send connector request email for {connector_name}: {e}" + "Failed to send connector request email for %s: %s", connector_name, e ) logger.info( - f"Connector request submitted: {connector_name} by user {user_email or 'anonymous'} (tenant: {tenant_id})" + "Connector request submitted: %s by user %s (tenant: %s)", + connector_name, + user_email or "anonymous", + tenant_id, ) return StatusResponse( @@ -2100,16 +2111,16 @@ def trigger_indexing_for_cc_pair( num_triggers += 1 logger.info( - f"connector_run_once - marking cc_pair with indexing trigger: " - f"connector={connector_id} " - f"cc_pair={cc_pair.id} " - f"indexing_trigger={indexing_mode}" + "connector_run_once - marking cc_pair with indexing trigger: connector=%s cc_pair=%s indexing_trigger=%s", + connector_id, + cc_pair.id, + indexing_mode, ) priority = OnyxCeleryPriority.HIGH # run the beat task to pick up the triggers immediately - logger.info(f"Sending indexing check task with priority {priority}") + logger.info("Sending indexing check task with priority %s", priority) client_app.send_task( OnyxCeleryTask.CHECK_FOR_INDEXING, priority=priority, diff --git a/backend/onyx/server/documents/document_utils.py b/backend/onyx/server/documents/document_utils.py index 168e87692c..292e945a62 100644 --- a/backend/onyx/server/documents/document_utils.py +++ b/backend/onyx/server/documents/document_utils.py @@ -43,12 +43,12 @@ def validate_pkcs12_content(file_bytes: bytes) -> bool: if _is_password_related_error(e): # These errors indicate the file format is correct but password is wrong/missing logger.debug( - f"PKCS#12 format appears valid, password-related error: {e}" + "PKCS#12 format appears valid, password-related error: %s", e ) return True else: # Other ValueError likely indicates format issues - logger.debug(f"PKCS#12 format validation failed: {e}") + logger.debug("PKCS#12 format validation failed: %s", e) return False except Exception as e: # Try with empty password as fallback @@ -58,18 +58,19 @@ def validate_pkcs12_content(file_bytes: bytes) -> bool: except ValueError as e2: if _is_password_related_error(e2): logger.debug( - f"PKCS#12 format appears valid with empty password attempt: {e2}" + "PKCS#12 format appears valid with empty password attempt: %s", + e2, ) return True else: logger.debug( - f"PKCS#12 validation failed on both attempts: {e}, {e2}" + "PKCS#12 validation failed on both attempts: %s, %s", e, e2 ) return False except Exception: - logger.debug(f"PKCS#12 validation failed: {e}") + logger.debug("PKCS#12 validation failed: %s", e) return False except Exception as e: - logger.debug(f"Unexpected error during PKCS#12 validation: {e}") + logger.debug("Unexpected error during PKCS#12 validation: %s", e) return False diff --git a/backend/onyx/server/features/build/api/api.py b/backend/onyx/server/features/build/api/api.py index 1a571aefe5..b845637a2b 100644 --- a/backend/onyx/server/features/build/api/api.py +++ b/backend/onyx/server/features/build/api/api.py @@ -391,7 +391,7 @@ def _proxy_request( if request.query_params: target_url = f"{target_url}?{request.query_params}" - logger.debug(f"Proxying request to: {target_url}") + logger.debug("Proxying request to: %s", target_url) try: # Make the request to the target URL @@ -437,10 +437,10 @@ def _proxy_request( ) except httpx.TimeoutException: - logger.error(f"Timeout while proxying request to {target_url}") + logger.error("Timeout while proxying request to %s", target_url) raise HTTPException(status_code=504, detail="Gateway timeout") except httpx.RequestError as e: - logger.error(f"Error proxying request to {target_url}: {e}") + logger.error("Error proxying request to %s: %s", target_url, e) raise HTTPException(status_code=502, detail="Bad gateway") @@ -546,7 +546,7 @@ def reset_sandbox( raise except Exception as e: db_session.rollback() - logger.error(f"Failed to reset sandbox for user {user.id}: {e}") + logger.error("Failed to reset sandbox for user %s: %s", user.id, e) raise HTTPException( status_code=500, detail=f"Failed to reset sandbox: {e}", diff --git a/backend/onyx/server/features/build/api/sessions_api.py b/backend/onyx/server/features/build/api/sessions_api.py index 319f792d9f..440139d18d 100644 --- a/backend/onyx/server/features/build/api/sessions_api.py +++ b/backend/onyx/server/features/build/api/sessions_api.py @@ -148,7 +148,7 @@ def create_session( raise HTTPException(status_code=429, detail=str(e)) except Exception as e: db_session.rollback() - logger.error(f"Session creation failed: {e}") + logger.error("Session creation failed: %s", e) raise HTTPException(status_code=500, detail=f"Session creation failed: {e}") finally: if lock.owned(): @@ -341,7 +341,7 @@ def delete_session( except Exception as e: # Sandbox termination failed - rollback to preserve session db_session.rollback() - logger.error(f"Failed to delete session {session_id}: {e}") + logger.error("Failed to delete session %s: %s", session_id, e) raise HTTPException( status_code=500, detail=f"Failed to delete session: {e}", @@ -418,7 +418,8 @@ def restore_session( if not is_healthy: logger.warning( - f"Sandbox {sandbox.id} marked as RUNNING but pod is unhealthy/missing. Entering recovery mode." + "Sandbox %s marked as RUNNING but pod is unhealthy/missing. Entering recovery mode.", + sandbox.id, ) # Terminate to clean up any lingering K8s resources sandbox_manager.terminate(sandbox.id) @@ -487,7 +488,7 @@ def restore_session( db_session.commit() except Exception as e: logger.error( - f"Snapshot restore failed for session {session_id}: {e}" + "Snapshot restore failed for session %s: %s", session_id, e ) session.nextjs_port = None db_session.commit() @@ -504,11 +505,13 @@ def restore_session( db_session.commit() else: logger.warning( - f"Sandbox {sandbox.id} status is {sandbox.status} after re-provision, expected RUNNING" + "Sandbox %s status is %s after re-provision, expected RUNNING", + sandbox.id, + sandbox.status, ) except Exception as e: - logger.error(f"Failed to restore session {session_id}: {e}", exc_info=True) + logger.error("Failed to restore session %s: %s", session_id, e, exc_info=True) raise HTTPException( status_code=500, detail=f"Failed to restore session: {e}", diff --git a/backend/onyx/server/features/build/api/subscription_check.py b/backend/onyx/server/features/build/api/subscription_check.py index 0704bdbbad..c4f42550a2 100644 --- a/backend/onyx/server/features/build/api/subscription_check.py +++ b/backend/onyx/server/features/build/api/subscription_check.py @@ -45,7 +45,7 @@ def is_user_subscribed(user: User, db_session: Session) -> bool: # noqa: ARG001 # Subscribed = NOT on trial return not on_trial except Exception as e: - logger.warning(f"Subscription check failed for tenant {tenant_id}: {e}") + logger.warning("Subscription check failed for tenant %s: %s", tenant_id, e) # Default to non-subscribed (safer/more restrictive) return False diff --git a/backend/onyx/server/features/build/api/user_library.py b/backend/onyx/server/features/build/api/user_library.py index 852fa9cd7c..f348e900f4 100644 --- a/backend/onyx/server/features/build/api/user_library.py +++ b/backend/onyx/server/features/build/api/user_library.py @@ -483,7 +483,10 @@ async def upload_files( _trigger_sandbox_sync(str(user.id), tenant_id, source=USER_LIBRARY_SOURCE_DIR) logger.info( - f"Uploaded {len(uploaded_entries)} files ({total_size} bytes) for user {user.id}" + "Uploaded %s files (%s bytes) for user %s", + len(uploaded_entries), + total_size, + user.id, ) return UploadResponse( @@ -566,7 +569,9 @@ async def upload_zip( # Validate individual file size if file_size > USER_LIBRARY_MAX_FILE_SIZE_BYTES: - logger.warning(f"Skipping '{zip_info.filename}' - exceeds max size") + logger.warning( + "Skipping '%s' - exceeds max size", zip_info.filename + ) continue # Skip PDFs that would trip the per-file or per-batch image @@ -684,7 +689,10 @@ async def upload_zip( _trigger_sandbox_sync(str(user.id), tenant_id, source=USER_LIBRARY_SOURCE_DIR) logger.info( - f"Extracted {len(uploaded_entries)} files ({total_size} bytes) from zip for user {user.id}" + "Extracted %s files (%s bytes) from zip for user %s", + len(uploaded_entries), + total_size, + user.id, ) return UploadResponse( @@ -826,7 +834,7 @@ def delete_file( else: writer.delete_raw_file(file_path) except Exception as e: - logger.warning(f"Failed to delete file at path {file_path}: {e}") + logger.warning("Failed to delete file at path %s: %s", file_path, e) else: # Fallback for documents created before file_path was stored storage_key = doc_metadata.get("storage_key") or doc_metadata.get("s3_key") @@ -840,11 +848,12 @@ def delete_file( writer.delete_raw_file(storage_key) else: logger.warning( - f"Cannot delete file in local mode without file_path: {document_id}" + "Cannot delete file in local mode without file_path: %s", + document_id, ) except Exception as e: logger.warning( - f"Failed to delete storage object {storage_key}: {e}" + "Failed to delete storage object %s: %s", storage_key, e ) # Delete from document table diff --git a/backend/onyx/server/features/build/db/build_session.py b/backend/onyx/server/features/build/db/build_session.py index 06f1e54ead..f6eb2dd4b8 100644 --- a/backend/onyx/server/features/build/db/build_session.py +++ b/backend/onyx/server/features/build/db/build_session.py @@ -55,7 +55,10 @@ def create_build_session__no_commit( db_session.flush() logger.info( - f"Created build session {session.id} for user {user_id} (demo_data={demo_data_enabled})" + "Created build session %s for user %s (demo_data=%s)", + session.id, + user_id, + demo_data_enabled, ) return session @@ -158,7 +161,7 @@ def update_session_status( if session: session.status = status db_session.commit() - logger.info(f"Updated build session {session_id} status to {status}") + logger.info("Updated build session %s status to %s", session_id, status) def set_build_session_sharing_scope( @@ -177,7 +180,7 @@ def set_build_session_sharing_scope( return None session.sharing_scope = sharing_scope db_session.commit() - logger.info(f"Set build session {session_id} sharing_scope={sharing_scope}") + logger.info("Set build session %s sharing_scope=%s", session_id, sharing_scope) return session @@ -197,7 +200,7 @@ def delete_build_session__no_commit( db_session.delete(session) db_session.flush() - logger.info(f"Deleted build session {session_id}") + logger.info("Deleted build session %s", session_id) return True @@ -220,7 +223,7 @@ def update_sandbox_status( sandbox.container_id = container_id sandbox.last_heartbeat = datetime.now(tz=timezone.utc) db_session.commit() - logger.info(f"Updated sandbox {sandbox_id} status to {status}") + logger.info("Updated sandbox %s status to %s", sandbox_id, status) def update_sandbox_heartbeat( @@ -253,7 +256,7 @@ def create_artifact( db_session.commit() db_session.refresh(artifact) - logger.info(f"Created artifact {artifact.id} for session {session_id}") + logger.info("Created artifact %s for session %s", artifact.id, session_id) return artifact @@ -287,7 +290,7 @@ def update_artifact( artifact.name = name artifact.updated_at = datetime.now(tz=timezone.utc) db_session.commit() - logger.info(f"Updated artifact {artifact_id}") + logger.info("Updated artifact %s", artifact_id) # Message operations @@ -320,8 +323,12 @@ def create_message( db_session.refresh(message) logger.info( - f"Created {message_type.value} message {message.id} for session {session_id} " - f"turn={turn_index} type={message_metadata.get('type')}" + "Created %s message %s for session %s turn=%s type=%s", + message_type.value, + message.id, + session_id, + turn_index, + message_metadata.get("type"), ) return message @@ -354,7 +361,7 @@ def update_message( db_session.refresh(message) logger.info( - f"Updated message {message_id} metadata type={message_metadata.get('type')}" + "Updated message %s metadata type=%s", message_id, message_metadata.get("type") ) return message @@ -403,7 +410,9 @@ def upsert_agent_plan( db_session.commit() db_session.refresh(existing_plan) logger.info( - f"Updated agent_plan_update message {existing_plan.id} for session {session_id}" + "Updated agent_plan_update message %s for session %s", + existing_plan.id, + session_id, ) return existing_plan @@ -438,16 +447,16 @@ def _is_port_available(port: int) -> bool: """ import socket - logger.debug(f"Checking if port {port} is available") + logger.debug("Checking if port %s is available", port) # Check IPv4 wildcard (0.0.0.0) - this will detect any IPv4 listener try: with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock: sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) sock.bind(("0.0.0.0", port)) - logger.debug(f"Port {port} IPv4 wildcard bind successful") + logger.debug("Port %s IPv4 wildcard bind successful", port) except OSError as e: - logger.debug(f"Port {port} IPv4 wildcard not available: {e}") + logger.debug("Port %s IPv4 wildcard not available: %s", port, e) return False # Check IPv6 wildcard (::) - this will detect any IPv6 listener @@ -457,12 +466,12 @@ def _is_port_available(port: int) -> bool: # IPV6_V6ONLY must be False to allow dual-stack behavior sock.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, 0) sock.bind(("::", port)) - logger.debug(f"Port {port} IPv6 wildcard bind successful") + logger.debug("Port %s IPv6 wildcard bind successful", port) except OSError as e: - logger.debug(f"Port {port} IPv6 wildcard not available: {e}") + logger.debug("Port %s IPv6 wildcard not available: %s", port, e) return False - logger.debug(f"Port {port} is available") + logger.debug("Port %s is available", port) return True @@ -523,7 +532,7 @@ def mark_user_sessions_idle__no_commit(db_session: Session, user_id: UUID) -> in .update({BuildSession.status: BuildSessionStatus.IDLE}) ) db_session.flush() - logger.info(f"Marked {result} sessions as IDLE for user {user_id}") + logger.info("Marked %s sessions as IDLE for user %s", result, user_id) return result @@ -548,7 +557,7 @@ def clear_nextjs_ports_for_user(db_session: Session, user_id: UUID) -> int: .update({BuildSession.nextjs_port: None}) ) db_session.flush() - logger.info(f"Cleared {result} nextjs_port allocations for user {user_id}") + logger.info("Cleared %s nextjs_port allocations for user %s", result, user_id) return result diff --git a/backend/onyx/server/features/build/indexing/persistent_document_writer.py b/backend/onyx/server/features/build/indexing/persistent_document_writer.py index 3422ec9d48..a61d96f5a1 100644 --- a/backend/onyx/server/features/build/indexing/persistent_document_writer.py +++ b/backend/onyx/server/features/build/indexing/persistent_document_writer.py @@ -275,7 +275,7 @@ class PersistentDocumentWriter: with open(path, "w", encoding="utf-8") as f: json.dump(content, f, indent=2, default=str) - logger.debug(f"Wrote document to {path}") + logger.debug("Wrote document to %s", path) def write_raw_file( self, @@ -314,7 +314,7 @@ class PersistentDocumentWriter: with open(full_path, "wb") as f: f.write(content) - logger.debug(f"Wrote raw file to {full_path}") + logger.debug("Wrote raw file to %s", full_path) return str(full_path) def delete_raw_file(self, path: str) -> None: @@ -336,9 +336,9 @@ class PersistentDocumentWriter: if full_path.exists(): full_path.unlink() - logger.debug(f"Deleted raw file at {full_path}") + logger.debug("Deleted raw file at %s", full_path) else: - logger.warning(f"File not found for deletion: {full_path}") + logger.warning("File not found for deletion: %s", full_path) class S3PersistentDocumentWriter: @@ -438,9 +438,9 @@ class S3PersistentDocumentWriter: Body=json_content.encode("utf-8"), ContentType="application/json", ) - logger.debug(f"Wrote document to s3://{self.bucket}/{s3_key}") + logger.debug("Wrote document to s3://%s/%s", self.bucket, s3_key) except ClientError as e: - logger.error(f"Failed to write to S3: {e}") + logger.error("Failed to write to S3: %s", e) raise def write_raw_file( @@ -477,10 +477,10 @@ class S3PersistentDocumentWriter: Body=content, ContentType=content_type or "application/octet-stream", ) - logger.debug(f"Wrote raw file to s3://{self.bucket}/{s3_key}") + logger.debug("Wrote raw file to s3://%s/%s", self.bucket, s3_key) return s3_key except ClientError as e: - logger.error(f"Failed to write raw file to S3: {e}") + logger.error("Failed to write raw file to S3: %s", e) raise def delete_raw_file(self, s3_key: str) -> None: @@ -493,9 +493,9 @@ class S3PersistentDocumentWriter: try: s3_client.delete_object(Bucket=self.bucket, Key=s3_key) - logger.debug(f"Deleted raw file at s3://{self.bucket}/{s3_key}") + logger.debug("Deleted raw file at s3://%s/%s", self.bucket, s3_key) except ClientError as e: - logger.error(f"Failed to delete raw file from S3: {e}") + logger.error("Failed to delete raw file from S3: %s", e) raise def delete_raw_file_by_path(self, path: str) -> None: diff --git a/backend/onyx/server/features/build/sandbox/kubernetes/internal/acp_exec_client.py b/backend/onyx/server/features/build/sandbox/kubernetes/internal/acp_exec_client.py index 850d448cc0..0d53eeb701 100644 --- a/backend/onyx/server/features/build/sandbox/kubernetes/internal/acp_exec_client.py +++ b/backend/onyx/server/features/build/sandbox/kubernetes/internal/acp_exec_client.py @@ -190,7 +190,7 @@ class ACPExecClient: f"XDG_DATA_HOME={data_dir} exec opencode acp --cwd {safe_cwd}", ] - logger.info(f"[ACP] Starting client: pod={self._pod_name} cwd={cwd}") + logger.info("[ACP] Starting client: pod=%s cwd=%s", self._pod_name, cwd) try: self._ws_client = k8s_stream( @@ -220,9 +220,11 @@ class ACPExecClient: # Initialize ACP connection (no session creation) self._initialize(timeout=timeout) - logger.info(f"[ACP] Client started: pod={self._pod_name}") + logger.info("[ACP] Client started: pod=%s", self._pod_name) except Exception as e: - logger.error(f"[ACP] Client start failed: pod={self._pod_name} error={e}") + logger.error( + "[ACP] Client start failed: pod=%s error=%s", self._pod_name, e + ) self.stop() raise RuntimeError(f"Failed to start ACP exec client: {e}") from e @@ -243,7 +245,9 @@ class ACPExecClient: stderr_data = self._ws_client.read_stderr(timeout=0.01) if stderr_data: logger.warning( - f"[ACP] stderr pod={self._pod_name}: {stderr_data.strip()[:500]}" + "[ACP] stderr pod=%s: %s", + self._pod_name, + stderr_data.strip()[:500], ) # Read stdout @@ -263,23 +267,23 @@ class ACPExecClient: self._response_queue.put(message) except json.JSONDecodeError: logger.warning( - f"[ACP] Invalid JSON from agent: {line[:100]}" + "[ACP] Invalid JSON from agent: %s", line[:100] ) else: - logger.warning(f"[ACP] WebSocket closed: pod={self._pod_name}") + logger.warning("[ACP] WebSocket closed: pod=%s", self._pod_name) break except Exception as e: if not self._stop_reader.is_set(): - logger.warning(f"[ACP] Reader error: {e}, pod={self._pod_name}") + logger.warning("[ACP] Reader error: %s, pod=%s", e, self._pod_name) break def stop(self) -> None: """Stop the exec session and clean up.""" session_ids = list(self._state.sessions.keys()) logger.info( - f"[ACP] Stopping client: pod={self._pod_name} sessions={session_ids}" + "[ACP] Stopping client: pod=%s sessions=%s", self._pod_name, session_ids ) self._stop_reader.set() @@ -408,7 +412,7 @@ class ACPExecClient: raise RuntimeError("No session ID returned from session/new") self._state.sessions[session_id] = ACPSession(session_id=session_id, cwd=cwd) - logger.info(f"[ACP] Created session: acp_session={session_id} cwd={cwd}") + logger.info("[ACP] Created session: acp_session=%s cwd=%s", session_id, cwd) return session_id @@ -423,10 +427,12 @@ class ACPExecClient: request_id = self._send_request("session/list", {"cwd": cwd}) result = self._wait_for_response(request_id, timeout) sessions = result.get("sessions", []) - logger.info(f"[ACP] session/list: {len(sessions)} sessions for cwd={cwd}") + logger.info( + "[ACP] session/list: %s sessions for cwd=%s", len(sessions), cwd + ) return sessions except Exception as e: - logger.info(f"[ACP] session/list unavailable: {e}") + logger.info("[ACP] session/list unavailable: %s", e) return [] def _resume_session(self, session_id: str, cwd: str, timeout: float = 30.0) -> str: @@ -456,7 +462,7 @@ class ACPExecClient: resumed_id = result.get("sessionId", session_id) self._state.sessions[resumed_id] = ACPSession(session_id=resumed_id, cwd=cwd) - logger.info(f"[ACP] Resumed session: acp_session={resumed_id} cwd={cwd}") + logger.info("[ACP] Resumed session: acp_session=%s cwd=%s", resumed_id, cwd) return resumed_id def _try_resume_existing_session(self, cwd: str, timeout: float) -> str | None: @@ -487,14 +493,18 @@ class ACPExecClient: return None logger.info( - f"[ACP] Resuming existing session: acp_session={target_id} (found {len(sessions)})" + "[ACP] Resuming existing session: acp_session=%s (found %s)", + target_id, + len(sessions), ) try: return self._resume_session(target_id, cwd, timeout) except Exception as e: logger.warning( - f"[ACP] session/resume failed for {target_id}: {e}, falling back to session/new" + "[ACP] session/resume failed for %s: %s, falling back to session/new", + target_id, + e, ) return None @@ -545,7 +555,10 @@ class ACPExecClient: packet_logger = get_packet_logger() logger.info( - f"[ACP] Sending prompt: acp_session={session_id} pod={self._pod_name} queue_backlog={self._response_queue.qsize()}" + "[ACP] Sending prompt: acp_session=%s pod=%s queue_backlog=%s", + session_id, + self._pod_name, + self._response_queue.qsize(), ) prompt_content = [{"type": "text", "text": message}] @@ -566,13 +579,15 @@ class ACPExecClient: if remaining <= 0: completion_reason = "timeout" logger.warning( - f"[ACP] Prompt timeout: acp_session={session_id} events={events_yielded}, sending session/cancel" + "[ACP] Prompt timeout: acp_session=%s events=%s, sending session/cancel", + session_id, + events_yielded, ) try: self.cancel(session_id=session_id) except Exception as cancel_err: logger.warning( - f"[ACP] session/cancel failed on timeout: {cancel_err}" + "[ACP] session/cancel failed on timeout: %s", cancel_err ) yield Error(code=-1, message="Timeout waiting for response") break @@ -600,7 +615,7 @@ class ACPExecClient: if "error" in message_data: error_data = message_data["error"] completion_reason = "jsonrpc_error" - logger.warning(f"[ACP] Prompt error: {error_data}") + logger.warning("[ACP] Prompt error: %s", error_data) packet_logger.log_jsonrpc_response( request_id, error=error_data, context="k8s" ) @@ -618,13 +633,15 @@ class ACPExecClient: events_yielded += 1 yield prompt_response except ValidationError as e: - logger.error(f"[ACP] PromptResponse validation failed: {e}") + logger.error("[ACP] PromptResponse validation failed: %s", e) elapsed_ms = (time.time() - start_time) * 1000 logger.info( - f"[ACP] Prompt complete: " - f"reason={completion_reason} acp_session={session_id} " - f"events={events_yielded} elapsed={elapsed_ms:.0f}ms" + "[ACP] Prompt complete: reason=%s acp_session=%s events=%s elapsed=%sms", + completion_reason, + session_id, + events_yielded, + format(elapsed_ms, ".0f"), ) break @@ -645,16 +662,18 @@ class ACPExecClient: completion_reason = "prompt_response_via_notification" elapsed_ms = (time.time() - start_time) * 1000 logger.info( - f"[ACP] Prompt complete: " - f"reason={completion_reason} acp_session={session_id} " - f"events={events_yielded} elapsed={elapsed_ms:.0f}ms" + "[ACP] Prompt complete: reason=%s acp_session=%s events=%s elapsed=%sms", + completion_reason, + session_id, + events_yielded, + format(elapsed_ms, ".0f"), ) break # Handle requests from agent - send error response elif "method" in message_data and "id" in message_data: logger.debug( - f"[ACP] Unsupported agent request: method={message_data['method']}" + "[ACP] Unsupported agent request: method=%s", message_data["method"] ) self._send_error_response( message_data["id"], @@ -664,10 +683,10 @@ class ACPExecClient: else: logger.warning( - f"[ACP] Unhandled message: " - f"id={message_data.get('id')} " - f"method={message_data.get('method')} " - f"keys={list(message_data.keys())}" + "[ACP] Unhandled message: id=%s method=%s keys=%s", + message_data.get("id"), + message_data.get("method"), + list(message_data.keys()), ) def _process_session_update( @@ -697,14 +716,14 @@ class ACPExecClient: try: yield cast(ACPEvent, model_class.model_validate(update)) except ValidationError as e: - logger.warning(f"[ACP] Validation error for {update_type}: {e}") + logger.warning("[ACP] Validation error for %s: %s", update_type, e) elif update_type not in ( "user_message_chunk", "available_commands_update", "session_info_update", "usage_update", ): - logger.debug(f"[ACP] Unknown update type: {update_type}") + logger.debug("[ACP] Unknown update type: %s", update_type) def _send_error_response(self, request_id: int, code: int, message: str) -> None: """Send an error response to an agent request.""" diff --git a/backend/onyx/server/features/build/sandbox/kubernetes/kubernetes_sandbox_manager.py b/backend/onyx/server/features/build/sandbox/kubernetes/kubernetes_sandbox_manager.py index 068c9f83ef..2e1b9866ee 100644 --- a/backend/onyx/server/features/build/sandbox/kubernetes/kubernetes_sandbox_manager.py +++ b/backend/onyx/server/features/build/sandbox/kubernetes/kubernetes_sandbox_manager.py @@ -359,7 +359,9 @@ class KubernetesSandboxManager(SandboxManager): self._skills_path = Path(__file__).parent / "docker" / "skills" logger.info( - f"KubernetesSandboxManager initialized: namespace={self._namespace}, image={self._image}" + "KubernetesSandboxManager initialized: namespace=%s, image=%s", + self._namespace, + self._image, ) def _get_pod_name(self, sandbox_id: str) -> str: @@ -697,7 +699,7 @@ done # Service exists - check if it's being deleted if svc.metadata.deletion_timestamp: logger.info( - f"Service {service_name} is terminating, waiting for deletion" + "Service %s is terminating, waiting for deletion", service_name ) self._wait_for_resource_deletion("service", service_name) # Now create a fresh service @@ -706,14 +708,14 @@ done namespace=self._namespace, body=service, ) - logger.info(f"Recreated Service {service_name} after termination") + logger.info("Recreated Service %s after termination", service_name) else: - logger.debug(f"Service {service_name} already exists and is active") + logger.debug("Service %s already exists and is active", service_name) except ApiException as e: if e.status == 404: # Service doesn't exist, create it - logger.info(f"Creating missing Service {service_name}") + logger.info("Creating missing Service %s", service_name) service = self._create_sandbox_service(sandbox_id, tenant_id) try: self._core_api.create_namespaced_service( @@ -724,7 +726,7 @@ done if svc_e.status != 409: # Ignore AlreadyExists raise logger.debug( - f"Service {service_name} was created by another request" + "Service %s was created by another request", service_name ) else: raise @@ -841,15 +843,15 @@ done conditions = pod.status.conditions or [] for condition in conditions: if condition.type == "Ready" and condition.status == "True": - logger.info(f"Pod {pod_name} is ready") + logger.info("Pod %s is ready", pod_name) return True - logger.debug(f"Pod {pod_name} status: {phase}, waiting...") + logger.debug("Pod %s status: %s, waiting...", pod_name, phase) except ApiException as e: if e.status == 404: raise RuntimeError(f"Pod {pod_name} was deleted") - logger.warning(f"Error checking pod status: {e}") + logger.warning("Error checking pod status: %s", e) time.sleep(POD_READY_POLL_INTERVAL_SECONDS) @@ -865,7 +867,7 @@ done except ApiException: pass # Pod might be deleted, ignore - logger.warning(f"Timeout waiting for pod {pod_name} to become ready") + logger.warning("Timeout waiting for pod %s to become ready", pod_name) return False def _pod_exists_and_healthy(self, pod_name: str) -> bool: @@ -935,7 +937,10 @@ done RuntimeError: If provisioning fails """ logger.info( - f"Starting Kubernetes sandbox provisioning for sandbox {sandbox_id}, user {user_id}, tenant {tenant_id}" + "Starting Kubernetes sandbox provisioning for sandbox %s, user %s, tenant %s", + sandbox_id, + user_id, + tenant_id, ) pod_name = self._get_pod_name(str(sandbox_id)) @@ -943,20 +948,20 @@ done # Check if pod already exists and is healthy (idempotency check) if self._pod_exists_and_healthy(pod_name): logger.info( - f"Pod {pod_name} already exists and is healthy, reusing existing pod" + "Pod %s already exists and is healthy, reusing existing pod", pod_name ) # Ensure service exists and is not terminating self._ensure_service_exists(sandbox_id, tenant_id) # Wait for pod to be ready if it's still pending - logger.info(f"Waiting for existing pod {pod_name} to become ready...") + logger.info("Waiting for existing pod %s to become ready...", pod_name) if not self._wait_for_pod_ready(pod_name): raise RuntimeError( f"Timeout waiting for existing sandbox pod {pod_name} to become ready" ) logger.info( - f"Reusing existing Kubernetes sandbox {sandbox_id}, pod: {pod_name}" + "Reusing existing Kubernetes sandbox %s, pod: %s", sandbox_id, pod_name ) return SandboxInfo( sandbox_id=sandbox_id, @@ -967,7 +972,7 @@ done try: # 1. Create Pod (user-level only, no session setup) - logger.debug(f"Creating Pod {pod_name}") + logger.debug("Creating Pod %s", pod_name) pod = self._create_sandbox_pod( sandbox_id=str(sandbox_id), user_id=str(user_id), @@ -983,19 +988,21 @@ done # Pod was created by another concurrent request # Check if it's healthy and reuse it logger.warning( - f"Pod {pod_name} already exists (409 conflict, this shouldn't normally happen), " - "checking if it's healthy..." + "Pod %s already exists (409 conflict, this shouldn't normally happen), checking if it's healthy...", + pod_name, ) if self._pod_exists_and_healthy(pod_name): logger.warning( - f"During provisioning, discovered that pod {pod_name} already exists. Reusing" + "During provisioning, discovered that pod %s already exists. Reusing", + pod_name, ) # Continue to ensure service exists and wait for ready else: # Pod exists but is not healthy - this shouldn't happen often # but could occur if a previous provision failed mid-way logger.warning( - f"Pod {pod_name} exists but is not healthy, waiting for it to become ready or fail" + "Pod %s exists but is not healthy, waiting for it to become ready or fail", + pod_name, ) else: raise @@ -1004,14 +1011,16 @@ done self._ensure_service_exists(sandbox_id, tenant_id) # 3. Wait for pod to be ready - logger.info(f"Waiting for pod {pod_name} to become ready...") + logger.info("Waiting for pod %s to become ready...", pod_name) if not self._wait_for_pod_ready(pod_name): raise RuntimeError( f"Timeout waiting for sandbox pod {pod_name} to become ready" ) logger.info( - f"Provisioned Kubernetes sandbox {sandbox_id}, pod: {pod_name} (no sessions yet)" + "Provisioned Kubernetes sandbox %s, pod: %s (no sessions yet)", + sandbox_id, + pod_name, ) return SandboxInfo( @@ -1026,12 +1035,15 @@ done # Check if pod is healthy - if so, don't clean up (another request may own it) if self._pod_exists_and_healthy(pod_name): logger.warning( - f"Kubernetes sandbox provisioning failed for sandbox {sandbox_id}: {e}, " - "but pod is healthy (likely owned by concurrent request), not cleaning up" + "Kubernetes sandbox provisioning failed for sandbox %s: %s, but pod is healthy (likely owned by concurrent request), not cleaning up", + sandbox_id, + e, ) else: logger.error( - f"Kubernetes sandbox provisioning failed for sandbox {sandbox_id}: {e}", + "Kubernetes sandbox provisioning failed for sandbox %s: %s", + sandbox_id, + e, exc_info=True, ) self._cleanup_kubernetes_resources(str(sandbox_id)) @@ -1075,20 +1087,27 @@ done raise ValueError(f"Unknown resource type: {resource_type}") # Resource still exists, wait and retry - logger.debug(f"Waiting for {resource_type} {name} to be deleted...") + logger.debug("Waiting for %s %s to be deleted...", resource_type, name) time.sleep(RESOURCE_DELETION_POLL_INTERVAL_SECONDS) except ApiException as e: if e.status == 404: # Resource is gone - logger.debug(f"{resource_type.capitalize()} {name} fully deleted") + logger.debug( + "%s %s fully deleted", resource_type.capitalize(), name + ) return True # Other error, log and continue waiting - logger.warning(f"Error checking {resource_type} {name} status: {e}") + logger.warning( + "Error checking %s %s status: %s", resource_type, name, e + ) time.sleep(RESOURCE_DELETION_POLL_INTERVAL_SECONDS) logger.warning( - f"Timeout waiting for {resource_type} {name} to be deleted after {timeout}s" + "Timeout waiting for %s %s to be deleted after %ss", + resource_type, + name, + timeout, ) return False @@ -1118,14 +1137,14 @@ done name=service_name, namespace=self._namespace, ) - logger.debug(f"Deleted Service {service_name}") + logger.debug("Deleted Service %s", service_name) service_deleted = True except ApiException as e: if e.status == 404: # Already deleted service_deleted = True else: - logger.error(f"Error deleting Service {service_name}: {e}") + logger.error("Error deleting Service %s: %s", service_name, e) raise pod_deleted = False @@ -1134,14 +1153,14 @@ done name=pod_name, namespace=self._namespace, ) - logger.debug(f"Deleted Pod {pod_name}") + logger.debug("Deleted Pod %s", pod_name) pod_deleted = True except ApiException as e: if e.status == 404: # Already deleted pod_deleted = True else: - logger.error(f"Error deleting Pod {pod_name}: {e}") + logger.error("Error deleting Pod %s: %s", pod_name, e) raise # Wait for resources to be fully deleted to prevent 409 conflicts @@ -1165,7 +1184,7 @@ done # Clean up Kubernetes resources (needs string for pod/service names) self._cleanup_kubernetes_resources(str(sandbox_id)) - logger.info(f"Terminated Kubernetes sandbox {sandbox_id}") + logger.info("Terminated Kubernetes sandbox %s", sandbox_id) def setup_session_workspace( self, @@ -1217,9 +1236,9 @@ done """ if snapshot_path: logger.warning( - f"Snapshot restoration requested but not supported in Kubernetes mode. " - f"Snapshot path {snapshot_path} will be ignored. " - f"Session {session_id} will start with fresh outputs template." + "Snapshot restoration requested but not supported in Kubernetes mode. Snapshot path %s will be ignored. Session %s will start with fresh outputs template.", + snapshot_path, + session_id, ) pod_name = self._get_pod_name(str(sandbox_id)) @@ -1360,7 +1379,7 @@ echo "Session workspace setup complete" """ logger.info( - f"Setting up session workspace {session_id} in sandbox {sandbox_id}" + "Setting up session workspace %s in sandbox %s", session_id, sandbox_id ) try: @@ -1377,14 +1396,17 @@ echo "Session workspace setup complete" tty=False, ) - logger.debug(f"Session setup output: {exec_response}") + logger.debug("Session setup output: %s", exec_response) logger.info( - f"Set up session workspace {session_id} in sandbox {sandbox_id}" + "Set up session workspace %s in sandbox %s", session_id, sandbox_id ) except Exception as e: logger.error( - f"Failed to setup session workspace {session_id} in sandbox {sandbox_id}: {e}", + "Failed to setup session workspace %s in sandbox %s: %s", + session_id, + sandbox_id, + e, exc_info=True, ) raise RuntimeError( @@ -1427,7 +1449,7 @@ echo "Session cleanup complete" """ logger.info( - f"Cleaning up session workspace {session_id} in sandbox {sandbox_id}" + "Cleaning up session workspace %s in sandbox %s", session_id, sandbox_id ) try: @@ -1443,19 +1465,21 @@ echo "Session cleanup complete" tty=False, ) - logger.debug(f"Session cleanup output: {exec_response}") + logger.debug("Session cleanup output: %s", exec_response) logger.info( - f"Cleaned up session workspace {session_id} in sandbox {sandbox_id}" + "Cleaned up session workspace %s in sandbox %s", session_id, sandbox_id ) except ApiException as e: if e.status == 404: # Pod not found, nothing to clean up - logger.debug(f"Pod {pod_name} not found, skipping cleanup") + logger.debug("Pod %s not found, skipping cleanup", pod_name) else: - logger.warning(f"Error cleaning up session workspace {session_id}: {e}") + logger.warning( + "Error cleaning up session workspace %s: %s", session_id, e + ) except Exception as e: - logger.warning(f"Error cleaning up session workspace {session_id}: {e}") + logger.warning("Error cleaning up session workspace %s: %s", session_id, e) def create_snapshot( self, @@ -1527,12 +1551,12 @@ echo "SNAPSHOT_CREATED" tty=False, ) - logger.debug(f"Snapshot exec output: {resp}") + logger.debug("Snapshot exec output: %s", resp) # Check if nothing was snapshotted if "EMPTY_SNAPSHOT" in resp: logger.info( - f"No outputs or attachments to snapshot for session {session_id}" + "No outputs or attachments to snapshot for session %s", session_id ) return None @@ -1550,7 +1574,7 @@ echo "SNAPSHOT_CREATED" # Storage path must match the S3 upload path (without s3://bucket/ prefix) storage_path = f"{tenant_id}/snapshots/{session_id_str}/{snapshot_id}.tar.gz" - logger.info(f"Created snapshot for session {session_id}") + logger.info("Created snapshot for session %s", session_id) return SnapshotResult( storage_path=storage_path, @@ -1598,13 +1622,17 @@ echo "SNAPSHOT_CREATED" result = "WORKSPACE_FOUND" in resp logger.info( - f"[WORKSPACE_CHECK] session={session_id}, path={session_path}, raw_resp={resp!r}, result={result}" + "[WORKSPACE_CHECK] session=%s, path=%s, raw_resp=%r, result=%s", + session_id, + session_path, + resp, + result, ) return result except ApiException as e: logger.warning( - f"Failed to check session workspace exists for {session_id}: {e}" + "Failed to check session workspace exists for %s: %s", session_id, e ) return False @@ -1844,7 +1872,10 @@ echo "Session config regeneration complete" acp_client.start(cwd=session_path) logger.info( - f"[SANDBOX-ACP] Created ephemeral ACP client: sandbox={sandbox_id} pod={pod_name} api_pod={_API_SERVER_HOSTNAME}" + "[SANDBOX-ACP] Created ephemeral ACP client: sandbox=%s pod=%s api_pod=%s", + sandbox_id, + pod_name, + _API_SERVER_HOSTNAME, ) return acp_client @@ -1882,7 +1913,10 @@ echo "Session config regeneration complete" acp_session_id = acp_client.resume_or_create_session(cwd=session_path) logger.info( - f"[SANDBOX-ACP] Sending message: session={session_id} acp_session={acp_session_id} api_pod={_API_SERVER_HOSTNAME}" + "[SANDBOX-ACP] Sending message: session=%s acp_session=%s api_pod=%s", + session_id, + acp_session_id, + _API_SERVER_HOSTNAME, ) # Log the send_message call at sandbox manager level @@ -1900,22 +1934,26 @@ echo "Session config regeneration complete" yield event logger.info( - f"[SANDBOX-ACP] send_message completed: " - f"session={session_id} events={events_count} " - f"got_prompt_response={got_prompt_response}" + "[SANDBOX-ACP] send_message completed: session=%s events=%s got_prompt_response=%s", + session_id, + events_count, + got_prompt_response, ) packet_logger.log_session_end( session_id, success=True, events_count=events_count ) except GeneratorExit: logger.warning( - f"[SANDBOX-ACP] GeneratorExit: session={session_id} events={events_count}, sending session/cancel" + "[SANDBOX-ACP] GeneratorExit: session=%s events=%s, sending session/cancel", + session_id, + events_count, ) try: acp_client.cancel(session_id=acp_session_id) except Exception as cancel_err: logger.warning( - f"[SANDBOX-ACP] session/cancel failed on GeneratorExit: {cancel_err}" + "[SANDBOX-ACP] session/cancel failed on GeneratorExit: %s", + cancel_err, ) packet_logger.log_session_end( session_id, @@ -1926,13 +1964,17 @@ echo "Session config regeneration complete" raise except Exception as e: logger.error( - f"[SANDBOX-ACP] Exception: session={session_id} events={events_count} error={e}, sending session/cancel" + "[SANDBOX-ACP] Exception: session=%s events=%s error=%s, sending session/cancel", + session_id, + events_count, + e, ) try: acp_client.cancel(session_id=acp_session_id) except Exception as cancel_err: logger.warning( - f"[SANDBOX-ACP] session/cancel failed on Exception: {cancel_err}" + "[SANDBOX-ACP] session/cancel failed on Exception: %s", + cancel_err, ) packet_logger.log_session_end( session_id, @@ -1943,7 +1985,10 @@ echo "Session config regeneration complete" raise except BaseException as e: logger.error( - f"[SANDBOX-ACP] {type(e).__name__}: session={session_id} error={e}" + "[SANDBOX-ACP] %s: session=%s error=%s", + type(e).__name__, + session_id, + e, ) packet_logger.log_session_end( session_id, @@ -1959,7 +2004,9 @@ echo "Session config regeneration complete" acp_client.stop() except Exception as e: logger.warning( - f"[SANDBOX-ACP] Failed to stop ephemeral ACP client: session={session_id} error={e}" + "[SANDBOX-ACP] Failed to stop ephemeral ACP client: session=%s error=%s", + session_id, + e, ) def list_directory( @@ -1991,7 +2038,7 @@ echo "Session config regeneration complete" # Use shlex.quote to prevent command injection quoted_path = shlex.quote(target_path) - logger.info(f"Listing directory {target_path} in pod {pod_name}") + logger.info("Listing directory %s in pod %s", target_path, pod_name) # Use exec to list directory # -L follows symlinks (important for files/ -> /workspace/demo_data) @@ -2032,10 +2079,10 @@ echo "Session config regeneration complete" entries = [] lines = ls_output.strip().split("\n") - logger.debug(f"Parsing {len(lines)} lines of ls output for {base_path}") + logger.debug("Parsing %s lines of ls output for %s", len(lines), base_path) for line in lines: - logger.debug(f"Parsing line: {line}") + logger.debug("Parsing line: %s", line) # Skip header line and . / .. entries if line.startswith("total") or not line: @@ -2151,7 +2198,7 @@ echo "Session config regeneration complete" try: content = base64.b64decode(resp.strip()) except binascii.Error as e: - logger.error(f"Failed to decode base64 content: {e}") + logger.error("Failed to decode base64 content: %s", e) raise RuntimeError(f"Failed to decode file content: {e}") from e return content @@ -2349,11 +2396,11 @@ fi stdout=True, tty=False, ) - logger.debug(f"File sync response: {resp}") + logger.debug("File sync response: %s", resp) # Check if sync succeeded based on output markers if "SYNC_FAILED" in resp: - logger.warning(f"File sync failed for sandbox {sandbox_id}") + logger.warning("File sync failed for sandbox %s", sandbox_id) return False return True @@ -2416,10 +2463,12 @@ fi tty=False, ) logger.debug( - f"Ensure AGENTS.md attachments section for session {session_id}: {resp.strip()}" + "Ensure AGENTS.md attachments section for session %s: %s", + session_id, + resp.strip(), ) except ApiException as e: - logger.warning(f"Failed to ensure AGENTS.md attachments section: {e}") + logger.warning("Failed to ensure AGENTS.md attachments section: %s", e) def upload_file( self, @@ -2532,7 +2581,7 @@ echo "$base" stderr_data += ws_client.read_stderr() or "" if stderr_data.strip(): - logger.warning(f"Upload stderr: {stderr_data.strip()}") + logger.warning("Upload stderr: %s", stderr_data.strip()) # Last line of output is the final filename final_filename = stdout_data.strip().split("\n")[-1] @@ -2543,7 +2592,10 @@ echo "$base" ) logger.info( - f"Uploaded file to session {session_id}: attachments/{final_filename} ({len(content)} bytes)" + "Uploaded file to session %s: attachments/%s (%s bytes)", + session_id, + final_filename, + len(content), ) # Ensure AGENTS.md has the attachments section @@ -2616,10 +2668,10 @@ echo "$base" deleted = "DELETED" in resp if deleted: - logger.info(f"Deleted file from session {session_id}: {path}") + logger.info("Deleted file from session %s: %s", session_id, path) else: logger.debug( - f"File not found for deletion in session {session_id}: {path}" + "File not found for deletion in session %s: %s", session_id, path ) return deleted @@ -2684,11 +2736,11 @@ fi total_size = int(parts[1]) return file_count, total_size except ValueError: - logger.warning(f"Failed to parse upload stats: {resp}") + logger.warning("Failed to parse upload stats: %s", resp) return 0, 0 return 0, 0 except ApiException as e: - logger.warning(f"Failed to get upload stats: {e}") + logger.warning("Failed to get upload stats: %s", e) return 0, 0 diff --git a/backend/onyx/server/features/build/sandbox/local/local_sandbox_manager.py b/backend/onyx/server/features/build/sandbox/local/local_sandbox_manager.py index 5218fe709a..421d3b7dc1 100644 --- a/backend/onyx/server/features/build/sandbox/local/local_sandbox_manager.py +++ b/backend/onyx/server/features/build/sandbox/local/local_sandbox_manager.py @@ -137,8 +137,8 @@ class LocalSandboxManager(SandboxManager): error_msg += "\n".join(f" - {template}" for template in missing_templates) raise RuntimeError(error_msg) - logger.debug(f"Outputs template found at {outputs_path}") - logger.debug(f"Venv template found at {venv_path}") + logger.debug("Outputs template found at %s", outputs_path) + logger.debug("Venv template found at %s", venv_path) def _get_sandbox_path(self, sandbox_id: str | UUID) -> Path: """Get the filesystem path for a sandbox based on sandbox_id. @@ -189,7 +189,7 @@ class LocalSandboxManager(SandboxManager): excluded_set = {p.lstrip("/") for p in excluded_paths} if not source_path.exists(): - logger.warning(f"Source path does not exist: {source_path}") + logger.warning("Source path does not exist: %s", source_path) return # Iterate through top-level items in source @@ -245,7 +245,7 @@ class LocalSandboxManager(SandboxManager): if item.is_dir(): # Check if entire directory is excluded if rel_path in excluded_set: - logger.debug(f"Excluding directory: user_library/{rel_path}") + logger.debug("Excluding directory: user_library/%s", rel_path) continue # Recurse into directory - only create if it has content @@ -260,7 +260,7 @@ class LocalSandboxManager(SandboxManager): else: # Check if file is excluded if rel_path in excluded_set: - logger.debug(f"Excluding file: user_library/{rel_path}") + logger.debug("Excluding file: user_library/%s", rel_path) continue # Create parent directory if needed (lazy creation) @@ -303,16 +303,19 @@ class LocalSandboxManager(SandboxManager): RuntimeError: If provisioning fails """ logger.info( - f"Starting sandbox provisioning for sandbox {sandbox_id}, user {user_id}, tenant {tenant_id}" + "Starting sandbox provisioning for sandbox %s, user %s, tenant %s", + sandbox_id, + user_id, + tenant_id, ) # Create sandbox directory structure (user-level only) - logger.info(f"Creating sandbox directory structure for sandbox {sandbox_id}") + logger.info("Creating sandbox directory structure for sandbox %s", sandbox_id) sandbox_path = self._directory_manager.create_sandbox_directory(str(sandbox_id)) - logger.debug(f"Sandbox directory created at {sandbox_path}") + logger.debug("Sandbox directory created at %s", sandbox_path) logger.info( - f"Provisioned sandbox {sandbox_id} at {sandbox_path} (no sessions yet)" + "Provisioned sandbox %s at %s (no sessions yet)", sandbox_id, sandbox_path ) return SandboxInfo( @@ -350,7 +353,10 @@ class LocalSandboxManager(SandboxManager): self._nextjs_processes.pop(key, None) except Exception as e: logger.warning( - f"Failed to stop Next.js for sandbox {sandbox_id}, session {session_id}: {e}" + "Failed to stop Next.js for sandbox %s, session %s: %s", + sandbox_id, + session_id, + e, ) # Stop all ACP clients for this sandbox (keyed by (sandbox_id, session_id)) @@ -365,7 +371,10 @@ class LocalSandboxManager(SandboxManager): del self._acp_clients[key] except Exception as e: logger.warning( - f"Failed to stop ACP client for sandbox {sandbox_id}, session {key[1]}: {e}" + "Failed to stop ACP client for sandbox %s, session %s: %s", + sandbox_id, + key[1], + e, ) # Cleanup directory @@ -377,7 +386,7 @@ class LocalSandboxManager(SandboxManager): f"Failed to cleanup sandbox directory {sandbox_path}: {e}" ) from e - logger.info(f"Terminated sandbox {sandbox_id}") + logger.info("Terminated sandbox %s", sandbox_id) def setup_session_workspace( self, @@ -433,14 +442,16 @@ class LocalSandboxManager(SandboxManager): ) logger.info( - f"Setting up session workspace for session {session_id} in sandbox {sandbox_id}" + "Setting up session workspace for session %s in sandbox %s", + session_id, + sandbox_id, ) # Create session directory session_path = self._directory_manager.create_session_directory( sandbox_path, str(session_id) ) - logger.debug(f"Session directory created at {session_path}") + logger.debug("Session directory created at %s", session_path) try: # Setup files access - choose between demo data or user files @@ -449,9 +460,9 @@ class LocalSandboxManager(SandboxManager): symlink_target = Path(DEMO_DATA_PATH) if not symlink_target.exists(): logger.warning( - f"Demo data directory does not exist: {symlink_target}" + "Demo data directory does not exist: %s", symlink_target ) - logger.info(f"Setting up files symlink to demo data: {symlink_target}") + logger.info("Setting up files symlink to demo data: %s", symlink_target) self._directory_manager.setup_files_symlink( session_path, symlink_target ) @@ -461,7 +472,8 @@ class LocalSandboxManager(SandboxManager): if excluded_user_library_paths: # Create filtered file structure with symlinks to enabled files only logger.debug( - f"Setting up filtered files with {len(excluded_user_library_paths)} exclusions" + "Setting up filtered files with %s exclusions", + len(excluded_user_library_paths), ) self._setup_filtered_files( session_path=session_path, @@ -471,7 +483,7 @@ class LocalSandboxManager(SandboxManager): else: # No exclusions: simple symlink to entire directory logger.debug( - f"Setting up files symlink to user files: {source_path}" + "Setting up files symlink to user files: %s", source_path ) self._directory_manager.setup_files_symlink( session_path, source_path @@ -482,7 +494,9 @@ class LocalSandboxManager(SandboxManager): # Setup org_info directory with user identity (at session root) if user_work_area: - logger.debug(f"Setting up org_info for {user_work_area}/{user_level}") + logger.debug( + "Setting up org_info for %s/%s", user_work_area, user_level + ) self._directory_manager.setup_org_info( session_path, user_work_area, user_level ) @@ -502,7 +516,9 @@ class LocalSandboxManager(SandboxManager): # Setup opencode.json with LLM provider configuration logger.debug( - f"Setting up opencode config with provider: {llm_config.provider}, model: {llm_config.model_name}" + "Setting up opencode config with provider: %s, model: %s", + llm_config.provider, + llm_config.model_name, ) self._directory_manager.setup_opencode_config( sandbox_path=session_path, @@ -518,7 +534,9 @@ class LocalSandboxManager(SandboxManager): web_dir = self._directory_manager.get_web_path( sandbox_path, str(session_id) ) - logger.info(f"Starting Next.js server at {web_dir} on port {nextjs_port}") + logger.info( + "Starting Next.js server at %s on port %s", web_dir, nextjs_port + ) nextjs_process = self._process_manager.start_nextjs_server( web_dir, nextjs_port @@ -547,15 +565,17 @@ class LocalSandboxManager(SandboxManager): ) logger.debug("Agent instructions ready") - logger.info(f"Set up session workspace {session_id} at {session_path}") + logger.info("Set up session workspace %s at %s", session_id, session_path) except Exception as e: # Cleanup on failure logger.error( - f"Session workspace setup failed for session {session_id}: {e}", + "Session workspace setup failed for session %s: %s", + session_id, + e, exc_info=True, ) - logger.info(f"Cleaning up session directory at {session_path}") + logger.info("Cleaning up session directory at %s", session_path) self._directory_manager.cleanup_session_directory( sandbox_path, str(session_id) ) @@ -598,16 +618,16 @@ class LocalSandboxManager(SandboxManager): if client: try: client.stop() - logger.debug(f"Stopped ACP client for session {session_id}") + logger.debug("Stopped ACP client for session %s", session_id) except Exception as e: logger.warning( - f"Failed to stop ACP client for session {session_id}: {e}" + "Failed to stop ACP client for session %s: %s", session_id, e ) # Cleanup session directory sandbox_path = self._get_sandbox_path(sandbox_id) self._directory_manager.cleanup_session_directory(sandbox_path, str(session_id)) - logger.info(f"Cleaned up session workspace {session_id}") + logger.info("Cleaned up session workspace %s", session_id) def _stop_nextjs_process( self, process: subprocess.Popen[bytes], session_id: UUID @@ -621,19 +641,23 @@ class LocalSandboxManager(SandboxManager): if process.poll() is not None: # Process already terminated logger.debug( - f"Next.js server for session {session_id} already terminated (exit code: {process.returncode})" + "Next.js server for session %s already terminated (exit code: %s)", + session_id, + process.returncode, ) return try: logger.info( - f"Stopping Next.js server (PID {process.pid}) for session {session_id}" + "Stopping Next.js server (PID %s) for session %s", + process.pid, + session_id, ) self._process_manager.terminate_process(process.pid) - logger.debug(f"Next.js server stopped for session {session_id}") + logger.debug("Next.js server stopped for session %s", session_id) except Exception as e: logger.warning( - f"Failed to stop Next.js server for session {session_id}: {e}" + "Failed to stop Next.js server for session %s: %s", session_id, e ) def _stop_nextjs_server_on_port(self, port: int, session_id: UUID) -> None: @@ -665,26 +689,35 @@ class LocalSandboxManager(SandboxManager): ] if pids: logger.info( - f"Found {len(pids)} process(es) on port {port} for session {session_id}, stopping all" + "Found %s process(es) on port %s for session %s, stopping all", + len(pids), + port, + session_id, ) for pid in pids: try: logger.debug( - f"Stopping Next.js server (PID {pid}) on port {port} for session {session_id}" + "Stopping Next.js server (PID %s) on port %s for session %s", + pid, + port, + session_id, ) self._process_manager.terminate_process(pid) except Exception as e: logger.warning( - f"Failed to stop process {pid} on port {port}: {e}" + "Failed to stop process %s on port %s: %s", pid, port, e ) return else: logger.debug( - f"No process found on port {port} for session {session_id}" + "No process found on port %s for session %s", port, session_id ) except subprocess.TimeoutExpired: logger.warning( - f"lsof timed out after {LSOF_TIMEOUT_SECONDS}s while looking for process on port {port} for session {session_id}" + "lsof timed out after %ss while looking for process on port %s for session %s", + LSOF_TIMEOUT_SECONDS, + port, + session_id, ) except FileNotFoundError: # lsof not available, try psutil @@ -708,32 +741,41 @@ class LocalSandboxManager(SandboxManager): if pids_to_stop: logger.info( - f"Found {len(pids_to_stop)} process(es) on port {port} for session {session_id}, stopping all" + "Found %s process(es) on port %s for session %s, stopping all", + len(pids_to_stop), + port, + session_id, ) for pid in pids_to_stop: try: logger.debug( - f"Stopping Next.js server (PID {pid}) on port {port} for session {session_id}" + "Stopping Next.js server (PID %s) on port %s for session %s", + pid, + port, + session_id, ) self._process_manager.terminate_process(pid) except Exception as e: logger.warning( - f"Failed to stop process {pid} on port {port}: {e}" + "Failed to stop process %s on port %s: %s", pid, port, e ) return logger.debug( - f"No process found on port {port} for session {session_id}" + "No process found on port %s for session %s", port, session_id ) except ImportError: logger.warning( - f"Neither lsof nor psutil available to find process on port {port}" + "Neither lsof nor psutil available to find process on port %s", port ) except Exception as e: - logger.warning(f"Failed to find process on port {port}: {e}") + logger.warning("Failed to find process on port %s: %s", port, e) except Exception as e: logger.warning( - f"Failed to stop Next.js server on port {port} for session {session_id}: {e}" + "Failed to stop Next.js server on port %s for session %s: %s", + port, + session_id, + e, ) def create_snapshot( @@ -810,14 +852,18 @@ class LocalSandboxManager(SandboxManager): with httpx.Client(timeout=1.0) as client: client.get(f"http://localhost:{nextjs_port}") logger.info( - f"Port {nextjs_port} already alive for session {session_id} (orphan process) — skipping restart" + "Port %s already alive for session %s (orphan process) — skipping restart", + nextjs_port, + session_id, ) return except Exception: pass # Port is dead; proceed with restart logger.info( - f"Starting Next.js for session {session_id} on port {nextjs_port}" + "Starting Next.js for session %s on port %s", + session_id, + nextjs_port, ) sandbox_path = self._get_sandbox_path(sandbox_id) web_dir = self._directory_manager.get_web_path( @@ -825,7 +871,9 @@ class LocalSandboxManager(SandboxManager): ) if not web_dir.exists(): logger.warning( - f"Web dir missing for session {session_id}: {web_dir} — cannot restart Next.js" + "Web dir missing for session %s: %s — cannot restart Next.js", + session_id, + web_dir, ) return process = self._process_manager.start_nextjs_server( @@ -834,11 +882,13 @@ class LocalSandboxManager(SandboxManager): with self._nextjs_lock: self._nextjs_processes[process_key] = process logger.info( - f"Auto-restarted Next.js for session {session_id} on port {nextjs_port}" + "Auto-restarted Next.js for session %s on port %s", + session_id, + nextjs_port, ) except Exception as e: logger.error( - f"Failed to auto-restart Next.js for session {session_id}: {e}" + "Failed to auto-restart Next.js for session %s: %s", session_id, e ) finally: self._nextjs_starting.discard(process_key) @@ -929,7 +979,9 @@ class LocalSandboxManager(SandboxManager): sandbox_id, session_id, str(session_path), context="local" ) logger.info( - f"Creating new ACP client for sandbox {sandbox_id}, session {session_id}" + "Creating new ACP client for sandbox %s, session %s", + sandbox_id, + session_id, ) # Create and start ACP client for this session @@ -1119,7 +1171,10 @@ class LocalSandboxManager(SandboxManager): target_path.chmod(0o644) logger.info( - f"Uploaded file to session {session_id}: attachments/{filename} ({len(content)} bytes)" + "Uploaded file to session %s: attachments/%s (%s bytes)", + session_id, + filename, + len(content), ) # Inject attachments section into AGENTS.md if not already present @@ -1204,14 +1259,16 @@ class LocalSandboxManager(SandboxManager): raise ValueError("Path traversal not allowed") if not file_path.exists(): - logger.debug(f"File not found for deletion in session {session_id}: {path}") + logger.debug( + "File not found for deletion in session %s: %s", session_id, path + ) return False if file_path.is_dir(): raise ValueError("Cannot delete directory") file_path.unlink() - logger.info(f"Deleted file from session {session_id}: {path}") + logger.info("Deleted file from session %s: %s", session_id, path) return True @@ -1389,6 +1446,6 @@ class LocalSandboxManager(SandboxManager): """ source_info = f" source={source}" if source else "" logger.debug( - f"sync_files called for local sandbox {sandbox_id}{source_info} - no-op" + "sync_files called for local sandbox %s%s - no-op", sandbox_id, source_info ) return True diff --git a/backend/onyx/server/features/build/sandbox/local/process_manager.py b/backend/onyx/server/features/build/sandbox/local/process_manager.py index 193528f230..e835fa6d3f 100644 --- a/backend/onyx/server/features/build/sandbox/local/process_manager.py +++ b/backend/onyx/server/features/build/sandbox/local/process_manager.py @@ -46,25 +46,25 @@ class ProcessManager: Raises: RuntimeError: If server fails to start within timeout """ - logger.info(f"Starting Next.js server in {web_dir} on port {port}") + logger.info("Starting Next.js server in %s on port %s", web_dir, port) # Clear Next.js cache to avoid stale paths from template next_cache = web_dir / ".next" if next_cache.exists(): - logger.debug(f"Clearing Next.js cache at {next_cache}") + logger.debug("Clearing Next.js cache at %s", next_cache) shutil.rmtree(next_cache) # Verify web_dir exists and has package.json if not web_dir.exists(): - logger.error(f"Web directory does not exist: {web_dir}") + logger.error("Web directory does not exist: %s", web_dir) raise RuntimeError(f"Web directory does not exist: {web_dir}") package_json = web_dir / "package.json" if not package_json.exists(): - logger.error(f"package.json not found in {web_dir}") + logger.error("package.json not found in %s", web_dir) raise RuntimeError(f"package.json not found in {web_dir}") - logger.debug(f"Starting npm run dev command in {web_dir}") + logger.debug("Starting npm run dev command in %s", web_dir) # CRITICAL: Inherit stdout/stderr (None) to prevent pipe buffer overflow. # When PIPE is used but never drained, the buffer fills up (64KB on most systems) # and the subprocess blocks indefinitely on write, causing the server to freeze. @@ -77,18 +77,21 @@ class ProcessManager: stdout=None, stderr=None, ) - logger.info(f"Next.js process started with PID {process.pid}") + logger.info("Next.js process started with PID %s", process.pid) # Wait for server to be ready server_url = f"http://localhost:{port}" - logger.info(f"Waiting for Next.js server at {server_url} (timeout: {timeout}s)") + logger.info( + "Waiting for Next.js server at %s (timeout: %ss)", server_url, timeout + ) if not self._wait_for_server(server_url, timeout=timeout, process=process): # Check if process died if process.poll() is not None: logger.error( - f"Next.js server process died with code {process.returncode}. " - f"Check the terminal or logs in {web_dir} for details." + "Next.js server process died with code %s. Check the terminal or logs in %s for details.", + process.returncode, + web_dir, ) raise RuntimeError( f"Next.js server process died with code {process.returncode}. Check server logs for details." @@ -96,14 +99,16 @@ class ProcessManager: # Process still running but server not responding logger.error( - f"Next.js server failed to respond within {timeout} seconds (process still running with PID {process.pid})" + "Next.js server failed to respond within %s seconds (process still running with PID %s)", + timeout, + process.pid, ) raise RuntimeError( f"Next.js server failed to start within {timeout} seconds" ) - logger.info(f"Next.js server is ready at {server_url}") + logger.info("Next.js server is ready at %s", server_url) return process def _wait_for_server( @@ -135,8 +140,10 @@ class ProcessManager: # Check if process died early if process is not None and process.poll() is not None: logger.warning( - f"Process died during wait (exit code: {process.returncode}) " - f"after {elapsed:.1f}s and {attempt_count} attempts" + "Process died during wait (exit code: %s) after %ss and %s attempts", + process.returncode, + format(elapsed, ".1f"), + attempt_count, ) return False @@ -144,28 +151,41 @@ class ProcessManager: with urllib.request.urlopen(url, timeout=2) as response: if response.status == 200: logger.debug( - f"Server ready after {elapsed:.1f}s and {attempt_count} attempts" + "Server ready after %ss and %s attempts", + format(elapsed, ".1f"), + attempt_count, ) return True except urllib.error.HTTPError as e: # Log HTTP errors (server responding but with error) if time.time() - last_log_time >= 10: logger.debug( - f"HTTP error {e.code} from {url} after {elapsed:.1f}s ({attempt_count} attempts)" + "HTTP error %s from %s after %ss (%s attempts)", + e.code, + url, + format(elapsed, ".1f"), + attempt_count, ) last_log_time = time.time() except (urllib.error.URLError, TimeoutError) as e: # Log connection errors periodically (every 10 seconds) if time.time() - last_log_time >= 10: logger.debug( - f"Still waiting for {url} after {elapsed:.1f}s ({attempt_count} attempts): {type(e).__name__}" + "Still waiting for %s after %ss (%s attempts): %s", + url, + format(elapsed, ".1f"), + attempt_count, + type(e).__name__, ) last_log_time = time.time() time.sleep(poll_interval) logger.warning( - f"Server at {url} did not become available within {timeout}s ({attempt_count} attempts)" + "Server at %s did not become available within %ss (%s attempts)", + url, + timeout, + attempt_count, ) return False diff --git a/backend/onyx/server/features/build/sandbox/manager/directory_manager.py b/backend/onyx/server/features/build/sandbox/manager/directory_manager.py index abeec8810c..c728547f3d 100644 --- a/backend/onyx/server/features/build/sandbox/manager/directory_manager.py +++ b/backend/onyx/server/features/build/sandbox/manager/directory_manager.py @@ -142,7 +142,7 @@ class DirectoryManager: session_path = sandbox_path / "sessions" / session_id if session_path.exists(): shutil.rmtree(session_path) - logger.info(f"Cleaned up session directory: {session_path}") + logger.info("Cleaned up session directory: %s", session_path) def get_session_path(self, sandbox_path: Path, session_id: str) -> Path: """Get path to session workspace. @@ -195,7 +195,9 @@ class DirectoryManager: persona = get_persona_info(user_work_area, user_level) if not persona: logger.debug( - f"No persona found for work_area={user_work_area}, level={user_level}, skipping org_info setup" + "No persona found for work_area=%s, level=%s, skipping org_info setup", + user_work_area, + user_level, ) return @@ -218,11 +220,13 @@ class DirectoryManager: ) logger.info( - f"Created org_info with identity: {persona['name']} <{persona['email']}>" + "Created org_info with identity: %s <%s>", + persona["name"], + persona["email"], ) except Exception as e: # Don't fail provisioning if org_info setup fails - logger.warning(f"Failed to setup org_info: {e}") + logger.warning("Failed to setup org_info: %s", e) def setup_outputs_directory(self, sandbox_path: Path) -> None: """Copy outputs template and create additional directories. @@ -316,7 +320,7 @@ class DirectoryManager: # Write the generated content agent_md_path.write_text(content) - logger.debug(f"Generated AGENTS.md at {agent_md_path}") + logger.debug("Generated AGENTS.md at %s", agent_md_path) def setup_skills(self, sandbox_path: Path, overwrite: bool = True) -> None: """Copy skills directory to .opencode/skills. @@ -333,13 +337,15 @@ class DirectoryManager: if not self._skills_path.exists(): logger.warning( - f"Skills path {self._skills_path} does not exist, skipping skills setup" + "Skills path %s does not exist, skipping skills setup", + self._skills_path, ) return if not overwrite and skills_dest.exists(): logger.debug( - f"Skills directory already exists at {skills_dest}, skipping skills setup" + "Skills directory already exists at %s, skipping skills setup", + skills_dest, ) return @@ -355,11 +361,15 @@ class DirectoryManager: # Verify the copy succeeded if not skills_dest.exists(): logger.error( - f"Skills copy failed: destination {skills_dest} does not exist after copy" + "Skills copy failed: destination %s does not exist after copy", + skills_dest, ) except Exception as e: logger.error( - f"Failed to copy skills from {self._skills_path} to {skills_dest}: {e}", + "Failed to copy skills from %s to %s: %s", + self._skills_path, + skills_dest, + e, exc_info=True, ) raise @@ -394,7 +404,7 @@ class DirectoryManager: config_path = sandbox_path / "opencode.json" if not overwrite and config_path.exists(): logger.debug( - f"opencode.json already exists at {config_path}, skipping config setup" + "opencode.json already exists at %s, skipping config setup", config_path ) return diff --git a/backend/onyx/server/features/build/sandbox/manager/snapshot_manager.py b/backend/onyx/server/features/build/sandbox/manager/snapshot_manager.py index 32a38b653c..3e2c26882f 100644 --- a/backend/onyx/server/features/build/sandbox/manager/snapshot_manager.py +++ b/backend/onyx/server/features/build/sandbox/manager/snapshot_manager.py @@ -102,13 +102,16 @@ class SnapshotManager: ) logger.info( - f"Created snapshot {snapshot_id} for sandbox {sandbox_id}, size: {size_bytes} bytes" + "Created snapshot %s for sandbox %s, size: %s bytes", + snapshot_id, + sandbox_id, + size_bytes, ) return snapshot_id, storage_path, size_bytes except Exception as e: - logger.error(f"Failed to create snapshot for sandbox {sandbox_id}: {e}") + logger.error("Failed to create snapshot for sandbox %s: %s", sandbox_id, e) raise RuntimeError(f"Failed to create snapshot: {e}") from e finally: # Cleanup temp file @@ -117,7 +120,7 @@ class SnapshotManager: Path(tmp_path).unlink(missing_ok=True) except Exception as cleanup_error: logger.warning( - f"Failed to cleanup temp file {tmp_path}: {cleanup_error}" + "Failed to cleanup temp file %s: %s", tmp_path, cleanup_error ) def restore_snapshot( @@ -176,10 +179,10 @@ class SnapshotManager: ) tar.extractall(target_path) - logger.info(f"Restored snapshot from {storage_path} to {target_path}") + logger.info("Restored snapshot from %s to %s", storage_path, target_path) except Exception as e: - logger.error(f"Failed to restore snapshot {storage_path}: {e}") + logger.error("Failed to restore snapshot %s: %s", storage_path, e) raise RuntimeError(f"Failed to restore snapshot: {e}") from e finally: # Cleanup temp file @@ -188,7 +191,7 @@ class SnapshotManager: Path(tmp_path).unlink(missing_ok=True) except Exception as cleanup_error: logger.warning( - f"Failed to cleanup temp file {tmp_path}: {cleanup_error}" + "Failed to cleanup temp file %s: %s", tmp_path, cleanup_error ) # Close the file IO if it's still open try: @@ -208,10 +211,10 @@ class SnapshotManager: """ try: self._file_store.delete_file(storage_path) - logger.info(f"Deleted snapshot: {storage_path}") + logger.info("Deleted snapshot: %s", storage_path) except Exception as e: # Log but don't fail if snapshot doesn't exist - logger.warning(f"Failed to delete snapshot {storage_path}: {e}") + logger.warning("Failed to delete snapshot %s: %s", storage_path, e) raise RuntimeError(f"Failed to delete snapshot: {e}") from e def get_snapshot_size(self, storage_path: str) -> int | None: diff --git a/backend/onyx/server/features/build/sandbox/util/agent_instructions.py b/backend/onyx/server/features/build/sandbox/util/agent_instructions.py index 0f61e49ffc..ebbb6f813e 100644 --- a/backend/onyx/server/features/build/sandbox/util/agent_instructions.py +++ b/backend/onyx/server/features/build/sandbox/util/agent_instructions.py @@ -255,7 +255,7 @@ def _scan_skills_directory(skills_path: Path) -> str: description = extract_skill_description(skill_md) skills_list.append(f"- **{skill_dir.name}**: {description}") except Exception as e: - logger.warning(f"Error scanning skills directory: {e}") + logger.warning("Error scanning skills directory: %s", e) return "Error loading skills." if not skills_list: @@ -419,7 +419,7 @@ def build_knowledge_sources_section(files_path: Path) -> str: sections.append("\n".join(lines)) except Exception as e: - logger.warning(f"Error building knowledge sources section: {e}") + logger.warning("Error building knowledge sources section: %s", e) return "Error scanning knowledge sources." if not sections: @@ -460,7 +460,7 @@ def generate_agent_instructions( Generated AGENTS.md content with placeholders replaced """ if not template_path.exists(): - logger.warning(f"AGENTS.template.md not found at {template_path}") + logger.warning("AGENTS.template.md not found at %s", template_path) return "# Agent Instructions\n\nNo custom instructions provided." # Read template content diff --git a/backend/onyx/server/features/build/session/manager.py b/backend/onyx/server/features/build/session/manager.py index f54f7e24d5..9dd51ca02f 100644 --- a/backend/onyx/server/features/build/session/manager.py +++ b/backend/onyx/server/features/build/session/manager.py @@ -342,7 +342,8 @@ class SessionManager: ) else: logger.warning( - f"Requested provider type {requested_provider_type} not found, falling back to default" + "Requested provider type %s not found, falling back to default", + requested_provider_type, ) # Fallback to system default @@ -462,7 +463,10 @@ class SessionManager: self._db_session.flush() session_id = str(build_session.id) logger.info( - f"Created build session {session_id} for user {user_id} (port: {nextjs_port})" + "Created build session %s for user %s (port: %s)", + session_id, + user_id, + nextjs_port, ) # Check if user already has a sandbox (one sandbox per user model) @@ -480,7 +484,10 @@ class SessionManager: ): # Re-provision sandbox (pod doesn't exist or failed) logger.info( - f"Re-provisioning {sandbox.status.value} sandbox {sandbox_id} for user {user_id}" + "Re-provisioning %s sandbox %s for user %s", + sandbox.status.value, + sandbox_id, + user_id, ) sandbox_info = self._sandbox_manager.provision( sandbox_id=sandbox_id, @@ -496,7 +503,9 @@ class SessionManager: # Verify pod is healthy before reusing (use short timeout for quick check) if not self._sandbox_manager.health_check(sandbox_id, timeout=5.0): logger.warning( - f"Sandbox {sandbox_id} marked as {sandbox.status} but pod is unhealthy/missing. Entering recovery mode." + "Sandbox %s marked as %s but pod is unhealthy/missing. Entering recovery mode.", + sandbox_id, + sandbox.status, ) # Terminate to clean up any lingering K8s resources self._sandbox_manager.terminate(sandbox_id) @@ -507,7 +516,7 @@ class SessionManager: ) logger.info( - f"Re-provisioning sandbox {sandbox_id} for user {user_id}" + "Re-provisioning sandbox %s for user %s", sandbox_id, user_id ) sandbox_info = self._sandbox_manager.provision( sandbox_id=sandbox_id, @@ -521,7 +530,10 @@ class SessionManager: ) else: logger.info( - f"Reusing existing sandbox {sandbox_id} (status: {sandbox.status}) for new session {session_id}" + "Reusing existing sandbox %s (status: %s) for new session %s", + sandbox_id, + sandbox.status, + session_id, ) else: # PROVISIONING status - sandbox is being created by another request @@ -539,7 +551,9 @@ class SessionManager: user_id=user_id, ) sandbox_id = sandbox.id - logger.info(f"Created sandbox record {sandbox_id} for session {session_id}") + logger.info( + "Created sandbox record %s for session %s", sandbox_id, session_id + ) # Provision sandbox (no DB operations inside) sandbox_info = self._sandbox_manager.provision( @@ -556,7 +570,7 @@ class SessionManager: # Set up session workspace within the sandbox logger.info( - f"Setting up session workspace {session_id} in sandbox {sandbox.id}" + "Setting up session workspace %s in sandbox %s", session_id, sandbox.id ) # Fetch user data for personalization in AGENTS.md user = fetch_user_by_id(self._db_session, user_id) @@ -572,7 +586,8 @@ class SessionManager: ) if excluded_user_library_paths: logger.debug( - f"Excluding {len(excluded_user_library_paths)} disabled user library paths" + "Excluding %s disabled user library paths", + len(excluded_user_library_paths), ) self._sandbox_manager.setup_session_workspace( @@ -592,7 +607,9 @@ class SessionManager: sandbox_id = sandbox.id logger.info( - f"Successfully created session {session_id} with workspace in sandbox {sandbox.id}" + "Successfully created session %s with workspace in sandbox %s", + session_id, + sandbox.id, ) return build_session @@ -636,7 +653,7 @@ class SessionManager: ) if existing: logger.info( - f"Existing empty session {existing.id} found for user {user_id}" + "Existing empty session %s found for user %s", existing.id, user_id ) # Verify sandbox is healthy before returning existing session sandbox = get_sandbox_by_user_id(self._db_session, user_id) @@ -654,23 +671,28 @@ class SessionManager: ) if is_healthy and workspace_exists: logger.info( - f"Returning existing empty session {existing.id} for user {user_id}" + "Returning existing empty session %s for user %s", + existing.id, + user_id, ) return existing elif not is_healthy: logger.warning( - f"Empty session {existing.id} has unhealthy sandbox {sandbox.id}. Deleting and creating fresh session." + "Empty session %s has unhealthy sandbox %s. Deleting and creating fresh session.", + existing.id, + sandbox.id, ) else: logger.warning( - f"Empty session {existing.id} workspace missing in sandbox " - f"{sandbox.id}. Deleting and creating fresh session." + "Empty session %s workspace missing in sandbox %s. Deleting and creating fresh session.", + existing.id, + sandbox.id, ) else: logger.warning( - f"Empty session {existing.id} has no active sandbox " - f"(sandbox={'missing' if not sandbox else sandbox.status}). " - f"Deleting and creating fresh session." + "Empty session %s has no active sandbox (sandbox=%s). Deleting and creating fresh session.", + existing.id, + "missing" if not sandbox else sandbox.status, ) # Delete the stale empty session - create_session__no_commit will @@ -702,7 +724,7 @@ class SessionManager: empty_session = get_empty_session_for_user(user_id, self._db_session) if not empty_session: - logger.info(f"No empty session found for user {user_id}") + logger.info("No empty session found for user %s", user_id) return False session_id = empty_session.id @@ -717,15 +739,19 @@ class SessionManager: nextjs_port=empty_session.nextjs_port, ) logger.info( - f"Cleaned up session workspace {session_id} in sandbox {sandbox.id}" + "Cleaned up session workspace %s in sandbox %s", + session_id, + sandbox.id, ) except Exception as e: # Log but don't fail - session can still be deleted - logger.warning(f"Failed to cleanup session workspace {session_id}: {e}") + logger.warning( + "Failed to cleanup session workspace %s: %s", session_id, e + ) # Delete session (cascade deletes artifacts) delete_build_session__no_commit(session_id, user_id, self._db_session) - logger.info(f"Deleted empty session {session_id} for user {user_id}") + logger.info("Deleted empty session %s for user %s", session_id, user_id) return True @@ -878,7 +904,7 @@ class SessionManager: else f"Build Session {str(session_id)[:8]}" ) except Exception as e: - logger.warning(f"Failed to generate session name with LLM: {e}") + logger.warning("Failed to generate session name with LLM: %s", e) # Fallback to simple truncation return user_message[:40].strip() + ("..." if len(user_message) > 40 else "") @@ -928,7 +954,7 @@ class SessionManager: return self._parse_suggestions(raw_output) except Exception as e: - logger.warning(f"Failed to generate follow-up suggestions with LLM: {e}") + logger.warning("Failed to generate follow-up suggestions with LLM: %s", e) return [] def _parse_suggestions(self, raw_output: str) -> list[dict[str, str]]: @@ -998,7 +1024,7 @@ class SessionManager: # Silent fail - return empty list logger.warning( - f"Failed to parse suggestions from LLM output: {raw_output[:200]}" + "Failed to parse suggestions from LLM output: %s", raw_output[:200] ) return [] @@ -1038,12 +1064,16 @@ class SessionManager: nextjs_port=session.nextjs_port, ) logger.info( - f"Cleaned up session workspace {session_id} in sandbox {sandbox.id}" + "Cleaned up session workspace %s in sandbox %s", + session_id, + sandbox.id, ) except Exception as e: # Log but don't fail - session can still be deleted even if # workspace cleanup fails (e.g., if pod is already terminated) - logger.warning(f"Failed to cleanup session workspace {session_id}: {e}") + logger.warning( + "Failed to cleanup session workspace %s: %s", session_id, e + ) # Delete snapshot files from S3 before removing DB records snapshots = get_snapshots_for_session(self._db_session, session_id) @@ -1059,7 +1089,9 @@ class SessionManager: snapshot_manager.delete_snapshot(snapshot.storage_path) except Exception as e: logger.warning( - f"Failed to delete snapshot file {snapshot.storage_path}: {e}" + "Failed to delete snapshot file %s: %s", + snapshot.storage_path, + e, ) # Delete session (uses flush, caller commits) @@ -1508,7 +1540,7 @@ class SessionManager: "error": str(e), }, ) - logger.exception(f"RuntimeError in build message streaming: {e}") + logger.exception("RuntimeError in build message streaming: %s", e) yield _format_packet_event(error_packet) except Exception as e: error_packet = ErrorPacket(message=str(e)) @@ -2241,13 +2273,13 @@ class SessionManager: return False if sandbox.status == SandboxStatus.TERMINATED: - logger.info(f"Sandbox {sandbox.id} already terminated") + logger.info("Sandbox %s already terminated", sandbox.id) return True try: # Terminate the sandbox (this cleans up all resources) self._sandbox_manager.terminate(sandbox.id) - logger.info(f"Terminated sandbox {sandbox.id} for user {user_id}") + logger.info("Terminated sandbox %s for user %s", sandbox.id, user_id) # Update status in database update_sandbox_status__no_commit( @@ -2258,5 +2290,5 @@ class SessionManager: return True except Exception as e: - logger.error(f"Failed to terminate sandbox {sandbox.id}: {e}") + logger.error("Failed to terminate sandbox %s: %s", sandbox.id, e) raise RuntimeError(f"Failed to terminate sandbox: {e}") from e diff --git a/backend/onyx/server/features/input_prompt/api.py b/backend/onyx/server/features/input_prompt/api.py index d3546d3308..a8ac00ca9f 100644 --- a/backend/onyx/server/features/input_prompt/api.py +++ b/backend/onyx/server/features/input_prompt/api.py @@ -96,7 +96,7 @@ def patch_input_prompt( ) except ValueError as e: error_msg = "Error occurred while updated input prompt" - logger.warning(f"{error_msg}. Stack trace: {e}") + logger.warning("%s. Stack trace: %s", error_msg, e) raise HTTPException(status_code=404, detail=error_msg) return InputPromptSnapshot.from_model(updated_input_prompt) @@ -116,7 +116,7 @@ def delete_input_prompt( except ValueError as e: error_msg = "Error occurred while deleting input prompt" - logger.warning(f"{error_msg}. Stack trace: {e}") + logger.warning("%s. Stack trace: %s", error_msg, e) raise HTTPException(status_code=404, detail=error_msg) @@ -131,7 +131,7 @@ def delete_public_input_prompt( except ValueError as e: error_msg = "Error occurred while deleting input prompt" - logger.warning(f"{error_msg}. Stack trace: {e}") + logger.warning("%s. Stack trace: %s", error_msg, e) raise HTTPException(status_code=404, detail=error_msg) diff --git a/backend/onyx/server/features/mcp/api.py b/backend/onyx/server/features/mcp/api.py index 5b6dcbb8d0..6c47fde6fa 100644 --- a/backend/onyx/server/features/mcp/api.py +++ b/backend/onyx/server/features/mcp/api.py @@ -452,7 +452,7 @@ def test_mcp_server_credentials( return False, "Failed to retrieve tools list from server." except Exception as e: - logger.error(f"Failed to test MCP server credentials: {e}") + logger.error("Failed to test MCP server credentials: %s", e) return False, f"Connection failed: {str(e)}" @@ -500,7 +500,7 @@ async def _connect_oauth( ) -> MCPUserOAuthConnectResponse: """Connect OAuth flow for per-user MCP server authentication""" - logger.info(f"Initiating per-user OAuth for server: {request.server_id}") + logger.info("Initiating per-user OAuth for server: %s", request.server_id) try: server_id = int(request.server_id) @@ -610,7 +610,7 @@ async def _connect_oauth( # always make a http request for the initial probe transport = mcp_server.transport if is_connected else MCPTransport.STREAMABLE_HTTP probe_url = mcp_server.server_url - logger.info(f"Probing OAuth server at: {probe_url}") + logger.info("Probing OAuth server at: %s", probe_url) oauth_auth = make_oauth_provider( mcp_server, @@ -633,7 +633,7 @@ async def _connect_oauth( transport=transport, auth=oauth_auth, ) - logger.info(f"OAuth initialization completed successfully: {x}") + logger.info("OAuth initialization completed successfully: %s", x) return x except Exception: logger.exception("OAuth initialization failed") @@ -674,21 +674,21 @@ async def _connect_oauth( try: init_result = init_task.result() logger.info( - f"OAuth initialization completed during timeout: {init_result}" + "OAuth initialization completed during timeout: %s", init_result ) return MCPUserOAuthConnectResponse( server_id=int(request.server_id), oauth_url=request.return_path, ) except Exception as e: - logger.error(f"OAuth initialization failed during timeout: {e}") + logger.error("OAuth initialization failed during timeout: %s", e) raise HTTPException( status_code=400, detail=f"OAuth initialization failed: {str(e)}" ) raise HTTPException(status_code=400, detail="Auth URL retrieval timed out") logger.info( - f"Connected to auth url: {oauth_url} for mcp server: {mcp_server.name}" + "Connected to auth url: %s for mcp server: %s", oauth_url, mcp_server.name ) return MCPUserOAuthConnectResponse( server_id=int(request.server_id), oauth_url=oauth_url @@ -699,13 +699,13 @@ async def _connect_oauth( t.cancel() try: init_result = init_task.result() - logger.info(f"OAuth initialization completed without redirect: {init_result}") + logger.info("OAuth initialization completed without redirect: %s", init_result) except Exception as e: if isinstance(e, ExceptionGroup): saved_e = log_exception_group(e) else: saved_e = e - logger.error(f"OAuth initialization failed: {saved_e}") + logger.error("OAuth initialization failed: %s", saved_e) # If initialize failed and we also didn't get an auth URL, surface an error raise HTTPException( status_code=400, detail=f"Failed to initialize OAuth client: {str(saved_e)}" @@ -788,7 +788,10 @@ async def process_oauth_callback( db_session.commit() logger.info( - f"server_id={str(mcp_server.id)} server_name={mcp_server.name} return_path={state_data.return_path}" + "server_id=%s server_name=%s return_path=%s", + str(mcp_server.id), + mcp_server.name, + state_data.return_path, ) return MCPOAuthCallbackResponse( @@ -808,7 +811,7 @@ def save_user_credentials( ) -> MCPApiKeyResponse: """Save user credentials for template-based MCP server authentication""" - logger.info(f"Saving user credentials for server: {request.server_id}") + logger.info("Saving user credentials for server: %s", request.server_id) try: server_id = request.server_id @@ -855,7 +858,7 @@ def save_user_credentials( config_data[field_key] = field_val except Exception as e: - logger.error(f"Failed to process authentication template: {e}") + logger.error("Failed to process authentication template: %s", e) raise HTTPException( status_code=400, detail=f"Failed to process authentication template: {str(e)}", @@ -906,7 +909,7 @@ def save_user_credentials( raise # Re-raise HTTP exceptions except Exception as e: logger.warning( - f"Could not validate credentials for server {mcp_server.name}: {e}" + "Could not validate credentials for server %s: %s", mcp_server.name, e ) validation_message = "Credentials saved but could not be validated" @@ -920,7 +923,7 @@ def save_user_credentials( ) logger.info( - f"User credentials saved for server {mcp_server.name} and user {email}" + "User credentials saved for server %s and user %s", mcp_server.name, email ) db_session.commit() @@ -934,7 +937,7 @@ def save_user_credentials( ) except Exception as e: - logger.error(f"Failed to save user credentials: {e}") + logger.error("Failed to save user credentials: %s", e) raise HTTPException(status_code=500, detail="Failed to save user credentials") @@ -954,12 +957,16 @@ class ServerToolsResponse(BaseModel): def _ensure_mcp_server_owner_or_admin(server: DbMCPServer, user: User) -> None: logger.info( - f"Ensuring MCP server owner or admin: {server.name} {user} {user.role} server.owner={server.owner}" + "Ensuring MCP server owner or admin: %s %s %s server.owner=%s", + server.name, + user, + user.role, + server.owner, ) if user.role == UserRole.ADMIN: return - logger.info(f"User email: {user.email} server.owner={server.owner}") + logger.info("User email: %s server.owner=%s", user.email, server.owner) if server.owner != user.email: raise HTTPException( status_code=403, @@ -1029,7 +1036,7 @@ def _db_mcp_server_to_api_mcp_server( else: admin_credentials = {} logger.warning( - f"No admin client info found for server {db_server.name}" + "No admin client info found for server %s", db_server.name ) else: # currently: per user auth using api key OR oauth user_config = get_user_connection_config(db_server.id, email, db) @@ -1070,7 +1077,7 @@ def _db_mcp_server_to_api_mcp_server( ) elif can_view_admin_credentials: admin_credentials = {} - logger.warning(f"No client info found for server {db_server.name}") + logger.warning("No client info found for server %s", db_server.name) # Get auth template if this is a per-user auth server auth_template = None @@ -1088,7 +1095,7 @@ def _db_mcp_server_to_api_mcp_server( ) except Exception as e: logger.warning( - f"Failed to parse auth template for server {db_server.name}: {e}" + "Failed to parse auth template for server %s: %s", db_server.name, e ) is_authenticated: bool = ( @@ -1136,7 +1143,7 @@ def get_mcp_servers_for_assistant( ) -> MCPServersResponse: """Get MCP servers for an assistant""" - logger.info(f"Fetching MCP servers for assistant: {assistant_id}") + logger.info("Fetching MCP servers for assistant: %s", assistant_id) try: persona_id = int(assistant_id) @@ -1153,7 +1160,7 @@ def get_mcp_servers_for_assistant( except ValueError: raise HTTPException(status_code=400, detail="Invalid assistant ID") except Exception as e: - logger.error(f"Failed to fetch MCP servers: {e}") + logger.error("Failed to fetch MCP servers: %s", e) raise HTTPException(status_code=500, detail="Failed to fetch MCP servers") @@ -1277,7 +1284,7 @@ def get_mcp_server_tools_snapshots( # Re-raise HTTP exceptions (e.g. 401, 400) so they are returned to client raise e - logger.error(f"Failed to discover tools for MCP server: {e}") + logger.error("Failed to discover tools for MCP server: %s", e) raise HTTPException(status_code=500, detail="Failed to discover tools") # Fetch and return tools from database @@ -1350,7 +1357,7 @@ def _list_mcp_tools_by_id( user: User, ) -> MCPToolListResponse: """List available tools from an existing MCP server""" - logger.info(f"Listing tools for MCP server: {server_id}") + logger.info("Listing tools for MCP server: %s", server_id) try: # Get the MCP server @@ -1412,7 +1419,7 @@ def _list_mcp_tools_by_id( import time t1 = time.time() - logger.info(f"Discovering tools for MCP server: {mcp_server.name}: {t1}") + logger.info("Discovering tools for MCP server: %s: %s", mcp_server.name, t1) server_url = mcp_server.server_url if mcp_server.transport is None: @@ -1428,7 +1435,10 @@ def _list_mcp_tools_by_id( auth=auth, ) logger.info( - f"Discovered {len(discovered_tools)} tools for MCP server: {mcp_server.name}: {time.time() - t1}" + "Discovered %s tools for MCP server: %s: %s", + len(discovered_tools), + mcp_server.name, + time.time() - t1, ) update_mcp_server__no_commit( server_id=server_id, @@ -1569,7 +1579,7 @@ def _upsert_mcp_server( ) logger.info( - f"Updated existing MCP server '{request.name}' with ID {mcp_server.id}" + "Updated existing MCP server '%s' with ID %s", request.name, mcp_server.id ) else: @@ -1596,7 +1606,9 @@ def _upsert_mcp_server( db_session=db_session, ) - logger.info(f"Created new MCP server '{request.name}' with ID {mcp_server.id}") + logger.info( + "Created new MCP server '%s' with ID %s", request.name, mcp_server.id + ) # PT_OAUTH doesn't need stored connection config (uses user's login token) if ( @@ -1779,7 +1791,7 @@ def update_mcp_server_status( user: User = Depends(current_curator_or_admin_user), ) -> dict[str, str]: """Update the status of an MCP server""" - logger.info(f"Updating MCP server {server_id} status to {status}") + logger.info("Updating MCP server %s status to %s", server_id, status) try: mcp_server = get_mcp_server_by_id(server_id, db) @@ -1795,7 +1807,7 @@ def update_mcp_server_status( ) db.commit() - logger.info(f"Successfully updated MCP server {server_id} status to {status}") + logger.info("Successfully updated MCP server %s status to %s", server_id, status) return {"message": f"Server status updated to {status.value}"} @@ -1820,7 +1832,7 @@ def get_mcp_servers_for_admin( return MCPServersResponse(mcp_servers=mcp_servers) except Exception as e: - logger.error(f"Failed to fetch MCP servers for admin: {type(e)}:{e}") + logger.error("Failed to fetch MCP servers for admin: %s:%s", type(e), e) raise HTTPException(status_code=500, detail="Failed to fetch MCP servers") @@ -1831,7 +1843,7 @@ def get_mcp_server_db_tools( user: User = Depends(current_curator_or_admin_user), ) -> ServerToolsResponse: """Get existing database tools created for an MCP server""" - logger.info(f"Getting database tools for MCP server: {server_id}") + logger.info("Getting database tools for MCP server: %s", server_id) try: # Verify the server exists @@ -1898,7 +1910,7 @@ def upsert_mcp_server( action_verb = "Updated" if request.existing_server_id else "Created" logger.info( - f"{action_verb} MCP server '{request.name}' with ID {mcp_server.id}" + "%s MCP server '%s' with ID %s", action_verb, request.name, mcp_server.id ) if mcp_server.auth_type is None: @@ -2070,10 +2082,13 @@ def delete_mcp_server_admin( # Log tools that will be deleted for debugging tools_to_delete = get_tools_by_mcp_server_id(server_id, db_session) logger.info( - f"Deleting MCP server {server_id} ({server.name}) with {len(tools_to_delete)} tools" + "Deleting MCP server %s (%s) with %s tools", + server_id, + server.name, + len(tools_to_delete), ) for tool in tools_to_delete: - logger.debug(f" - Tool to delete: {tool.name} (ID: {tool.id})") + logger.debug(" - Tool to delete: %s (ID: %s)", tool.name, tool.id) # Cascade behavior handled by FK ondelete in DB delete_mcp_server(server_id, db_session) @@ -2082,12 +2097,14 @@ def delete_mcp_server_admin( remaining_tools = get_tools_by_mcp_server_id(server_id, db_session) if remaining_tools: logger.error( - f"WARNING: {len(remaining_tools)} tools still exist after deleting MCP server {server_id}" + "WARNING: %s tools still exist after deleting MCP server %s", + len(remaining_tools), + server_id, ) # Manually delete them as a fallback for tool in remaining_tools: logger.info( - f"Manually deleting orphaned tool: {tool.name} (ID: {tool.id})" + "Manually deleting orphaned tool: %s (ID: %s)", tool.name, tool.id ) delete_tool__no_commit(tool.id, db_session) db_session.commit() @@ -2096,5 +2113,5 @@ def delete_mcp_server_admin( except ValueError: raise HTTPException(status_code=404, detail="MCP server not found") except Exception as e: - logger.error(f"Failed to delete MCP server {server_id}: {e}") + logger.error("Failed to delete MCP server %s: %s", server_id, e) raise HTTPException(status_code=500, detail="Failed to delete MCP server") diff --git a/backend/onyx/server/features/oauth_config/api.py b/backend/onyx/server/features/oauth_config/api.py index 51ee1178df..f2df6a4248 100644 --- a/backend/onyx/server/features/oauth_config/api.py +++ b/backend/onyx/server/features/oauth_config/api.py @@ -237,13 +237,13 @@ def handle_oauth_callback( ) except ValueError as e: - logger.error(f"OAuth callback error: {e}") + logger.error("OAuth callback error: %s", e) return OAuthCallbackResponse( redirect_url="/chat", error=str(e), ) except Exception as e: - logger.error(f"Unexpected OAuth callback error: {e}") + logger.error("Unexpected OAuth callback error: %s", e) return OAuthCallbackResponse( redirect_url="/chat", error="An unexpected error occurred during OAuth callback", diff --git a/backend/onyx/server/features/persona/api.py b/backend/onyx/server/features/persona/api.py index dca9a3d460..83daeb2e25 100644 --- a/backend/onyx/server/features/persona/api.py +++ b/backend/onyx/server/features/persona/api.py @@ -574,14 +574,17 @@ def get_persona_avatar( file_record = get_filerecord_by_file_id_optional(file_id, db_session) if not file_record: logger.warning( - f"Persona {persona_id} references avatar file {file_id} with no " - f"matching FileRecord; rejecting." + "Persona %s references avatar file %s with no matching FileRecord; rejecting.", + persona_id, + file_id, ) raise OnyxError(OnyxErrorCode.NOT_FOUND, "Avatar not found") if file_record.file_origin != FileOrigin.CHAT_UPLOAD: logger.warning( - f"Persona {persona_id} avatar references file {file_id} with " - f"unexpected origin {file_record.file_origin}; rejecting." + "Persona %s avatar references file %s with unexpected origin %s; rejecting.", + persona_id, + file_id, + file_record.file_origin, ) raise OnyxError(OnyxErrorCode.NOT_FOUND, "Avatar not found") diff --git a/backend/onyx/server/features/projects/api.py b/backend/onyx/server/features/projects/api.py index dd9d27518a..d7bbb22348 100644 --- a/backend/onyx/server/features/projects/api.py +++ b/backend/onyx/server/features/projects/api.py @@ -59,7 +59,7 @@ def _trigger_user_file_project_sync( from onyx.background.task_utils import drain_project_sync_loop background_tasks.add_task(drain_project_sync_loop, tenant_id) - logger.info(f"Queued in-process project sync for user_file_id={user_file_id}") + logger.info("Queued in-process project sync for user_file_id=%s", user_file_id) return from onyx.background.celery.tasks.user_file_processing.tasks import ( @@ -74,9 +74,10 @@ def _trigger_user_file_project_sync( queue_depth = get_user_file_project_sync_queue_depth(client_app) if queue_depth > USER_FILE_PROJECT_SYNC_MAX_QUEUE_DEPTH: logger.warning( - f"Skipping immediate project sync for user_file_id={user_file_id} due to " - f"queue depth {queue_depth}>{USER_FILE_PROJECT_SYNC_MAX_QUEUE_DEPTH}. " - "It will be picked up by beat later." + "Skipping immediate project sync for user_file_id=%s due to queue depth %s>%s. It will be picked up by beat later.", + user_file_id, + queue_depth, + USER_FILE_PROJECT_SYNC_MAX_QUEUE_DEPTH, ) return @@ -90,11 +91,11 @@ def _trigger_user_file_project_sync( ) if not enqueued: logger.info( - f"Skipped duplicate project sync enqueue for user_file_id={user_file_id}" + "Skipped duplicate project sync enqueue for user_file_id=%s", user_file_id ) return - logger.info(f"Triggered project sync for user_file_id={user_file_id}") + logger.info("Triggered project sync for user_file_id=%s", user_file_id) @router.get("", tags=PUBLIC_API_TAGS) @@ -159,7 +160,7 @@ def upload_user_files( return CategorizedFilesSnapshot.from_result(categorized_files_result) except Exception as e: - logger.exception(f"Error uploading files - {type(e).__name__}: {str(e)}") + logger.exception("Error uploading files - %s: %s", type(e).__name__, str(e)) raise HTTPException( status_code=500, detail="Failed to upload files. Please try again or contact support if the issue persists.", @@ -480,7 +481,7 @@ def delete_user_file( from onyx.background.task_utils import drain_delete_loop bg_tasks.add_task(drain_delete_loop, tenant_id) - logger.info(f"Queued in-process delete for user_file_id={user_file.id}") + logger.info("Queued in-process delete for user_file_id=%s", user_file.id) else: from onyx.background.celery.versioned_apps.client import app as client_app @@ -491,7 +492,9 @@ def delete_user_file( priority=OnyxCeleryPriority.HIGH, ) logger.info( - f"Triggered delete for user_file_id={user_file.id} with task_id={task.id}" + "Triggered delete for user_file_id=%s with task_id=%s", + user_file.id, + task.id, ) return UserFileDeleteResult( diff --git a/backend/onyx/server/features/projects/projects_file_utils.py b/backend/onyx/server/features/projects/projects_file_utils.py index 9552bcfd92..aa8d43fd5d 100644 --- a/backend/onyx/server/features/projects/projects_file_utils.py +++ b/backend/onyx/server/features/projects/projects_file_utils.py @@ -49,9 +49,10 @@ def get_upload_size_bytes(upload: UploadFile) -> int | None: return size except Exception as e: logger.warning( - "Could not determine upload size via stream seek " - f"(filename='{get_safe_filename(upload)}', " - f"error_type={type(e).__name__}, error={e})" + "Could not determine upload size via stream seek (filename='%s', error_type=%s, error=%s)", + get_safe_filename(upload), + type(e).__name__, + e, ) return None @@ -61,7 +62,8 @@ def is_upload_too_large(upload: UploadFile, max_bytes: int) -> bool: size_bytes = get_upload_size_bytes(upload) if size_bytes is None: logger.warning( - f"Could not determine upload size; skipping size-limit check for '{get_safe_filename(upload)}'" + "Could not determine upload size; skipping size-limit check for '%s'", + get_safe_filename(upload), ) return False return size_bytes > max_bytes @@ -226,7 +228,7 @@ def categorize_uploaded_files( token_count = estimate_image_tokens_for_upload(upload) except (UnidentifiedImageError, OSError) as e: logger.warning( - f"Failed to process image file '{filename}': {str(e)}" + "Failed to process image file '%s': %s", filename, str(e) ) results.rejected.append( RejectedFile( @@ -256,7 +258,7 @@ def categorize_uploaded_files( file_name=filename, extension=extension, ): - logger.warning(f"{filename} is password protected") + logger.warning("%s is password protected", filename) results.rejected.append( RejectedFile( filename=filename, reason="Document is password protected" @@ -328,11 +330,13 @@ def categorize_uploaded_files( upload.file.seek(0) except Exception as e: logger.warning( - f"Failed to reset file pointer for '{filename}': {str(e)}" + "Failed to reset file pointer for '%s': %s", + filename, + str(e), ) continue - logger.warning(f"No text content extracted from '{filename}'") + logger.warning("No text content extracted from '%s'", filename) results.rejected.append( RejectedFile( filename=filename, @@ -370,11 +374,14 @@ def categorize_uploaded_files( upload.file.seek(0) except Exception as e: logger.warning( - f"Failed to reset file pointer for '{filename}': {str(e)}" + "Failed to reset file pointer for '%s': %s", filename, str(e) ) except Exception as e: logger.warning( - f"Failed to process uploaded file '{get_safe_filename(upload)}' (error_type={type(e).__name__}, error={str(e)})" + "Failed to process uploaded file '%s' (error_type=%s, error=%s)", + get_safe_filename(upload), + type(e).__name__, + str(e), ) results.rejected.append( RejectedFile( diff --git a/backend/onyx/server/features/release_notes/utils.py b/backend/onyx/server/features/release_notes/utils.py index bb99a427c8..81f8c38825 100644 --- a/backend/onyx/server/features/release_notes/utils.py +++ b/backend/onyx/server/features/release_notes/utils.py @@ -120,7 +120,7 @@ def get_cached_etag() -> str | None: return etag.decode("utf-8") return None except Exception as e: - logger.error(f"Failed to get cached etag: {e}") + logger.error("Failed to get cached etag: %s", e) return None @@ -139,7 +139,7 @@ def get_last_fetch_time() -> datetime | None: return last_fetch except Exception as e: - logger.error(f"Failed to get last fetch time from cache: {e}") + logger.error("Failed to get last fetch time from cache: %s", e) return None @@ -152,7 +152,7 @@ def save_fetch_metadata(etag: str | None) -> None: if etag: cache.set(REDIS_KEY_ETAG, etag, ex=REDIS_CACHE_TTL) except Exception as e: - logger.error(f"Failed to save fetch metadata to cache: {e}") + logger.error("Failed to save fetch metadata to cache: %s", e) def is_cache_stale() -> bool: @@ -229,7 +229,7 @@ def ensure_release_notes_fresh_and_notify(db_session: Session) -> None: create_release_notifications_for_versions(db_session, entries) except Exception as e: - logger.error(f"Failed to check release notes: {e}") + logger.error("Failed to check release notes: %s", e) # Update timestamp even on failure to prevent retry storms # We don't save etag on failure to allow retry with conditional request save_fetch_metadata(None) diff --git a/backend/onyx/server/federated/api.py b/backend/onyx/server/federated/api.py index b4e6c06799..d5cffe1536 100644 --- a/backend/onyx/server/federated/api.py +++ b/backend/onyx/server/federated/api.py @@ -72,7 +72,10 @@ def create_federated_connector( tenant_id = get_current_tenant_id() logger.info( - f"Creating federated connector: source={federated_connector_data.source}, user={user.email}, tenant_id={tenant_id}" + "Creating federated connector: source=%s, user=%s, tenant_id=%s", + federated_connector_data.source, + user.email, + tenant_id, ) try: @@ -85,7 +88,8 @@ def create_federated_connector( ) logger.info( - f"Successfully created federated connector with id={federated_connector.id}" + "Successfully created federated connector with id=%s", + federated_connector.id, ) return FederatedConnectorResponse( @@ -94,11 +98,11 @@ def create_federated_connector( ) except ValueError as e: - logger.warning(f"Validation error creating federated connector: {e}") + logger.warning("Validation error creating federated connector: %s", e) db_session.rollback() raise HTTPException(status_code=400, detail=str(e)) except Exception as e: - logger.error(f"Error creating federated connector: {e}") + logger.error("Error creating federated connector: %s", e) db_session.rollback() raise HTTPException(status_code=500, detail=str(e)) @@ -141,7 +145,7 @@ def get_entities( except HTTPException: raise except Exception as e: - logger.error(f"Error fetching entities for federated connector {id}: {e}") + logger.error("Error fetching entities for federated connector %s: %s", id, e) raise HTTPException(status_code=500, detail=str(e)) @@ -185,7 +189,7 @@ def get_credentials_schema( raise except Exception as e: logger.error( - f"Error fetching credentials schema for federated connector {id}: {e}" + "Error fetching credentials schema for federated connector %s: %s", id, e ) raise HTTPException(status_code=500, detail=str(e)) @@ -214,7 +218,7 @@ def get_configuration_schema_by_source( return ConfigurationSchemaResponse(configuration=configuration_dict) except Exception as e: - logger.error(f"Error fetching configuration schema for source {source}: {e}") + logger.error("Error fetching configuration schema for source %s: %s", source, e) raise HTTPException(status_code=500, detail=str(e)) @@ -245,7 +249,7 @@ def get_credentials_schema_by_source( except HTTPException: raise except Exception as e: - logger.error(f"Error fetching credentials schema for source {source}: {e}") + logger.error("Error fetching credentials schema for source %s: %s", source, e) raise HTTPException(status_code=500, detail=str(e)) @@ -269,7 +273,7 @@ def validate_credentials( except HTTPException: raise except Exception as e: - logger.error(f"Error validating credentials for source {source}: {e}") + logger.error("Error validating credentials for source %s: %s", source, e) raise HTTPException(status_code=500, detail=str(e)) @@ -313,7 +317,7 @@ def validate_entities( except HTTPException: raise except Exception as e: - logger.error(f"Error validating entities for federated connector {id}: {e}") + logger.error("Error validating entities for federated connector %s: %s", id, e) return Response(status_code=500) @@ -353,7 +357,7 @@ def get_authorize_url( # Generate state parameter and store session info logger.info( - f"Generating OAuth state for federated_connector_id={id}, user_id={user.id}" + "Generating OAuth state for federated_connector_id=%s, user_id=%s", id, user.id ) state = generate_oauth_state( federated_connector_id=id, @@ -362,7 +366,7 @@ def get_authorize_url( # Add state to the OAuth URL authorize_url = add_state_to_oauth_url(base_authorize_url, state) - logger.info(f"Generated OAuth authorize URL with state for connector {id}") + logger.info("Generated OAuth authorize URL with state for connector %s", id) return AuthorizeUrlResponse(authorize_url=authorize_url) @@ -431,7 +435,9 @@ def handle_oauth_callback_generic( # Store OAuth token in database if we have an access token if oauth_result.access_token: logger.info( - f"Storing OAuth token for federated_connector_id={federated_connector_id}, user_id={oauth_session.user_id}" + "Storing OAuth token for federated_connector_id=%s, user_id=%s", + federated_connector_id, + oauth_session.user_id, ) update_federated_connector_oauth_token( db_session=db_session, @@ -586,7 +592,7 @@ def update_federated_connector_endpoint( return get_federated_connector_detail(id, user, db_session) except ValueError as e: - logger.warning(f"Validation error updating federated connector {id}: {e}") + logger.warning("Validation error updating federated connector %s: %s", id, e) raise HTTPException(status_code=400, detail=str(e)) diff --git a/backend/onyx/server/manage/administrative.py b/backend/onyx/server/manage/administrative.py index 9f1a63de5b..dc7d73e483 100644 --- a/backend/onyx/server/manage/administrative.py +++ b/backend/onyx/server/manage/administrative.py @@ -199,7 +199,8 @@ def create_deletion_attempt_for_connector_id( ) logger.info( - f"create_deletion_attempt_for_connector_id - running check_for_connector_deletion: cc_pair={cc_pair.id}" + "create_deletion_attempt_for_connector_id - running check_for_connector_deletion: cc_pair=%s", + cc_pair.id, ) if cc_pair.connector.source == DocumentSource.FILE: diff --git a/backend/onyx/server/manage/discord_bot/utils.py b/backend/onyx/server/manage/discord_bot/utils.py index 60467f5637..c4c5f2cc0a 100644 --- a/backend/onyx/server/manage/discord_bot/utils.py +++ b/backend/onyx/server/manage/discord_bot/utils.py @@ -21,7 +21,7 @@ def generate_discord_registration_key(tenant_id: str) -> str: encoded_tenant = quote(tenant_id) random_token = secrets.token_urlsafe(16) - logger.info(f"Generated Discord registration key for tenant {tenant_id}") + logger.info("Generated Discord registration key for tenant %s", tenant_id) return f"{REGISTRATION_KEY_PREFIX}{encoded_tenant}.{random_token}" diff --git a/backend/onyx/server/manage/embedding/api.py b/backend/onyx/server/manage/embedding/api.py index fa2ff9699a..e304f56101 100644 --- a/backend/onyx/server/manage/embedding/api.py +++ b/backend/onyx/server/manage/embedding/api.py @@ -59,7 +59,7 @@ def test_embedding_configuration( except Exception as e: error_msg = "An error occurred while testing your embedding model. Please check your configuration." - logger.error(f"{error_msg} Error message: {e}", exc_info=True) + logger.error("%s Error message: %s", error_msg, e, exc_info=True) raise OnyxError(OnyxErrorCode.VALIDATION_ERROR, error_msg) diff --git a/backend/onyx/server/manage/image_generation/api.py b/backend/onyx/server/manage/image_generation/api.py index 40e25a94eb..524a27ae87 100644 --- a/backend/onyx/server/manage/image_generation/api.py +++ b/backend/onyx/server/manage/image_generation/api.py @@ -282,7 +282,7 @@ def test_image_generation( except Exception as e: # Log only exception type to avoid exposing sensitive data # (LiteLLM errors may contain URLs with API keys or auth tokens) - logger.warning(f"Image generation test failed: {type(e).__name__}") + logger.warning("Image generation test failed: %s", type(e).__name__) raise HTTPException( status_code=400, detail=f"Image generation test failed: {type(e).__name__}", diff --git a/backend/onyx/server/manage/llm/api.py b/backend/onyx/server/manage/llm/api.py index da8a00eb08..563d77fc24 100644 --- a/backend/onyx/server/manage/llm/api.py +++ b/backend/onyx/server/manage/llm/api.py @@ -171,10 +171,13 @@ def _sync_fetched_models( ) if new_count > 0: logger.info( - f"Added {new_count} new {source_label} models to provider '{provider_name}'" + "Added %s new %s models to provider '%s'", + new_count, + source_label, + provider_name, ) except ValueError as e: - logger.warning(f"Failed to sync {source_label} models to DB: {e}") + logger.warning("Failed to sync %s models to DB: %s", source_label, e) def _mask_provider_credentials(provider_view: LLMProviderView) -> None: @@ -402,7 +405,8 @@ def list_llm_providers( from_model_end = datetime.now(timezone.utc) from_model_duration = (from_model_end - from_model_start).total_seconds() logger.debug( - f"LLMProviderView.from_model took {from_model_duration:.2f} seconds" + "LLMProviderView.from_model took %s seconds", + format(from_model_duration, ".2f"), ) _mask_provider_credentials(full_llm_provider) @@ -410,7 +414,9 @@ def list_llm_providers( end_time = datetime.now(timezone.utc) duration = (end_time - start_time).total_seconds() - logger.debug(f"Completed fetching LLM providers in {duration:.2f} seconds") + logger.debug( + "Completed fetching LLM providers in %s seconds", format(duration, ".2f") + ) return LLMProviderResponse[LLMProviderView].from_models( providers=llm_provider_list, @@ -662,7 +668,7 @@ def get_vision_capable_providers( for provider_id, model_names in provider_models.items() ] - logger.debug(f"Found {len(vision_provider_response)} vision-capable providers") + logger.debug("Found %s vision-capable providers", len(vision_provider_response)) return LLMProviderResponse[VisionProviderResponse].from_models( providers=vision_provider_response, @@ -713,7 +719,9 @@ def list_llm_provider_basics( end_time = datetime.now(timezone.utc) duration = (end_time - start_time).total_seconds() logger.debug( - f"Completed fetching {len(accessible_providers)} user-accessible providers in {duration:.2f} seconds" + "Completed fetching %s user-accessible providers in %s seconds", + len(accessible_providers), + format(duration, ".2f"), ) return LLMProviderResponse[LLMProviderDescriptor].from_models( @@ -778,7 +786,7 @@ def list_llm_providers_for_persona( and should NOT block the UI. """ start_time = datetime.now(timezone.utc) - logger.debug(f"Starting to fetch LLM providers for persona {persona_id}") + logger.debug("Starting to fetch LLM providers for persona %s", persona_id) persona = fetch_persona_with_groups(db_session, persona_id) if not persona: @@ -811,7 +819,10 @@ def list_llm_providers_for_persona( end_time = datetime.now(timezone.utc) duration = (end_time - start_time).total_seconds() logger.debug( - f"Completed fetching {len(llm_provider_list)} LLM providers for persona {persona_id} in {duration:.2f} seconds" + "Completed fetching %s LLM providers for persona %s in %s seconds", + len(llm_provider_list), + persona_id, + format(duration, ".2f"), ) # Get the default model and vision model for the persona @@ -1010,7 +1021,7 @@ def get_bedrock_available_models( "supports_image_input": infer_vision_support(profile_id), } except Exception as e: - logger.warning(f"Couldn't fetch inference profiles for Bedrock: {e}") + logger.warning("Couldn't fetch inference profiles for Bedrock: %s", e) # Prefer profiles: de-dupe available models, then add profile IDs candidates = (available_models - cross_region_models) | profile_ids diff --git a/backend/onyx/server/manage/search_settings.py b/backend/onyx/server/manage/search_settings.py index 4ef9978dd4..064a25772c 100644 --- a/backend/onyx/server/manage/search_settings.py +++ b/backend/onyx/server/manage/search_settings.py @@ -254,7 +254,7 @@ def update_saved_search_settings( ) logger.info( - f"Updated current search settings to {search_settings.model_dump_json()}" + "Updated current search settings to %s", search_settings.model_dump_json() ) # Re-sync default to match PRESENT search settings @@ -332,7 +332,8 @@ def _sync_default_contextual_model(db_session: Session) -> None: ) except ValueError as e: logger.error( - f"Error syncing default contextual model, defaulting to no contextual model: {e}" + "Error syncing default contextual model, defaulting to no contextual model: %s", + e, ) update_no_default_contextual_rag_provider( db_session=db_session, diff --git a/backend/onyx/server/manage/users.py b/backend/onyx/server/manage/users.py index 2b30f3f1a7..c2f1845a3b 100644 --- a/backend/onyx/server/manage/users.py +++ b/backend/onyx/server/manage/users.py @@ -508,7 +508,7 @@ def bulk_invite_users( )(new_invited_emails, tenant_id) except Exception as e: - logger.error(f"Failed to add users to tenant {tenant_id}: {str(e)}") + logger.error("Failed to add users to tenant %s: %s", tenant_id, str(e)) initial_invited_users = get_invited_users() @@ -548,7 +548,7 @@ def bulk_invite_users( send_user_email_invite(email, current_user, AUTH_TYPE) email_invite_status = EmailInviteStatus.SENT except Exception as e: - logger.error(f"Error sending email invite to invited users: {e}") + logger.error("Error sending email invite to invited users: %s", e) email_invite_status = EmailInviteStatus.SEND_FAILED if MULTI_TENANT and not DEV_MODE: @@ -559,7 +559,7 @@ def bulk_invite_users( "onyx.server.tenants.billing", "register_tenant_users", None )(tenant_id, get_live_users_count(db_session)) except Exception as e: - logger.error(f"Failed to register tenant users: {str(e)}") + logger.error("Failed to register tenant users: %s", str(e)) logger.info( "Reverting changes: removing users from tenant and resetting invited users" ) @@ -650,7 +650,7 @@ def deactivate_user_api( raise HTTPException(status_code=404, detail="User not found") if user_to_deactivate.is_active is False: - logger.warning("{} is already deactivated".format(user_to_deactivate.email)) + logger.warning("%s is already deactivated", user_to_deactivate.email) deactivate_user(user_to_deactivate, db_session) @@ -675,9 +675,7 @@ async def delete_user( raise HTTPException(status_code=404, detail="User not found") if user_to_delete.is_active is True: - logger.warning( - "{} must be deactivated before deleting".format(user_to_delete.email) - ) + logger.warning("%s must be deactivated before deleting", user_to_delete.email) raise HTTPException( status_code=400, detail="User must be deactivated before deleting" ) @@ -691,7 +689,7 @@ async def delete_user( "onyx.server.tenants.user_mapping", "remove_users_from_tenant", None )([user_email.user_email], tenant_id) delete_user_from_db(user_to_delete, db_session) - logger.info(f"Deleted user {user_to_delete.email}") + logger.info("Deleted user %s", user_to_delete.email) # Invalidate license cache so used_seats reflects the new count # Only for self-hosted (non-multi-tenant) deployments @@ -702,7 +700,7 @@ async def delete_user( except Exception as e: db_session.rollback() - logger.error(f"Error deleting user {user_to_delete.email}: {str(e)}") + logger.error("Error deleting user %s: %s", user_to_delete.email, str(e)) raise HTTPException(status_code=500, detail="Error deleting user") @@ -719,7 +717,7 @@ def activate_user_api( raise HTTPException(status_code=404, detail="User not found") if user_to_activate.is_active is True: - logger.warning("{} is already activated".format(user_to_activate.email)) + logger.warning("%s is already activated", user_to_activate.email) return # Check seat availability before activating @@ -816,7 +814,7 @@ def get_current_auth_token_creation_redis( return token_creation_time except Exception as e: - logger.error(f"Error retrieving token expiration from Redis: {e}") + logger.error("Error retrieving token expiration from Redis: %s", e) return None diff --git a/backend/onyx/server/manage/voice/api.py b/backend/onyx/server/manage/voice/api.py index 96c89142f3..c4738b837a 100644 --- a/backend/onyx/server/manage/voice/api.py +++ b/backend/onyx/server/manage/voice/api.py @@ -141,7 +141,7 @@ async def upsert_voice_provider_endpoint( raise except Exception as e: db_session.rollback() - logger.error(f"Voice provider credential validation failed on save: {e}") + logger.error("Voice provider credential validation failed on save: %s", e) raise OnyxError( OnyxErrorCode.VALIDATION_ERROR, VOICE_PROVIDER_VALIDATION_FAILURE_MESSAGE, @@ -268,13 +268,13 @@ async def test_voice_provider( except OnyxError: raise except Exception as e: - logger.error(f"Voice provider connection test failed: {e}") + logger.error("Voice provider connection test failed: %s", e) raise OnyxError( OnyxErrorCode.VALIDATION_ERROR, VOICE_PROVIDER_VALIDATION_FAILURE_MESSAGE, ) from e - logger.info(f"Voice provider test succeeded for {request.provider_type}.") + logger.info("Voice provider test succeeded for %s.", request.provider_type) return VoiceProviderUpdateSuccess() diff --git a/backend/onyx/server/manage/voice/user_api.py b/backend/onyx/server/manage/voice/user_api.py index aaed4593a1..16f296c940 100644 --- a/backend/onyx/server/manage/voice/user_api.py +++ b/backend/onyx/server/manage/voice/user_api.py @@ -107,7 +107,7 @@ async def transcribe_audio( f"Speech-to-text not implemented for {provider_db.provider_type}.", ) from exc except Exception as exc: - logger.error(f"Transcription failed: {exc}") + logger.error("Transcription failed: %s", exc) raise OnyxError( OnyxErrorCode.INTERNAL_ERROR, "Transcription failed. Please try again.", @@ -154,7 +154,9 @@ async def synthesize_speech( text = body.text voice = body.voice speed = body.speed - logger.info(f"TTS request: text length={len(text)}, voice={voice}, speed={speed}") + logger.info( + "TTS request: text length=%s, voice=%s, speed=%s", len(text), voice, speed + ) # Use short-lived session to fetch provider config, then release connection # before starting the long-running streaming response @@ -188,13 +190,16 @@ async def synthesize_speech( ) logger.info( - f"TTS using provider: {provider_db.provider_type}, voice: {final_voice}, speed: {final_speed}" + "TTS using provider: %s, voice: %s, speed: %s", + provider_db.provider_type, + final_voice, + final_speed, ) try: provider = get_voice_provider(provider_db) except ValueError as exc: - logger.error(f"Failed to get voice provider: {exc}") + logger.error("Failed to get voice provider: %s", exc) raise OnyxError(OnyxErrorCode.INTERNAL_ERROR, str(exc)) from exc # Pull the first chunk before returning the StreamingResponse. If the @@ -218,7 +223,7 @@ async def synthesize_speech( async for chunk in stream_iter: chunk_count += 1 yield chunk - logger.info(f"TTS streaming complete: {chunk_count} chunks sent") + logger.info("TTS streaming complete: %s chunks sent", chunk_count) return StreamingResponse( audio_stream(), diff --git a/backend/onyx/server/manage/voice/websocket_api.py b/backend/onyx/server/manage/voice/websocket_api.py index bb343c8e3d..827b8bedf3 100644 --- a/backend/onyx/server/manage/voice/websocket_api.py +++ b/backend/onyx/server/manage/voice/websocket_api.py @@ -79,7 +79,7 @@ class ChunkedTranscriber: return " ".join(self.transcripts) return None except Exception as e: - logger.error(f"Transcription error: {e}") + logger.error("Transcription error: %s", e) self.chunk_buffer = io.BytesIO() self.chunk_bytes = 0 return None @@ -95,7 +95,7 @@ class ChunkedTranscriber: if transcript and transcript.strip(): return transcript.strip() except Exception as e: - logger.error(f"Final transcription error: {e}") + logger.error("Final transcription error: %s", e) return " ".join(self.transcripts) @@ -122,7 +122,9 @@ async def handle_streaming_transcription( if result.text and (result.text != last_transcript or result.is_vad_end): last_transcript = result.text logger.debug( - f"Streaming transcription: got transcript: {result.text[:50]}... (is_vad_end={result.is_vad_end})" + "Streaming transcription: got transcript: %s... (is_vad_end=%s)", + result.text[:50], + result.is_vad_end, ) await websocket.send_json( { @@ -142,7 +144,9 @@ async def handle_streaming_transcription( if msg_type == "websocket.disconnect": logger.info( - f"Streaming transcription: client disconnected after {chunk_count} chunks ({total_bytes} bytes)" + "Streaming transcription: client disconnected after %s chunks (%s bytes)", + chunk_count, + total_bytes, ) break @@ -152,7 +156,8 @@ async def handle_streaming_transcription( # Enforce per-message size limit if chunk_size > WS_MAX_MESSAGE_SIZE: logger.warning( - f"Streaming transcription: message too large ({chunk_size} bytes)" + "Streaming transcription: message too large (%s bytes)", + chunk_size, ) await websocket.send_json( {"type": "error", "message": "Message too large"} @@ -162,7 +167,8 @@ async def handle_streaming_transcription( # Enforce total connection size limit if total_bytes + chunk_size > WS_MAX_TOTAL_BYTES: logger.warning( - f"Streaming transcription: total size limit exceeded ({total_bytes + chunk_size} bytes)" + "Streaming transcription: total size limit exceeded (%s bytes)", + total_bytes + chunk_size, ) await websocket.send_json( {"type": "error", "message": "Total size limit exceeded"} @@ -172,7 +178,10 @@ async def handle_streaming_transcription( chunk_count += 1 total_bytes += chunk_size logger.debug( - f"Streaming transcription: received chunk {chunk_count} ({chunk_size} bytes, total: {total_bytes})" + "Streaming transcription: received chunk %s (%s bytes, total: %s)", + chunk_count, + chunk_size, + total_bytes, ) await transcriber.send_audio(message["bytes"]) @@ -180,7 +189,7 @@ async def handle_streaming_transcription( try: data = json.loads(message["text"]) logger.debug( - f"Streaming transcription: received text message: {data}" + "Streaming transcription: received text message: %s", data ) if data.get("type") == "end": logger.info( @@ -189,8 +198,8 @@ async def handle_streaming_transcription( final_transcript = await transcriber.close() receive_task.cancel() logger.info( - "Streaming transcription: final transcript: " - f"{final_transcript[:100] if final_transcript else '(empty)'}..." + "Streaming transcription: final transcript: %s...", + final_transcript[:100] if final_transcript else "(empty)", ) await websocket.send_json( { @@ -208,10 +217,11 @@ async def handle_streaming_transcription( transcriber.reset_transcript() except json.JSONDecodeError: logger.warning( - f"Streaming transcription: failed to parse JSON: {message.get('text', '')[:100]}" + "Streaming transcription: failed to parse JSON: %s", + message.get("text", "")[:100], ) except Exception as e: - logger.error(f"Streaming transcription: error: {e}", exc_info=True) + logger.error("Streaming transcription: error: %s", e, exc_info=True) raise finally: receive_task.cancel() @@ -220,7 +230,9 @@ async def handle_streaming_transcription( except asyncio.CancelledError: pass logger.info( - f"Streaming transcription: handler finished. Processed {chunk_count} chunks, {total_bytes} total bytes" + "Streaming transcription: handler finished. Processed %s chunks, %s total bytes", + chunk_count, + total_bytes, ) @@ -239,7 +251,9 @@ async def handle_chunked_transcription( if msg_type == "websocket.disconnect": logger.info( - f"Chunked transcription: client disconnected after {chunk_count} chunks ({total_bytes} bytes)" + "Chunked transcription: client disconnected after %s chunks (%s bytes)", + chunk_count, + total_bytes, ) break @@ -249,7 +263,7 @@ async def handle_chunked_transcription( # Enforce per-message size limit if chunk_size > WS_MAX_MESSAGE_SIZE: logger.warning( - f"Chunked transcription: message too large ({chunk_size} bytes)" + "Chunked transcription: message too large (%s bytes)", chunk_size ) await websocket.send_json( {"type": "error", "message": "Message too large"} @@ -259,7 +273,8 @@ async def handle_chunked_transcription( # Enforce total connection size limit if total_bytes + chunk_size > WS_MAX_TOTAL_BYTES: logger.warning( - f"Chunked transcription: total size limit exceeded ({total_bytes + chunk_size} bytes)" + "Chunked transcription: total size limit exceeded (%s bytes)", + total_bytes + chunk_size, ) await websocket.send_json( {"type": "error", "message": "Total size limit exceeded"} @@ -269,13 +284,16 @@ async def handle_chunked_transcription( chunk_count += 1 total_bytes += chunk_size logger.debug( - f"Chunked transcription: received chunk {chunk_count} ({chunk_size} bytes, total: {total_bytes})" + "Chunked transcription: received chunk %s (%s bytes, total: %s)", + chunk_count, + chunk_size, + total_bytes, ) transcript = await transcriber.add_chunk(message["bytes"]) if transcript: logger.debug( - f"Chunked transcription: got transcript: {transcript[:50]}..." + "Chunked transcription: got transcript: %s...", transcript[:50] ) await websocket.send_json( { @@ -288,12 +306,13 @@ async def handle_chunked_transcription( elif "text" in message: try: data = json.loads(message["text"]) - logger.debug(f"Chunked transcription: received text message: {data}") + logger.debug("Chunked transcription: received text message: %s", data) if data.get("type") == "end": logger.info("Chunked transcription: end signal received, flushing") final_transcript = await transcriber.flush() logger.info( - f"Chunked transcription: final transcript: {final_transcript[:100] if final_transcript else '(empty)'}..." + "Chunked transcription: final transcript: %s...", + final_transcript[:100] if final_transcript else "(empty)", ) await websocket.send_json( { @@ -305,11 +324,14 @@ async def handle_chunked_transcription( break except json.JSONDecodeError: logger.warning( - f"Chunked transcription: failed to parse JSON: {message.get('text', '')[:100]}" + "Chunked transcription: failed to parse JSON: %s", + message.get("text", "")[:100], ) logger.info( - f"Chunked transcription: handler finished. Processed {chunk_count} chunks, {total_bytes} total bytes" + "Chunked transcription: handler finished. Processed %s chunks, %s total bytes", + chunk_count, + total_bytes, ) @@ -337,7 +359,7 @@ async def websocket_transcribe( await websocket.accept() logger.info("WebSocket transcribe: connection accepted") except Exception as e: - logger.error(f"WebSocket transcribe: failed to accept connection: {e}") + logger.error("WebSocket transcribe: failed to accept connection: %s", e) return streaming_transcriber = None @@ -372,16 +394,18 @@ async def websocket_transcribe( return logger.info( - f"WebSocket transcribe: creating voice provider: {provider_db.provider_type}" + "WebSocket transcribe: creating voice provider: %s", + provider_db.provider_type, ) try: provider = get_voice_provider(provider_db) logger.info( - f"WebSocket transcribe: voice provider created, streaming supported: {provider.supports_streaming_stt()}" + "WebSocket transcribe: voice provider created, streaming supported: %s", + provider.supports_streaming_stt(), ) except ValueError as e: logger.error( - f"WebSocket transcribe: failed to create voice provider: {e}" + "WebSocket transcribe: failed to create voice provider: %s", e ) await websocket.send_json({"type": "error", "message": str(e)}) return @@ -397,7 +421,8 @@ async def websocket_transcribe( await handle_streaming_transcription(websocket, streaming_transcriber) except Exception as e: logger.error( - f"WebSocket transcribe: failed to create streaming transcriber: {e}" + "WebSocket transcribe: failed to create streaming transcriber: %s", + e, ) if VOICE_DISABLE_STREAMING_FALLBACK: await websocket.send_json( @@ -427,7 +452,7 @@ async def websocket_transcribe( except WebSocketDisconnect: logger.debug("WebSocket transcribe: client disconnected") except Exception as e: - logger.error(f"WebSocket transcribe: unhandled error: {e}", exc_info=True) + logger.error("WebSocket transcribe: unhandled error: %s", e, exc_info=True) try: # Send generic error to avoid leaking sensitive details await websocket.send_json( @@ -464,14 +489,16 @@ async def handle_streaming_synthesis( audio_chunk = await synthesizer.receive_audio() if audio_chunk is None: logger.info( - f"Streaming synthesis: audio stream ended, sent {chunk_count} chunks, {total_bytes} bytes" + "Streaming synthesis: audio stream ended, sent %s chunks, %s bytes", + chunk_count, + total_bytes, ) try: await websocket.send_json({"type": "audio_done"}) logger.info("Streaming synthesis: sent audio_done to client") except Exception as e: logger.warning( - f"Streaming synthesis: failed to send audio_done: {e}" + "Streaming synthesis: failed to send audio_done: %s", e ) break if audio_chunk: # Skip empty chunks @@ -481,15 +508,15 @@ async def handle_streaming_synthesis( await websocket.send_bytes(audio_chunk) except Exception as e: logger.warning( - f"Streaming synthesis: failed to send chunk: {e}" + "Streaming synthesis: failed to send chunk: %s", e ) break except asyncio.CancelledError: logger.info( - f"Streaming synthesis: send_audio cancelled after {chunk_count} chunks" + "Streaming synthesis: send_audio cancelled after %s chunks", chunk_count ) except Exception as e: - logger.error(f"Streaming synthesis: send_audio error: {e}") + logger.error("Streaming synthesis: send_audio error: %s", e) send_task: asyncio.Task | None = None disconnected = False @@ -514,7 +541,8 @@ async def handle_streaming_synthesis( msg_size = len(message["text"]) if msg_size > WS_MAX_TEXT_MESSAGE_SIZE: logger.warning( - f"Streaming synthesis: text message too large ({msg_size} bytes)" + "Streaming synthesis: text message too large (%s bytes)", + msg_size, ) await websocket.send_json( {"type": "error", "message": "Message too large"} @@ -529,7 +557,8 @@ async def handle_streaming_synthesis( # Enforce per-text size limit if len(text) > WS_MAX_TTS_TEXT_LENGTH: logger.warning( - f"Streaming synthesis: text too long ({len(text)} chars)" + "Streaming synthesis: text too long (%s chars)", + len(text), ) await websocket.send_json( {"type": "error", "message": "Text too long"} @@ -541,7 +570,8 @@ async def handle_streaming_synthesis( if send_task is None: send_task = asyncio.create_task(send_audio()) logger.debug( - f"Streaming synthesis: forwarding text chunk ({len(text)} chars)" + "Streaming synthesis: forwarding text chunk (%s chars)", + len(text), ) await synthesizer.send_text(text) @@ -570,13 +600,14 @@ async def handle_streaming_synthesis( except json.JSONDecodeError: logger.warning( - f"Streaming synthesis: failed to parse JSON: {message.get('text', '')[:100]}" + "Streaming synthesis: failed to parse JSON: %s", + message.get("text", "")[:100], ) except WebSocketDisconnect: logger.debug("Streaming synthesis: client disconnected during synthesis") except Exception as e: - logger.error(f"Streaming synthesis: error: {e}", exc_info=True) + logger.error("Streaming synthesis: error: %s", e, exc_info=True) finally: if send_task and not send_task.done(): logger.info("Streaming synthesis: waiting for send_task to finish") @@ -635,7 +666,7 @@ async def handle_chunked_synthesis( msg_size = len(message["text"]) if msg_size > WS_MAX_TEXT_MESSAGE_SIZE: logger.warning( - f"Chunked synthesis: text message too large ({msg_size} bytes)" + "Chunked synthesis: text message too large (%s bytes)", msg_size ) await websocket.send_json( {"type": "error", "message": "Message too large"} @@ -646,7 +677,8 @@ async def handle_chunked_synthesis( data = json.loads(message["text"]) except json.JSONDecodeError: logger.warning( - f"Chunked synthesis: failed to parse JSON: {message.get('text', '')[:100]}" + "Chunked synthesis: failed to parse JSON: %s", + message.get("text", "")[:100], ) continue @@ -656,7 +688,7 @@ async def handle_chunked_synthesis( # Enforce per-text size limit if len(text) > WS_MAX_TTS_TEXT_LENGTH: logger.warning( - f"Chunked synthesis: text too long ({len(text)} chars)" + "Chunked synthesis: text too long (%s chars)", len(text) ) await websocket.send_json( {"type": "error", "message": "Text too long"} @@ -665,7 +697,9 @@ async def handle_chunked_synthesis( if text: text_buffer.append(text) logger.debug( - f"Chunked synthesis: buffered text ({len(text)} chars), total buffered: {len(text_buffer)} chunks" + "Chunked synthesis: buffered text (%s chars), total buffered: %s chunks", + len(text), + len(text_buffer), ) if isinstance(data.get("voice"), str) and data["voice"]: voice = data["voice"] @@ -682,7 +716,7 @@ async def handle_chunked_synthesis( chunk_count = 0 total_bytes = 0 logger.info( - f"Chunked synthesis: sending full text ({len(full_text)} chars)" + "Chunked synthesis: sending full text (%s chars)", len(full_text) ) async for audio_chunk in provider.synthesize_stream( full_text, voice=voice, speed=speed @@ -694,13 +728,15 @@ async def handle_chunked_synthesis( await websocket.send_bytes(audio_chunk) await websocket.send_json({"type": "audio_done"}) logger.info( - f"Chunked synthesis: sent audio_done after {chunk_count} chunks, {total_bytes} bytes" + "Chunked synthesis: sent audio_done after %s chunks, %s bytes", + chunk_count, + total_bytes, ) break except WebSocketDisconnect: logger.debug("Chunked synthesis: client disconnected") except Exception as e: - logger.error(f"Chunked synthesis: error: {e}", exc_info=True) + logger.error("Chunked synthesis: error: %s", e, exc_info=True) raise finally: logger.info("Chunked synthesis: handler finished") @@ -730,7 +766,7 @@ async def websocket_synthesize( await websocket.accept() logger.info("WebSocket synthesize: connection accepted") except Exception as e: - logger.error(f"WebSocket synthesize: failed to accept connection: {e}") + logger.error("WebSocket synthesize: failed to accept connection: %s", e) return streaming_synthesizer: StreamingSynthesizerProtocol | None = None @@ -765,16 +801,18 @@ async def websocket_synthesize( return logger.info( - f"WebSocket synthesize: creating voice provider: {provider_db.provider_type}" + "WebSocket synthesize: creating voice provider: %s", + provider_db.provider_type, ) try: provider = get_voice_provider(provider_db) logger.info( - f"WebSocket synthesize: voice provider created, streaming TTS supported: {provider.supports_streaming_tts()}" + "WebSocket synthesize: voice provider created, streaming TTS supported: %s", + provider.supports_streaming_tts(), ) except ValueError as e: logger.error( - f"WebSocket synthesize: failed to create voice provider: {e}" + "WebSocket synthesize: failed to create voice provider: %s", e ) await websocket.send_json({"type": "error", "message": str(e)}) return @@ -805,7 +843,8 @@ async def websocket_synthesize( await handle_streaming_synthesis(websocket, streaming_synthesizer) except Exception as e: logger.error( - f"WebSocket synthesize: failed to create streaming synthesizer: {e}" + "WebSocket synthesize: failed to create streaming synthesizer: %s", + e, ) if VOICE_DISABLE_STREAMING_FALLBACK: await websocket.send_json( @@ -836,7 +875,7 @@ async def websocket_synthesize( except WebSocketDisconnect: logger.debug("WebSocket synthesize: client disconnected") except Exception as e: - logger.error(f"WebSocket synthesize: unhandled error: {e}", exc_info=True) + logger.error("WebSocket synthesize: unhandled error: %s", e, exc_info=True) try: # Send generic error to avoid leaking sensitive details await websocket.send_json( diff --git a/backend/onyx/server/manage/web_search/api.py b/backend/onyx/server/manage/web_search/api.py index fe74747512..3b6f4dca78 100644 --- a/backend/onyx/server/manage/web_search/api.py +++ b/backend/onyx/server/manage/web_search/api.py @@ -449,6 +449,6 @@ def test_content_provider( ) from e logger.info( - f"Web content provider test succeeded for {request.provider_type.value}." + "Web content provider test succeeded for %s.", request.provider_type.value ) return {"status": "ok"} diff --git a/backend/onyx/server/metrics/indexing_pipeline.py b/backend/onyx/server/metrics/indexing_pipeline.py index 7f1fa57380..9183dc779a 100644 --- a/backend/onyx/server/metrics/indexing_pipeline.py +++ b/backend/onyx/server/metrics/indexing_pipeline.py @@ -125,12 +125,14 @@ class _CachedCollector(Collector): return result except concurrent.futures.TimeoutError: logger.warning( - f"{type(self).__name__}._collect_fresh() timed out after {self._collect_timeout}s, returning stale cache" + "%s._collect_fresh() timed out after %ss, returning stale cache", + type(self).__name__, + self._collect_timeout, ) return self._cached_result if self._cached_result is not None else [] except Exception: self._inflight = None - logger.exception(f"Error in {type(self).__name__}.collect()") + logger.exception("Error in %s.collect()", type(self).__name__) # Return stale cache on error rather than nothing — avoids # metrics disappearing during transient failures. return self._cached_result if self._cached_result is not None else [] diff --git a/backend/onyx/server/metrics/indexing_task_metrics.py b/backend/onyx/server/metrics/indexing_task_metrics.py index cf44be256d..1af3a0f770 100644 --- a/backend/onyx/server/metrics/indexing_task_metrics.py +++ b/backend/onyx/server/metrics/indexing_task_metrics.py @@ -160,7 +160,8 @@ def _resolve_connector(cc_pair_id: int, tenant_id: str) -> ConnectorInfo: return info except Exception: logger.debug( - f"Failed to resolve connector info for cc_pair_id={cc_pair_id}", + "Failed to resolve connector info for cc_pair_id=%s", + cc_pair_id, exc_info=True, ) return _UNKNOWN_CONNECTOR diff --git a/backend/onyx/server/metrics/metrics_server.py b/backend/onyx/server/metrics/metrics_server.py index 3c9f00beed..26dad6cbec 100644 --- a/backend/onyx/server/metrics/metrics_server.py +++ b/backend/onyx/server/metrics/metrics_server.py @@ -51,12 +51,12 @@ def start_metrics_server(worker_type: str) -> int | None: with _server_lock: if _server_started: - logger.debug(f"Metrics server already started for {worker_type}") + logger.debug("Metrics server already started for %s", worker_type) return None enabled = os.environ.get("PROMETHEUS_METRICS_ENABLED", "true").lower() if enabled in ("false", "0", "no"): - logger.info(f"Prometheus metrics server disabled for {worker_type}") + logger.info("Prometheus metrics server disabled for %s", worker_type) return None port_str = os.environ.get("PROMETHEUS_METRICS_PORT") @@ -65,16 +65,17 @@ def start_metrics_server(worker_type: str) -> int | None: port = int(port_str) except ValueError: logger.warning( - f"Invalid PROMETHEUS_METRICS_PORT '{port_str}' for {worker_type}, " - "must be a numeric port. Skipping metrics server." + "Invalid PROMETHEUS_METRICS_PORT '%s' for %s, must be a numeric port. Skipping metrics server.", + port_str, + worker_type, ) return None elif worker_type in _DEFAULT_PORTS: port = _DEFAULT_PORTS[worker_type] else: logger.info( - f"No default metrics port for worker type '{worker_type}' " - "and PROMETHEUS_METRICS_PORT not set. Skipping metrics server." + "No default metrics port for worker type '%s' and PROMETHEUS_METRICS_PORT not set. Skipping metrics server.", + worker_type, ) return None @@ -82,11 +83,11 @@ def start_metrics_server(worker_type: str) -> int | None: start_http_server(port) _server_started = True logger.info( - f"Prometheus metrics server started on :{port} for {worker_type}" + "Prometheus metrics server started on :%s for %s", port, worker_type ) return port except OSError as e: logger.warning( - f"Failed to start metrics server on :{port} for {worker_type}: {e}" + "Failed to start metrics server on :%s for %s: %s", port, worker_type, e ) return None diff --git a/backend/onyx/server/metrics/postgres_connection_pool.py b/backend/onyx/server/metrics/postgres_connection_pool.py index 69e6e0e94f..47b44f2257 100644 --- a/backend/onyx/server/metrics/postgres_connection_pool.py +++ b/backend/onyx/server/metrics/postgres_connection_pool.py @@ -240,12 +240,14 @@ def setup_postgres_connection_pool_metrics( pool = sync_engine.pool if not isinstance(pool, QueuePool): logger.info( - f"Skipping pool metrics for engine '{label}' ({type(pool).__name__} — no pool state)" + "Skipping pool metrics for engine '%s' (%s — no pool state)", + label, + type(pool).__name__, ) continue collector.add_pool(label, pool) _register_pool_events(sync_engine, label) - logger.info(f"Registered pool metrics for engine '{label}'") + logger.info("Registered pool metrics for engine '%s'", label) REGISTRY.register(collector) diff --git a/backend/onyx/server/middleware/latency_logging.py b/backend/onyx/server/middleware/latency_logging.py index ed269a545d..b061a370c8 100644 --- a/backend/onyx/server/middleware/latency_logging.py +++ b/backend/onyx/server/middleware/latency_logging.py @@ -17,7 +17,10 @@ def add_latency_logging_middleware(app: FastAPI, logger: logging.LoggerAdapter) response = await call_next(request) process_time = time.monotonic() - start_time logger.debug( - f"Path: {request.url.path} - Method: {request.method} - " - f"Status Code: {response.status_code} - Time: {process_time:.4f} secs" + "Path: %s - Method: %s - Status Code: %s - Time: %s secs", + request.url.path, + request.method, + response.status_code, + format(process_time, ".4f"), ) return response diff --git a/backend/onyx/server/pat/api.py b/backend/onyx/server/pat/api.py index ece3b06e5d..0b71851fac 100644 --- a/backend/onyx/server/pat/api.py +++ b/backend/onyx/server/pat/api.py @@ -59,7 +59,7 @@ def create_token( except ValueError as e: raise HTTPException(status_code=400, detail=str(e)) - logger.info(f"User {user.email} created PAT '{request.name}'") + logger.info("User %s created PAT '%s'", user.email, request.name) return CreatedTokenResponse( id=pat.id, @@ -85,5 +85,5 @@ def delete_token( status_code=404, detail="Token not found or not owned by user" ) - logger.info(f"User {user.email} revoked token {token_id}") + logger.info("User %s revoked token %s", user.email, token_id) return {"message": "Token deleted successfully"} diff --git a/backend/onyx/server/query_and_chat/chat_backend.py b/backend/onyx/server/query_and_chat/chat_backend.py index 18e49b1437..ee316bbaa1 100644 --- a/backend/onyx/server/query_and_chat/chat_backend.py +++ b/backend/onyx/server/query_and_chat/chat_backend.py @@ -561,7 +561,7 @@ def handle_send_chat_message( Returns: StreamingResponse | ChatFullResponse: Either streams or returns complete response. """ - logger.debug(f"Received new chat message: {chat_message_req.message}") + logger.debug("Received new chat message: %s", chat_message_req.message) tenant_id = get_current_tenant_id() mt_cloud_telemetry( diff --git a/backend/onyx/server/query_and_chat/query_backend.py b/backend/onyx/server/query_and_chat/query_backend.py index 5176c4c1e7..0c2ee12c3b 100644 --- a/backend/onyx/server/query_and_chat/query_backend.py +++ b/backend/onyx/server/query_and_chat/query_backend.py @@ -40,7 +40,7 @@ def admin_search( tenant_id = get_current_tenant_id() query = question.query - logger.notice(f"Received admin search query: {query}") + logger.notice("Received admin search query: %s", query) user_acl_filters = build_access_filters_for_user(user, db_session) final_filters = IndexFilters( diff --git a/backend/onyx/server/query_and_chat/session_loading.py b/backend/onyx/server/query_and_chat/session_loading.py index c803a4b508..5c090a3e59 100644 --- a/backend/onyx/server/query_and_chat/session_loading.py +++ b/backend/onyx/server/query_and_chat/session_loading.py @@ -727,7 +727,7 @@ def translate_assistant_message_to_packets( ) except Exception as e: - logger.warning(f"Error processing tool call {tool_call.id}: {e}") + logger.warning("Error processing tool call %s: %s", tool_call.id, e) continue if research_agent_count > 1: diff --git a/backend/onyx/server/saml.py b/backend/onyx/server/saml.py index 384ee538b0..db03b4f56b 100644 --- a/backend/onyx/server/saml.py +++ b/backend/onyx/server/saml.py @@ -59,7 +59,7 @@ async def upsert_saml_user(email: str) -> User: SAML users never use this password directly as they authenticate via their Identity Provider, but we need a valid password to satisfy system requirements. """ - logger.debug(f"Attempting to upsert SAML user with email: {email}") + logger.debug("Attempting to upsert SAML user with email: %s", email) get_user_db_context = contextlib.asynccontextmanager(get_user_db) get_user_manager_context = contextlib.asynccontextmanager(get_user_manager) diff --git a/backend/onyx/server/settings/store.py b/backend/onyx/server/settings/store.py index 4e1237b626..8ce4a76192 100644 --- a/backend/onyx/server/settings/store.py +++ b/backend/onyx/server/settings/store.py @@ -32,10 +32,10 @@ def load_settings() -> Settings: ) except KvKeyNotFoundError: # Default to empty settings if no settings have been set yet - logger.debug(f"No settings found in KV store for key: {KV_SETTINGS_KEY}") + logger.debug("No settings found in KV store for key: %s", KV_SETTINGS_KEY) settings = Settings() except Exception as e: - logger.error(f"Error loading settings from KV store: {str(e)}") + logger.error("Error loading settings from KV store: %s", str(e)) settings = Settings() cache = get_cache_backend() @@ -48,7 +48,7 @@ def load_settings() -> Settings: anonymous_user_enabled = False cache.set(OnyxRedisLocks.ANONYMOUS_USER_ENABLED, "0", ex=SETTINGS_TTL) except Exception as e: - logger.error(f"Error loading anonymous user setting from cache: {str(e)}") + logger.error("Error loading anonymous user setting from cache: %s", str(e)) anonymous_user_enabled = False settings.anonymous_user_enabled = anonymous_user_enabled diff --git a/backend/onyx/setup.py b/backend/onyx/setup.py index 321f7f7103..e94f0579a3 100644 --- a/backend/onyx/setup.py +++ b/backend/onyx/setup.py @@ -102,10 +102,10 @@ def setup_onyx( # Expire all old embedding models indexing attempts, technically redundant cancel_indexing_attempts_past_model(db_session) - logger.notice(f'Using Embedding model: "{search_settings.model_name}"') + logger.notice('Using Embedding model: "%s"', search_settings.model_name) if search_settings.query_prefix or search_settings.passage_prefix: - logger.notice(f'Query embedding prefix: "{search_settings.query_prefix}"') - logger.notice(f'Passage embedding prefix: "{search_settings.passage_prefix}"') + logger.notice('Query embedding prefix: "%s"', search_settings.query_prefix) + logger.notice('Passage embedding prefix: "%s"', search_settings.passage_prefix) # setup Postgres with default credential, llm providers, etc. setup_postgres(db_session) @@ -145,7 +145,9 @@ def setup_onyx( "Could not connect to a document index within the specified timeout." ) - logger.notice(f"Model Server: http://{MODEL_SERVER_HOST}:{MODEL_SERVER_PORT}") + logger.notice( + "Model Server: http://%s:%s", MODEL_SERVER_HOST, MODEL_SERVER_PORT + ) if search_settings.provider_type is None: # In integration tests, do not block API startup on warm-up warm_up_bi_encoder( @@ -165,7 +167,7 @@ def mark_reindex_flag(db_session: Session) -> None: kv_store = get_kv_store() try: value = kv_store.load(KV_REINDEX_KEY) - logger.debug(f"Re-indexing flag has value {value}") + logger.debug("Re-indexing flag has value %s", value) return except KvKeyNotFoundError: # Only need to update the flag if it hasn't been set @@ -200,7 +202,10 @@ def setup_document_indices( for x in range(num_attempts): try: logger.notice( - f"Setting up document index {document_index.__class__.__name__} (attempt {x + 1}/{num_attempts})..." + "Setting up document index %s (attempt %s/%s)...", + document_index.__class__.__name__, + x + 1, + num_attempts, ) document_index.ensure_indices_exist( primary_embedding_dim=index_setting.final_embedding_dim, @@ -218,22 +223,24 @@ def setup_document_indices( ) logger.notice( - f"Document index {document_index.__class__.__name__} setup complete." + "Document index %s setup complete.", + document_index.__class__.__name__, ) document_index_setup_success = True break except Exception: logger.exception( - f"Document index {document_index.__class__.__name__} setup did not succeed. " - "The relevant service may not be ready yet. " - f"Retrying in {WAIT_SECONDS} seconds." + "Document index %s setup did not succeed. The relevant service may not be ready yet. Retrying in %s seconds.", + document_index.__class__.__name__, + WAIT_SECONDS, ) time.sleep(WAIT_SECONDS) if not document_index_setup_success: logger.error( - f"Document index {document_index.__class__.__name__} setup did not succeed. " - f"Attempt limit reached. ({num_attempts})" + "Document index %s setup did not succeed. Attempt limit reached. (%s)", + document_index.__class__.__name__, + num_attempts, ) return False @@ -286,18 +293,18 @@ def setup_postgres(db_session: Session) -> None: def update_default_multipass_indexing(db_session: Session) -> None: docs_exist = check_docs_exist(db_session) connectors_exist = check_connectors_exist(db_session) - logger.debug(f"Docs exist: {docs_exist}, Connectors exist: {connectors_exist}") + logger.debug("Docs exist: %s, Connectors exist: %s", docs_exist, connectors_exist) if not docs_exist and not connectors_exist: logger.info( "No existing docs or connectors found. Checking GPU availability for multipass indexing." ) gpu_available = gpu_status_request(indexing=True) - logger.info(f"GPU available: {gpu_available}") + logger.info("GPU available: %s", gpu_available) current_settings = get_current_search_settings(db_session) - logger.notice(f"Updating multipass indexing setting to: {gpu_available}") + logger.notice("Updating multipass indexing setting to: %s", gpu_available) updated_settings = SavedSearchSettings.from_db_model(current_settings) # Enable multipass indexing if GPU is available or if using a cloud provider updated_settings.multipass_indexing = ( @@ -309,7 +316,7 @@ def update_default_multipass_indexing(db_session: Session) -> None: settings = load_settings() settings.gpu_enabled = gpu_available store_settings(settings) - logger.notice(f"Updated settings with GPU availability: {gpu_available}") + logger.notice("Updated settings with GPU availability: %s", gpu_available) else: logger.debug( @@ -342,7 +349,7 @@ def setup_vespa_multitenant(supported_indices: list[SupportedEmbeddingModel]) -> VESPA_ATTEMPTS = 5 for x in range(VESPA_ATTEMPTS): try: - logger.notice(f"Setting up Vespa (attempt {x + 1}/{VESPA_ATTEMPTS})...") + logger.notice("Setting up Vespa (attempt %s/%s)...", x + 1, VESPA_ATTEMPTS) VespaIndex.register_multitenant_indices( indices=[index.index_name for index in supported_indices] + [ @@ -362,11 +369,12 @@ def setup_vespa_multitenant(supported_indices: list[SupportedEmbeddingModel]) -> return True except Exception: logger.notice( - f"Vespa setup did not succeed. The Vespa service may not be ready yet. Retrying in {WAIT_SECONDS} seconds." + "Vespa setup did not succeed. The Vespa service may not be ready yet. Retrying in %s seconds.", + WAIT_SECONDS, ) time.sleep(WAIT_SECONDS) logger.error( - f"Vespa setup did not succeed. Attempt limit reached. ({VESPA_ATTEMPTS})" + "Vespa setup did not succeed. Attempt limit reached. (%s)", VESPA_ATTEMPTS ) return False diff --git a/backend/onyx/tools/fake_tools/research_agent.py b/backend/onyx/tools/fake_tools/research_agent.py index c729a8ac82..4ccf39f5f8 100644 --- a/backend/onyx/tools/fake_tools/research_agent.py +++ b/backend/onyx/tools/fake_tools/research_agent.py @@ -260,8 +260,9 @@ def run_research_agent_call( elapsed_seconds = time.monotonic() - start_time if elapsed_seconds > RESEARCH_AGENT_FORCE_REPORT_SECONDS: logger.info( - f"Research agent exceeded {RESEARCH_AGENT_FORCE_REPORT_SECONDS}s " - f"(elapsed: {elapsed_seconds:.1f}s), forcing intermediate report generation" + "Research agent exceeded %ss (elapsed: %ss), forcing intermediate report generation", + RESEARCH_AGENT_FORCE_REPORT_SECONDS, + format(elapsed_seconds, ".1f"), ) break @@ -608,7 +609,7 @@ def run_research_agent_call( ) except Exception as e: - logger.error(f"Error running research agent call: {e}") + logger.error("Error running research agent call: %s", e) emitter.emit( Packet( placement=Placement(turn_index=turn_index, tab_index=tab_index), @@ -633,7 +634,9 @@ def _on_research_agent_timeout( RESEARCH_AGENT_TASK_KEY, "unknown" ) logger.warning( - f"Research agent timed out after {RESEARCH_AGENT_TIMEOUT_SECONDS} seconds for task: {research_task}" + "Research agent timed out after %s seconds for task: %s", + RESEARCH_AGENT_TIMEOUT_SECONDS, + research_task, ) return ResearchAgentCallResult( intermediate_report=RESEARCH_AGENT_TIMEOUT_MESSAGE, @@ -762,9 +765,9 @@ if __name__ == "__main__": if tool.name != "generate_image" ] - logger.info(f"Running research agent with prompt: {RESEARCH_PROMPT}") - logger.info(f"LLM: {llm.config.model_provider}/{llm.config.model_name}") - logger.info(f"Tools: {[t.name for t in tools]}") + logger.info("Running research agent with prompt: %s", RESEARCH_PROMPT) + logger.info("LLM: %s/%s", llm.config.model_provider, llm.config.model_name) + logger.info("Tools: %s", [t.name for t in tools]) result = run_research_agent_call( research_agent_call=ToolCallKickoff( diff --git a/backend/onyx/tools/tool_constructor.py b/backend/onyx/tools/tool_constructor.py index ceb7086212..59be686270 100644 --- a/backend/onyx/tools/tool_constructor.py +++ b/backend/onyx/tools/tool_constructor.py @@ -160,7 +160,10 @@ def _construct_tools_impl( # Log which tools are attached to the persona for debugging persona_tool_names = [t.name for t in persona.tools] logger.debug( - f"Constructing tools for persona '{persona.name}' (id={persona.id}): {persona_tool_names}" + "Constructing tools for persona '%s' (id=%s): %s", + persona.name, + persona.id, + persona_tool_names, ) mcp_tool_cache: dict[int, dict[int, MCPTool]] = {} @@ -264,7 +267,7 @@ def _construct_tools_impl( WebSearchTool(tool_id=db_tool_model.id, emitter=emitter) ] except ValueError as e: - logger.error(f"Failed to initialize Internet Search Tool: {e}") + logger.error("Failed to initialize Internet Search Tool: %s", e) raise ValueError( "Internet search tool requires a search provider API key, please contact your Onyx admin to get it added!" ) @@ -281,7 +284,7 @@ def _construct_tools_impl( ) ] except RuntimeError as e: - logger.error(f"Failed to initialize Open URL Tool: {e}") + logger.error("Failed to initialize Open URL Tool: %s", e) raise ValueError( "Open URL tool requires a web content provider, please contact your Onyx admin to get it configured!" ) @@ -335,7 +338,7 @@ def _construct_tools_impl( if db_tool_model.oauth_config_id: if user.is_anonymous: logger.warning( - f"Anonymous user cannot use OAuth tool {db_tool_model.id}" + "Anonymous user cannot use OAuth tool %s", db_tool_model.id ) continue oauth_config = get_oauth_config( @@ -346,15 +349,17 @@ def _construct_tools_impl( oauth_token_for_tool = token_manager.get_valid_access_token() if not oauth_token_for_tool: logger.warning( - f"No valid OAuth token found for tool {db_tool_model.id} " - f"with OAuth config {db_tool_model.oauth_config_id}" + "No valid OAuth token found for tool %s with OAuth config %s", + db_tool_model.id, + db_tool_model.oauth_config_id, ) # Priority 2: Passthrough auth (user's login OAuth token) elif db_tool_model.passthrough_auth: if user.is_anonymous: logger.warning( - f"Anonymous user cannot use passthrough auth tool {db_tool_model.id}" + "Anonymous user cannot use passthrough auth tool %s", + db_tool_model.id, ) continue oauth_token_for_tool = user_oauth_token @@ -400,7 +405,8 @@ def _construct_tools_impl( # Pass-through OAuth: use the user's login OAuth token if user.is_anonymous: logger.warning( - f"Anonymous user cannot use PT_OAUTH MCP server {mcp_server.id}" + "Anonymous user cannot use PT_OAUTH MCP server %s", + mcp_server.id, ) continue mcp_user_oauth_token = user_oauth_token @@ -451,7 +457,9 @@ def _construct_tools_impl( tool_dict[saved_tool.id] = [cast(Tool, mcp_tool)] if db_tool_model.id not in tool_dict: logger.warning( - f"Tool '{expected_tool_name}' not found in MCP server '{mcp_server.name}'" + "Tool '%s' not found in MCP server '%s'", + expected_tool_name, + mcp_server.name, ) if ( diff --git a/backend/onyx/tools/tool_implementations/custom/custom_tool.py b/backend/onyx/tools/tool_implementations/custom/custom_tool.py index 97ef347a9f..f076ead9b9 100644 --- a/backend/onyx/tools/tool_implementations/custom/custom_tool.py +++ b/backend/onyx/tools/tool_implementations/custom/custom_tool.py @@ -79,9 +79,8 @@ class CustomTool(Tool[None]): ) if has_auth_header and self._user_oauth_token: logger.warning( - f"Tool '{self._name}' has both an Authorization " - "header and OAuth token set. This is likely a configuration " - "error as the OAuth token will override the custom header." + "Tool '%s' has both an Authorization header and OAuth token set. This is likely a configuration error as the OAuth token will override the custom header.", + self._name, ) if self._user_oauth_token: @@ -206,7 +205,9 @@ class CustomTool(Tool[None]): message=f"{self._name} action failed because of authentication error", ) logger.warning( - f"Auth error from custom tool '{self._name}': HTTP {response.status_code}" + "Auth error from custom tool '%s': HTTP %s", + self._name, + response.status_code, ) tool_result: Any @@ -235,14 +236,14 @@ class CustomTool(Tool[None]): data = tool_result except JSONDecodeError: logger.exception( - f"Failed to parse response as JSON for tool '{self._name}'" + "Failed to parse response as JSON for tool '%s'", self._name ) tool_result = response.text response_type = "text" data = tool_result logger.info( - f"Returning tool response for {self._name} with type {response_type}" + "Returning tool response for %s with type %s", self._name, response_type ) # Emit CustomToolDelta packet diff --git a/backend/onyx/tools/tool_implementations/images/image_generation_tool.py b/backend/onyx/tools/tool_implementations/images/image_generation_tool.py index cbf3d25be1..55239b5e4c 100644 --- a/backend/onyx/tools/tool_implementations/images/image_generation_tool.py +++ b/backend/onyx/tools/tool_implementations/images/image_generation_tool.py @@ -181,7 +181,7 @@ class ImageGenerationTool(Tool[None]): size = "1024x1792" else: size = "1024x1024" - logger.debug(f"Generating image with model: {self.model}, size: {size}") + logger.debug("Generating image with model: %s, size: %s", self.model, size) try: response = self.img_provider.generate_image( prompt=prompt, @@ -215,12 +215,12 @@ class ImageGenerationTool(Tool[None]): ) except requests.RequestException as e: - logger.error(f"Error fetching or converting image: {e}") + logger.error("Error fetching or converting image: %s", e) raise ToolExecutionException( "Failed to fetch or convert the generated image", emit_error_packet=True ) except Exception as e: - logger.debug(f"Error occurred during image generation: {e}") + logger.debug("Error occurred during image generation: %s", e) error_message = str(e) if "OpenAIException" in str(type(e)): diff --git a/backend/onyx/tools/tool_implementations/mcp/mcp_client.py b/backend/onyx/tools/tool_implementations/mcp/mcp_client.py index 49a940acc5..0d8fd34152 100644 --- a/backend/onyx/tools/tool_implementations/mcp/mcp_client.py +++ b/backend/onyx/tools/tool_implementations/mcp/mcp_client.py @@ -190,7 +190,7 @@ def _call_mcp_client_function_sync( try: return run_async_sync_no_cancel(run_client_function()) except Exception as e: - logger.error(f"Failed to call MCP client function: {e}") + logger.error("Failed to call MCP client function: %s", e) if isinstance(e, ExceptionGroup): original_exception = e saved_e = log_exception_group(e) @@ -288,12 +288,12 @@ async def _discover_mcp_tools(session: ClientSession) -> list[MCPLibTool]: t1 = time.time() init_result = await session.initialize() # sends JSON-RPC "initialize" - logger.info(f"Initialized with server: {init_result.serverInfo}") - logger.info(f"Initialized with server time: {time.time() - t1}") + logger.info("Initialized with server: %s", init_result.serverInfo) + logger.info("Initialized with server time: %s", time.time() - t1) # 2) tools/list t2 = time.time() tools_response = await session.list_tools() # sends JSON-RPC "tools/list" - logger.info(f"Listed tools with server time: {time.time() - t2}") + logger.info("Listed tools with server time: %s", time.time() - t2) return tools_response.tools diff --git a/backend/onyx/tools/tool_implementations/mcp/mcp_tool.py b/backend/onyx/tools/tool_implementations/mcp/mcp_tool.py index 168a30593a..1fb7f57cf7 100644 --- a/backend/onyx/tools/tool_implementations/mcp/mcp_tool.py +++ b/backend/onyx/tools/tool_implementations/mcp/mcp_tool.py @@ -153,7 +153,9 @@ class MCPTool(Tool[None]): ] if denylisted_provided: logger.warning( - f"MCP tool '{self._name}' received denylisted headers that were filtered: {denylisted_provided}" + "MCP tool '%s' received denylisted headers that were filtered: %s", + self._name, + denylisted_provided, ) # Priority 2: Base headers from connection config (DB) - overrides request @@ -187,7 +189,8 @@ class MCPTool(Tool[None]): f"using this tool." ) logger.warning( - f"Authentication required for MCP tool '{self._name}' but no credentials found" + "Authentication required for MCP tool '%s' but no credentials found", + self._name, ) error_result = {"error": auth_error_msg} @@ -224,9 +227,8 @@ class MCPTool(Tool[None]): ): if self.mcp_server.transport == MCPTransport.SSE: logger.warning( - f"MCP tool '{self._name}': OAuth token refresh is not supported " - f"for SSE transport — auth provider will be ignored. " - f"Re-authentication may be required after token expiry." + "MCP tool '%s': OAuth token refresh is not supported for SSE transport — auth provider will be ignored. Re-authentication may be required after token expiry.", + self._name, ) else: from onyx.server.features.mcp.api import make_oauth_provider @@ -252,7 +254,7 @@ class MCPTool(Tool[None]): auth=auth, ) - logger.info(f"MCP tool '{self._name}' executed successfully") + logger.info("MCP tool '%s' executed successfully", self._name) # Format the tool result for response tool_result_dict = {"tool_result": tool_result} @@ -281,7 +283,7 @@ class MCPTool(Tool[None]): except Exception as e: error_str = str(e).lower() - logger.error(f"Failed to execute MCP tool '{self._name}': {e}") + logger.error("Failed to execute MCP tool '%s': %s", self._name, e) # Check for authentication-related errors auth_error_indicators = [ diff --git a/backend/onyx/tools/tool_implementations/memory/memory_tool.py b/backend/onyx/tools/tool_implementations/memory/memory_tool.py index 0cac782ce1..0b8af0c6c9 100644 --- a/backend/onyx/tools/tool_implementations/memory/memory_tool.py +++ b/backend/onyx/tools/tool_implementations/memory/memory_tool.py @@ -136,7 +136,7 @@ class MemoryTool(Tool[MemoryToolOverrideKwargs]): user_role=override_kwargs.user_role, ) - logger.info(f"New memory to be added: {memory_text}") + logger.info("New memory to be added: %s", memory_text) operation: Literal["add", "update"] = ( "update" if index_to_replace is not None else "add" diff --git a/backend/onyx/tools/tool_implementations/open_url/firecrawl.py b/backend/onyx/tools/tool_implementations/open_url/firecrawl.py index ab034953e2..5ba06ca88e 100644 --- a/backend/onyx/tools/tool_implementations/open_url/firecrawl.py +++ b/backend/onyx/tools/tool_implementations/open_url/firecrawl.py @@ -156,7 +156,7 @@ class FirecrawlClient(WebContentProvider): published_date = None if not text: - logger.warning(f"Firecrawl returned empty content for url={url}") + logger.warning("Firecrawl returned empty content for url=%s", url) return ExtractedContentFields( text=text or "", diff --git a/backend/onyx/tools/tool_implementations/open_url/open_url_tool.py b/backend/onyx/tools/tool_implementations/open_url/open_url_tool.py index 5374a15fe1..d8a56433c2 100644 --- a/backend/onyx/tools/tool_implementations/open_url/open_url_tool.py +++ b/backend/onyx/tools/tool_implementations/open_url/open_url_tool.py @@ -483,7 +483,9 @@ class OpenURLTool(Tool[OpenURLToolOverrideKwargs]): if len(urls) > override_kwargs.max_urls: logger.warning( - f"OpenURL tool received {len(urls)} URLs, but the max is {override_kwargs.max_urls}." + "OpenURL tool received %s URLs, but the max is %s.", + len(urls), + override_kwargs.max_urls, ) urls = urls[: override_kwargs.max_urls] @@ -608,7 +610,7 @@ class OpenURLTool(Tool[OpenURLToolOverrideKwargs]): if failure_descriptions else "Failed to fetch content from the requested resources." ) - logger.warning(f"OpenURL tool failed: {failure_msg}") + logger.warning("OpenURL tool failed: %s", failure_msg) return ToolResponse(rich_response=None, llm_facing_response=failure_msg) for section in inference_sections: @@ -723,7 +725,9 @@ class OpenURLTool(Tool[OpenURLToolOverrideKwargs]): ) except Exception as exc: logger.warning( - f"Indexed retrieval failed for document IDs {document_ids}: {exc}", + "Indexed retrieval failed for document IDs %s: %s", + document_ids, + exc, exc_info=True, ) return IndexedRetrievalResult( diff --git a/backend/onyx/tools/tool_implementations/python/code_interpreter_client.py b/backend/onyx/tools/tool_implementations/python/code_interpreter_client.py index 2d177d8d46..bea086d483 100644 --- a/backend/onyx/tools/tool_implementations/python/code_interpreter_client.py +++ b/backend/onyx/tools/tool_implementations/python/code_interpreter_client.py @@ -138,7 +138,7 @@ class CodeInterpreterClient: response.raise_for_status() result = response.json().get("status") == "ok" except Exception as e: - logger.warning(f"Exception caught when checking health, e={e}") + logger.warning("Exception caught when checking health, e=%s", e) result = False _health_cache[self.base_url] = (time.monotonic(), result) @@ -223,7 +223,7 @@ class CodeInterpreterClient: if model_cls is not None: yield model_cls(**json.loads(data)) else: - logger.warning(f"Unknown SSE event type: {event_type}") + logger.warning("Unknown SSE event type: %s", event_type) event_type = None data_lines = [] elif line.startswith("event:"): @@ -233,7 +233,9 @@ class CodeInterpreterClient: if event_type is not None or data_lines: logger.warning( - f"SSE stream ended with incomplete event: event_type={event_type}, data_lines={data_lines}" + "SSE stream ended with incomplete event: event_type=%s, data_lines=%s", + event_type, + data_lines, ) def _batch_as_stream( diff --git a/backend/onyx/tools/tool_implementations/python/python_tool.py b/backend/onyx/tools/tool_implementations/python/python_tool.py index 2b9fe59f93..06603f1667 100644 --- a/backend/onyx/tools/tool_implementations/python/python_tool.py +++ b/backend/onyx/tools/tool_implementations/python/python_tool.py @@ -64,7 +64,7 @@ def _truncate_output(output: str, max_length: int, label: str = "output") -> str truncated += ( f"\n... [output truncated, {len(output) - max_length} characters omitted]" ) - logger.debug(f"Truncated {label}: {truncated}") + logger.debug("Truncated %s: %s", label, truncated) return truncated @@ -201,13 +201,13 @@ class PythonTool(Tool[PythonToolOverrideKwargs]): # Stage for execution files_to_stage.append({"path": file_name, "file_id": ci_file_id}) - logger.info(f"Staged file for Python execution: {file_name}") + logger.info("Staged file for Python execution: %s", file_name) except Exception as e: - logger.warning(f"Failed to stage file {file_name}: {e}") + logger.warning("Failed to stage file %s: %s", file_name, e) try: - logger.debug(f"Executing code: {code}") + logger.debug("Executing code: %s", code) # Execute code with streaming (falls back to batch if unavailable) stdout_parts: list[str] = [] @@ -300,7 +300,9 @@ class PythonTool(Tool[PythonToolOverrideKwargs]): except Exception as e: logger.error( - f"Failed to handle generated file {workspace_file.path}: {e}" + "Failed to handle generated file %s: %s", + workspace_file.path, + e, ) # Cleanup Code Interpreter files (generated files) @@ -309,7 +311,9 @@ class PythonTool(Tool[PythonToolOverrideKwargs]): client.delete_file(ci_file_id) except Exception as e: logger.error( - f"Failed to delete Code Interpreter generated file {ci_file_id}: {e}" + "Failed to delete Code Interpreter generated file %s: %s", + ci_file_id, + e, ) # Note: staged input files are intentionally not deleted here because @@ -348,7 +352,7 @@ class PythonTool(Tool[PythonToolOverrideKwargs]): ) except Exception as e: - logger.error(f"Python execution failed: {e}") + logger.error("Python execution failed: %s", e) error_msg = str(e) # Emit error delta diff --git a/backend/onyx/tools/tool_implementations/search/search_tool.py b/backend/onyx/tools/tool_implementations/search/search_tool.py index 069f93189b..19d839d176 100644 --- a/backend/onyx/tools/tool_implementations/search/search_tool.py +++ b/backend/onyx/tools/tool_implementations/search/search_tool.py @@ -212,7 +212,11 @@ def _trim_sections_by_tokens( break logger.debug( - f"Trimmed sections from {len(sections)} to {len(trimmed_sections)} ({total_tokens} tokens, budget: {max_tokens})" + "Trimmed sections from %s to %s (%s tokens, budget: %s)", + len(sections), + len(trimmed_sections), + total_tokens, + max_tokens, ) return trimmed_sections @@ -301,12 +305,13 @@ class SearchTool(Tool[SearchToolOverrideKwargs]): ): entities = mapping.federated_connector.config or {} found_slack_connector = True - logger.debug(f"Found Slack federated connector config: {entities}") + logger.debug("Found Slack federated connector config: %s", entities) break if not found_slack_connector: logger.debug( - f"Skipping Slack federated search: no Slack federated connector linked to document sets {document_set_names}" + "Skipping Slack federated search: no Slack federated connector linked to document sets %s", + document_set_names, ) return None, None, {} @@ -337,7 +342,7 @@ class SearchTool(Tool[SearchToolOverrideKwargs]): ) access_token = user_token or bot_token except Exception as e: - logger.warning(f"Could not fetch Slack bot tokens: {e}") + logger.warning("Could not fetch Slack bot tokens: %s", e) # Case 2: Web user with federated OAuth (if bot context didn't yield a token) if not access_token and self.user: @@ -361,7 +366,7 @@ class SearchTool(Tool[SearchToolOverrideKwargs]): access_token = slack_oauth_token.token.get_value(apply_mask=False) entities = slack_oauth_token.federated_connector.config or {} except Exception as e: - logger.warning(f"Could not fetch Slack OAuth token: {e}") + logger.warning("Could not fetch Slack OAuth token: %s", e) return access_token, bot_token, entities @@ -405,11 +410,11 @@ class SearchTool(Tool[SearchToolOverrideKwargs]): search_settings=search_settings, ) - logger.info(f"Slack federated search returned {len(chunks)} chunks") + logger.info("Slack federated search returned %s chunks", len(chunks)) return chunks except Exception as e: - logger.error(f"Slack federated search error: {e}", exc_info=True) + logger.error("Slack federated search error: %s", e, exc_info=True) return [] def _run_search_for_query( @@ -650,7 +655,8 @@ class SearchTool(Tool[SearchToolOverrideKwargs]): # End timing for query expansion/rephrase query_expansion_elapsed = time.time() - query_expansion_start_time logger.debug( - f"Search tool - Query expansion/rephrase took {query_expansion_elapsed:.3f} seconds" + "Search tool - Query expansion/rephrase took %s seconds", + format(query_expansion_elapsed, ".3f"), ) semantic_query = expansion_results[0] # str keyword_queries = ( @@ -704,7 +710,9 @@ class SearchTool(Tool[SearchToolOverrideKwargs]): seen_lower.add(query_lower) logger.debug( - f"All Queries (sorted by weight): {all_queries}, Keyword queries: {[q for q, _ in deduplicated_keyword_queries]}" + "All Queries (sorted by weight): %s, Keyword queries: %s", + all_queries, + [q for q, _ in deduplicated_keyword_queries], ) # Emit the queries early so the UI can display them immediately @@ -854,8 +862,9 @@ class SearchTool(Tool[SearchToolOverrideKwargs]): # End timing for LLM document selection document_selection_elapsed = time.time() - document_selection_start_time logger.debug( - f"Search tool - LLM picking documents took {document_selection_elapsed:.3f} seconds " - f"(selected {len(selected_sections)} sections)" + "Search tool - LLM picking documents took %s seconds (selected %s sections)", + format(document_selection_elapsed, ".3f"), + len(selected_sections), ) # Create a set of best document IDs for quick lookup @@ -896,7 +905,8 @@ class SearchTool(Tool[SearchToolOverrideKwargs]): return expanded_section if expanded_section is not None else section except Exception as e: logger.warning( - f"Error processing section context expansion: {e}. Using original section." + "Error processing section context expansion: %s. Using original section.", + e, ) return section @@ -924,8 +934,9 @@ class SearchTool(Tool[SearchToolOverrideKwargs]): # End timing for document expansion document_expansion_elapsed = time.time() - document_expansion_start_time logger.debug( - f"Search tool - Expansion of selected documents took {document_expansion_elapsed:.3f} seconds " - f"(expanded {len(expanded_sections)} sections)" + "Search tool - Expansion of selected documents took %s seconds (expanded %s sections)", + format(document_expansion_elapsed, ".3f"), + len(expanded_sections), ) if not expanded_sections: @@ -945,10 +956,11 @@ class SearchTool(Tool[SearchToolOverrideKwargs]): # End overall timing overall_elapsed = time.time() - overall_start_time logger.debug( - f"Search tool - Total execution time: {overall_elapsed:.3f} seconds " - f"(query expansion: {query_expansion_elapsed:.3f}s, " - f"document selection: {document_selection_elapsed:.3f}s, " - f"document expansion: {document_expansion_elapsed:.3f}s)" + "Search tool - Total execution time: %s seconds (query expansion: %ss, document selection: %ss, document expansion: %ss)", + format(overall_elapsed, ".3f"), + format(query_expansion_elapsed, ".3f"), + format(document_selection_elapsed, ".3f"), + format(document_expansion_elapsed, ".3f"), ) return ToolResponse( diff --git a/backend/onyx/tools/tool_implementations/search/search_utils.py b/backend/onyx/tools/tool_implementations/search/search_utils.py index 3670ccb46e..eb626a6e6c 100644 --- a/backend/onyx/tools/tool_implementations/search/search_utils.py +++ b/backend/onyx/tools/tool_implementations/search/search_utils.py @@ -178,7 +178,7 @@ def _retrieve_adjacent_chunks( # Sort by chunk_id to ensure correct order chunks_above.sort(key=lambda c: c.chunk_id) except Exception as e: - logger.warning(f"Failed to retrieve chunks above section: {e}") + logger.warning("Failed to retrieve chunks above section: %s", e) # Retrieve chunks below (if any) if num_chunks_below > 0: @@ -200,7 +200,7 @@ def _retrieve_adjacent_chunks( # Sort by chunk_id to ensure correct order chunks_below.sort(key=lambda c: c.chunk_id) except Exception as e: - logger.warning(f"Failed to retrieve chunks below section: {e}") + logger.warning("Failed to retrieve chunks below section: %s", e) return chunks_above, chunks_below @@ -415,21 +415,24 @@ def expand_section_with_context( if classification == ContextExpansionType.NOT_RELEVANT: # Filter out this section logger.debug( - f"LLM classified section as NOT_RELEVANT: {section.center_chunk.semantic_identifier}" + "LLM classified section as NOT_RELEVANT: %s", + section.center_chunk.semantic_identifier, ) return None elif classification == ContextExpansionType.MAIN_SECTION_ONLY: # Return original section unchanged logger.debug( - f"LLM classified section as MAIN_SECTION_ONLY: {section.center_chunk.semantic_identifier}" + "LLM classified section as MAIN_SECTION_ONLY: %s", + section.center_chunk.semantic_identifier, ) return section elif classification == ContextExpansionType.INCLUDE_ADJACENT_SECTIONS: # Use the 2 chunks we already retrieved for the prompt logger.debug( - f"LLM classified section as INCLUDE_ADJACENT_SECTIONS: {section.center_chunk.semantic_identifier}" + "LLM classified section as INCLUDE_ADJACENT_SECTIONS: %s", + section.center_chunk.semantic_identifier, ) all_chunks = chunks_above_for_prompt + section.chunks + chunks_below_for_prompt @@ -448,11 +451,13 @@ def expand_section_with_context( # Fetch 5 chunks above and below (optimal single retrieval) if expand_override: logger.debug( - f"Section marked for FULL_DOCUMENT expansion (override): {section.center_chunk.semantic_identifier}" + "Section marked for FULL_DOCUMENT expansion (override): %s", + section.center_chunk.semantic_identifier, ) else: logger.debug( - f"LLM classified section as FULL_DOCUMENT: {section.center_chunk.semantic_identifier}" + "LLM classified section as FULL_DOCUMENT: %s", + section.center_chunk.semantic_identifier, ) chunks_above_full, chunks_below_full = _retrieve_adjacent_chunks( @@ -467,7 +472,8 @@ def expand_section_with_context( if not all_chunks: logger.warning( - f"No chunks found for full document context expansion: {section.center_chunk.semantic_identifier}" + "No chunks found for full document context expansion: %s", + section.center_chunk.semantic_identifier, ) return section @@ -482,6 +488,7 @@ def expand_section_with_context( else: # Unknown classification - default to returning original section logger.warning( - f"Unknown context classification {classification}, returning original section" + "Unknown context classification %s, returning original section", + classification, ) return section diff --git a/backend/onyx/tools/tool_implementations/web_search/clients/brave_client.py b/backend/onyx/tools/tool_implementations/web_search/clients/brave_client.py index 34ae2bcf21..d377624d9d 100644 --- a/backend/onyx/tools/tool_implementations/web_search/clients/brave_client.py +++ b/backend/onyx/tools/tool_implementations/web_search/clients/brave_client.py @@ -42,7 +42,7 @@ class BraveClient(WebSearchProvider): "Accept": "application/json", "X-Subscription-Token": api_key, } - logger.debug(f"Count of results passed to BraveClient: {num_results}") + logger.debug("Count of results passed to BraveClient: %s", num_results) self._num_results = max(1, min(num_results, BRAVE_MAX_RESULTS_PER_REQUEST)) self._timeout_seconds = timeout_seconds self._country = _normalize_country(country) diff --git a/backend/onyx/tools/tool_implementations/web_search/clients/google_pse_client.py b/backend/onyx/tools/tool_implementations/web_search/clients/google_pse_client.py index a0f37d1b68..01e9b6d690 100644 --- a/backend/onyx/tools/tool_implementations/web_search/clients/google_pse_client.py +++ b/backend/onyx/tools/tool_implementations/web_search/clients/google_pse_client.py @@ -103,7 +103,9 @@ class GooglePSEClient(WebSearchProvider): ) except ValueError: logger.debug( - f"Failed to parse published_date '{published_str}' for link {link}" + "Failed to parse published_date '%s' for link %s", + published_str, + link, ) published_date = None diff --git a/backend/onyx/tools/tool_implementations/web_search/clients/searxng_client.py b/backend/onyx/tools/tool_implementations/web_search/clients/searxng_client.py index 1d0c32aadb..aec0374585 100644 --- a/backend/onyx/tools/tool_implementations/web_search/clients/searxng_client.py +++ b/backend/onyx/tools/tool_implementations/web_search/clients/searxng_client.py @@ -15,7 +15,7 @@ class SearXNGClient(WebSearchProvider): searxng_base_url: str, num_results: int = 10, ) -> None: - logger.debug(f"Initializing SearXNGClient with base URL: {searxng_base_url}") + logger.debug("Initializing SearXNGClient with base URL: %s", searxng_base_url) self._searxng_base_url = searxng_base_url self._num_results = num_results @@ -26,7 +26,7 @@ class SearXNGClient(WebSearchProvider): "format": "json", } logger.debug( - f"Searching with payload: {payload} to {self._searxng_base_url}/search" + "Searching with payload: %s to %s/search", payload, self._searxng_base_url ) response = requests.post( f"{self._searxng_base_url}/search", @@ -50,14 +50,17 @@ class SearXNGClient(WebSearchProvider): def test_connection(self) -> dict[str, str]: try: - logger.debug(f"Testing connection to {self._searxng_base_url}/config") + logger.debug("Testing connection to %s/config", self._searxng_base_url) response = requests.get(f"{self._searxng_base_url}/config") - logger.debug(f"Response: {response.status_code}, text: {response.text}") + logger.debug("Response: %s, text: %s", response.status_code, response.text) response.raise_for_status() except requests.HTTPError as e: status_code = e.response.status_code logger.debug( - f"HTTPError: status_code={status_code}, e.response={e.response.status_code if e.response else None}, error={e}" + "HTTPError: status_code=%s, e.response=%s, error=%s", + status_code, + e.response.status_code if e.response else None, + e, ) if status_code == 429: raise HTTPException( diff --git a/backend/onyx/tools/tool_implementations/web_search/web_search_tool.py b/backend/onyx/tools/tool_implementations/web_search/web_search_tool.py index 3edec229bd..c950d2f8c1 100644 --- a/backend/onyx/tools/tool_implementations/web_search/web_search_tool.py +++ b/backend/onyx/tools/tool_implementations/web_search/web_search_tool.py @@ -192,7 +192,7 @@ class WebSearchTool(Tool[WebSearchToolOverrideKwargs]): return (results, None) except Exception as e: error_msg = str(e) - logger.warning(f"Web search query '{query}' failed: {error_msg}") + logger.warning("Web search query '%s' failed: %s", query, error_msg) return (None, error_msg) def run( @@ -256,8 +256,10 @@ class WebSearchTool(Tool[WebSearchToolOverrideKwargs]): # Log partial failures but continue if we have at least one success if failed_queries and valid_results: logger.warning( - f"Web search partial failure: {len(failed_queries)}/{len(queries)} " - f"queries failed. Failed queries: {json.dumps(failed_queries)}" + "Web search partial failure: %s/%s queries failed. Failed queries: %s", + len(failed_queries), + len(queries), + json.dumps(failed_queries), ) # If all queries failed, raise ToolCallException with details diff --git a/backend/onyx/tools/tool_runner.py b/backend/onyx/tools/tool_runner.py index 74b605b9eb..4a306bdbc7 100644 --- a/backend/onyx/tools/tool_runner.py +++ b/backend/onyx/tools/tool_runner.py @@ -137,7 +137,7 @@ def _safe_run_single_tool( except ToolCallException as e: # ToolCallException is an expected error from tool execution # Use llm_facing_message which is specifically designed for LLM consumption - logger.error(f"Tool call error for {tool.name}: {e}") + logger.error("Tool call error for %s: %s", tool.name, e) tool_response = ToolResponse( rich_response=None, llm_facing_response=GENERIC_TOOL_ERROR_MESSAGE.format( @@ -160,7 +160,7 @@ def _safe_run_single_tool( ) except ToolExecutionException as e: # Unexpected error during tool execution - logger.error(f"Unexpected error running tool {tool.name}: {e}") + logger.error("Unexpected error running tool %s: %s", tool.name, e) tool_response = ToolResponse( rich_response=None, llm_facing_response=GENERIC_TOOL_ERROR_MESSAGE.format(error=str(e)), @@ -187,7 +187,7 @@ def _safe_run_single_tool( ) except Exception as e: # Unexpected error during tool execution - logger.error(f"Unexpected error running tool {tool.name}: {e}") + logger.error("Unexpected error running tool %s: %s", tool.name, e) tool_response = ToolResponse( rich_response=None, llm_facing_response=GENERIC_TOOL_ERROR_MESSAGE.format(error=str(e)), @@ -293,7 +293,7 @@ def run_tool_calls( filtered_tool_calls: list[ToolCallKickoff] = [] for tool_call in merged_tool_calls: if tool_call.tool_name not in tools_by_name: - logger.warning(f"Tool {tool_call.tool_name} not found in tools list") + logger.warning("Tool %s not found in tools list", tool_call.tool_name) continue filtered_tool_calls.append(tool_call) diff --git a/backend/onyx/tracing/framework/_error_tracing.py b/backend/onyx/tracing/framework/_error_tracing.py index 1dadadcc17..5bd90b0f2e 100644 --- a/backend/onyx/tracing/framework/_error_tracing.py +++ b/backend/onyx/tracing/framework/_error_tracing.py @@ -18,4 +18,4 @@ def attach_error_to_current_span(error: SpanError) -> None: if span: attach_error_to_span(span, error) else: - logger.warning(f"No span to add error {error} to") + logger.warning("No span to add error %s to", error) diff --git a/backend/onyx/tracing/framework/provider.py b/backend/onyx/tracing/framework/provider.py index 8b7c6f50d8..eb4a5e09c2 100644 --- a/backend/onyx/tracing/framework/provider.py +++ b/backend/onyx/tracing/framework/provider.py @@ -56,7 +56,9 @@ class SynchronousMultiTracingProcessor(TracingProcessor): processor.on_trace_start(trace) except Exception as e: logger.error( - f"Error in trace processor {processor} during on_trace_start: {e}" + "Error in trace processor %s during on_trace_start: %s", + processor, + e, ) def on_trace_end(self, trace: Trace) -> None: @@ -68,7 +70,7 @@ class SynchronousMultiTracingProcessor(TracingProcessor): processor.on_trace_end(trace) except Exception as e: logger.error( - f"Error in trace processor {processor} during on_trace_end: {e}" + "Error in trace processor %s during on_trace_end: %s", processor, e ) def on_span_start(self, span: Span[Any]) -> None: @@ -80,7 +82,7 @@ class SynchronousMultiTracingProcessor(TracingProcessor): processor.on_span_start(span) except Exception as e: logger.error( - f"Error in trace processor {processor} during on_span_start: {e}" + "Error in trace processor %s during on_span_start: %s", processor, e ) def on_span_end(self, span: Span[Any]) -> None: @@ -92,7 +94,7 @@ class SynchronousMultiTracingProcessor(TracingProcessor): processor.on_span_end(span) except Exception as e: logger.error( - f"Error in trace processor {processor} during on_span_end: {e}" + "Error in trace processor %s during on_span_end: %s", processor, e ) def shutdown(self) -> None: @@ -100,11 +102,11 @@ class SynchronousMultiTracingProcessor(TracingProcessor): Called when the application stops. """ for processor in self._processors: - logger.debug(f"Shutting down trace processor {processor}") + logger.debug("Shutting down trace processor %s", processor) try: processor.shutdown() except Exception as e: - logger.error(f"Error shutting down trace processor {processor}: {e}") + logger.error("Error shutting down trace processor %s: %s", processor, e) def force_flush(self) -> None: """ @@ -114,7 +116,7 @@ class SynchronousMultiTracingProcessor(TracingProcessor): try: processor.force_flush() except Exception as e: - logger.error(f"Error flushing trace processor {processor}: {e}") + logger.error("Error flushing trace processor %s: %s", processor, e) class TraceProvider(ABC): @@ -234,12 +236,12 @@ class DefaultTraceProvider(TraceProvider): Create a new trace. """ if disabled: - logger.debug(f"Tracing is disabled. Not creating trace {name}") + logger.debug("Tracing is disabled. Not creating trace %s", name) return NoOpTrace() trace_id = trace_id or self.gen_trace_id() - logger.debug(f"Creating trace {name} with id {trace_id}") + logger.debug("Creating trace %s with id %s", name, trace_id) return TraceImpl( name=name, @@ -260,7 +262,7 @@ class DefaultTraceProvider(TraceProvider): Create a new span. """ if disabled: - logger.debug(f"Tracing is disabled. Not creating span {span_data}") + logger.debug("Tracing is disabled. Not creating span %s", span_data) return NoOpSpan(span_data) trace_id: str @@ -280,7 +282,9 @@ class DefaultTraceProvider(TraceProvider): current_span, NoOpSpan ): logger.debug( - f"Parent {current_span} or {current_trace} is no-op, returning NoOpSpan" + "Parent %s or %s is no-op, returning NoOpSpan", + current_span, + current_trace, ) return NoOpSpan(span_data) @@ -289,13 +293,13 @@ class DefaultTraceProvider(TraceProvider): elif isinstance(parent, Trace): if isinstance(parent, NoOpTrace): - logger.debug(f"Parent {parent} is no-op, returning NoOpSpan") + logger.debug("Parent %s is no-op, returning NoOpSpan", parent) return NoOpSpan(span_data) trace_id = parent.trace_id parent_id = None elif isinstance(parent, Span): if isinstance(parent, NoOpSpan): - logger.debug(f"Parent {parent} is no-op, returning NoOpSpan") + logger.debug("Parent %s is no-op, returning NoOpSpan", parent) return NoOpSpan(span_data) parent_id = parent.span_id trace_id = parent.trace_id @@ -316,4 +320,4 @@ class DefaultTraceProvider(TraceProvider): logger.debug("Shutting down trace provider") self._multi_processor.shutdown() except Exception as e: - logger.error(f"Error shutting down trace provider: {e}") + logger.error("Error shutting down trace provider: %s", e) diff --git a/backend/onyx/tracing/langfuse_tracing_processor.py b/backend/onyx/tracing/langfuse_tracing_processor.py index 52629c4e1b..d62718df9e 100644 --- a/backend/onyx/tracing/langfuse_tracing_processor.py +++ b/backend/onyx/tracing/langfuse_tracing_processor.py @@ -81,7 +81,7 @@ class LangfuseTracingProcessor(TracingProcessor): return mask_sensitive_data(data) except Exception as e: - logger.warning(f"Failed to mask data: {e}") + logger.warning("Failed to mask data: %s", e) return data def _calculate_cost(self, data: GenerationSpanData) -> Optional[float]: @@ -105,7 +105,7 @@ class LangfuseTracingProcessor(TracingProcessor): # Convert cents to dollars for Langfuse return cost_cents / 100.0 except Exception as e: - logger.debug(f"Failed to calculate cost: {e}") + logger.debug("Failed to calculate cost: %s", e) return None def on_trace_start(self, trace: Trace) -> None: @@ -140,7 +140,7 @@ class LangfuseTracingProcessor(TracingProcessor): # Use trace_id as key for root span's ID (children with no parent_id will use this) self._langfuse_span_ids[trace.trace_id] = langfuse_span.id except Exception as e: - logger.error(f"Error starting Langfuse trace: {e}") + logger.error("Error starting Langfuse trace: %s", e) def on_trace_end(self, trace: Trace) -> None: """Called when a trace is finished.""" @@ -163,7 +163,7 @@ class LangfuseTracingProcessor(TracingProcessor): ) langfuse_span.end() except Exception as e: - logger.error(f"Error ending Langfuse trace: {e}") + logger.error("Error ending Langfuse trace: %s", e) def on_span_start(self, span: Span[SpanData]) -> None: """Called when a span is started. @@ -192,7 +192,8 @@ class LangfuseTracingProcessor(TracingProcessor): # If no trace ID found, we can't create a properly linked span if langfuse_trace_id is None: logger.warning( - f"No Langfuse trace ID found for span {span.span_id}, creating orphan" + "No Langfuse trace ID found for span %s, creating orphan", + span.span_id, ) # Fall back to creating an orphan span # In Langfuse SDK v3, use start_observation instead of start_span @@ -265,7 +266,7 @@ class LangfuseTracingProcessor(TracingProcessor): # Store Langfuse span ID for future children to reference self._langfuse_span_ids[span.span_id] = langfuse_span.id except Exception as e: - logger.error(f"Error starting Langfuse span: {e}") + logger.error("Error starting Langfuse span: %s", e) def on_span_end(self, span: Span[SpanData]) -> None: """Called when a span is finished.""" @@ -335,7 +336,7 @@ class LangfuseTracingProcessor(TracingProcessor): self._last_output[trace_id] = output_data except Exception as e: - logger.error(f"Error ending Langfuse span: {e}") + logger.error("Error ending Langfuse span: %s", e) def _get_generation_name(self, data: GenerationSpanData) -> str: """Get a descriptive name for a generation span.""" @@ -397,7 +398,7 @@ class LangfuseTracingProcessor(TracingProcessor): if client: client.flush() except Exception as e: - logger.warning(f"Failed to flush Langfuse client: {e}") + logger.warning("Failed to flush Langfuse client: %s", e) def shutdown(self) -> None: """Called when the application stops.""" @@ -407,4 +408,4 @@ class LangfuseTracingProcessor(TracingProcessor): if client: client.shutdown() except Exception as e: - logger.warning(f"Failed to shutdown Langfuse client: {e}") + logger.warning("Failed to shutdown Langfuse client: %s", e) diff --git a/backend/onyx/tracing/setup.py b/backend/onyx/tracing/setup.py index 6c87329956..3194fcd2a7 100644 --- a/backend/onyx/tracing/setup.py +++ b/backend/onyx/tracing/setup.py @@ -35,7 +35,7 @@ def setup_tracing() -> list[str]: _setup_braintrust() initialized_providers.append("braintrust") except Exception as e: - logger.error(f"Failed to initialize Braintrust tracing: {e}") + logger.error("Failed to initialize Braintrust tracing: %s", e) else: logger.info("Braintrust API key not provided, skipping Braintrust setup") @@ -45,7 +45,7 @@ def setup_tracing() -> list[str]: _setup_langfuse() initialized_providers.append("langfuse") except Exception as e: - logger.error(f"Failed to initialize Langfuse tracing: {e}") + logger.error("Failed to initialize Langfuse tracing: %s", e) else: logger.info("Langfuse credentials not provided, skipping Langfuse setup") @@ -53,7 +53,7 @@ def setup_tracing() -> list[str]: if initialized_providers: logger.notice( - f"Tracing initialized with providers: {', '.join(initialized_providers)}" + "Tracing initialized with providers: %s", ", ".join(initialized_providers) ) else: logger.info("No tracing providers configured") diff --git a/backend/onyx/utils/error_handling.py b/backend/onyx/utils/error_handling.py index 87bba28c56..0c9753eeac 100644 --- a/backend/onyx/utils/error_handling.py +++ b/backend/onyx/utils/error_handling.py @@ -19,6 +19,6 @@ def handle_connector_error(e: Exception, context: str) -> None: Raises: The original exception if CONTINUE_ON_CONNECTOR_FAILURE is False """ - logger.error(f"Error in {context}: {e}", exc_info=e) + logger.error("Error in %s: %s", context, e, exc_info=e) if not CONTINUE_ON_CONNECTOR_FAILURE: raise diff --git a/backend/onyx/utils/file.py b/backend/onyx/utils/file.py index f620770630..6ad06def14 100644 --- a/backend/onyx/utils/file.py +++ b/backend/onyx/utils/file.py @@ -27,10 +27,10 @@ class OnyxStaticFileManager: if matches: mime_type = cast(str, matches[0].mime_type) except (OSError, FileNotFoundError, PermissionError) as e: - logger.error(f"Failed to read file {filename}: {e}") + logger.error("Failed to read file %s: %s", filename, e) return None except Exception as e: - logger.error(f"Unexpected exception reading file {filename}: {e}") + logger.error("Unexpected exception reading file %s: %s", filename, e) return None return FileWithMimeType(data=file_content, mime_type=mime_type) diff --git a/backend/onyx/utils/gpu_utils.py b/backend/onyx/utils/gpu_utils.py index 10f42b5168..080d493ffc 100644 --- a/backend/onyx/utils/gpu_utils.py +++ b/backend/onyx/utils/gpu_utils.py @@ -31,7 +31,7 @@ def _get_gpu_status_from_model_server(indexing: bool) -> bool: gpu_status = response.json() return gpu_status["gpu_available"] except requests.RequestException as e: - logger.error(f"Error: Unable to fetch GPU status. Error: {str(e)}") + logger.error("Error: Unable to fetch GPU status. Error: %s", str(e)) raise # Re-raise exception to trigger a retry diff --git a/backend/onyx/utils/headers.py b/backend/onyx/utils/headers.py index 3e939daef8..3da83a12ff 100644 --- a/backend/onyx/utils/headers.py +++ b/backend/onyx/utils/headers.py @@ -22,7 +22,7 @@ def clean_header_list(headers_to_clean: list[HeaderItemDict]) -> dict[str, str]: value = item["value"] if key in cleaned_headers: logger.warning( - f"Duplicate header {key} found in custom headers, ignoring..." + "Duplicate header %s found in custom headers, ignoring...", key ) continue cleaned_headers[key] = value diff --git a/backend/onyx/utils/long_term_log.py b/backend/onyx/utils/long_term_log.py index c7d0435ea1..8b71c606c6 100644 --- a/backend/onyx/utils/long_term_log.py +++ b/backend/onyx/utils/long_term_log.py @@ -45,7 +45,7 @@ class LongTermLogger: # Delete oldest files that exceed the limit for file in files[self.max_files_per_category :]: if not file.is_file(): - logger.debug(f"File already deleted: {file}") + logger.debug("File already deleted: %s", file) continue try: file.unlink() diff --git a/backend/onyx/utils/sitemap.py b/backend/onyx/utils/sitemap.py index 38a11c4a96..435267bd9f 100644 --- a/backend/onyx/utils/sitemap.py +++ b/backend/onyx/utils/sitemap.py @@ -22,7 +22,7 @@ def _get_sitemap_locations_from_robots(base_url: str) -> Set[str]: sitemap_url = line.split(":", 1)[1].strip() sitemap_urls.add(sitemap_url) except Exception as e: - logger.warning(f"Error fetching robots.txt: {e}") + logger.warning("Error fetching robots.txt: %s", e) return sitemap_urls @@ -54,7 +54,7 @@ def _extract_urls_from_sitemap(sitemap_url: str) -> Set[str]: urls.add(url.text) except Exception as e: - logger.warning(f"Error processing sitemap {sitemap_url}: {e}") + logger.warning("Error processing sitemap %s: %s", sitemap_url, e) return urls diff --git a/backend/onyx/utils/supervisord_watchdog.py b/backend/onyx/utils/supervisord_watchdog.py index a9538de5a7..e1a4052bca 100644 --- a/backend/onyx/utils/supervisord_watchdog.py +++ b/backend/onyx/utils/supervisord_watchdog.py @@ -21,7 +21,7 @@ def main(key: str, program: str, conf: str) -> None: consecutive number of times and the last successful lookup is more than a threshold time, the specified program will be restarted. """ - logger.info(f"supervisord_watchdog starting: program={program} conf={conf}") + logger.info("supervisord_watchdog starting: program=%s conf=%s", program, conf) r = get_redis_client() @@ -39,13 +39,13 @@ def main(key: str, program: str, conf: str) -> None: heartbeat = r.exists(key) except Exception: logger.exception( - f"Exception checking for celery beat heartbeat: key={key}." + "Exception checking for celery beat heartbeat: key=%s.", key ) continue # happy path ... just continue if heartbeat: - logger.debug(f"Key lookup succeeded: key={key}") + logger.debug("Key lookup succeeded: key=%s", key) last_heartbeat = time.monotonic() num_lookup_failures = 0 continue @@ -54,9 +54,10 @@ def main(key: str, program: str, conf: str) -> None: num_lookup_failures += 1 if num_lookup_failures <= MAX_LOOKUP_FAILURES: logger.warning( - f"Key lookup failed: key={key} " - f"lookup_failures={num_lookup_failures} " - f"max_lookup_failures={MAX_LOOKUP_FAILURES}" + "Key lookup failed: key=%s lookup_failures=%s max_lookup_failures=%s", + key, + num_lookup_failures, + MAX_LOOKUP_FAILURES, ) continue @@ -64,22 +65,24 @@ def main(key: str, program: str, conf: str) -> None: elapsed = now - last_heartbeat if elapsed <= MAX_AGE_SECONDS: logger.warning( - f"Key lookup failed: key={key} " - f"lookup_failures={num_lookup_failures} " - f"max_lookup_failures={MAX_LOOKUP_FAILURES} " - f"elapsed={elapsed:.2f} " - f"elapsed_threshold={MAX_AGE_SECONDS}" + "Key lookup failed: key=%s lookup_failures=%s max_lookup_failures=%s elapsed=%s elapsed_threshold=%s", + key, + num_lookup_failures, + MAX_LOOKUP_FAILURES, + format(elapsed, ".2f"), + MAX_AGE_SECONDS, ) continue # all conditions have been exceeded ... restart the process logger.warning( - f"Key lookup failure thresholds exceeded - restarting {program}: " - f"key={key} " - f"lookup_failures={num_lookup_failures} " - f"max_lookup_failures={MAX_LOOKUP_FAILURES} " - f"elapsed={elapsed:.2f} " - f"elapsed_threshold={MAX_AGE_SECONDS}" + "Key lookup failure thresholds exceeded - restarting %s: key=%s lookup_failures=%s max_lookup_failures=%s elapsed=%s elapsed_threshold=%s", + program, + key, + num_lookup_failures, + MAX_LOOKUP_FAILURES, + format(elapsed, ".2f"), + MAX_AGE_SECONDS, ) subprocess.call(["supervisorctl", "-c", conf, "restart", program]) diff --git a/backend/onyx/utils/telemetry.py b/backend/onyx/utils/telemetry.py index 337bc9f6a1..7731dff6b1 100644 --- a/backend/onyx/utils/telemetry.py +++ b/backend/onyx/utils/telemetry.py @@ -162,7 +162,9 @@ def mt_cloud_telemetry( all_properties = {**properties} if properties else {} if properties and "tenant_id" in properties: logger.warning( - f"tenant_id already in properties: {properties}. Overwriting with new value {tenant_id}." + "tenant_id already in properties: %s. Overwriting with new value %s.", + properties, + tenant_id, ) all_properties["tenant_id"] = tenant_id diff --git a/backend/onyx/utils/threadpool_concurrency.py b/backend/onyx/utils/threadpool_concurrency.py index 745189a340..7c72a97967 100644 --- a/backend/onyx/utils/threadpool_concurrency.py +++ b/backend/onyx/utils/threadpool_concurrency.py @@ -347,7 +347,7 @@ def run_functions_tuples_in_parallel( try: results.append((index, future.result())) except Exception as e: - logger.exception(f"Function at index {index} failed due to {e}") + logger.exception("Function at index %s failed due to %s", index, e) results.append((index, None)) if not allow_failures: raise @@ -357,7 +357,7 @@ def run_functions_tuples_in_parallel( index = future_to_index[future] func, args = functions_with_args[index] logger.warning( - f"Function at index {index} timed out after {timeout} seconds" + "Function at index %s timed out after %s seconds", index, timeout ) if timeout_callback: @@ -378,7 +378,7 @@ def run_functions_tuples_in_parallel( try: results.append((index, future.result())) except Exception as e: - logger.exception(f"Function at index {index} failed due to {e}") + logger.exception("Function at index %s failed due to %s", index, e) results.append((index, None)) if not allow_failures: @@ -437,7 +437,7 @@ def run_functions_in_parallel( try: results[result_id] = future.result() except Exception as e: - logger.exception(f"Function with ID {result_id} failed due to {e}") + logger.exception("Function with ID %s failed due to %s", result_id, e) results[result_id] = None if not allow_failures: diff --git a/backend/onyx/utils/timing.py b/backend/onyx/utils/timing.py index 9a49dabc0c..fe32eb081a 100644 --- a/backend/onyx/utils/timing.py +++ b/backend/onyx/utils/timing.py @@ -106,7 +106,7 @@ def log_generator_function_time( finally: elapsed_time_str = f"{time.monotonic() - start_time:.3f}" log_name = func_name or func.__name__ - logger.info(f"{log_name} took {elapsed_time_str} seconds") + logger.info("%s took %s seconds", log_name, elapsed_time_str) if not print_only: optional_telemetry( record_type=RecordType.LATENCY, diff --git a/backend/onyx/utils/url.py b/backend/onyx/utils/url.py index 74ff3f1842..fd68cd2bd9 100644 --- a/backend/onyx/utils/url.py +++ b/backend/onyx/utils/url.py @@ -116,7 +116,7 @@ def _validate_and_resolve_url(url: str) -> tuple[str, str, int]: try: addr_info = socket.getaddrinfo(hostname, port) except socket.gaierror as e: - logger.warning(f"DNS resolution failed for hostname '{hostname}': {e}") + logger.warning("DNS resolution failed for hostname '%s': %s", hostname, e) raise SSRFException(f"Could not resolve hostname '{hostname}': {e}") if not addr_info: diff --git a/backend/onyx/utils/variable_functionality.py b/backend/onyx/utils/variable_functionality.py index f67210b363..2bb4a14977 100644 --- a/backend/onyx/utils/variable_functionality.py +++ b/backend/onyx/utils/variable_functionality.py @@ -192,7 +192,9 @@ def fetch_ee_implementation_or_noop( try: return fetch_versioned_implementation(module, attribute) except Exception as e: - logger.error(f"Failed to fetch implementation for {module}.{attribute}: {e}") + logger.error( + "Failed to fetch implementation for %s.%s: %s", module, attribute, e + ) raise diff --git a/backend/onyx/voice/providers/elevenlabs.py b/backend/onyx/voice/providers/elevenlabs.py index e3918404f4..962b40afc4 100644 --- a/backend/onyx/voice/providers/elevenlabs.py +++ b/backend/onyx/voice/providers/elevenlabs.py @@ -110,7 +110,7 @@ class ElevenLabsStreamingTranscriber(StreamingTranscriberProtocol): self._logger = setup_logger() self._logger.info( - f"ElevenLabsStreamingTranscriber: initializing with model {model}" + "ElevenLabsStreamingTranscriber: initializing with model %s", model ) self.api_key = api_key self.model = model @@ -149,8 +149,11 @@ class ElevenLabsStreamingTranscriber(StreamingTranscriberProtocol): f"&min_silence_duration_ms={DEFAULT_MIN_SILENCE_DURATION_MS}" ) self._logger.info( - f"ElevenLabsStreamingTranscriber: connecting to {url} " - f"(input={self.input_sample_rate}Hz, target={self.target_sample_rate}Hz)" + "ElevenLabsStreamingTranscriber: connecting to %s " + "(input=%sHz, target=%sHz)", + url, + self.input_sample_rate, + self.target_sample_rate, ) try: @@ -159,12 +162,14 @@ class ElevenLabsStreamingTranscriber(StreamingTranscriberProtocol): headers={"xi-api-key": self.api_key}, ) self._logger.info( - f"ElevenLabsStreamingTranscriber: connected successfully, " - f"ws.closed={self._ws.closed}, close_code={self._ws.close_code}" + "ElevenLabsStreamingTranscriber: connected successfully, " + "ws.closed=%s, close_code=%s", + self._ws.closed, + self._ws.close_code, ) except Exception as e: self._logger.error( - f"ElevenLabsStreamingTranscriber: failed to connect: {e}" + "ElevenLabsStreamingTranscriber: failed to connect: %s", e ) if self._session: await self._session.close() @@ -185,7 +190,7 @@ class ElevenLabsStreamingTranscriber(StreamingTranscriberProtocol): try: async for msg in self._ws: self._logger.debug( - f"ElevenLabsStreamingTranscriber: raw message type: {msg.type}" + "ElevenLabsStreamingTranscriber: raw message type: %s", msg.type ) if msg.type == aiohttp.WSMsgType.TEXT: parsed_data: Any = None @@ -194,7 +199,8 @@ class ElevenLabsStreamingTranscriber(StreamingTranscriberProtocol): parsed_data = json.loads(msg.data) except json.JSONDecodeError: self._logger.error( - f"ElevenLabsStreamingTranscriber: failed to parse JSON: {msg.data[:200]}" + "ElevenLabsStreamingTranscriber: failed to parse JSON: %s", + msg.data[:200], ) continue if not isinstance(parsed_data, dict): @@ -207,18 +213,21 @@ class ElevenLabsStreamingTranscriber(StreamingTranscriberProtocol): # ElevenLabs uses message_type field - fail fast if missing if "message_type" not in data and "type" not in data: self._logger.error( - f"ElevenLabsStreamingTranscriber: malformed packet missing 'message_type' field: {data}" + "ElevenLabsStreamingTranscriber: malformed packet missing 'message_type' field: %s", + data, ) continue msg_type = data.get("message_type", data.get("type", "")) self._logger.info( - f"ElevenLabsStreamingTranscriber: received message_type: '{msg_type}', data keys: {list(data.keys())}" + "ElevenLabsStreamingTranscriber: received message_type: '%s', data keys: %s", + msg_type, + list(data.keys()), ) # Check for error in various formats if "error" in data or msg_type == ElevenLabsSTTMessageType.ERROR: error_msg = data.get("error", data.get("message", data)) self._logger.error( - f"ElevenLabsStreamingTranscriber: API error: {error_msg}" + "ElevenLabsStreamingTranscriber: API error: %s", error_msg ) continue @@ -226,15 +235,18 @@ class ElevenLabsStreamingTranscriber(StreamingTranscriberProtocol): # See https://elevenlabs.io/docs/api-reference/speech-to-text/realtime if msg_type == ElevenLabsSTTMessageType.SESSION_STARTED: self._logger.info( - f"ElevenLabsStreamingTranscriber: session started, " - f"id={data.get('session_id')}, config={data.get('config')}" + "ElevenLabsStreamingTranscriber: session started, " + "id=%s, config=%s", + data.get("session_id"), + data.get("config"), ) elif msg_type == ElevenLabsSTTMessageType.PARTIAL_TRANSCRIPT: # Interim result — updated as more audio is processed text = data.get("text", "") if text: self._logger.info( - f"ElevenLabsStreamingTranscriber: partial_transcript: {text[:50]}..." + "ElevenLabsStreamingTranscriber: partial_transcript: %s...", + text[:50], ) self._final_transcript = text await self._transcript_queue.put( @@ -245,7 +257,8 @@ class ElevenLabsStreamingTranscriber(StreamingTranscriberProtocol): text = data.get("text", "") if text: self._logger.info( - f"ElevenLabsStreamingTranscriber: committed_transcript: {text[:50]}..." + "ElevenLabsStreamingTranscriber: committed_transcript: %s...", + text[:50], ) self._final_transcript = text await self._transcript_queue.put( @@ -256,7 +269,8 @@ class ElevenLabsStreamingTranscriber(StreamingTranscriberProtocol): text = data.get("text", "") or self._final_transcript if text: self._logger.info( - f"ElevenLabsStreamingTranscriber: utterance_end: {text[:50]}..." + "ElevenLabsStreamingTranscriber: utterance_end: %s...", + text[:50], ) self._final_transcript = text await self._transcript_queue.put( @@ -270,37 +284,46 @@ class ElevenLabsStreamingTranscriber(StreamingTranscriberProtocol): else: # Log unhandled message types with full data for debugging self._logger.warning( - f"ElevenLabsStreamingTranscriber: unhandled message_type: {msg_type}, full data: {data}" + "ElevenLabsStreamingTranscriber: unhandled message_type: %s, full data: %s", + msg_type, + data, ) elif msg.type == aiohttp.WSMsgType.BINARY: self._logger.debug( - f"ElevenLabsStreamingTranscriber: received binary message: {len(msg.data)} bytes" + "ElevenLabsStreamingTranscriber: received binary message: %s bytes", + len(msg.data), ) elif msg.type == aiohttp.WSMsgType.CLOSED: close_code = self._ws.close_code if self._ws else "N/A" self._logger.info( - f"ElevenLabsStreamingTranscriber: WebSocket closed by server, close_code={close_code}" + "ElevenLabsStreamingTranscriber: WebSocket closed by server, close_code=%s", + close_code, ) break elif msg.type == aiohttp.WSMsgType.ERROR: self._logger.error( - f"ElevenLabsStreamingTranscriber: WebSocket error: {self._ws.exception() if self._ws else 'N/A'}" + "ElevenLabsStreamingTranscriber: WebSocket error: %s", + self._ws.exception() if self._ws else "N/A", ) break elif msg.type == aiohttp.WSMsgType.CLOSE: self._logger.info( - f"ElevenLabsStreamingTranscriber: WebSocket CLOSE frame received, data={msg.data}, extra={msg.extra}" + "ElevenLabsStreamingTranscriber: WebSocket CLOSE frame received, data=%s, extra=%s", + msg.data, + msg.extra, ) break except Exception as e: self._logger.error( - f"ElevenLabsStreamingTranscriber: error in receive loop: {e}", + "ElevenLabsStreamingTranscriber: error in receive loop: %s", + e, exc_info=True, ) finally: close_code = self._ws.close_code if self._ws else "N/A" self._logger.info( - f"ElevenLabsStreamingTranscriber: receive loop ended, close_code={close_code}" + "ElevenLabsStreamingTranscriber: receive loop ended, close_code=%s", + close_code, ) await self._transcript_queue.put(None) # Signal end @@ -343,7 +366,7 @@ class ElevenLabsStreamingTranscriber(StreamingTranscriberProtocol): return if self._ws.closed: self._logger.warning( - f"send_audio: WebSocket is closed, close_code={self._ws.close_code}" + "send_audio: WebSocket is closed, close_code=%s", self._ws.close_code ) return @@ -358,12 +381,15 @@ class ElevenLabsStreamingTranscriber(StreamingTranscriberProtocol): "sample_rate": self.target_sample_rate, } self._logger.info( - f"send_audio: {len(chunk)} bytes -> {len(resampled)} bytes (resampled) -> {len(audio_b64)} chars base64" + "send_audio: %s bytes -> %s bytes (resampled) -> %s chars base64", + len(chunk), + len(resampled), + len(audio_b64), ) await self._ws.send_str(json.dumps(message)) self._logger.info("send_audio: message sent successfully") except Exception as e: - self._logger.error(f"send_audio: failed to send: {e}", exc_info=True) + self._logger.error("send_audio: failed to send: %s", e, exc_info=True) raise async def receive_transcript(self) -> TranscriptResult | None: @@ -387,7 +413,7 @@ class ElevenLabsStreamingTranscriber(StreamingTranscriberProtocol): ) await self._ws.close() except Exception as e: - self._logger.debug(f"Error closing WebSocket: {e}") + self._logger.debug("Error closing WebSocket: %s", e) if self._receive_task and not self._receive_task.done(): self._receive_task.cancel() try: @@ -504,8 +530,11 @@ class ElevenLabsStreamingSynthesizer(StreamingSynthesizerProtocol): # Check isFinal separately - a message can have both audio AND isFinal if "isFinal" in data: self._logger.info( - f"ElevenLabsStreamingSynthesizer: received isFinal={data['isFinal']}, " - f"chunks so far: {chunk_count}, bytes: {total_bytes}" + "ElevenLabsStreamingSynthesizer: received isFinal=%s, " + "chunks so far: %s, bytes: %s", + data["isFinal"], + chunk_count, + total_bytes, ) if data.get("isFinal"): self._logger.info( @@ -516,7 +545,7 @@ class ElevenLabsStreamingSynthesizer(StreamingSynthesizerProtocol): # Check for errors if "error" in data or data.get("type") == "error": self._logger.error( - f"ElevenLabsStreamingSynthesizer: received error: {data}" + "ElevenLabsStreamingSynthesizer: received error: %s", data ) elif msg.type == aiohttp.WSMsgType.BINARY: chunk_count += 1 @@ -527,14 +556,17 @@ class ElevenLabsStreamingSynthesizer(StreamingSynthesizerProtocol): aiohttp.WSMsgType.ERROR, ): self._logger.info( - f"ElevenLabsStreamingSynthesizer: WebSocket closed/error, type={msg.type}" + "ElevenLabsStreamingSynthesizer: WebSocket closed/error, type=%s", + msg.type, ) break except Exception as e: - self._logger.error(f"ElevenLabsStreamingSynthesizer receive error: {e}") + self._logger.error("ElevenLabsStreamingSynthesizer receive error: %s", e) finally: self._logger.info( - f"ElevenLabsStreamingSynthesizer: receive loop ended, {chunk_count} chunks, {total_bytes} bytes" + "ElevenLabsStreamingSynthesizer: receive loop ended, %s chunks, %s bytes", + chunk_count, + total_bytes, ) await self._audio_queue.put(None) # Signal end of stream @@ -550,7 +582,9 @@ class ElevenLabsStreamingSynthesizer(StreamingSynthesizerProtocol): """ if self._ws and not self._closed and text.strip(): self._logger.info( - f"ElevenLabsStreamingSynthesizer: sending text ({len(text)} chars): '{text}'" + "ElevenLabsStreamingSynthesizer: sending text (%s chars): '%s'", + len(text), + text, ) # Let ElevenLabs buffer and auto-generate based on chunk_length_schedule # Don't trigger generation here - wait for flush() at the end @@ -564,8 +598,11 @@ class ElevenLabsStreamingSynthesizer(StreamingSynthesizerProtocol): self._logger.info("ElevenLabsStreamingSynthesizer: text sent successfully") else: self._logger.warning( - f"ElevenLabsStreamingSynthesizer: skipping send_text - " - f"ws={self._ws is not None}, closed={self._closed}, text='{text[:30] if text else ''}'" + "ElevenLabsStreamingSynthesizer: skipping send_text - " + "ws=%s, closed=%s, text='%s'", + self._ws is not None, + self._closed, + text[:30] if text else "", ) async def receive_audio(self) -> bytes | None: @@ -588,7 +625,9 @@ class ElevenLabsStreamingSynthesizer(StreamingSynthesizerProtocol): self._logger.info("ElevenLabsStreamingSynthesizer: end-of-input sent") else: self._logger.warning( - f"ElevenLabsStreamingSynthesizer: skipping flush - ws={self._ws is not None}, closed={self._closed}" + "ElevenLabsStreamingSynthesizer: skipping flush - ws=%s, closed=%s", + self._ws is not None, + self._closed, ) async def close(self) -> None: @@ -691,7 +730,9 @@ class ElevenLabsVoiceProvider(VoiceProviderInterface): form_data.add_field("model_id", batch_model) logger.info( - f"ElevenLabs transcribe: sending {len(audio_data)} bytes, format={audio_format}" + "ElevenLabs transcribe: sending %s bytes, format=%s", + len(audio_data), + audio_format, ) with traced_llm_call( @@ -705,14 +746,14 @@ class ElevenLabsVoiceProvider(VoiceProviderInterface): ) as response: if response.status != 200: error_text = await response.text() - logger.error(f"ElevenLabs transcribe failed: {error_text}") + logger.error("ElevenLabs transcribe failed: %s", error_text) raise RuntimeError( f"ElevenLabs transcription failed: {error_text}" ) result = await response.json() text = result.get("text", "") - logger.info(f"ElevenLabs transcribe: got result: {text[:50]}...") + logger.info("ElevenLabs transcribe: got result: %s...", text[:50]) return text async def synthesize_stream( @@ -741,7 +782,11 @@ class ElevenLabsVoiceProvider(VoiceProviderInterface): url = f"{self.api_base}/v1/text-to-speech/{voice_id}/stream" logger.info( - f"ElevenLabs TTS: starting synthesis, text='{text[:50]}...', voice={voice_id}, model={self.tts_model}, speed={speed}" + "ElevenLabs TTS: starting synthesis, text='%s...', voice=%s, model=%s, speed=%s", + text[:50], + voice_id, + self.tts_model, + speed, ) headers = { @@ -769,11 +814,13 @@ class ElevenLabsVoiceProvider(VoiceProviderInterface): async with aiohttp.ClientSession() as session: async with session.post(url, headers=headers, json=payload) as response: logger.info( - f"ElevenLabs TTS: got response status={response.status}, content-type={response.headers.get('content-type')}" + "ElevenLabs TTS: got response status=%s, content-type=%s", + response.status, + response.headers.get("content-type"), ) if response.status != 200: error_text = await response.text() - logger.error(f"ElevenLabs TTS failed: {error_text}") + logger.error("ElevenLabs TTS failed: %s", error_text) raise RuntimeError(f"ElevenLabs TTS failed: {error_text}") # Use 8192 byte chunks for smoother streaming @@ -785,7 +832,9 @@ class ElevenLabsVoiceProvider(VoiceProviderInterface): total_bytes += len(chunk) yield chunk logger.info( - f"ElevenLabs TTS: streaming complete, {chunk_count} chunks, {total_bytes} total bytes" + "ElevenLabs TTS: streaming complete, %s chunks, %s total bytes", + chunk_count, + total_bytes, ) async def validate_credentials(self) -> None: diff --git a/backend/onyx/voice/providers/openai.py b/backend/onyx/voice/providers/openai.py index 214663df8a..6be02b2fee 100644 --- a/backend/onyx/voice/providers/openai.py +++ b/backend/onyx/voice/providers/openai.py @@ -73,7 +73,7 @@ class OpenAIStreamingTranscriber(StreamingTranscriberProtocol): self._logger = setup_logger() self._logger.info( - f"OpenAIStreamingTranscriber: initializing with model {model}" + "OpenAIStreamingTranscriber: initializing with model %s", model ) self.api_key = api_key self.model = model @@ -102,7 +102,7 @@ class OpenAIStreamingTranscriber(StreamingTranscriberProtocol): self._ws = await self._session.ws_connect(url, headers=headers) self._logger.info("Connected to OpenAI Realtime API") except Exception as e: - self._logger.error(f"Failed to connect to OpenAI Realtime API: {e}") + self._logger.error("Failed to connect to OpenAI Realtime API: %s", e) raise # Configure the session for transcription @@ -123,7 +123,7 @@ class OpenAIStreamingTranscriber(StreamingTranscriberProtocol): }, } await self._ws.send_str(json.dumps(config_message)) - self._logger.info(f"Sent config for model: {self.model} with server VAD") + self._logger.info("Sent config for model: %s with server VAD", self.model) # Start receiving transcripts self._receive_task = asyncio.create_task(self._receive_loop()) @@ -138,12 +138,12 @@ class OpenAIStreamingTranscriber(StreamingTranscriberProtocol): if msg.type == aiohttp.WSMsgType.TEXT: data = json.loads(msg.data) msg_type = data.get("type", "") - self._logger.debug(f"Received message type: {msg_type}") + self._logger.debug("Received message type: %s", msg_type) # Handle errors if msg_type == OpenAIRealtimeMessageType.ERROR: error = data.get("error", {}) - self._logger.error(f"OpenAI error: {error}") + self._logger.error("OpenAI error: %s", error) continue # Handle VAD events @@ -165,7 +165,7 @@ class OpenAIStreamingTranscriber(StreamingTranscriberProtocol): if msg_type == OpenAIRealtimeMessageType.TRANSCRIPTION_DELTA: delta = data.get("delta", "") if delta: - self._logger.info(f"OpenAI: Transcription delta: {delta}") + self._logger.info("OpenAI: Transcription delta: %s", delta) self._current_turn_transcript += delta # Show accumulated + current turn transcript full_transcript = self._accumulated_transcript @@ -179,7 +179,8 @@ class OpenAIStreamingTranscriber(StreamingTranscriberProtocol): transcript = data.get("transcript", "") if transcript: self._logger.info( - f"OpenAI: Transcription completed (VAD turn end): {transcript[:50]}..." + "OpenAI: Transcription completed (VAD turn end): %s...", + transcript[:50], ) # This is the final transcript for this VAD turn self._current_turn_transcript = transcript @@ -202,17 +203,17 @@ class OpenAIStreamingTranscriber(StreamingTranscriberProtocol): ): # Log any other message types we might be missing self._logger.info( - f"OpenAI: Unhandled message type '{msg_type}': {data}" + "OpenAI: Unhandled message type '%s': %s", msg_type, data ) elif msg.type == aiohttp.WSMsgType.ERROR: - self._logger.error(f"WebSocket error: {self._ws.exception()}") + self._logger.error("WebSocket error: %s", self._ws.exception()) break elif msg.type == aiohttp.WSMsgType.CLOSED: self._logger.info("WebSocket closed by server") break except Exception as e: - self._logger.error(f"Error in receive loop: {e}") + self._logger.error("Error in receive loop: %s", e) finally: await self._transcript_queue.put(None) @@ -224,8 +225,10 @@ class OpenAIStreamingTranscriber(StreamingTranscriberProtocol): # So chunk_bytes / 48000 = duration in seconds duration_ms = (len(chunk) / 48000) * 1000 self._logger.debug( - f"Sending {len(chunk)} bytes ({duration_ms:.1f}ms) of audio to OpenAI. " - f"First 10 bytes: {chunk[:10].hex() if len(chunk) >= 10 else chunk.hex()}" + "Sending %s bytes (%sms) of audio to OpenAI. " "First 10 bytes: %s", + len(chunk), + format(duration_ms, ".1f"), + chunk[:10].hex() if len(chunk) >= 10 else chunk.hex(), ) message = { "type": "input_audio_buffer.append", @@ -257,7 +260,7 @@ class OpenAIStreamingTranscriber(StreamingTranscriberProtocol): json.dumps({"type": "input_audio_buffer.commit"}) ) except Exception as e: - self._logger.debug(f"Error sending commit (may be expected): {e}") + self._logger.debug("Error sending commit (may be expected): %s", e) # Wait for *new* transcription to arrive (up to 5 seconds) self._logger.info("Waiting for transcription to complete...") @@ -266,7 +269,7 @@ class OpenAIStreamingTranscriber(StreamingTranscriberProtocol): await asyncio.sleep(0.1) if self._accumulated_transcript != transcript_before_commit: self._logger.info( - f"Got final transcript: {self._accumulated_transcript[:50]}..." + "Got final transcript: %s...", self._accumulated_transcript[:50] ) break else: @@ -387,7 +390,7 @@ class OpenAIStreamingSynthesizer(StreamingSynthesizerProtocol): except asyncio.CancelledError: break except Exception as e: - self._logger.error(f"Error processing text queue: {e}") + self._logger.error("Error processing text queue: %s", e) async def _synthesize_text(self, text: str) -> None: """Make HTTP TTS request and stream audio to queue.""" @@ -413,7 +416,7 @@ class OpenAIStreamingSynthesizer(StreamingSynthesizerProtocol): ) as response: if response.status != 200: error_text = await response.text() - self._logger.error(f"OpenAI TTS error: {error_text}") + self._logger.error("OpenAI TTS error: %s", error_text) return # Use 8192 byte chunks for smoother streaming @@ -424,7 +427,7 @@ class OpenAIStreamingSynthesizer(StreamingSynthesizerProtocol): if chunk: await self._audio_queue.put(chunk) except Exception as e: - self._logger.error(f"OpenAIStreamingSynthesizer synthesis error: {e}") + self._logger.error("OpenAIStreamingSynthesizer synthesis error: %s", e) async def send_text(self, text: str) -> None: """Queue text to be synthesized via HTTP streaming.""" diff --git a/backend/scripts/celery_purge_queue.py b/backend/scripts/celery_purge_queue.py index 0cc13c521d..207ccb80c6 100644 --- a/backend/scripts/celery_purge_queue.py +++ b/backend/scripts/celery_purge_queue.py @@ -47,7 +47,7 @@ def celery_purge_queue(queue: str, tenant_id: str) -> None: # noqa: ARG001 length = celery_get_queue_length(queue, r) - logger.info(f"queue={queue} length={length}") + logger.info("queue=%s length=%s", queue, length) # processed = 0 # deleted = 0 diff --git a/backend/scripts/chat_feedback_dump.py b/backend/scripts/chat_feedback_dump.py index 71a4ecba7c..87ad1f2117 100644 --- a/backend/scripts/chat_feedback_dump.py +++ b/backend/scripts/chat_feedback_dump.py @@ -190,13 +190,13 @@ def process_all_chat_feedback(onyx_url: str, api_key: str | None) -> None: if not all_users: raise RuntimeError("manage_users returned None") - logger.info(f"Accepted users: {len(all_users.accepted)}") + logger.info("Accepted users: %s", len(all_users.accepted)) user_ids: list[UUID] = [user.id for user in all_users.accepted] for user_id in user_ids: r_sessions = get_chat_sessions(onyx_url, headers, user_id) - logger.info(f"user={user_id} num_sessions={len(r_sessions.sessions)}") + logger.info("user=%s num_sessions=%s", user_id, len(r_sessions.sessions)) for session in r_sessions.sessions: s: ChatSessionSnapshot try: @@ -206,11 +206,12 @@ def process_all_chat_feedback(onyx_url: str, api_key: str | None) -> None: for m in s.messages: # ty: ignore[possibly-unresolved-reference] logger.info( - f"user={user_id} " - f"session={session.id} " - f"message={m.message} " - f"feedback_type={m.feedback_type} " - f"feedback_text={m.feedback_text}" + "user=%s session=%s message=%s feedback_type=%s feedback_text=%s", + user_id, + session.id, + m.message, + m.feedback_type, + m.feedback_text, ) diff --git a/backend/scripts/chat_loadtest.py b/backend/scripts/chat_loadtest.py index d669eef71f..25e29976e4 100644 --- a/backend/scripts/chat_loadtest.py +++ b/backend/scripts/chat_loadtest.py @@ -150,7 +150,7 @@ class ChatLoadTester: parent_message_id = metrics.total_tokens # Simplified for example except Exception as e: - logger.error(f"Error in chat session: {e}") + logger.error("Error in chat session: %s", e) async def run_load_test(self) -> None: """Run multiple concurrent chat sessions""" @@ -164,10 +164,10 @@ class ChatLoadTester: def print_results(self, total_time: float) -> None: """Print load test results and metrics""" logger.info("\n=== Load Test Results ===") - logger.info(f"Total Time: {total_time:.2f} seconds") - logger.info(f"Concurrent Sessions: {self.num_concurrent}") - logger.info(f"Messages per Session: {self.messages_per_session}") - logger.info(f"Total Messages: {len(self.metrics)}") + logger.info("Total Time: %s seconds", format(total_time, ".2f")) + logger.info("Concurrent Sessions: %s", self.num_concurrent) + logger.info("Messages per Session: %s", self.messages_per_session) + logger.info("Total Messages: %s", len(self.metrics)) if self.metrics: avg_response_time = statistics.mean(m.total_time for m in self.metrics) @@ -179,10 +179,17 @@ class ChatLoadTester: m.tokens_per_second for m in self.metrics ) - logger.info(f"\nAverage Response Time: {avg_response_time:.2f} seconds") - logger.info(f"Average Time to Documents: {avg_first_doc:.2f} seconds") - logger.info(f"Average Time to First Answer: {avg_first_answer:.2f} seconds") - logger.info(f"Average Tokens/Second: {avg_tokens_per_sec:.2f}") + logger.info( + "\nAverage Response Time: %s seconds", format(avg_response_time, ".2f") + ) + logger.info( + "Average Time to Documents: %s seconds", format(avg_first_doc, ".2f") + ) + logger.info( + "Average Time to First Answer: %s seconds", + format(avg_first_answer, ".2f"), + ) + logger.info("Average Tokens/Second: %s", format(avg_tokens_per_sec, ".2f")) def main() -> None: diff --git a/backend/scripts/debugging/onyx_db.py b/backend/scripts/debugging/onyx_db.py index 09158035a2..d34b32bccb 100644 --- a/backend/scripts/debugging/onyx_db.py +++ b/backend/scripts/debugging/onyx_db.py @@ -46,7 +46,7 @@ class SQLAlchemyDebugging: tenant_ids = get_all_tenant_ids() num_tenant_ids = len(tenant_ids) - logger.info(f"Found {num_tenant_ids} tenant id's.") + logger.info("Found %s tenant id's.", num_tenant_ids) num_processed = 0 for tenant_id in tenant_ids: @@ -81,9 +81,14 @@ class SQLAlchemyDebugging: total_chunks = total_chunks or 0 logger.info( - f"{num_processed} of {num_tenant_ids}: Tenant '{tenant_id}': " - f"first_email={first_email} user_count={user_count} " - f"docs={total_documents} chunks={total_chunks}" + "%s of %s: Tenant '%s': first_email=%s user_count=%s docs=%s chunks=%s", + num_processed, + num_tenant_ids, + tenant_id, + first_email, + user_count, + total_documents, + total_chunks, ) tenants_to_total_chunks[tenant_id] = TenantMetadata( @@ -93,7 +98,7 @@ class SQLAlchemyDebugging: num_chunks=total_chunks, ) except Exception as e: - logger.error(f"Error processing tenant '{tenant_id}': {e}") + logger.error("Error processing tenant '%s': %s", tenant_id, e) finally: CURRENT_TENANT_ID_CONTEXTVAR.reset(token) @@ -120,14 +125,14 @@ class SQLAlchemyDebugging: metadata.num_chunks, ] ) - logger.info(f"Successfully wrote statistics to {filename}") + logger.info("Successfully wrote statistics to %s", filename) # output top k by chunks top_k_tenants = heapq.nlargest( k, tenants_to_total_chunks.items(), key=lambda x: x[1].num_docs ) - logger.info(f"Top {k} tenants by total chunks: {top_k_tenants}") + logger.info("Top %s tenants by total chunks: %s", k, top_k_tenants) def main() -> None: @@ -150,7 +155,7 @@ def main() -> None: args = parser.parse_args() - logger.info(f"{args}") + logger.info("%s", args) connection_string = build_connection_string( db_api=SYNC_DB_API, diff --git a/backend/scripts/debugging/onyx_redis.py b/backend/scripts/debugging/onyx_redis.py index 7135a2abc6..21824c39a8 100644 --- a/backend/scripts/debugging/onyx_redis.py +++ b/backend/scripts/debugging/onyx_redis.py @@ -126,7 +126,7 @@ def onyx_redis( return 1 tenant_id = get_current_tenant_id() - logger.info(f"Purging locks associated with deleting cc_pair={cc_pair_id}.") + logger.info("Purging locks associated with deleting cc_pair=%s.", cc_pair_id) redis_connector = RedisConnector(tenant_id, cc_pair_id) redis_delete_if_exists_helper( @@ -181,12 +181,12 @@ def onyx_redis( if user_email not in current_invited_users: current_invited_users.append(user_email) if dry_run: - logger.info(f"(DRY-RUN) Would add {user_email} to invited users") + logger.info("(DRY-RUN) Would add %s to invited users", user_email) else: write_invited_users(current_invited_users) - logger.info(f"Added {user_email} to invited users") + logger.info("Added %s to invited users", user_email) else: - logger.info(f"{user_email} is already in the invited users list") + logger.info("%s is already in the invited users list", user_email) return 0 else: pass @@ -195,7 +195,7 @@ def onyx_redis( def flush_batch_delete(batch_keys: list[bytes], r: Redis) -> None: - logger.info(f"Flushing {len(batch_keys)} operations to Redis.") + logger.info("Flushing %s operations to Redis.", len(batch_keys)) with r.pipeline() as pipe: for batch_key in batch_keys: pipe.delete(batch_key) @@ -209,13 +209,13 @@ def redis_delete_if_exists_helper(key: str, dry_run: bool, r: Redis) -> bool: """ if not r.exists(key): - logger.info(f"Did not find {key}.") + logger.info("Did not find %s.", key) return False if dry_run: - logger.info(f"(DRY-RUN) Deleting {key}.") + logger.info("(DRY-RUN) Deleting %s.", key) else: - logger.info(f"Deleting {key}.") + logger.info("Deleting %s.", key) r.delete(key) return True @@ -229,7 +229,9 @@ def purge_by_match_and_type( """ logger.info( - f"purge_by_match_and_type start: match_pattern={match_pattern} match_type={match_type}" + "purge_by_match_and_type start: match_pattern=%s match_type=%s", + match_pattern, + match_type, ) # cursor = "0" @@ -252,10 +254,10 @@ def purge_by_match_and_type( count += 1 if dry_run: - logger.info(f"(DRY-RUN) Deleting item {count}: {key_str}") + logger.info("(DRY-RUN) Deleting item %s: %s", count, key_str) continue - logger.info(f"Deleting item {count}: {key_str}") + logger.info("Deleting item %s: %s", count, key_str) batch_keys.append(key) @@ -268,10 +270,10 @@ def purge_by_match_and_type( flush_batch_delete(batch_keys, r) batch_keys.clear() - logger.info(f"Deleted {count} matches.") + logger.info("Deleted %s matches.", count) elapsed = time.monotonic() - start - logger.info(f"Time elapsed: {elapsed:.2f}s") + logger.info("Time elapsed: %ss", format(elapsed, ".2f")) return 0 @@ -311,9 +313,9 @@ def get_user_token_from_redis(r: Redis, user_email: str) -> str | None: matching_key = key_str break except json.JSONDecodeError: - logger.error(f"Failed to decode JSON for key: {key_str}") + logger.error("Failed to decode JSON for key: %s", key_str) except Exception as e: - logger.error(f"Error processing JWT for key: {key_str}. Error: {str(e)}") + logger.error("Error processing JWT for key: %s. Error: %s", key_str, str(e)) if matching_key: return matching_key[len(REDIS_AUTH_KEY_PREFIX) :] @@ -357,19 +359,19 @@ def delete_user_token_from_redis( matching_key = key_str break except json.JSONDecodeError: - logger.error(f"Failed to decode JSON for key: {key_str}") + logger.error("Failed to decode JSON for key: %s", key_str) except Exception as e: - logger.error(f"Error processing JWT for key: {key_str}. Error: {str(e)}") + logger.error("Error processing JWT for key: %s. Error: %s", key_str, str(e)) if matching_key: if dry_run: - logger.info(f"(DRY-RUN) Would delete token key: {matching_key}") + logger.info("(DRY-RUN) Would delete token key: %s", matching_key) else: r.delete(matching_key) - logger.info(f"Deleted token for user: {user_email}") + logger.info("Deleted token for user: %s", user_email) return True else: - logger.info(f"No token found for user: {user_email}") + logger.info("No token found for user: %s", user_email) return False diff --git a/backend/scripts/debugging/onyx_vespa_schemas.py b/backend/scripts/debugging/onyx_vespa_schemas.py index bc765144f4..340a26f465 100644 --- a/backend/scripts/debugging/onyx_vespa_schemas.py +++ b/backend/scripts/debugging/onyx_vespa_schemas.py @@ -46,7 +46,7 @@ def write_schema( with open(index_filename, "w", encoding="utf-8") as f: f.write(schema) - logger.info(f"Wrote {index_filename}") + logger.info("Wrote %s", index_filename) def generate_document_entries() -> str: @@ -85,7 +85,7 @@ def write_cloud_services(cloud_services_template_path: str, output_path: Path) - with open(services_file, "w", encoding="utf-8") as f: f.write(services_xml) - logger.info(f"Wrote {services_file}") + logger.info("Wrote %s", services_file) def main() -> None: @@ -138,7 +138,7 @@ def main() -> None: ) num_indexes += 2 - logger.info(f"Wrote {num_indexes} indexes.") + logger.info("Wrote %s indexes.", num_indexes) # Generate cloud services configuration if template is provided if args.cloud_services_template: @@ -146,7 +146,7 @@ def main() -> None: write_cloud_services(args.cloud_services_template, output_path) else: logger.error( - f"Cloud services template not found: {args.cloud_services_template}" + "Cloud services template not found: %s", args.cloud_services_template ) diff --git a/backend/scripts/debugging/opensearch/opensearch_debug.py b/backend/scripts/debugging/opensearch/opensearch_debug.py index 30e83f4837..b7d148b769 100644 --- a/backend/scripts/debugging/opensearch/opensearch_debug.py +++ b/backend/scripts/debugging/opensearch/opensearch_debug.py @@ -18,6 +18,7 @@ Dependencies: backend/shared_configs/configs.py backend/onyx/document_index/opensearch/client.py """ + import argparse import json import os diff --git a/backend/scripts/force_delete_connector_by_id.py b/backend/scripts/force_delete_connector_by_id.py index 23e54638df..0987115fd2 100755 --- a/backend/scripts/force_delete_connector_by_id.py +++ b/backend/scripts/force_delete_connector_by_id.py @@ -138,8 +138,10 @@ def _unsafe_deletion( db_session.commit() logger.notice( - "Successfully deleted connector_credential_pair with connector_id:" - f" '{connector_id}' and credential_id: '{credential_id}'. Deleted {num_docs_deleted} docs." + "Successfully deleted connector_credential_pair with connector_id: '%s' and credential_id: '%s'. Deleted %s docs.", + connector_id, + credential_id, + num_docs_deleted, ) return num_docs_deleted @@ -151,7 +153,7 @@ def _delete_connector(cc_pair_id: int, db_session: Session) -> None: Are you SURE you want to continue? (enter 'Y' to continue): " ) if user_input != "Y": - logger.notice(f"You entered {user_input}. Exiting!") + logger.notice("You entered %s. Exiting!", user_input) return logger.notice("Getting connector credential pair") @@ -161,13 +163,13 @@ def _delete_connector(cc_pair_id: int, db_session: Session) -> None: ) if not cc_pair: - logger.error(f"Connector credential pair with ID {cc_pair_id} not found") + logger.error("Connector credential pair with ID %s not found", cc_pair_id) return if cc_pair.status == ConnectorCredentialPairStatus.ACTIVE: logger.error( - f"Connector {cc_pair.connector.name} is active, cannot continue. \ - Please navigate to the connector and pause before attempting again" + "Connector %s is active, cannot continue. Please navigate to the connector and pause before attempting again", + cc_pair.connector.name, ) return @@ -176,8 +178,9 @@ def _delete_connector(cc_pair_id: int, db_session: Session) -> None: if cc_pair is None: logger.error( - f"Connector with ID '{connector_id}' and credential ID " - f"'{credential_id}' does not exist. Has it already been deleted?", + "Connector with ID '%s' and credential ID '%s' does not exist. Has it already been deleted?", + connector_id, + credential_id, ) return @@ -194,8 +197,9 @@ def _delete_connector(cc_pair_id: int, db_session: Session) -> None: if not validated_cc_pair: logger.error( - f"Cannot run deletion attempt - connector_credential_pair with Connector ID: " - f"{connector_id} and Credential ID: {credential_id} does not exist." + "Cannot run deletion attempt - connector_credential_pair with Connector ID: %s and Credential ID: %s does not exist.", + connector_id, + credential_id, ) file_ids: list[str] = ( @@ -219,16 +223,16 @@ def _delete_connector(cc_pair_id: int, db_session: Session) -> None: cc_pair=cc_pair, pair_id=cc_pair_id, ) - logger.notice(f"Deleted {files_deleted_count} files!") + logger.notice("Deleted %s files!", files_deleted_count) except Exception as e: - logger.error(f"Failed to delete connector due to {e}") + logger.error("Failed to delete connector due to %s", e) if file_ids: logger.notice("Deleting stored files!") file_store = get_default_file_store() for file_id in file_ids: - logger.notice(f"Deleting file {file_id}") + logger.notice("Deleting file %s", file_id) file_store.delete_file(file_id) db_session.commit() diff --git a/backend/scripts/reset_indexes.py b/backend/scripts/reset_indexes.py index af01b02d6e..81694794b7 100644 --- a/backend/scripts/reset_indexes.py +++ b/backend/scripts/reset_indexes.py @@ -36,7 +36,7 @@ def wipe_vespa_index() -> bool: response.raise_for_status() response_json = response.json() - logger.info(f"Response: {response_json}") + logger.info("Response: %s", response_json) continuation = response_json.get("continuation") should_continue = bool(continuation) @@ -46,7 +46,7 @@ def wipe_vespa_index() -> bool: logger.exception("Request failed") sleep(2**attempt) # Exponential backoff else: - logger.error(f"Max retries ({RETRIES}) exceeded. Exiting.") + logger.error("Max retries (%s) exceeded. Exiting.", RETRIES) return False return True diff --git a/backend/scripts/save_load_state.py b/backend/scripts/save_load_state.py index ace45ab040..e3255408c1 100644 --- a/backend/scripts/save_load_state.py +++ b/backend/scripts/save_load_state.py @@ -40,7 +40,7 @@ def load_postgres(filename: str, container_name: str) -> None: alembic_cfg = Config("alembic.ini") command.upgrade(alembic_cfg, "head") except Exception as e: - logger.error(f"Alembic upgrade failed: {e}") + logger.error("Alembic upgrade failed: %s", e) host_file_path = os.path.abspath(filename) diff --git a/backend/tests/external_dependency_unit/file_store/test_file_store_non_mocked.py b/backend/tests/external_dependency_unit/file_store/test_file_store_non_mocked.py index ef539c3fe3..d9ca248051 100644 --- a/backend/tests/external_dependency_unit/file_store/test_file_store_non_mocked.py +++ b/backend/tests/external_dependency_unit/file_store/test_file_store_non_mocked.py @@ -129,7 +129,9 @@ def file_store( # Initialize the store and ensure bucket exists store.initialize() logger.info( - f"Successfully initialized {backend_config['backend_name']} file store with bucket {TEST_BUCKET_NAME}" + "Successfully initialized %s file store with bucket %s", + backend_config["backend_name"], + TEST_BUCKET_NAME, ) yield store @@ -151,10 +153,12 @@ def file_store( Delete={"Objects": objects_to_delete}, ) logger.info( - f"Cleaned up {len(objects_to_delete)} test objects from {backend_config['backend_name']}" + "Cleaned up %s test objects from %s", + len(objects_to_delete), + backend_config["backend_name"], ) except Exception as e: - logger.warning(f"Failed to cleanup test objects: {e}") + logger.warning("Failed to cleanup test objects: %s", e) class TestS3BackedFileStore: diff --git a/backend/tests/integration/common_utils/document_acl.py b/backend/tests/integration/common_utils/document_acl.py index 861d2de11c..6aca1b57ea 100644 --- a/backend/tests/integration/common_utils/document_acl.py +++ b/backend/tests/integration/common_utils/document_acl.py @@ -67,27 +67,29 @@ def get_user_document_access_via_acl( # Get the actual User object from the database user = fetch_user_by_id(db_session, UUID(test_user.id)) if not user: - logger.error(f"Could not find user with ID {test_user.id}") + logger.error("Could not find user with ID %s", test_user.id) return [] user_acl = get_user_acl(user, db_session) - logger.info(f"User {user.email} ACL entries: {user_acl}") + logger.info("User %s ACL entries: %s", user.email, user_acl) # Get document access information doc_access_map = _get_access_for_documents(document_ids, db_session) - logger.info(f"Found access info for {len(doc_access_map)} documents") + logger.info("Found access info for %s documents", len(doc_access_map)) accessible_docs = [] for doc_id, doc_access in doc_access_map.items(): doc_acl = doc_access.to_acl() - logger.info(f"Document {doc_id} ACL: {doc_acl}") + logger.info("Document %s ACL: %s", doc_id, doc_acl) # Check if user has any matching ACL entry if user_acl.intersection(doc_acl): accessible_docs.append(doc_id) - logger.info(f"User {user.email} has access to document {doc_id}") + logger.info("User %s has access to document %s", user.email, doc_id) else: - logger.info(f"User {user.email} does NOT have access to document {doc_id}") + logger.info( + "User %s does NOT have access to document %s", user.email, doc_id + ) return accessible_docs @@ -113,7 +115,7 @@ def get_all_connector_documents( result = db_session.execute(stmt) document_ids = [row[0] for row in result.fetchall()] logger.info( - f"Found {len(document_ids)} documents for connector {cc_pair.connector_id}" + "Found %s documents for connector %s", len(document_ids), cc_pair.connector_id ) return document_ids diff --git a/backend/tests/integration/common_utils/reset.py b/backend/tests/integration/common_utils/reset.py index f294d012be..51ff106fce 100644 --- a/backend/tests/integration/common_utils/reset.py +++ b/backend/tests/integration/common_utils/reset.py @@ -162,7 +162,9 @@ def drop_multitenant_postgres( TIMEOUT = 40 success = False for _ in range(NUM_TRIES): - logger.info(f"drop_multitenant_postgres_task starting... ({_ + 1}/{NUM_TRIES})") + logger.info( + "drop_multitenant_postgres_task starting... (%s/%s)", _ + 1, NUM_TRIES + ) try: run_with_timeout_multiproc( drop_multitenant_postgres_task, @@ -175,11 +177,15 @@ def drop_multitenant_postgres( break except TimeoutError: logger.warning( - f"drop_multitenant_postgres_task timed out, retrying... ({_ + 1}/{NUM_TRIES})" + "drop_multitenant_postgres_task timed out, retrying... (%s/%s)", + _ + 1, + NUM_TRIES, ) except RuntimeError: logger.warning( - f"drop_multitenant_postgres_task exceptioned, retrying... ({_ + 1}/{NUM_TRIES})" + "drop_multitenant_postgres_task exceptioned, retrying... (%s/%s)", + _ + 1, + NUM_TRIES, ) if not success: @@ -258,7 +264,7 @@ def reset_postgres( TIMEOUT = 40 success = False for _ in range(NUM_TRIES): - logger.info(f"Downgrading Postgres... ({_ + 1}/{NUM_TRIES})") + logger.info("Downgrading Postgres... (%s/%s)", _ + 1, NUM_TRIES) try: run_with_timeout_multiproc( downgrade_postgres, @@ -274,11 +280,11 @@ def reset_postgres( break except TimeoutError: logger.warning( - f"Postgres downgrade timed out, retrying... ({_ + 1}/{NUM_TRIES})" + "Postgres downgrade timed out, retrying... (%s/%s)", _ + 1, NUM_TRIES ) except RuntimeError: logger.warning( - f"Postgres downgrade exceptioned, retrying... ({_ + 1}/{NUM_TRIES})" + "Postgres downgrade exceptioned, retrying... (%s/%s)", _ + 1, NUM_TRIES ) if not success: diff --git a/backend/tests/integration/connector_job_tests/github/test_github_permission_sync.py b/backend/tests/integration/connector_job_tests/github/test_github_permission_sync.py index 99ff973d62..7eb5042395 100644 --- a/backend/tests/integration/connector_job_tests/github/test_github_permission_sync.py +++ b/backend/tests/integration/connector_job_tests/github/test_github_permission_sync.py @@ -54,7 +54,7 @@ def test_github_private_repo_permission_sync( pytest.fail(f"Failed to change repository {repo_owner}/{repo_name} to private") # Add test-team to repository at the start - logger.info(f"Adding test-team to repository {repo_owner}/{repo_name}") + logger.info("Adding test-team to repository %s/%s", repo_owner, repo_name) team_added = github_manager.add_team_to_repository( repo_owner=repo_owner, repo_name=repo_name, @@ -64,7 +64,7 @@ def test_github_private_repo_permission_sync( if not team_added: logger.warning( - f"Failed to add test-team to repository {repo_owner}/{repo_name}" + "Failed to add test-team to repository %s/%s", repo_owner, repo_name ) try: @@ -104,10 +104,10 @@ def test_github_private_repo_permission_sync( ) logger.info( - f"test_user_1 has access to {len(accessible_docs_user1)} documents" + "test_user_1 has access to %s documents", len(accessible_docs_user1) ) logger.info( - f"test_user_2 has access to {len(accessible_docs_user2)} documents" + "test_user_2 has access to %s documents", len(accessible_docs_user2) ) # test_user_1 (part of test-team) should have access @@ -125,14 +125,16 @@ def test_github_private_repo_permission_sync( finally: # Remove test-team from repository at the end - logger.info(f"Removing test-team from repository {repo_owner}/{repo_name}") + logger.info("Removing test-team from repository %s/%s", repo_owner, repo_name) team_removed = github_manager.remove_team_from_repository( repo_owner=repo_owner, repo_name=repo_name, team_slug="test-team" ) if not team_removed: logger.warning( - f"Failed to remove test-team from repository {repo_owner}/{repo_name}" + "Failed to remove test-team from repository %s/%s", + repo_owner, + repo_name, ) @@ -167,7 +169,7 @@ def test_github_public_repo_permission_sync( repo_name = github_connector.connector_specific_config["repositories"] # Change repository to public - logger.info(f"Changing repository {repo_owner}/{repo_name} to public") + logger.info("Changing repository %s/%s to public", repo_owner, repo_name) success = github_manager.change_repository_visibility( repo_owner=repo_owner, repo_name=repo_name, visibility="public" ) @@ -179,7 +181,9 @@ def test_github_public_repo_permission_sync( current_visibility = github_manager.get_repository_visibility( repo_owner=repo_owner, repo_name=repo_name ) - logger.info(f"Repository {repo_owner}/{repo_name} visibility: {current_visibility}") + logger.info( + "Repository %s/%s visibility: %s", repo_owner, repo_name, current_visibility + ) assert ( current_visibility == "public" ), f"Repository should be public, but is {current_visibility}" @@ -219,8 +223,12 @@ def test_github_public_repo_permission_sync( db_session=db_session, ) - logger.info(f"test_user_1 has access to {len(accessible_docs_user1)} documents") - logger.info(f"test_user_2 has access to {len(accessible_docs_user2)} documents") + logger.info( + "test_user_1 has access to %s documents", len(accessible_docs_user1) + ) + logger.info( + "test_user_2 has access to %s documents", len(accessible_docs_user2) + ) # Both users should have access to the public repository documents assert len(accessible_docs_user1) > 0, ( @@ -273,7 +281,7 @@ def test_github_internal_repo_permission_sync( repo_name = github_connector.connector_specific_config["repositories"] # Change repository to internal - logger.info(f"Changing repository {repo_owner}/{repo_name} to internal") + logger.info("Changing repository %s/%s to internal", repo_owner, repo_name) success = github_manager.change_repository_visibility( repo_owner=repo_owner, repo_name=repo_name, visibility="internal" ) @@ -285,7 +293,9 @@ def test_github_internal_repo_permission_sync( current_visibility = github_manager.get_repository_visibility( repo_owner=repo_owner, repo_name=repo_name ) - logger.info(f"Repository {repo_owner}/{repo_name} visibility: {current_visibility}") + logger.info( + "Repository %s/%s visibility: %s", repo_owner, repo_name, current_visibility + ) assert ( current_visibility == "internal" ), f"Repository should be internal, but is {current_visibility}" @@ -325,8 +335,12 @@ def test_github_internal_repo_permission_sync( db_session=db_session, ) - logger.info(f"test_user_1 has access to {len(accessible_docs_user1)} documents") - logger.info(f"test_user_2 has access to {len(accessible_docs_user2)} documents") + logger.info( + "test_user_1 has access to %s documents", len(accessible_docs_user1) + ) + logger.info( + "test_user_2 has access to %s documents", len(accessible_docs_user2) + ) # For internal repositories: # - test_user_1 should have access (assuming they're part of the organization) diff --git a/backend/tests/integration/connector_job_tests/github/utils.py b/backend/tests/integration/connector_job_tests/github/utils.py index bd675333ca..de4bcff28d 100644 --- a/backend/tests/integration/connector_job_tests/github/utils.py +++ b/backend/tests/integration/connector_job_tests/github/utils.py @@ -52,7 +52,7 @@ class GitHubManager: # Check if we have admin permissions if not repo.permissions.admin: logger.error( - f"No admin permissions for repository {repo_owner}/{repo_name}" + "No admin permissions for repository %s/%s", repo_owner, repo_name ) return False @@ -60,16 +60,19 @@ class GitHubManager: try: repo.edit(visibility=visibility) except GithubException as e: - logger.warning(f"Could not set repository to {visibility}: {e}") + logger.warning("Could not set repository to %s: %s", visibility, e) return False logger.info( - f"Successfully changed {repo_owner}/{repo_name} visibility to {visibility}" + "Successfully changed %s/%s visibility to %s", + repo_owner, + repo_name, + visibility, ) return True except GithubException as e: - logger.error(f"Failed to change repository visibility: {e}") + logger.error("Failed to change repository visibility: %s", e) return False def add_team_to_repository( @@ -108,12 +111,16 @@ class GitHubManager: team.set_repo_permission(repo, permission) logger.info( - f"Successfully added team {team_slug} to {repo_owner}/{repo_name} with {permission} permissions" + "Successfully added team %s to %s/%s with %s permissions", + team_slug, + repo_owner, + repo_name, + permission, ) return True except GithubException as e: - logger.error(f"Failed to add team to repository: {e}") + logger.error("Failed to add team to repository: %s", e) return False def remove_team_from_repository( @@ -142,12 +149,15 @@ class GitHubManager: team.remove_from_repos(repo) logger.info( - f"Successfully removed team {team_slug} from {repo_owner}/{repo_name}" + "Successfully removed team %s from %s/%s", + team_slug, + repo_owner, + repo_name, ) return True except GithubException as e: - logger.error(f"Failed to remove team from repository: {e}") + logger.error("Failed to remove team from repository: %s", e) return False def get_repository_visibility( @@ -173,5 +183,5 @@ class GitHubManager: return "private" if repo.private else "public" except GithubException as e: - logger.error(f"Failed to get repository visibility: {e}") + logger.error("Failed to get repository visibility: %s", e) return None diff --git a/backend/tests/integration/connector_job_tests/sharepoint/test_sharepoint_permissions.py b/backend/tests/integration/connector_job_tests/sharepoint/test_sharepoint_permissions.py index 6478904599..00196cd88a 100644 --- a/backend/tests/integration/connector_job_tests/sharepoint/test_sharepoint_permissions.py +++ b/backend/tests/integration/connector_job_tests/sharepoint/test_sharepoint_permissions.py @@ -49,8 +49,8 @@ def test_public_documents_accessible_by_all_users( db_session=db_session, ) - logger.info(f"User 1 has access to {len(accessible_docs_user1)} documents") - logger.info(f"User 2 has access to {len(accessible_docs_user2)} documents") + logger.info("User 1 has access to %s documents", len(accessible_docs_user1)) + logger.info("User 2 has access to %s documents", len(accessible_docs_user2)) # For public documents, both users should have access to at least some docs assert len(accessible_docs_user1) == 8, ( @@ -106,8 +106,8 @@ def test_group_based_permissions( db_session=db_session, ) - logger.info(f"User 1 has access to {len(accessible_docs_user1)} documents") - logger.info(f"User 2 has access to {len(accessible_docs_user2)} documents") + logger.info("User 1 has access to %s documents", len(accessible_docs_user1)) + logger.info("User 2 has access to %s documents", len(accessible_docs_user2)) public_docs = get_documents_by_permission_type(all_document_ids, db_session) diff --git a/backend/tests/unit/onyx/auth/test_verify_auth_setting.py b/backend/tests/unit/onyx/auth/test_verify_auth_setting.py index a5eaead6da..25082eed82 100644 --- a/backend/tests/unit/onyx/auth/test_verify_auth_setting.py +++ b/backend/tests/unit/onyx/auth/test_verify_auth_setting.py @@ -51,4 +51,4 @@ def test_verify_auth_setting_valid_auth_types( verify_auth_setting() mock_logger.warning.assert_not_called() - mock_logger.notice.assert_called_once_with(f"Using Auth Type: {auth_type.value}") + mock_logger.notice.assert_called_once_with("Using Auth Type: %s", auth_type.value) diff --git a/backend/tests/unit/onyx/connectors/salesforce/test_salesforce_sqlite.py b/backend/tests/unit/onyx/connectors/salesforce/test_salesforce_sqlite.py index cbde6bdb31..cc5534e619 100644 --- a/backend/tests/unit/onyx/connectors/salesforce/test_salesforce_sqlite.py +++ b/backend/tests/unit/onyx/connectors/salesforce/test_salesforce_sqlite.py @@ -863,22 +863,27 @@ def test_salesforce_bulk_retrieve() -> None: # Count data rows num_data_rows = sum(1 for _ in reader) logger.info( - f"Counted {num_data_rows} data rows in {filename}" + "Counted %s data rows in %s", num_data_rows, filename ) total_data_rows += num_data_rows except StopIteration: # Handle empty file or file with only header logger.info( - f"File {filename} is empty or contains only a header." + "File %s is empty or contains only a header.", filename ) except Exception as e: - logger.error(f"Error reading or counting rows in {filename}: {e}") + logger.error( + "Error reading or counting rows in %s: %s", filename, e + ) logger.info( - f"Found {len(csv_files_found)} CSV files for {object_type} in {temp_dir}." + "Found %s CSV files for %s in %s.", + len(csv_files_found), + object_type, + temp_dir, ) logger.info( - f"Total data rows across all CSVs for {object_type}: {total_data_rows}" + "Total data rows across all CSVs for %s: %s", object_type, total_data_rows ) assert total_data_rows > 1100 and total_data_rows < 1200 @@ -1003,10 +1008,12 @@ def test_salesforce_connector_single() -> None: # parent_reference_fields_by_type: dict[str, dict[str, list[str]]] = {} # Step 1 - make a list of all the types to download (parent + direct child + USER_OBJECT_TYPE) - logger.info(f"Parent object types: num={len(parent_types)} list={parent_types}") + logger.info("Parent object types: num=%s list=%s", len(parent_types), parent_types) for parent_type_working in parent_types: child_types_working = sf_client.get_children_of_sf_type(parent_type_working) - logger.debug(f"Found {len(child_types)} child types for {parent_type_working}") + logger.debug( + "Found %s child types for %s", len(child_types), parent_type_working + ) for child_type, child_relationship in child_types_working.items(): # onyx_sf_type = OnyxSalesforceType(child_type, sf_client) @@ -1032,7 +1039,10 @@ def test_salesforce_connector_single() -> None: child_types.update(list(child_types_working.keys())) logger.info( - f"Child object types: parent={parent_type_working} num={len(child_types_working)} list={child_types_working.keys()}" + "Child object types: parent=%s num=%s list=%s", + parent_type_working, + len(child_types_working), + child_types_working.keys(), ) # queryable_fields_attachment = _get_all_queryable_fields_of_sf_type(sf_client, "Attachment") @@ -1117,7 +1127,7 @@ def test_salesforce_connector_single() -> None: result = sf_client.query(query) print(f"{result=}") except Exception: - logger.exception(f"Query failed: {query=}") + logger.exception("Query failed: query=%r", query) for child_relationship in child_relationships_batch: relationship_status[child_relationship] = False else: @@ -1150,7 +1160,7 @@ def test_salesforce_connector_single() -> None: result = sf_client.query(query) print(f"{result=}") except Exception: - logger.exception(f"Query failed: {query=}") + logger.exception("Query failed: query=%r", query) for child_relationship in child_relationships_batch: relationship_status[child_relationship] = False else: diff --git a/backend/tests/utils/aws_secrets.py b/backend/tests/utils/aws_secrets.py index a889d953e8..7b1b162187 100644 --- a/backend/tests/utils/aws_secrets.py +++ b/backend/tests/utils/aws_secrets.py @@ -124,7 +124,7 @@ def _get_aws_secrets( secrets[enum_type(key_name)] = secret_value except ValueError: logger.warning( - f"Secret '{key_name}' not in {enum_type.__name__}, skipping" + "Secret '%s' not in %s, skipping", key_name, enum_type.__name__ ) for error in response.get("Errors", []): @@ -132,7 +132,7 @@ def _get_aws_secrets( error_code = error.get("ErrorCode", "unknown") message = error.get("Message", "unknown error") logger.warning( - f"Failed to fetch secret '{secret_id}': [{error_code}] {message}" + "Failed to fetch secret '%s': [%s] %s", secret_id, error_code, message ) return secrets @@ -165,15 +165,17 @@ def get_secrets( if secrets: local_names = ", ".join(k.value for k in secrets) - logger.info(f"Resolved {len(secrets)} secret(s) locally: {local_names}") + logger.info("Resolved %s secret(s) locally: %s", len(secrets), local_names) remaining: list[AnySecret] = [k for k in keys if k not in secrets] if remaining: aws_secrets = _get_aws_secrets(remaining, enum_type) secrets.update(aws_secrets) logger.info( - f"Fetched {len(aws_secrets)}/{len(remaining)} secret(s) from AWS " - f"(prefix: {enum_type.aws_prefix()!r})" + "Fetched %s/%s secret(s) from AWS (prefix: %r)", + len(aws_secrets), + len(remaining), + enum_type.aws_prefix(), ) return cast("dict[TestSecret, str] | dict[DeploySecret, str]", secrets) diff --git a/pyproject.toml b/pyproject.toml index c45329dedd..bc4820a682 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -234,6 +234,7 @@ select = [ "ARG", "E", "F", + "G004", "I", "S324", "W",