databricks-tellr-app 0.3.13.dev1__tar.gz → 0.3.13.dev2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/PKG-INFO +1 -1
- {databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/databricks_tellr_app.egg-info/PKG-INFO +1 -1
- {databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/databricks_tellr_app.egg-info/SOURCES.txt +4 -0
- {databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/pyproject.toml +1 -1
- {databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/api/main.py +18 -0
- databricks_tellr_app-0.3.13.dev2/src/api/middleware/csrf.py +109 -0
- databricks_tellr_app-0.3.13.dev2/src/api/middleware/security_headers.py +97 -0
- {databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/api/services/chat_service.py +9 -70
- {databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/README.md +0 -0
- {databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/databricks_tellr_app/__init__.py +0 -0
- {databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/databricks_tellr_app/run.py +0 -0
- {databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/databricks_tellr_app.egg-info/dependency_links.txt +0 -0
- {databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/databricks_tellr_app.egg-info/requires.txt +0 -0
- {databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/databricks_tellr_app.egg-info/top_level.txt +0 -0
- {databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/setup.cfg +0 -0
- {databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/setup.py +0 -0
- {databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/__init__.py +0 -0
- {databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/api/__init__.py +0 -0
- {databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/api/_otel_bootstrap.py +0 -0
- {databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/api/fixtures/tour_demo_deck.json +0 -0
- {databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/api/mcp_auth.py +0 -0
- {databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/api/mcp_server.py +0 -0
- {databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/api/middleware/__init__.py +0 -0
- {databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/api/middleware/request_logging.py +0 -0
- {databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/api/routes/__init__.py +0 -0
- {databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/api/routes/admin.py +0 -0
- {databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/api/routes/admin_usage.py +0 -0
- {databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/api/routes/agent_config.py +0 -0
- {databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/api/routes/chat.py +0 -0
- {databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/api/routes/deck_contributors.py +0 -0
- {databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/api/routes/export.py +0 -0
- {databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/api/routes/feedback.py +0 -0
- {databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/api/routes/google_slides.py +0 -0
- {databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/api/routes/images.py +0 -0
- {databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/api/routes/local_version.py +0 -0
- {databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/api/routes/profiles.py +0 -0
- {databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/api/routes/sessions.py +0 -0
- {databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/api/routes/settings/__init__.py +0 -0
- {databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/api/routes/settings/contributors.py +0 -0
- {databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/api/routes/settings/deck_prompts.py +0 -0
- {databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/api/routes/settings/identities.py +0 -0
- {databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/api/routes/settings/slide_styles.py +0 -0
- {databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/api/routes/setup.py +0 -0
- {databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/api/routes/slides.py +0 -0
- {databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/api/routes/tools.py +0 -0
- {databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/api/routes/tour.py +0 -0
- {databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/api/routes/verification.py +0 -0
- {databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/api/routes/version.py +0 -0
- {databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/api/schemas/__init__.py +0 -0
- {databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/api/schemas/agent_config.py +0 -0
- {databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/api/schemas/feedback.py +0 -0
- {databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/api/schemas/requests.py +0 -0
- {databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/api/schemas/responses.py +0 -0
- {databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/api/schemas/settings/__init__.py +0 -0
- {databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/api/schemas/settings/requests.py +0 -0
- {databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/api/schemas/settings/responses.py +0 -0
- {databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/api/schemas/streaming.py +0 -0
- {databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/api/services/__init__.py +0 -0
- {databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/api/services/export_job_queue.py +0 -0
- {databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/api/services/feedback_service.py +0 -0
- {databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/api/services/job_queue.py +0 -0
- {databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/api/services/session_manager.py +0 -0
- {databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/api/services/session_naming.py +0 -0
- {databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/api/services/usage_events.py +0 -0
- {databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/api/services/usage_service.py +0 -0
- {databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/api/utils/__init__.py +0 -0
- {databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/api/utils/validation.py +0 -0
- {databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/core/__init__.py +0 -0
- {databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/core/config_loader.py +0 -0
- {databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/core/context_utils.py +0 -0
- {databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/core/database.py +0 -0
- {databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/core/databricks_client.py +0 -0
- {databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/core/defaults.py +0 -0
- {databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/core/encryption.py +0 -0
- {databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/core/init_default_profile.py +0 -0
- {databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/core/lakebase.py +0 -0
- {databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/core/migrate_profiles_to_agent_config.py +0 -0
- {databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/core/mlflow_agent_spans.py +0 -0
- {databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/core/mlflow_tracing.py +0 -0
- {databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/core/otel_logging.py +0 -0
- {databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/core/permission_context.py +0 -0
- {databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/core/prompt_modules.py +0 -0
- {databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/core/settings_db.py +0 -0
- {databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/core/user_context.py +0 -0
- {databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/database/__init__.py +0 -0
- {databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/database/models/__init__.py +0 -0
- {databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/database/models/deck_contributor.py +0 -0
- {databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/database/models/feedback.py +0 -0
- {databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/database/models/genie_space.py +0 -0
- {databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/database/models/google_global_credentials.py +0 -0
- {databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/database/models/google_oauth_token.py +0 -0
- {databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/database/models/identity.py +0 -0
- {databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/database/models/image.py +0 -0
- {databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/database/models/profile.py +0 -0
- {databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/database/models/profile_contributor.py +0 -0
- {databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/database/models/prompts.py +0 -0
- {databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/database/models/request_log.py +0 -0
- {databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/database/models/session.py +0 -0
- {databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/database/models/slide_deck_prompt.py +0 -0
- {databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/database/models/slide_style_library.py +0 -0
- {databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/database/models/usage_event.py +0 -0
- {databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/database/models/user_preference.py +0 -0
- {databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/domain/__init__.py +0 -0
- {databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/domain/slide.py +0 -0
- {databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/domain/slide_deck.py +0 -0
- {databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/services/__init__.py +0 -0
- {databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/services/agent.py +0 -0
- {databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/services/agent_factory.py +0 -0
- {databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/services/config_service.py +0 -0
- {databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/services/config_validator.py +0 -0
- {databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/services/drive_uploader.py +0 -0
- {databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/services/evaluation/__init__.py +0 -0
- {databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/services/evaluation/llm_judge.py +0 -0
- {databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/services/genie_service.py +0 -0
- {databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/services/google_slides_auth.py +0 -0
- {databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/services/google_slides_prompts_defaults.py +0 -0
- {databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/services/html_to_google_slides.py +0 -0
- {databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/services/html_to_pptx.py +0 -0
- {databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/services/identity_provider.py +0 -0
- {databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/services/identity_providers/__init__.py +0 -0
- {databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/services/identity_providers/account_provider.py +0 -0
- {databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/services/identity_providers/local_provider.py +0 -0
- {databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/services/identity_providers/workspace_provider.py +0 -0
- {databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/services/image_service.py +0 -0
- {databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/services/image_tools.py +0 -0
- {databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/services/permission_service.py +0 -0
- {databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/services/pptx_from_html_huashu.py +0 -0
- {databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/services/pptx_from_records.py +0 -0
- {databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/services/pptx_prompts_defaults.py +0 -0
- {databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/services/profile_service.py +0 -0
- {databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/services/streaming_callback.py +0 -0
- {databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/services/tools/__init__.py +0 -0
- {databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/services/tools/agent_bricks_tool.py +0 -0
- {databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/services/tools/genie_tool.py +0 -0
- {databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/services/tools/mcp_tool.py +0 -0
- {databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/services/tools/model_endpoint_tool.py +0 -0
- {databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/services/tools/vector_tool.py +0 -0
- {databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/services/validator.py +0 -0
- {databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/utils/__init__.py +0 -0
- {databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/utils/css_utils.py +0 -0
- {databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/utils/error_handling.py +0 -0
- {databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/utils/html_safety.py +0 -0
- {databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/utils/html_utils.py +0 -0
- {databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/utils/image_utils.py +0 -0
- {databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/utils/js_validator.py +0 -0
- {databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/utils/logging_config.py +0 -0
- {databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/utils/pi_filter.py +0 -0
- {databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/utils/slide_hash.py +0 -0
- {databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/utils/spotlight.py +0 -0
- {databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/utils/text_caps.py +0 -0
|
@@ -11,7 +11,9 @@ setup.py
|
|
|
11
11
|
./src/api/mcp_server.py
|
|
12
12
|
./src/api/fixtures/tour_demo_deck.json
|
|
13
13
|
./src/api/middleware/__init__.py
|
|
14
|
+
./src/api/middleware/csrf.py
|
|
14
15
|
./src/api/middleware/request_logging.py
|
|
16
|
+
./src/api/middleware/security_headers.py
|
|
15
17
|
./src/api/routes/__init__.py
|
|
16
18
|
./src/api/routes/admin.py
|
|
17
19
|
./src/api/routes/admin_usage.py
|
|
@@ -154,7 +156,9 @@ src/api/mcp_auth.py
|
|
|
154
156
|
src/api/mcp_server.py
|
|
155
157
|
src/api/fixtures/tour_demo_deck.json
|
|
156
158
|
src/api/middleware/__init__.py
|
|
159
|
+
src/api/middleware/csrf.py
|
|
157
160
|
src/api/middleware/request_logging.py
|
|
161
|
+
src/api/middleware/security_headers.py
|
|
158
162
|
src/api/routes/__init__.py
|
|
159
163
|
src/api/routes/admin.py
|
|
160
164
|
src/api/routes/admin_usage.py
|
|
@@ -417,6 +417,24 @@ from src.api.middleware.request_logging import RequestLoggingMiddleware
|
|
|
417
417
|
|
|
418
418
|
app.add_middleware(RequestLoggingMiddleware)
|
|
419
419
|
|
|
420
|
+
# === SDR-4437 Track A security middleware (PR-1a/PR-1b) ==================
|
|
421
|
+
# Keep ALL security-middleware registration inside this delimited block
|
|
422
|
+
# (PR-2 adds an exception handler below the middleware block — do not edit
|
|
423
|
+
# outside these markers). Starlette wraps later-added middleware outermost;
|
|
424
|
+
# ordering here is deliberate:
|
|
425
|
+
# SecurityHeaders (outermost, added last) -> CSRF -> RequestLogging ->
|
|
426
|
+
# user_auth -> CORS(dev) -> normalize_mcp_path -> routes
|
|
427
|
+
# Headers must stamp EVERY response (including CSRF 403s); CSRF must reject
|
|
428
|
+
# forged cross-site requests before OBO auth work and before request_logs
|
|
429
|
+
# DB writes. CSRF runs outside normalize_mcp_path, so it sees the raw
|
|
430
|
+
# un-rewritten /mcp path (its exemption handles both /mcp and /mcp/).
|
|
431
|
+
from src.api.middleware.csrf import CSRFProtectionMiddleware # noqa: E402
|
|
432
|
+
from src.api.middleware.security_headers import SecurityHeadersMiddleware # noqa: E402
|
|
433
|
+
|
|
434
|
+
app.add_middleware(CSRFProtectionMiddleware)
|
|
435
|
+
app.add_middleware(SecurityHeadersMiddleware)
|
|
436
|
+
# === end SDR-4437 Track A security middleware =============================
|
|
437
|
+
|
|
420
438
|
# Include API routers
|
|
421
439
|
app.include_router(admin.router)
|
|
422
440
|
app.include_router(admin_usage.router)
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
"""Origin-validation CSRF protection (SDR-4437 CRITICAL-2, PR-1b).
|
|
2
|
+
|
|
3
|
+
Threat model: the app sets no cookies of its own — auth arrives via
|
|
4
|
+
proxy-attested ``x-forwarded-*`` headers. The CSRF-relevant ambient
|
|
5
|
+
credential is the platform SSO cookie at the Databricks Apps proxy: a
|
|
6
|
+
cross-site browser request to the app URL rides that cookie, the proxy
|
|
7
|
+
authenticates it and forwards it carrying the victim's identity.
|
|
8
|
+
Validating the browser-attested ``Origin`` (falling back to ``Referer``)
|
|
9
|
+
against the app's own origin closes this at the app layer.
|
|
10
|
+
|
|
11
|
+
Deliberate choices (documented for security review):
|
|
12
|
+
|
|
13
|
+
- **Neither header present -> allow.** Cross-origin browser POSTs always
|
|
14
|
+
send ``Origin``, so the realistic attack always presents a mismatch;
|
|
15
|
+
header-less mutating requests are non-browser clients (scripts/curl via
|
|
16
|
+
the proxy), which rejecting would break for no security gain. A strict
|
|
17
|
+
mode can be revisited if security review requires it.
|
|
18
|
+
- **/mcp is exempt.** It authenticates with bearer tokens, not cookies, so
|
|
19
|
+
it is not a CSRF target. The mounted sub-app IS wrapped by the parent
|
|
20
|
+
app's middleware (that is how ``normalize_mcp_path`` works), so the
|
|
21
|
+
exemption must be this explicit path check, not an assumption that
|
|
22
|
+
mounts bypass middleware. This middleware runs OUTSIDE
|
|
23
|
+
``normalize_mcp_path``, so it sees the raw ``/mcp`` (un-rewritten) path
|
|
24
|
+
— both ``/mcp`` and ``/mcp/...`` are matched; ``/mcp-anything`` is not.
|
|
25
|
+
- **Expected origin:** ``DATABRICKS_APP_URL`` (platform-injected on
|
|
26
|
+
Databricks Apps deploys — same assumption as
|
|
27
|
+
``src/api/mcp_server.py::_public_app_url``). If unset, fall back to the
|
|
28
|
+
origin reconstructed from ``x-forwarded-proto``/``x-forwarded-host``
|
|
29
|
+
(the proxy always sends these; same pattern as
|
|
30
|
+
``google_slides.py::_build_redirect_uri``), so a missing var degrades to
|
|
31
|
+
a self-referential check instead of blocking all writes.
|
|
32
|
+
- **Inactive outside production** — consistent with the dev-only CORS gate
|
|
33
|
+
in ``main.py`` (dev auth is ``DEV_USER_ID`` with no proxy in front).
|
|
34
|
+
"""
|
|
35
|
+
|
|
36
|
+
import logging
|
|
37
|
+
import os
|
|
38
|
+
from urllib.parse import urlsplit
|
|
39
|
+
|
|
40
|
+
from starlette.middleware.base import BaseHTTPMiddleware
|
|
41
|
+
from starlette.requests import Request
|
|
42
|
+
from starlette.responses import JSONResponse, Response
|
|
43
|
+
|
|
44
|
+
logger = logging.getLogger(__name__)
|
|
45
|
+
|
|
46
|
+
_UNSAFE_METHODS = frozenset({"POST", "PUT", "PATCH", "DELETE"})
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def _origin_of(url: str) -> str:
|
|
50
|
+
"""Normalize a URL (or Origin header value) to ``scheme://host[:port]``.
|
|
51
|
+
|
|
52
|
+
Returns "" for values with no parseable scheme+host (e.g. ``null``).
|
|
53
|
+
"""
|
|
54
|
+
parts = urlsplit(url.strip())
|
|
55
|
+
if not parts.scheme or not parts.netloc:
|
|
56
|
+
return ""
|
|
57
|
+
return f"{parts.scheme.lower()}://{parts.netloc.lower()}"
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
class CSRFProtectionMiddleware(BaseHTTPMiddleware):
|
|
61
|
+
"""Reject state-changing requests whose Origin/Referer is cross-site."""
|
|
62
|
+
|
|
63
|
+
def __init__(self, app, enabled: bool | None = None):
|
|
64
|
+
super().__init__(app)
|
|
65
|
+
self.enabled = (
|
|
66
|
+
enabled
|
|
67
|
+
if enabled is not None
|
|
68
|
+
else os.getenv("ENVIRONMENT", "development") == "production"
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
async def dispatch(self, request: Request, call_next) -> Response:
|
|
72
|
+
if not self.enabled or request.method not in _UNSAFE_METHODS:
|
|
73
|
+
return await call_next(request)
|
|
74
|
+
|
|
75
|
+
path = request.url.path
|
|
76
|
+
if path == "/mcp" or path.startswith("/mcp/"):
|
|
77
|
+
return await call_next(request)
|
|
78
|
+
|
|
79
|
+
claimed = request.headers.get("origin") or request.headers.get("referer")
|
|
80
|
+
if not claimed:
|
|
81
|
+
return await call_next(request)
|
|
82
|
+
|
|
83
|
+
claimed_origin = _origin_of(claimed)
|
|
84
|
+
expected_origin = self._expected_origin(request)
|
|
85
|
+
if claimed_origin and expected_origin and claimed_origin == expected_origin:
|
|
86
|
+
return await call_next(request)
|
|
87
|
+
|
|
88
|
+
logger.warning(
|
|
89
|
+
"CSRF: rejected cross-origin %s %s (claimed=%r, expected=%r)",
|
|
90
|
+
request.method,
|
|
91
|
+
path,
|
|
92
|
+
claimed_origin,
|
|
93
|
+
expected_origin,
|
|
94
|
+
)
|
|
95
|
+
return JSONResponse(
|
|
96
|
+
status_code=403,
|
|
97
|
+
content={"detail": "Cross-origin request rejected"},
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
@staticmethod
|
|
101
|
+
def _expected_origin(request: Request) -> str:
|
|
102
|
+
app_url = os.getenv("DATABRICKS_APP_URL", "")
|
|
103
|
+
if app_url:
|
|
104
|
+
return _origin_of(app_url)
|
|
105
|
+
host = request.headers.get("x-forwarded-host", "")
|
|
106
|
+
proto = request.headers.get("x-forwarded-proto", "https")
|
|
107
|
+
if host:
|
|
108
|
+
return f"{proto.strip().lower()}://{host.split(',')[0].strip().lower()}"
|
|
109
|
+
return ""
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
"""App-origin security headers (SDR-4437 CRITICAL-1, PR-1a).
|
|
2
|
+
|
|
3
|
+
Sets browser hardening headers on every response that flows back through
|
|
4
|
+
the middleware stack (known limit: raw 500s from genuinely unhandled
|
|
5
|
+
exceptions are synthesized by Starlette's ``ServerErrorMiddleware``
|
|
6
|
+
outside all user middleware and are NOT stamped; those bodies are
|
|
7
|
+
``text/plain`` and non-executable), plus a
|
|
8
|
+
Content-Security-Policy differentiated by response type:
|
|
9
|
+
|
|
10
|
+
- HTML documents (the SPA index/catch-all AND the Google OAuth callback
|
|
11
|
+
popup, which carries inline <script>) get ``DOCUMENT_CSP``. Two
|
|
12
|
+
constraints rule out a strict policy here:
|
|
13
|
+
|
|
14
|
+
1. srcdoc inheritance: slides render in ``srcdoc`` iframes, and srcdoc
|
|
15
|
+
documents inherit the embedding document's CSP in addition to their
|
|
16
|
+
own SLIDE_CSP meta tag (both policies enforce; most restrictive
|
|
17
|
+
wins). A ``default-src 'self'`` header on the SPA would block every
|
|
18
|
+
slide's inline <script>, Chart.js/Tailwind CDN loads, and Google
|
|
19
|
+
Fonts — breaking slide rendering app-wide. The document policy must
|
|
20
|
+
therefore be a superset of SLIDE_CSP's allowances
|
|
21
|
+
(``src/utils/html_safety.py``; sync-tested in
|
|
22
|
+
``tests/unit/test_export_csp.py``).
|
|
23
|
+
2. Inline styles: the React app uses ``style={{...}}`` attributes and
|
|
24
|
+
Radix UI injects inline styles at runtime; CSP hashes apply to
|
|
25
|
+
<style>/<script> *elements*, not style *attributes*, so
|
|
26
|
+
``style-src`` needs 'unsafe-inline'.
|
|
27
|
+
|
|
28
|
+
``script-src 'unsafe-inline'`` + CDNs is a deliberate, documented
|
|
29
|
+
trade-off forced by srcdoc inheritance: slides remain confined by the
|
|
30
|
+
stricter SLIDE_CSP meta policy (connect-src 'none', form-action
|
|
31
|
+
'none', no eval) plus the html_safety.py scanner, and any header at
|
|
32
|
+
all is a strict improvement over the previous no-CSP state.
|
|
33
|
+
|
|
34
|
+
- Everything else (JSON APIs, assets, SSE) gets the deny-all
|
|
35
|
+
``API_CSP`` — nothing executes in a JSON response.
|
|
36
|
+
|
|
37
|
+
``frame-ancestors 'none'`` / ``X-Frame-Options: DENY`` is safe: all
|
|
38
|
+
in-app slide iframes use ``srcdoc`` (never a backend-served ``src``), and
|
|
39
|
+
the only other HTML endpoint, the Google OAuth callback, opens as a
|
|
40
|
+
popup, not an iframe.
|
|
41
|
+
|
|
42
|
+
``frame-src 'self'`` does not affect the srcdoc slide iframes: a srcdoc
|
|
43
|
+
document loads no URL (its address is the local scheme ``about:srcdoc``,
|
|
44
|
+
no fetch occurs), and per CSP3 local-scheme handling plus current
|
|
45
|
+
Chrome/Firefox/Safari behavior it is NOT subject to frame-src source
|
|
46
|
+
matching — even ``frame-src 'none'`` pages can create srcdoc iframes.
|
|
47
|
+
Instead it inherits this document policy, which is exactly the
|
|
48
|
+
srcdoc-inheritance constraint handled above. Pre-decided fallback if a
|
|
49
|
+
browser ever refuses the slide iframe with a frame-src violation
|
|
50
|
+
("Refused to frame ..."): ``frame-src 'self' about:`` — the ``about:``
|
|
51
|
+
scheme-source matches ``about:srcdoc`` (the historical workaround for
|
|
52
|
+
old-Chrome behavior that did URL-match about: frames).
|
|
53
|
+
"""
|
|
54
|
+
|
|
55
|
+
from starlette.middleware.base import BaseHTTPMiddleware
|
|
56
|
+
from starlette.requests import Request
|
|
57
|
+
from starlette.responses import Response
|
|
58
|
+
|
|
59
|
+
# CSP for HTML document responses: union of SPA needs and SLIDE_CSP
|
|
60
|
+
# allowances (kept a superset of SLIDE_CSP by test_export_csp.py).
|
|
61
|
+
DOCUMENT_CSP = (
|
|
62
|
+
"default-src 'self'; "
|
|
63
|
+
"script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://cdn.tailwindcss.com; "
|
|
64
|
+
"style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; "
|
|
65
|
+
"img-src 'self' data:; "
|
|
66
|
+
"font-src 'self' data: https://cdn.jsdelivr.net https://fonts.gstatic.com; "
|
|
67
|
+
"connect-src 'self'; "
|
|
68
|
+
"frame-src 'self'; "
|
|
69
|
+
"frame-ancestors 'none'; "
|
|
70
|
+
"object-src 'none'; "
|
|
71
|
+
"base-uri 'none'"
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
# CSP for everything else.
|
|
75
|
+
API_CSP = "default-src 'none'; frame-ancestors 'none'"
|
|
76
|
+
|
|
77
|
+
_STATIC_HEADERS = {
|
|
78
|
+
"X-Frame-Options": "DENY",
|
|
79
|
+
"X-Content-Type-Options": "nosniff",
|
|
80
|
+
"Referrer-Policy": "same-origin",
|
|
81
|
+
"Strict-Transport-Security": "max-age=31536000; includeSubDomains",
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
class SecurityHeadersMiddleware(BaseHTTPMiddleware):
|
|
86
|
+
"""Stamp security headers on every response (registered outermost)."""
|
|
87
|
+
|
|
88
|
+
async def dispatch(self, request: Request, call_next) -> Response:
|
|
89
|
+
response = await call_next(request)
|
|
90
|
+
for name, value in _STATIC_HEADERS.items():
|
|
91
|
+
response.headers[name] = value
|
|
92
|
+
content_type = response.headers.get("content-type", "")
|
|
93
|
+
if content_type.startswith("text/html"):
|
|
94
|
+
response.headers["Content-Security-Policy"] = DOCUMENT_CSP
|
|
95
|
+
else:
|
|
96
|
+
response.headers["Content-Security-Policy"] = API_CSP
|
|
97
|
+
return response
|
|
@@ -80,11 +80,6 @@ class ChatService:
|
|
|
80
80
|
# This avoids re-parsing HTML on every request
|
|
81
81
|
self._deck_cache: Dict[str, SlideDeck] = {}
|
|
82
82
|
|
|
83
|
-
# DB version each cached deck corresponds to. The cache is per-process
|
|
84
|
-
# while prod runs multiple uvicorn workers sharing one database, so a
|
|
85
|
-
# cache hit is only valid if the DB version hasn't moved on.
|
|
86
|
-
self._deck_cache_versions: Dict[str, int] = {}
|
|
87
|
-
|
|
88
83
|
logger.info("ChatService initialized successfully")
|
|
89
84
|
|
|
90
85
|
def _substitute_images_for_response(self, deck_dict, raw_html=None):
|
|
@@ -605,7 +600,7 @@ class ChatService:
|
|
|
605
600
|
slide_deck_dict = current_deck.to_dict()
|
|
606
601
|
|
|
607
602
|
try:
|
|
608
|
-
|
|
603
|
+
session_manager.save_slide_deck(
|
|
609
604
|
session_id=session_id,
|
|
610
605
|
title=current_deck.title,
|
|
611
606
|
html_content=current_deck.knit(),
|
|
@@ -615,7 +610,6 @@ class ChatService:
|
|
|
615
610
|
modified_by=_user,
|
|
616
611
|
expected_version=_deck_version_before_llm,
|
|
617
612
|
)
|
|
618
|
-
self._record_deck_version(session_id, save_result)
|
|
619
613
|
except VersionConflictError:
|
|
620
614
|
logger.warning(
|
|
621
615
|
"Chat save rejected: deck was edited during LLM call, reloading",
|
|
@@ -1391,7 +1385,7 @@ class ChatService:
|
|
|
1391
1385
|
slide_deck_dict = current_deck.to_dict()
|
|
1392
1386
|
|
|
1393
1387
|
try:
|
|
1394
|
-
|
|
1388
|
+
session_manager.save_slide_deck(
|
|
1395
1389
|
session_id=session_id,
|
|
1396
1390
|
title=current_deck.title,
|
|
1397
1391
|
html_content=current_deck.knit(),
|
|
@@ -1401,7 +1395,6 @@ class ChatService:
|
|
|
1401
1395
|
modified_by=_user,
|
|
1402
1396
|
expected_version=_deck_version_before_llm,
|
|
1403
1397
|
)
|
|
1404
|
-
self._record_deck_version(session_id, save_result)
|
|
1405
1398
|
except VersionConflictError:
|
|
1406
1399
|
logger.warning(
|
|
1407
1400
|
"Chat save rejected: deck was edited during LLM call, reloading",
|
|
@@ -1928,63 +1921,18 @@ class ChatService:
|
|
|
1928
1921
|
context = "\n\n[Attached images]\n" + "\n".join(image_descriptions)
|
|
1929
1922
|
return message + context
|
|
1930
1923
|
|
|
1931
|
-
def _deck_versions(self) -> Dict[str, int]:
|
|
1932
|
-
"""Return the cached-deck version map, creating it if needed.
|
|
1933
|
-
|
|
1934
|
-
Lazy creation keeps instances built without __init__ (test harnesses
|
|
1935
|
-
use ChatService.__new__) working. Callers must hold _cache_lock.
|
|
1936
|
-
"""
|
|
1937
|
-
try:
|
|
1938
|
-
return self._deck_cache_versions
|
|
1939
|
-
except AttributeError:
|
|
1940
|
-
self._deck_cache_versions = {}
|
|
1941
|
-
return self._deck_cache_versions
|
|
1942
|
-
|
|
1943
|
-
def _record_deck_version(self, session_id: str, save_result: Optional[Dict[str, Any]]) -> None:
|
|
1944
|
-
"""Remember which DB version the cached deck now corresponds to.
|
|
1945
|
-
|
|
1946
|
-
Call after save_slide_deck so the next _get_or_load_deck cache hit
|
|
1947
|
-
validates cleanly instead of reloading from the database.
|
|
1948
|
-
"""
|
|
1949
|
-
version = (save_result or {}).get("version")
|
|
1950
|
-
with self._cache_lock:
|
|
1951
|
-
if version is not None:
|
|
1952
|
-
self._deck_versions()[session_id] = version
|
|
1953
|
-
else:
|
|
1954
|
-
self._deck_versions().pop(session_id, None)
|
|
1955
|
-
|
|
1956
1924
|
def _get_or_load_deck(self, session_id: str) -> Optional[SlideDeck]:
|
|
1957
1925
|
"""Get deck from cache or load from database.
|
|
1958
1926
|
|
|
1959
1927
|
Thread-safe access to deck cache using _cache_lock.
|
|
1960
|
-
|
|
1961
|
-
The cache is per-process while prod runs multiple uvicorn workers
|
|
1962
|
-
sharing one database, so a cached deck is only served if its recorded
|
|
1963
|
-
version matches the current DB version; otherwise it is reloaded.
|
|
1964
|
-
|
|
1928
|
+
|
|
1965
1929
|
Uses deck_dict (slides array with individual scripts) when available,
|
|
1966
1930
|
falling back to from_html_string for legacy data.
|
|
1967
1931
|
"""
|
|
1968
1932
|
# Check cache first (with lock)
|
|
1969
1933
|
with self._cache_lock:
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
if cached_deck is not None:
|
|
1974
|
-
db_version = self._get_deck_version(session_id)
|
|
1975
|
-
if db_version is None or cached_version == db_version:
|
|
1976
|
-
return cached_deck
|
|
1977
|
-
logger.info(
|
|
1978
|
-
"Deck cache stale, reloading from database",
|
|
1979
|
-
extra={
|
|
1980
|
-
"session_id": session_id,
|
|
1981
|
-
"cached_version": cached_version,
|
|
1982
|
-
"db_version": db_version,
|
|
1983
|
-
},
|
|
1984
|
-
)
|
|
1985
|
-
with self._cache_lock:
|
|
1986
|
-
self._deck_cache.pop(session_id, None)
|
|
1987
|
-
self._deck_versions().pop(session_id, None)
|
|
1934
|
+
if session_id in self._deck_cache:
|
|
1935
|
+
return self._deck_cache[session_id]
|
|
1988
1936
|
|
|
1989
1937
|
# Try to load from database (outside lock to avoid blocking)
|
|
1990
1938
|
session_manager = get_session_manager()
|
|
@@ -2018,10 +1966,6 @@ class ChatService:
|
|
|
2018
1966
|
# Store in cache (with lock)
|
|
2019
1967
|
with self._cache_lock:
|
|
2020
1968
|
self._deck_cache[session_id] = deck
|
|
2021
|
-
if deck_data.get("version") is not None:
|
|
2022
|
-
self._deck_versions()[session_id] = deck_data["version"]
|
|
2023
|
-
else:
|
|
2024
|
-
self._deck_versions().pop(session_id, None)
|
|
2025
1969
|
return deck
|
|
2026
1970
|
except Exception as e:
|
|
2027
1971
|
logger.warning(f"Failed to load deck from database: {e}")
|
|
@@ -2064,7 +2008,6 @@ class ChatService:
|
|
|
2064
2008
|
"""Remove the cached deck for a session so the next read hits the DB."""
|
|
2065
2009
|
with self._cache_lock:
|
|
2066
2010
|
self._deck_cache.pop(session_id, None)
|
|
2067
|
-
self._deck_versions().pop(session_id, None)
|
|
2068
2011
|
|
|
2069
2012
|
def _replace_slide_htmls_from_cache(self, session_id: str, slide_context: Dict[str, Any]) -> Dict[str, Any]:
|
|
2070
2013
|
"""Replace frontend-supplied slide_htmls with backend cache versions.
|
|
@@ -2597,7 +2540,7 @@ class ChatService:
|
|
|
2597
2540
|
# Persist to database
|
|
2598
2541
|
deck_dict = current_deck.to_dict()
|
|
2599
2542
|
session_manager = get_session_manager()
|
|
2600
|
-
|
|
2543
|
+
session_manager.save_slide_deck(
|
|
2601
2544
|
session_id=session_id,
|
|
2602
2545
|
title=current_deck.title,
|
|
2603
2546
|
html_content=current_deck.knit(),
|
|
@@ -2606,7 +2549,6 @@ class ChatService:
|
|
|
2606
2549
|
deck_dict=deck_dict,
|
|
2607
2550
|
expected_version=expected_version,
|
|
2608
2551
|
)
|
|
2609
|
-
self._record_deck_version(session_id, save_result)
|
|
2610
2552
|
|
|
2611
2553
|
# Create save point
|
|
2612
2554
|
try:
|
|
@@ -2674,7 +2616,7 @@ class ChatService:
|
|
|
2674
2616
|
# Persist to database
|
|
2675
2617
|
deck_dict = current_deck.to_dict()
|
|
2676
2618
|
session_manager = get_session_manager()
|
|
2677
|
-
|
|
2619
|
+
session_manager.save_slide_deck(
|
|
2678
2620
|
session_id=session_id,
|
|
2679
2621
|
title=current_deck.title,
|
|
2680
2622
|
html_content=current_deck.knit(),
|
|
@@ -2683,7 +2625,6 @@ class ChatService:
|
|
|
2683
2625
|
deck_dict=deck_dict,
|
|
2684
2626
|
expected_version=expected_version,
|
|
2685
2627
|
)
|
|
2686
|
-
self._record_deck_version(session_id, save_result)
|
|
2687
2628
|
|
|
2688
2629
|
# Create save point immediately after persisting
|
|
2689
2630
|
try:
|
|
@@ -2740,7 +2681,7 @@ class ChatService:
|
|
|
2740
2681
|
# Persist to database
|
|
2741
2682
|
deck_dict = current_deck.to_dict()
|
|
2742
2683
|
session_manager = get_session_manager()
|
|
2743
|
-
|
|
2684
|
+
session_manager.save_slide_deck(
|
|
2744
2685
|
session_id=session_id,
|
|
2745
2686
|
title=current_deck.title,
|
|
2746
2687
|
html_content=current_deck.knit(),
|
|
@@ -2749,7 +2690,6 @@ class ChatService:
|
|
|
2749
2690
|
deck_dict=deck_dict,
|
|
2750
2691
|
expected_version=expected_version,
|
|
2751
2692
|
)
|
|
2752
|
-
self._record_deck_version(session_id, save_result)
|
|
2753
2693
|
|
|
2754
2694
|
# Create save point
|
|
2755
2695
|
try:
|
|
@@ -2804,7 +2744,7 @@ class ChatService:
|
|
|
2804
2744
|
# Persist to database
|
|
2805
2745
|
deck_dict = current_deck.to_dict()
|
|
2806
2746
|
session_manager = get_session_manager()
|
|
2807
|
-
|
|
2747
|
+
session_manager.save_slide_deck(
|
|
2808
2748
|
session_id=session_id,
|
|
2809
2749
|
title=current_deck.title,
|
|
2810
2750
|
html_content=current_deck.knit(),
|
|
@@ -2813,7 +2753,6 @@ class ChatService:
|
|
|
2813
2753
|
deck_dict=deck_dict,
|
|
2814
2754
|
expected_version=expected_version,
|
|
2815
2755
|
)
|
|
2816
|
-
self._record_deck_version(session_id, save_result)
|
|
2817
2756
|
|
|
2818
2757
|
# Create save point
|
|
2819
2758
|
try:
|
|
File without changes
|
|
File without changes
|
{databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/databricks_tellr_app/run.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/api/_otel_bootstrap.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/api/middleware/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
{databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/api/routes/__init__.py
RENAMED
|
File without changes
|
{databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/api/routes/admin.py
RENAMED
|
File without changes
|
{databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/api/routes/admin_usage.py
RENAMED
|
File without changes
|
{databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/api/routes/agent_config.py
RENAMED
|
File without changes
|
{databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/api/routes/chat.py
RENAMED
|
File without changes
|
|
File without changes
|
{databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/api/routes/export.py
RENAMED
|
File without changes
|
{databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/api/routes/feedback.py
RENAMED
|
File without changes
|
|
File without changes
|
{databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/api/routes/images.py
RENAMED
|
File without changes
|
|
File without changes
|
{databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/api/routes/profiles.py
RENAMED
|
File without changes
|
{databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/api/routes/sessions.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/api/routes/setup.py
RENAMED
|
File without changes
|
{databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/api/routes/slides.py
RENAMED
|
File without changes
|
{databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/api/routes/tools.py
RENAMED
|
File without changes
|
{databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/api/routes/tour.py
RENAMED
|
File without changes
|
{databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/api/routes/verification.py
RENAMED
|
File without changes
|
{databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/api/routes/version.py
RENAMED
|
File without changes
|
{databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/api/schemas/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
{databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/api/schemas/feedback.py
RENAMED
|
File without changes
|
{databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/api/schemas/requests.py
RENAMED
|
File without changes
|
{databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/api/schemas/responses.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/api/schemas/streaming.py
RENAMED
|
File without changes
|
{databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/api/services/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/api/services/job_queue.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/api/utils/__init__.py
RENAMED
|
File without changes
|
{databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/api/utils/validation.py
RENAMED
|
File without changes
|
|
File without changes
|
{databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/core/config_loader.py
RENAMED
|
File without changes
|
{databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/core/context_utils.py
RENAMED
|
File without changes
|
|
File without changes
|
{databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/core/databricks_client.py
RENAMED
|
File without changes
|
|
File without changes
|
{databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/core/encryption.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/core/mlflow_agent_spans.py
RENAMED
|
File without changes
|
{databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/core/mlflow_tracing.py
RENAMED
|
File without changes
|
{databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/core/otel_logging.py
RENAMED
|
File without changes
|
{databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/core/permission_context.py
RENAMED
|
File without changes
|
{databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/core/prompt_modules.py
RENAMED
|
File without changes
|
{databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/core/settings_db.py
RENAMED
|
File without changes
|
{databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/core/user_context.py
RENAMED
|
File without changes
|
{databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/database/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/database/models/image.py
RENAMED
|
File without changes
|
{databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/database/models/profile.py
RENAMED
|
File without changes
|
|
File without changes
|
{databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/database/models/prompts.py
RENAMED
|
File without changes
|
|
File without changes
|
{databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/database/models/session.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/domain/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
{databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/domain/slide_deck.py
RENAMED
|
File without changes
|
{databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/services/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
{databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/services/agent_factory.py
RENAMED
|
File without changes
|
{databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/services/config_service.py
RENAMED
|
File without changes
|
|
File without changes
|
{databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/services/drive_uploader.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/services/genie_service.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/services/html_to_pptx.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/services/image_service.py
RENAMED
|
File without changes
|
{databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/services/image_tools.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/services/tools/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/services/tools/mcp_tool.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/services/validator.py
RENAMED
|
File without changes
|
|
File without changes
|
{databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/utils/css_utils.py
RENAMED
|
File without changes
|
{databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/utils/error_handling.py
RENAMED
|
File without changes
|
{databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/utils/html_safety.py
RENAMED
|
File without changes
|
{databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/utils/html_utils.py
RENAMED
|
File without changes
|
{databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/utils/image_utils.py
RENAMED
|
File without changes
|
{databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/utils/js_validator.py
RENAMED
|
File without changes
|
{databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/utils/logging_config.py
RENAMED
|
File without changes
|
{databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/utils/pi_filter.py
RENAMED
|
File without changes
|
{databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/utils/slide_hash.py
RENAMED
|
File without changes
|
{databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/utils/spotlight.py
RENAMED
|
File without changes
|
{databricks_tellr_app-0.3.13.dev1 → databricks_tellr_app-0.3.13.dev2}/src/utils/text_caps.py
RENAMED
|
File without changes
|