databricks-tellr-app 0.3.11.dev5__tar.gz → 0.3.11.dev7__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.11.dev5 → databricks_tellr_app-0.3.11.dev7}/PKG-INFO +1 -1
- {databricks_tellr_app-0.3.11.dev5 → databricks_tellr_app-0.3.11.dev7}/databricks_tellr_app.egg-info/PKG-INFO +1 -1
- {databricks_tellr_app-0.3.11.dev5 → databricks_tellr_app-0.3.11.dev7}/databricks_tellr_app.egg-info/SOURCES.txt +4 -0
- {databricks_tellr_app-0.3.11.dev5 → databricks_tellr_app-0.3.11.dev7}/pyproject.toml +1 -1
- {databricks_tellr_app-0.3.11.dev5 → databricks_tellr_app-0.3.11.dev7}/src/api/routes/agent_config.py +42 -4
- {databricks_tellr_app-0.3.11.dev5 → databricks_tellr_app-0.3.11.dev7}/src/api/routes/settings/design_systems.py +330 -0
- {databricks_tellr_app-0.3.11.dev5 → databricks_tellr_app-0.3.11.dev7}/src/api/schemas/agent_config.py +5 -0
- {databricks_tellr_app-0.3.11.dev5 → databricks_tellr_app-0.3.11.dev7}/src/core/database.py +52 -6
- {databricks_tellr_app-0.3.11.dev5 → databricks_tellr_app-0.3.11.dev7}/src/core/prompt_modules.py +53 -3
- {databricks_tellr_app-0.3.11.dev5 → databricks_tellr_app-0.3.11.dev7}/src/database/models/__init__.py +4 -0
- databricks_tellr_app-0.3.11.dev7/src/database/models/design_system.py +357 -0
- {databricks_tellr_app-0.3.11.dev5 → databricks_tellr_app-0.3.11.dev7}/src/services/agent_factory.py +63 -12
- databricks_tellr_app-0.3.11.dev7/src/services/design_system_compiler.py +603 -0
- databricks_tellr_app-0.3.11.dev7/src/services/design_system_service.py +1004 -0
- databricks_tellr_app-0.3.11.dev7/src/services/design_system_templates.py +641 -0
- {databricks_tellr_app-0.3.11.dev5 → databricks_tellr_app-0.3.11.dev7}/src/services/tools/__init__.py +4 -0
- databricks_tellr_app-0.3.11.dev7/src/services/tools/ds_asset_tool.py +105 -0
- databricks_tellr_app-0.3.11.dev5/src/database/models/design_system.py +0 -198
- databricks_tellr_app-0.3.11.dev5/src/services/design_system_compiler.py +0 -277
- databricks_tellr_app-0.3.11.dev5/src/services/design_system_service.py +0 -482
- {databricks_tellr_app-0.3.11.dev5 → databricks_tellr_app-0.3.11.dev7}/README.md +0 -0
- {databricks_tellr_app-0.3.11.dev5 → databricks_tellr_app-0.3.11.dev7}/databricks_tellr_app/__init__.py +0 -0
- {databricks_tellr_app-0.3.11.dev5 → databricks_tellr_app-0.3.11.dev7}/databricks_tellr_app/run.py +0 -0
- {databricks_tellr_app-0.3.11.dev5 → databricks_tellr_app-0.3.11.dev7}/databricks_tellr_app.egg-info/dependency_links.txt +0 -0
- {databricks_tellr_app-0.3.11.dev5 → databricks_tellr_app-0.3.11.dev7}/databricks_tellr_app.egg-info/requires.txt +0 -0
- {databricks_tellr_app-0.3.11.dev5 → databricks_tellr_app-0.3.11.dev7}/databricks_tellr_app.egg-info/top_level.txt +0 -0
- {databricks_tellr_app-0.3.11.dev5 → databricks_tellr_app-0.3.11.dev7}/setup.cfg +0 -0
- {databricks_tellr_app-0.3.11.dev5 → databricks_tellr_app-0.3.11.dev7}/setup.py +0 -0
- {databricks_tellr_app-0.3.11.dev5 → databricks_tellr_app-0.3.11.dev7}/src/__init__.py +0 -0
- {databricks_tellr_app-0.3.11.dev5 → databricks_tellr_app-0.3.11.dev7}/src/api/__init__.py +0 -0
- {databricks_tellr_app-0.3.11.dev5 → databricks_tellr_app-0.3.11.dev7}/src/api/_otel_bootstrap.py +0 -0
- {databricks_tellr_app-0.3.11.dev5 → databricks_tellr_app-0.3.11.dev7}/src/api/fixtures/tour_demo_deck.json +0 -0
- {databricks_tellr_app-0.3.11.dev5 → databricks_tellr_app-0.3.11.dev7}/src/api/main.py +0 -0
- {databricks_tellr_app-0.3.11.dev5 → databricks_tellr_app-0.3.11.dev7}/src/api/mcp_auth.py +0 -0
- {databricks_tellr_app-0.3.11.dev5 → databricks_tellr_app-0.3.11.dev7}/src/api/mcp_server.py +0 -0
- {databricks_tellr_app-0.3.11.dev5 → databricks_tellr_app-0.3.11.dev7}/src/api/middleware/__init__.py +0 -0
- {databricks_tellr_app-0.3.11.dev5 → databricks_tellr_app-0.3.11.dev7}/src/api/middleware/request_logging.py +0 -0
- {databricks_tellr_app-0.3.11.dev5 → databricks_tellr_app-0.3.11.dev7}/src/api/routes/__init__.py +0 -0
- {databricks_tellr_app-0.3.11.dev5 → databricks_tellr_app-0.3.11.dev7}/src/api/routes/admin.py +0 -0
- {databricks_tellr_app-0.3.11.dev5 → databricks_tellr_app-0.3.11.dev7}/src/api/routes/chat.py +0 -0
- {databricks_tellr_app-0.3.11.dev5 → databricks_tellr_app-0.3.11.dev7}/src/api/routes/deck_contributors.py +0 -0
- {databricks_tellr_app-0.3.11.dev5 → databricks_tellr_app-0.3.11.dev7}/src/api/routes/export.py +0 -0
- {databricks_tellr_app-0.3.11.dev5 → databricks_tellr_app-0.3.11.dev7}/src/api/routes/feedback.py +0 -0
- {databricks_tellr_app-0.3.11.dev5 → databricks_tellr_app-0.3.11.dev7}/src/api/routes/google_slides.py +0 -0
- {databricks_tellr_app-0.3.11.dev5 → databricks_tellr_app-0.3.11.dev7}/src/api/routes/images.py +0 -0
- {databricks_tellr_app-0.3.11.dev5 → databricks_tellr_app-0.3.11.dev7}/src/api/routes/local_version.py +0 -0
- {databricks_tellr_app-0.3.11.dev5 → databricks_tellr_app-0.3.11.dev7}/src/api/routes/profiles.py +0 -0
- {databricks_tellr_app-0.3.11.dev5 → databricks_tellr_app-0.3.11.dev7}/src/api/routes/sessions.py +0 -0
- {databricks_tellr_app-0.3.11.dev5 → databricks_tellr_app-0.3.11.dev7}/src/api/routes/settings/__init__.py +0 -0
- {databricks_tellr_app-0.3.11.dev5 → databricks_tellr_app-0.3.11.dev7}/src/api/routes/settings/contributors.py +0 -0
- {databricks_tellr_app-0.3.11.dev5 → databricks_tellr_app-0.3.11.dev7}/src/api/routes/settings/deck_prompts.py +0 -0
- {databricks_tellr_app-0.3.11.dev5 → databricks_tellr_app-0.3.11.dev7}/src/api/routes/settings/identities.py +0 -0
- {databricks_tellr_app-0.3.11.dev5 → databricks_tellr_app-0.3.11.dev7}/src/api/routes/settings/slide_styles.py +0 -0
- {databricks_tellr_app-0.3.11.dev5 → databricks_tellr_app-0.3.11.dev7}/src/api/routes/setup.py +0 -0
- {databricks_tellr_app-0.3.11.dev5 → databricks_tellr_app-0.3.11.dev7}/src/api/routes/slides.py +0 -0
- {databricks_tellr_app-0.3.11.dev5 → databricks_tellr_app-0.3.11.dev7}/src/api/routes/tools.py +0 -0
- {databricks_tellr_app-0.3.11.dev5 → databricks_tellr_app-0.3.11.dev7}/src/api/routes/tour.py +0 -0
- {databricks_tellr_app-0.3.11.dev5 → databricks_tellr_app-0.3.11.dev7}/src/api/routes/verification.py +0 -0
- {databricks_tellr_app-0.3.11.dev5 → databricks_tellr_app-0.3.11.dev7}/src/api/routes/version.py +0 -0
- {databricks_tellr_app-0.3.11.dev5 → databricks_tellr_app-0.3.11.dev7}/src/api/schemas/__init__.py +0 -0
- {databricks_tellr_app-0.3.11.dev5 → databricks_tellr_app-0.3.11.dev7}/src/api/schemas/feedback.py +0 -0
- {databricks_tellr_app-0.3.11.dev5 → databricks_tellr_app-0.3.11.dev7}/src/api/schemas/requests.py +0 -0
- {databricks_tellr_app-0.3.11.dev5 → databricks_tellr_app-0.3.11.dev7}/src/api/schemas/responses.py +0 -0
- {databricks_tellr_app-0.3.11.dev5 → databricks_tellr_app-0.3.11.dev7}/src/api/schemas/settings/__init__.py +0 -0
- {databricks_tellr_app-0.3.11.dev5 → databricks_tellr_app-0.3.11.dev7}/src/api/schemas/settings/requests.py +0 -0
- {databricks_tellr_app-0.3.11.dev5 → databricks_tellr_app-0.3.11.dev7}/src/api/schemas/settings/responses.py +0 -0
- {databricks_tellr_app-0.3.11.dev5 → databricks_tellr_app-0.3.11.dev7}/src/api/schemas/streaming.py +0 -0
- {databricks_tellr_app-0.3.11.dev5 → databricks_tellr_app-0.3.11.dev7}/src/api/services/__init__.py +0 -0
- {databricks_tellr_app-0.3.11.dev5 → databricks_tellr_app-0.3.11.dev7}/src/api/services/chat_service.py +0 -0
- {databricks_tellr_app-0.3.11.dev5 → databricks_tellr_app-0.3.11.dev7}/src/api/services/export_job_queue.py +0 -0
- {databricks_tellr_app-0.3.11.dev5 → databricks_tellr_app-0.3.11.dev7}/src/api/services/feedback_service.py +0 -0
- {databricks_tellr_app-0.3.11.dev5 → databricks_tellr_app-0.3.11.dev7}/src/api/services/job_queue.py +0 -0
- {databricks_tellr_app-0.3.11.dev5 → databricks_tellr_app-0.3.11.dev7}/src/api/services/session_manager.py +0 -0
- {databricks_tellr_app-0.3.11.dev5 → databricks_tellr_app-0.3.11.dev7}/src/api/services/session_naming.py +0 -0
- {databricks_tellr_app-0.3.11.dev5 → databricks_tellr_app-0.3.11.dev7}/src/api/utils/__init__.py +0 -0
- {databricks_tellr_app-0.3.11.dev5 → databricks_tellr_app-0.3.11.dev7}/src/api/utils/validation.py +0 -0
- {databricks_tellr_app-0.3.11.dev5 → databricks_tellr_app-0.3.11.dev7}/src/core/__init__.py +0 -0
- {databricks_tellr_app-0.3.11.dev5 → databricks_tellr_app-0.3.11.dev7}/src/core/config_loader.py +0 -0
- {databricks_tellr_app-0.3.11.dev5 → databricks_tellr_app-0.3.11.dev7}/src/core/context_utils.py +0 -0
- {databricks_tellr_app-0.3.11.dev5 → databricks_tellr_app-0.3.11.dev7}/src/core/databricks_client.py +0 -0
- {databricks_tellr_app-0.3.11.dev5 → databricks_tellr_app-0.3.11.dev7}/src/core/defaults.py +0 -0
- {databricks_tellr_app-0.3.11.dev5 → databricks_tellr_app-0.3.11.dev7}/src/core/encryption.py +0 -0
- {databricks_tellr_app-0.3.11.dev5 → databricks_tellr_app-0.3.11.dev7}/src/core/init_default_profile.py +0 -0
- {databricks_tellr_app-0.3.11.dev5 → databricks_tellr_app-0.3.11.dev7}/src/core/lakebase.py +0 -0
- {databricks_tellr_app-0.3.11.dev5 → databricks_tellr_app-0.3.11.dev7}/src/core/migrate_profiles_to_agent_config.py +0 -0
- {databricks_tellr_app-0.3.11.dev5 → databricks_tellr_app-0.3.11.dev7}/src/core/mlflow_agent_spans.py +0 -0
- {databricks_tellr_app-0.3.11.dev5 → databricks_tellr_app-0.3.11.dev7}/src/core/mlflow_tracing.py +0 -0
- {databricks_tellr_app-0.3.11.dev5 → databricks_tellr_app-0.3.11.dev7}/src/core/otel_logging.py +0 -0
- {databricks_tellr_app-0.3.11.dev5 → databricks_tellr_app-0.3.11.dev7}/src/core/permission_context.py +0 -0
- {databricks_tellr_app-0.3.11.dev5 → databricks_tellr_app-0.3.11.dev7}/src/core/settings_db.py +0 -0
- {databricks_tellr_app-0.3.11.dev5 → databricks_tellr_app-0.3.11.dev7}/src/core/user_context.py +0 -0
- {databricks_tellr_app-0.3.11.dev5 → databricks_tellr_app-0.3.11.dev7}/src/database/__init__.py +0 -0
- {databricks_tellr_app-0.3.11.dev5 → databricks_tellr_app-0.3.11.dev7}/src/database/models/deck_contributor.py +0 -0
- {databricks_tellr_app-0.3.11.dev5 → databricks_tellr_app-0.3.11.dev7}/src/database/models/feedback.py +0 -0
- {databricks_tellr_app-0.3.11.dev5 → databricks_tellr_app-0.3.11.dev7}/src/database/models/genie_space.py +0 -0
- {databricks_tellr_app-0.3.11.dev5 → databricks_tellr_app-0.3.11.dev7}/src/database/models/google_global_credentials.py +0 -0
- {databricks_tellr_app-0.3.11.dev5 → databricks_tellr_app-0.3.11.dev7}/src/database/models/google_oauth_token.py +0 -0
- {databricks_tellr_app-0.3.11.dev5 → databricks_tellr_app-0.3.11.dev7}/src/database/models/identity.py +0 -0
- {databricks_tellr_app-0.3.11.dev5 → databricks_tellr_app-0.3.11.dev7}/src/database/models/image.py +0 -0
- {databricks_tellr_app-0.3.11.dev5 → databricks_tellr_app-0.3.11.dev7}/src/database/models/profile.py +0 -0
- {databricks_tellr_app-0.3.11.dev5 → databricks_tellr_app-0.3.11.dev7}/src/database/models/profile_contributor.py +0 -0
- {databricks_tellr_app-0.3.11.dev5 → databricks_tellr_app-0.3.11.dev7}/src/database/models/prompts.py +0 -0
- {databricks_tellr_app-0.3.11.dev5 → databricks_tellr_app-0.3.11.dev7}/src/database/models/request_log.py +0 -0
- {databricks_tellr_app-0.3.11.dev5 → databricks_tellr_app-0.3.11.dev7}/src/database/models/session.py +0 -0
- {databricks_tellr_app-0.3.11.dev5 → databricks_tellr_app-0.3.11.dev7}/src/database/models/slide_deck_prompt.py +0 -0
- {databricks_tellr_app-0.3.11.dev5 → databricks_tellr_app-0.3.11.dev7}/src/database/models/slide_style_library.py +0 -0
- {databricks_tellr_app-0.3.11.dev5 → databricks_tellr_app-0.3.11.dev7}/src/database/models/user_preference.py +0 -0
- {databricks_tellr_app-0.3.11.dev5 → databricks_tellr_app-0.3.11.dev7}/src/domain/__init__.py +0 -0
- {databricks_tellr_app-0.3.11.dev5 → databricks_tellr_app-0.3.11.dev7}/src/domain/slide.py +0 -0
- {databricks_tellr_app-0.3.11.dev5 → databricks_tellr_app-0.3.11.dev7}/src/domain/slide_deck.py +0 -0
- {databricks_tellr_app-0.3.11.dev5 → databricks_tellr_app-0.3.11.dev7}/src/services/__init__.py +0 -0
- {databricks_tellr_app-0.3.11.dev5 → databricks_tellr_app-0.3.11.dev7}/src/services/agent.py +0 -0
- {databricks_tellr_app-0.3.11.dev5 → databricks_tellr_app-0.3.11.dev7}/src/services/config_service.py +0 -0
- {databricks_tellr_app-0.3.11.dev5 → databricks_tellr_app-0.3.11.dev7}/src/services/config_validator.py +0 -0
- {databricks_tellr_app-0.3.11.dev5 → databricks_tellr_app-0.3.11.dev7}/src/services/drive_uploader.py +0 -0
- {databricks_tellr_app-0.3.11.dev5 → databricks_tellr_app-0.3.11.dev7}/src/services/evaluation/__init__.py +0 -0
- {databricks_tellr_app-0.3.11.dev5 → databricks_tellr_app-0.3.11.dev7}/src/services/evaluation/llm_judge.py +0 -0
- {databricks_tellr_app-0.3.11.dev5 → databricks_tellr_app-0.3.11.dev7}/src/services/genie_service.py +0 -0
- {databricks_tellr_app-0.3.11.dev5 → databricks_tellr_app-0.3.11.dev7}/src/services/google_slides_auth.py +0 -0
- {databricks_tellr_app-0.3.11.dev5 → databricks_tellr_app-0.3.11.dev7}/src/services/google_slides_prompts_defaults.py +0 -0
- {databricks_tellr_app-0.3.11.dev5 → databricks_tellr_app-0.3.11.dev7}/src/services/html_to_google_slides.py +0 -0
- {databricks_tellr_app-0.3.11.dev5 → databricks_tellr_app-0.3.11.dev7}/src/services/html_to_pptx.py +0 -0
- {databricks_tellr_app-0.3.11.dev5 → databricks_tellr_app-0.3.11.dev7}/src/services/identity_provider.py +0 -0
- {databricks_tellr_app-0.3.11.dev5 → databricks_tellr_app-0.3.11.dev7}/src/services/identity_providers/__init__.py +0 -0
- {databricks_tellr_app-0.3.11.dev5 → databricks_tellr_app-0.3.11.dev7}/src/services/identity_providers/account_provider.py +0 -0
- {databricks_tellr_app-0.3.11.dev5 → databricks_tellr_app-0.3.11.dev7}/src/services/identity_providers/local_provider.py +0 -0
- {databricks_tellr_app-0.3.11.dev5 → databricks_tellr_app-0.3.11.dev7}/src/services/identity_providers/workspace_provider.py +0 -0
- {databricks_tellr_app-0.3.11.dev5 → databricks_tellr_app-0.3.11.dev7}/src/services/image_service.py +0 -0
- {databricks_tellr_app-0.3.11.dev5 → databricks_tellr_app-0.3.11.dev7}/src/services/image_tools.py +0 -0
- {databricks_tellr_app-0.3.11.dev5 → databricks_tellr_app-0.3.11.dev7}/src/services/permission_service.py +0 -0
- {databricks_tellr_app-0.3.11.dev5 → databricks_tellr_app-0.3.11.dev7}/src/services/pptx_from_html_huashu.py +0 -0
- {databricks_tellr_app-0.3.11.dev5 → databricks_tellr_app-0.3.11.dev7}/src/services/pptx_from_records.py +0 -0
- {databricks_tellr_app-0.3.11.dev5 → databricks_tellr_app-0.3.11.dev7}/src/services/pptx_prompts_defaults.py +0 -0
- {databricks_tellr_app-0.3.11.dev5 → databricks_tellr_app-0.3.11.dev7}/src/services/profile_service.py +0 -0
- {databricks_tellr_app-0.3.11.dev5 → databricks_tellr_app-0.3.11.dev7}/src/services/streaming_callback.py +0 -0
- {databricks_tellr_app-0.3.11.dev5 → databricks_tellr_app-0.3.11.dev7}/src/services/tools/agent_bricks_tool.py +0 -0
- {databricks_tellr_app-0.3.11.dev5 → databricks_tellr_app-0.3.11.dev7}/src/services/tools/genie_tool.py +0 -0
- {databricks_tellr_app-0.3.11.dev5 → databricks_tellr_app-0.3.11.dev7}/src/services/tools/mcp_tool.py +0 -0
- {databricks_tellr_app-0.3.11.dev5 → databricks_tellr_app-0.3.11.dev7}/src/services/tools/model_endpoint_tool.py +0 -0
- {databricks_tellr_app-0.3.11.dev5 → databricks_tellr_app-0.3.11.dev7}/src/services/tools/vector_tool.py +0 -0
- {databricks_tellr_app-0.3.11.dev5 → databricks_tellr_app-0.3.11.dev7}/src/services/validator.py +0 -0
- {databricks_tellr_app-0.3.11.dev5 → databricks_tellr_app-0.3.11.dev7}/src/utils/__init__.py +0 -0
- {databricks_tellr_app-0.3.11.dev5 → databricks_tellr_app-0.3.11.dev7}/src/utils/css_utils.py +0 -0
- {databricks_tellr_app-0.3.11.dev5 → databricks_tellr_app-0.3.11.dev7}/src/utils/ds_asset_utils.py +0 -0
- {databricks_tellr_app-0.3.11.dev5 → databricks_tellr_app-0.3.11.dev7}/src/utils/error_handling.py +0 -0
- {databricks_tellr_app-0.3.11.dev5 → databricks_tellr_app-0.3.11.dev7}/src/utils/html_safety.py +0 -0
- {databricks_tellr_app-0.3.11.dev5 → databricks_tellr_app-0.3.11.dev7}/src/utils/html_utils.py +0 -0
- {databricks_tellr_app-0.3.11.dev5 → databricks_tellr_app-0.3.11.dev7}/src/utils/image_utils.py +0 -0
- {databricks_tellr_app-0.3.11.dev5 → databricks_tellr_app-0.3.11.dev7}/src/utils/js_validator.py +0 -0
- {databricks_tellr_app-0.3.11.dev5 → databricks_tellr_app-0.3.11.dev7}/src/utils/logging_config.py +0 -0
- {databricks_tellr_app-0.3.11.dev5 → databricks_tellr_app-0.3.11.dev7}/src/utils/pi_filter.py +0 -0
- {databricks_tellr_app-0.3.11.dev5 → databricks_tellr_app-0.3.11.dev7}/src/utils/slide_hash.py +0 -0
- {databricks_tellr_app-0.3.11.dev5 → databricks_tellr_app-0.3.11.dev7}/src/utils/spotlight.py +0 -0
- {databricks_tellr_app-0.3.11.dev5 → databricks_tellr_app-0.3.11.dev7}/src/utils/text_caps.py +0 -0
|
@@ -99,6 +99,7 @@ setup.py
|
|
|
99
99
|
./src/services/config_validator.py
|
|
100
100
|
./src/services/design_system_compiler.py
|
|
101
101
|
./src/services/design_system_service.py
|
|
102
|
+
./src/services/design_system_templates.py
|
|
102
103
|
./src/services/drive_uploader.py
|
|
103
104
|
./src/services/genie_service.py
|
|
104
105
|
./src/services/google_slides_auth.py
|
|
@@ -123,6 +124,7 @@ setup.py
|
|
|
123
124
|
./src/services/identity_providers/workspace_provider.py
|
|
124
125
|
./src/services/tools/__init__.py
|
|
125
126
|
./src/services/tools/agent_bricks_tool.py
|
|
127
|
+
./src/services/tools/ds_asset_tool.py
|
|
126
128
|
./src/services/tools/genie_tool.py
|
|
127
129
|
./src/services/tools/mcp_tool.py
|
|
128
130
|
./src/services/tools/model_endpoint_tool.py
|
|
@@ -243,6 +245,7 @@ src/services/config_service.py
|
|
|
243
245
|
src/services/config_validator.py
|
|
244
246
|
src/services/design_system_compiler.py
|
|
245
247
|
src/services/design_system_service.py
|
|
248
|
+
src/services/design_system_templates.py
|
|
246
249
|
src/services/drive_uploader.py
|
|
247
250
|
src/services/genie_service.py
|
|
248
251
|
src/services/google_slides_auth.py
|
|
@@ -267,6 +270,7 @@ src/services/identity_providers/local_provider.py
|
|
|
267
270
|
src/services/identity_providers/workspace_provider.py
|
|
268
271
|
src/services/tools/__init__.py
|
|
269
272
|
src/services/tools/agent_bricks_tool.py
|
|
273
|
+
src/services/tools/ds_asset_tool.py
|
|
270
274
|
src/services/tools/genie_tool.py
|
|
271
275
|
src/services/tools/mcp_tool.py
|
|
272
276
|
src/services/tools/model_endpoint_tool.py
|
{databricks_tellr_app-0.3.11.dev5 → databricks_tellr_app-0.3.11.dev7}/src/api/routes/agent_config.py
RENAMED
|
@@ -21,9 +21,25 @@ router = APIRouter(prefix="/api/sessions/{session_id}/agent-config", tags=["agen
|
|
|
21
21
|
|
|
22
22
|
|
|
23
23
|
def _validate_references(config: AgentConfig) -> None:
|
|
24
|
-
"""Validate
|
|
25
|
-
|
|
26
|
-
|
|
24
|
+
"""Validate the library references and SANITIZE the template pin, in place.
|
|
25
|
+
|
|
26
|
+
``design_system_id``, ``slide_style_id`` and ``deck_prompt_id`` stay STRICT
|
|
27
|
+
(422 on a missing/inactive entry — the existing convention). The template
|
|
28
|
+
pin is SELF-HEALING instead: template ids are re-minted by the sanctioned
|
|
29
|
+
delete+re-upload workflow, so a persisted config can legitimately hold a
|
|
30
|
+
stale pin — rejecting it would wedge EVERY later config update. When
|
|
31
|
+
``template_id`` does not resolve to a template belonging to the selected
|
|
32
|
+
design system (pin with no design system, another system's template, or a
|
|
33
|
+
missing/re-materialized template), it is cleared in place with a warning;
|
|
34
|
+
the caller persists and returns the sanitized config so the frontend state
|
|
35
|
+
syncs. This mirrors the deliberately lenient generation path.
|
|
36
|
+
"""
|
|
37
|
+
from src.database.models import (
|
|
38
|
+
DesignSystem,
|
|
39
|
+
DesignSystemTemplate,
|
|
40
|
+
SlideDeckPromptLibrary,
|
|
41
|
+
SlideStyleLibrary,
|
|
42
|
+
)
|
|
27
43
|
|
|
28
44
|
with get_db_session() as db:
|
|
29
45
|
if config.design_system_id is not None:
|
|
@@ -40,6 +56,26 @@ def _validate_references(config: AgentConfig) -> None:
|
|
|
40
56
|
status_code=422,
|
|
41
57
|
detail=f"design_system_id {config.design_system_id} not found",
|
|
42
58
|
)
|
|
59
|
+
if config.template_id is not None:
|
|
60
|
+
template = None
|
|
61
|
+
if config.design_system_id is not None:
|
|
62
|
+
template = (
|
|
63
|
+
db.query(DesignSystemTemplate)
|
|
64
|
+
.filter(
|
|
65
|
+
DesignSystemTemplate.id == config.template_id,
|
|
66
|
+
DesignSystemTemplate.design_system_id == config.design_system_id,
|
|
67
|
+
)
|
|
68
|
+
.first()
|
|
69
|
+
)
|
|
70
|
+
if template is None:
|
|
71
|
+
logger.warning(
|
|
72
|
+
"Clearing stale template pin %s (design_system_id=%s): the "
|
|
73
|
+
"template does not exist or does not belong to the selected "
|
|
74
|
+
"design system; saving the config without it",
|
|
75
|
+
config.template_id,
|
|
76
|
+
config.design_system_id,
|
|
77
|
+
)
|
|
78
|
+
config.template_id = None
|
|
43
79
|
if config.slide_style_id is not None:
|
|
44
80
|
style = (
|
|
45
81
|
db.query(SlideStyleLibrary)
|
|
@@ -115,7 +151,9 @@ async def get_agent_config(session_id: str):
|
|
|
115
151
|
async def put_agent_config(session_id: str, config: AgentConfig):
|
|
116
152
|
"""Replace the full agent config for a session."""
|
|
117
153
|
# Pydantic already validated duplicates via model_validator.
|
|
118
|
-
# Now validate foreign-key references
|
|
154
|
+
# Now validate foreign-key references; a stale template pin is sanitized
|
|
155
|
+
# (cleared in place) rather than rejected, so what is persisted and
|
|
156
|
+
# returned below is the effective config.
|
|
119
157
|
_validate_references(config)
|
|
120
158
|
|
|
121
159
|
try:
|
|
@@ -15,6 +15,7 @@ silently overwritten; the caller supplies a different name (import accepts a
|
|
|
15
15
|
"""
|
|
16
16
|
import logging
|
|
17
17
|
import os
|
|
18
|
+
import re
|
|
18
19
|
from typing import List, Optional
|
|
19
20
|
|
|
20
21
|
from fastapi import APIRouter, Depends, File, Form, HTTPException, Response, UploadFile, status
|
|
@@ -27,6 +28,8 @@ from src.database.models.design_system import (
|
|
|
27
28
|
MAX_BUNDLE_SIZE_BYTES,
|
|
28
29
|
DesignSystem,
|
|
29
30
|
DesignSystemAsset,
|
|
31
|
+
DesignSystemFile,
|
|
32
|
+
DesignSystemTemplate,
|
|
30
33
|
DesignSystemToken,
|
|
31
34
|
)
|
|
32
35
|
from src.services import design_system_service
|
|
@@ -65,6 +68,34 @@ class AssetOut(BaseModel):
|
|
|
65
68
|
url: str # served-asset endpoint (bytes are never inlined in listings)
|
|
66
69
|
|
|
67
70
|
|
|
71
|
+
class TemplateOut(BaseModel):
|
|
72
|
+
"""Picker view of one addressable template (Phase 4). The layout HTML is
|
|
73
|
+
deliberately NOT exposed here — source viewing is a later phase."""
|
|
74
|
+
id: int
|
|
75
|
+
name: str
|
|
76
|
+
description: Optional[str]
|
|
77
|
+
entry_path: str
|
|
78
|
+
thumbnail_url: Optional[str] # template-scoped thumbnail endpoint, or None
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
class DesignSystemTemplateListResponse(BaseModel):
|
|
82
|
+
templates: List[TemplateOut]
|
|
83
|
+
total: int
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
class FileEntryOut(BaseModel):
|
|
87
|
+
"""One node of the source-file tree (Phase 6) — metadata only, never bytes."""
|
|
88
|
+
path: str
|
|
89
|
+
kind: str
|
|
90
|
+
mime: str
|
|
91
|
+
size_bytes: int
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
class DesignSystemFileListResponse(BaseModel):
|
|
95
|
+
files: List[FileEntryOut]
|
|
96
|
+
total: int
|
|
97
|
+
|
|
98
|
+
|
|
68
99
|
class DesignSystemSummary(BaseModel):
|
|
69
100
|
"""List/picker view — counts only, no binary payloads."""
|
|
70
101
|
id: int
|
|
@@ -473,12 +504,123 @@ def set_default_design_system(ds_id: int, db: Session = Depends(get_db)):
|
|
|
473
504
|
)
|
|
474
505
|
|
|
475
506
|
|
|
507
|
+
def _template_thumbnail_url(ds_id: int, template_id: int) -> str:
|
|
508
|
+
return f"/api/settings/design-systems/{ds_id}/templates/{template_id}/thumbnail"
|
|
509
|
+
|
|
510
|
+
|
|
511
|
+
@router.get("/{ds_id}/templates", response_model=DesignSystemTemplateListResponse)
|
|
512
|
+
def list_design_system_templates(ds_id: int, db: Session = Depends(get_db)):
|
|
513
|
+
"""List a design system's addressable templates for the picker.
|
|
514
|
+
|
|
515
|
+
Rows are materialized lazily (from the manifest + retained bundle files) for
|
|
516
|
+
systems imported before templates were addressable entities, then persisted;
|
|
517
|
+
a system without retained template files simply lists zero templates.
|
|
518
|
+
"""
|
|
519
|
+
try:
|
|
520
|
+
ds = db.query(DesignSystem).filter(DesignSystem.id == ds_id).first()
|
|
521
|
+
if not ds:
|
|
522
|
+
raise HTTPException(
|
|
523
|
+
status_code=status.HTTP_404_NOT_FOUND,
|
|
524
|
+
detail=f"Design system {ds_id} not found",
|
|
525
|
+
)
|
|
526
|
+
from src.services.design_system_templates import materialize_templates
|
|
527
|
+
|
|
528
|
+
if materialize_templates(ds):
|
|
529
|
+
db.commit() # persist lazily-derived rows (and assign their ids)
|
|
530
|
+
|
|
531
|
+
templates = sorted(ds.templates, key=lambda t: t.id)
|
|
532
|
+
return DesignSystemTemplateListResponse(
|
|
533
|
+
templates=[
|
|
534
|
+
TemplateOut(
|
|
535
|
+
id=t.id,
|
|
536
|
+
name=t.name,
|
|
537
|
+
description=t.description,
|
|
538
|
+
entry_path=t.entry_path,
|
|
539
|
+
thumbnail_url=(
|
|
540
|
+
_template_thumbnail_url(ds_id, t.id)
|
|
541
|
+
if t.thumbnail_asset_id is not None
|
|
542
|
+
else None
|
|
543
|
+
),
|
|
544
|
+
)
|
|
545
|
+
for t in templates
|
|
546
|
+
],
|
|
547
|
+
total=len(templates),
|
|
548
|
+
)
|
|
549
|
+
except HTTPException:
|
|
550
|
+
raise
|
|
551
|
+
except Exception as e:
|
|
552
|
+
db.rollback()
|
|
553
|
+
logger.error(f"Error listing templates for design system {ds_id}: {e}", exc_info=True)
|
|
554
|
+
raise HTTPException(
|
|
555
|
+
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
|
556
|
+
detail="Failed to list design system templates",
|
|
557
|
+
)
|
|
558
|
+
|
|
559
|
+
|
|
476
560
|
# Raster image types that are safe to render inline. Anything else (notably
|
|
477
561
|
# image/svg+xml, which can carry inline <script>) is served as a download so a
|
|
478
562
|
# directly-navigated asset cannot execute script in the app origin (stored XSS).
|
|
479
563
|
_INLINE_SAFE_MIMES = frozenset({"image/png", "image/jpeg", "image/gif", "image/webp"})
|
|
480
564
|
|
|
481
565
|
|
|
566
|
+
@router.get("/{ds_id}/templates/{template_id}/thumbnail")
|
|
567
|
+
def serve_design_system_template_thumbnail(
|
|
568
|
+
ds_id: int, template_id: int, db: Session = Depends(get_db)
|
|
569
|
+
):
|
|
570
|
+
"""Serve a template's preview-screenshot bytes for the picker.
|
|
571
|
+
|
|
572
|
+
Ownership-validated end to end: the template must belong to the design
|
|
573
|
+
system in the path AND its thumbnail asset must belong to the same system —
|
|
574
|
+
404 otherwise. Served with ``X-Content-Type-Options: nosniff`` (and forced
|
|
575
|
+
to download for non-raster types), mirroring the asset endpoint.
|
|
576
|
+
"""
|
|
577
|
+
try:
|
|
578
|
+
template = (
|
|
579
|
+
db.query(DesignSystemTemplate)
|
|
580
|
+
.filter(
|
|
581
|
+
DesignSystemTemplate.id == template_id,
|
|
582
|
+
DesignSystemTemplate.design_system_id == ds_id,
|
|
583
|
+
)
|
|
584
|
+
.first()
|
|
585
|
+
)
|
|
586
|
+
if not template or template.thumbnail_asset_id is None:
|
|
587
|
+
raise HTTPException(
|
|
588
|
+
status_code=status.HTTP_404_NOT_FOUND,
|
|
589
|
+
detail=f"Thumbnail not found for template {template_id} "
|
|
590
|
+
f"of design system {ds_id}",
|
|
591
|
+
)
|
|
592
|
+
asset = (
|
|
593
|
+
db.query(DesignSystemAsset)
|
|
594
|
+
.filter(
|
|
595
|
+
DesignSystemAsset.id == template.thumbnail_asset_id,
|
|
596
|
+
DesignSystemAsset.design_system_id == ds_id,
|
|
597
|
+
)
|
|
598
|
+
.first()
|
|
599
|
+
)
|
|
600
|
+
if not asset:
|
|
601
|
+
raise HTTPException(
|
|
602
|
+
status_code=status.HTTP_404_NOT_FOUND,
|
|
603
|
+
detail=f"Thumbnail not found for template {template_id} "
|
|
604
|
+
f"of design system {ds_id}",
|
|
605
|
+
)
|
|
606
|
+
mime = str(asset.mime)
|
|
607
|
+
headers = {"X-Content-Type-Options": "nosniff"}
|
|
608
|
+
if mime not in _INLINE_SAFE_MIMES:
|
|
609
|
+
# Static value (no attacker-controlled filename) to avoid header injection.
|
|
610
|
+
headers["Content-Disposition"] = "attachment"
|
|
611
|
+
return Response(content=asset.data, media_type=mime, headers=headers)
|
|
612
|
+
except HTTPException:
|
|
613
|
+
raise
|
|
614
|
+
except Exception as e:
|
|
615
|
+
logger.error(
|
|
616
|
+
f"Error serving thumbnail for template {template_id}: {e}", exc_info=True
|
|
617
|
+
)
|
|
618
|
+
raise HTTPException(
|
|
619
|
+
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
|
620
|
+
detail="Failed to serve design system template thumbnail",
|
|
621
|
+
)
|
|
622
|
+
|
|
623
|
+
|
|
482
624
|
@router.get("/{ds_id}/assets/{asset_id}")
|
|
483
625
|
def serve_design_system_asset(ds_id: int, asset_id: int, db: Session = Depends(get_db)):
|
|
484
626
|
"""Serve a design-system asset's raw bytes (for preview + generation).
|
|
@@ -515,3 +657,191 @@ def serve_design_system_asset(ds_id: int, asset_id: int, db: Session = Depends(g
|
|
|
515
657
|
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
|
516
658
|
detail="Failed to serve design system asset",
|
|
517
659
|
)
|
|
660
|
+
|
|
661
|
+
|
|
662
|
+
# --- Source-file browser (v1 Phase 6) ----------------------------------------
|
|
663
|
+
|
|
664
|
+
# Extensions/MIMEs that are TEXT SOURCE material in the file browser. Anything
|
|
665
|
+
# matched here is served as ``text/plain`` so user-uploaded markup (HTML, SVG,
|
|
666
|
+
# JS, …) can never render or execute in the app origin — the browser is a SOURCE
|
|
667
|
+
# viewer, never a preview surface (template previews go through the thumbnail
|
|
668
|
+
# endpoint instead). The extension fallback covers rows whose stored MIME was
|
|
669
|
+
# unguessable at import time.
|
|
670
|
+
_TEXT_SOURCE_EXTENSIONS = frozenset(
|
|
671
|
+
("md", "markdown", "css", "json", "html", "htm", "js", "mjs", "svg", "txt", "xml")
|
|
672
|
+
)
|
|
673
|
+
_TEXT_SOURCE_MIMES = frozenset(
|
|
674
|
+
(
|
|
675
|
+
"application/json",
|
|
676
|
+
"application/javascript",
|
|
677
|
+
"application/ecmascript",
|
|
678
|
+
"application/xml",
|
|
679
|
+
)
|
|
680
|
+
)
|
|
681
|
+
|
|
682
|
+
# Percent-encoded '.', '/' or '\' still present AFTER the framework's one decode
|
|
683
|
+
# pass — only ever seen in double-encoding smuggling attempts, so reject outright.
|
|
684
|
+
_ENCODED_TRAVERSAL_RE = re.compile(r"%(2e|2f|5c)", re.IGNORECASE)
|
|
685
|
+
|
|
686
|
+
|
|
687
|
+
def _validated_file_path(raw: str) -> Optional[str]:
|
|
688
|
+
"""Return ``raw`` when it is a canonical bundle-relative path, else ``None``.
|
|
689
|
+
|
|
690
|
+
Stored ``design_system_file.path`` values are canonical by construction (the
|
|
691
|
+
importer normalizes them via ``_safe_relpath``), so the browser REJECTS any
|
|
692
|
+
non-canonical request instead of normalizing it into an accepted form:
|
|
693
|
+
backslashes, absolute/drive paths, empty/``.``/``..`` segments, and lingering
|
|
694
|
+
percent-encoded traversal bytes all fail. Lookups are DB-exact within one
|
|
695
|
+
design system (no filesystem involved), so this is defence-in-depth.
|
|
696
|
+
"""
|
|
697
|
+
if not raw or "\\" in raw:
|
|
698
|
+
return None
|
|
699
|
+
# NUL / C0 control characters never appear in a legitimate stored path, and
|
|
700
|
+
# NUL in particular must be rejected BEFORE the DB lookup: psycopg2 refuses
|
|
701
|
+
# NUL in a bound parameter (ValueError), which would surface as a 500
|
|
702
|
+
# instead of the uniform opaque 404 (SQLite masks this as a no-match 404).
|
|
703
|
+
if any(ord(ch) < 0x20 for ch in raw):
|
|
704
|
+
return None
|
|
705
|
+
if _ENCODED_TRAVERSAL_RE.search(raw):
|
|
706
|
+
return None
|
|
707
|
+
if raw.startswith("/") or re.match(r"^[A-Za-z]:", raw):
|
|
708
|
+
return None
|
|
709
|
+
if any(segment in ("", ".", "..") for segment in raw.split("/")):
|
|
710
|
+
return None
|
|
711
|
+
return raw
|
|
712
|
+
|
|
713
|
+
|
|
714
|
+
def _is_text_source(path: str, mime: str) -> bool:
|
|
715
|
+
"""True when a stored file is text source material (served as text/plain)."""
|
|
716
|
+
mime_l = (mime or "").lower()
|
|
717
|
+
if mime_l.startswith("text/"):
|
|
718
|
+
return True
|
|
719
|
+
if mime_l in _TEXT_SOURCE_MIMES or mime_l.endswith("+json") or mime_l.endswith("+xml"):
|
|
720
|
+
return True
|
|
721
|
+
base = path.rsplit("/", 1)[-1]
|
|
722
|
+
ext = base.rsplit(".", 1)[-1].lower() if "." in base else ""
|
|
723
|
+
return ext in _TEXT_SOURCE_EXTENSIONS
|
|
724
|
+
|
|
725
|
+
|
|
726
|
+
@router.get("/{ds_id}/files", response_model=DesignSystemFileListResponse)
|
|
727
|
+
def list_design_system_files(ds_id: int, db: Session = Depends(get_db)):
|
|
728
|
+
"""List a design system's retained bundle file tree — metadata only.
|
|
729
|
+
|
|
730
|
+
``design_system_file`` carries the COMPLETE tree for any system imported
|
|
731
|
+
since source retention (v1 Phase 1): SOURCE rows (readme/skill/css/template
|
|
732
|
+
HTML) plus path-only REFERENCE rows for every asset/font (their bytes live in
|
|
733
|
+
``design_system_asset``). The listing is a column projection — byte payloads
|
|
734
|
+
are never loaded, mirroring the blob-free list conventions above.
|
|
735
|
+
"""
|
|
736
|
+
try:
|
|
737
|
+
exists = db.query(DesignSystem.id).filter(DesignSystem.id == ds_id).first()
|
|
738
|
+
if not exists:
|
|
739
|
+
raise HTTPException(
|
|
740
|
+
status_code=status.HTTP_404_NOT_FOUND,
|
|
741
|
+
detail=f"Design system {ds_id} not found",
|
|
742
|
+
)
|
|
743
|
+
rows = (
|
|
744
|
+
db.query(
|
|
745
|
+
DesignSystemFile.path,
|
|
746
|
+
DesignSystemFile.kind,
|
|
747
|
+
DesignSystemFile.mime,
|
|
748
|
+
DesignSystemFile.size_bytes,
|
|
749
|
+
)
|
|
750
|
+
.filter(DesignSystemFile.design_system_id == ds_id)
|
|
751
|
+
.all()
|
|
752
|
+
)
|
|
753
|
+
# Sort in Python: deterministic byte order on every backend (SQL ORDER BY
|
|
754
|
+
# is collation-dependent on PostgreSQL).
|
|
755
|
+
rows = sorted(rows, key=lambda r: str(r[0]))
|
|
756
|
+
return DesignSystemFileListResponse(
|
|
757
|
+
files=[
|
|
758
|
+
FileEntryOut(path=path, kind=kind, mime=mime, size_bytes=size_bytes)
|
|
759
|
+
for path, kind, mime, size_bytes in rows
|
|
760
|
+
],
|
|
761
|
+
total=len(rows),
|
|
762
|
+
)
|
|
763
|
+
except HTTPException:
|
|
764
|
+
raise
|
|
765
|
+
except Exception as e:
|
|
766
|
+
logger.error(f"Error listing files for design system {ds_id}: {e}", exc_info=True)
|
|
767
|
+
raise HTTPException(
|
|
768
|
+
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
|
769
|
+
detail="Failed to list design system files",
|
|
770
|
+
)
|
|
771
|
+
|
|
772
|
+
|
|
773
|
+
@router.get("/{ds_id}/files/{file_path:path}")
|
|
774
|
+
def serve_design_system_file(ds_id: int, file_path: str, db: Session = Depends(get_db)):
|
|
775
|
+
"""Serve ONE stored bundle file's content — the "Open source file" endpoint.
|
|
776
|
+
|
|
777
|
+
Security posture for user-uploaded content (mirrors the thumbnail/asset
|
|
778
|
+
endpoints, hardened further because this endpoint serves markup sources):
|
|
779
|
+
|
|
780
|
+
- The requested path must be canonical (:func:`_validated_file_path` rejects
|
|
781
|
+
traversal, absolute, backslash and percent-encoded forms) and is looked up
|
|
782
|
+
by EXACT match scoped to this design system; reference rows resolve their
|
|
783
|
+
bytes through an ownership-checked ``design_system_asset`` lookup. Every
|
|
784
|
+
failure is the same opaque 404.
|
|
785
|
+
- Text sources (md/css/html/js/json/svg/…) are served as
|
|
786
|
+
``text/plain; charset=utf-8`` — uploaded markup never gets a renderable or
|
|
787
|
+
executable content type.
|
|
788
|
+
- EVERY response is ``Content-Disposition: attachment`` (static value — no
|
|
789
|
+
attacker-controlled filename, no header injection) with
|
|
790
|
+
``X-Content-Type-Options: nosniff``; nothing is served inline.
|
|
791
|
+
"""
|
|
792
|
+
not_found = HTTPException(
|
|
793
|
+
status_code=status.HTTP_404_NOT_FOUND,
|
|
794
|
+
detail=f"File not found for design system {ds_id}",
|
|
795
|
+
)
|
|
796
|
+
try:
|
|
797
|
+
validated = _validated_file_path(file_path)
|
|
798
|
+
if validated is None:
|
|
799
|
+
raise not_found
|
|
800
|
+
row = (
|
|
801
|
+
db.query(DesignSystemFile)
|
|
802
|
+
.filter(
|
|
803
|
+
DesignSystemFile.design_system_id == ds_id,
|
|
804
|
+
DesignSystemFile.path == validated,
|
|
805
|
+
)
|
|
806
|
+
.first()
|
|
807
|
+
)
|
|
808
|
+
if not row:
|
|
809
|
+
raise not_found
|
|
810
|
+
content = row.data
|
|
811
|
+
if content is None:
|
|
812
|
+
# Asset/font reference row: bytes live in design_system_asset. The
|
|
813
|
+
# asset must belong to the SAME design system — 404 otherwise.
|
|
814
|
+
if row.asset_id is None:
|
|
815
|
+
raise not_found
|
|
816
|
+
asset = (
|
|
817
|
+
db.query(DesignSystemAsset)
|
|
818
|
+
.filter(
|
|
819
|
+
DesignSystemAsset.id == row.asset_id,
|
|
820
|
+
DesignSystemAsset.design_system_id == ds_id,
|
|
821
|
+
)
|
|
822
|
+
.first()
|
|
823
|
+
)
|
|
824
|
+
if not asset:
|
|
825
|
+
raise not_found
|
|
826
|
+
content = asset.data
|
|
827
|
+
media_type = (
|
|
828
|
+
"text/plain; charset=utf-8"
|
|
829
|
+
if _is_text_source(str(row.path), str(row.mime))
|
|
830
|
+
else str(row.mime)
|
|
831
|
+
)
|
|
832
|
+
return Response(
|
|
833
|
+
content=content,
|
|
834
|
+
media_type=media_type,
|
|
835
|
+
headers={
|
|
836
|
+
"X-Content-Type-Options": "nosniff",
|
|
837
|
+
"Content-Disposition": "attachment",
|
|
838
|
+
},
|
|
839
|
+
)
|
|
840
|
+
except HTTPException:
|
|
841
|
+
raise
|
|
842
|
+
except Exception as e:
|
|
843
|
+
logger.error(f"Error serving file for design system {ds_id}: {e}", exc_info=True)
|
|
844
|
+
raise HTTPException(
|
|
845
|
+
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
|
846
|
+
detail="Failed to serve design system file",
|
|
847
|
+
)
|
|
@@ -62,6 +62,11 @@ class AgentConfig(BaseModel):
|
|
|
62
62
|
# A selected design system compiles to prompt text and, when set, takes
|
|
63
63
|
# precedence over slide_style_id (see agent_factory._get_prompt_content).
|
|
64
64
|
design_system_id: Optional[int] = None
|
|
65
|
+
# Optionally pins ONE of the selected design system's templates
|
|
66
|
+
# (design_system_template.id). Meaningful only alongside design_system_id;
|
|
67
|
+
# generation IGNORES it (with a log) unless the template exists and belongs
|
|
68
|
+
# to that design system, so a stale pin can never fail a request.
|
|
69
|
+
template_id: Optional[int] = None
|
|
65
70
|
deck_prompt_id: Optional[int] = None
|
|
66
71
|
system_prompt: Optional[str] = None
|
|
67
72
|
slide_editing_instructions: Optional[str] = None
|
|
@@ -530,6 +530,9 @@ def _run_migrations(engine, schema: str | None = None):
|
|
|
530
530
|
# --- design system: add is_active (soft-delete) to pre-existing tables ---
|
|
531
531
|
_migrate_design_system_soft_delete(conn, inspector, schema, _qual, is_sqlite)
|
|
532
532
|
|
|
533
|
+
# --- design system (v1): add font_mapping_json to pre-existing tables ---
|
|
534
|
+
_migrate_design_system_font_mapping(conn, inspector, schema, _qual, is_sqlite)
|
|
535
|
+
|
|
533
536
|
# --- keep newly created objects owned by the shared role (prod forks) ---
|
|
534
537
|
_reassign_new_objects_to_shared_owner(conn, is_sqlite)
|
|
535
538
|
|
|
@@ -537,10 +540,14 @@ def _run_migrations(engine, schema: str | None = None):
|
|
|
537
540
|
def _migrate_design_system_tables(conn, schema: str | None = None) -> None:
|
|
538
541
|
"""Create the additive design-system tables (idempotent, dialect-safe).
|
|
539
542
|
|
|
540
|
-
|
|
541
|
-
``design_system_asset``, ``design_system_token``
|
|
542
|
-
``
|
|
543
|
-
prompt path is unaffected.
|
|
543
|
+
Design System Library. Adds five NEW tables — ``design_system``,
|
|
544
|
+
``design_system_asset``, ``design_system_token``, ``design_system_file``,
|
|
545
|
+
``design_system_template`` — without touching ``slide_style_library`` or any
|
|
546
|
+
existing table, so the current slide-style prompt path is unaffected.
|
|
547
|
+
``design_system_file`` (v1 Phase 1) retains bundle source files + path
|
|
548
|
+
references, and ``design_system_template`` (v1 Phase 4) makes templates
|
|
549
|
+
individually addressable; both are created AFTER their parents so their
|
|
550
|
+
foreign keys to ``design_system`` and ``design_system_asset`` resolve.
|
|
544
551
|
|
|
545
552
|
``Base.metadata.create_all()`` (run first in ``init_db``) already creates these
|
|
546
553
|
on fresh installs; this hand-rolled step guarantees they also exist on
|
|
@@ -548,16 +555,24 @@ def _migrate_design_system_tables(conn, schema: str | None = None) -> None:
|
|
|
548
555
|
exercised on its own. Creation is driven from the ORM metadata via
|
|
549
556
|
``Table.create(checkfirst=True)`` — a single source of truth for the schema,
|
|
550
557
|
idempotent (a no-op when the table exists) and correctly compiled for both
|
|
551
|
-
PostgreSQL/Lakebase and the SQLite used in tests. Parent
|
|
558
|
+
PostgreSQL/Lakebase and the SQLite used in tests. Parent tables are created
|
|
552
559
|
first so the child foreign keys resolve.
|
|
553
560
|
"""
|
|
554
561
|
from src.database.models.design_system import (
|
|
555
562
|
DesignSystem,
|
|
556
563
|
DesignSystemAsset,
|
|
564
|
+
DesignSystemFile,
|
|
565
|
+
DesignSystemTemplate,
|
|
557
566
|
DesignSystemToken,
|
|
558
567
|
)
|
|
559
568
|
|
|
560
|
-
for model in (
|
|
569
|
+
for model in (
|
|
570
|
+
DesignSystem,
|
|
571
|
+
DesignSystemAsset,
|
|
572
|
+
DesignSystemToken,
|
|
573
|
+
DesignSystemFile,
|
|
574
|
+
DesignSystemTemplate,
|
|
575
|
+
):
|
|
561
576
|
table = model.__table__
|
|
562
577
|
# Match init_db()'s schema handling so a qualified deployment creates the
|
|
563
578
|
# tables in the Lakebase schema; guarded so it stays a no-op on repeat.
|
|
@@ -599,6 +614,37 @@ def _migrate_design_system_soft_delete(conn, inspector, schema, _qual, is_sqlite
|
|
|
599
614
|
))
|
|
600
615
|
|
|
601
616
|
|
|
617
|
+
def _migrate_design_system_font_mapping(conn, inspector, schema, _qual, is_sqlite) -> None:
|
|
618
|
+
"""Add ``font_mapping_json`` to ``design_system`` (v1 Phase 1, idempotent).
|
|
619
|
+
|
|
620
|
+
v1 Phase 1 stores a normalized font mapping (manifest ``fonts[]`` /
|
|
621
|
+
``brandFonts[]`` -> family/weight/style + token linkage) on the parent record.
|
|
622
|
+
``Base.metadata.create_all()`` adds the column on fresh installs, but does NOT
|
|
623
|
+
alter a ``design_system`` table provisioned by an earlier deploy; this
|
|
624
|
+
hand-rolled ALTER backfills it. The column is nullable (a bundle may declare
|
|
625
|
+
no fonts) so no default/backfill value is required. Column existence is probed
|
|
626
|
+
with the inspector passed from ``_run_migrations`` (or a fresh one when called
|
|
627
|
+
standalone). Dialect-safe: ``JSONB`` on PostgreSQL/Lakebase, ``JSON`` (TEXT
|
|
628
|
+
affinity) on the SQLite used in tests — matching the model's ``with_variant``
|
|
629
|
+
column and the existing ``agent_config JSON`` migration.
|
|
630
|
+
"""
|
|
631
|
+
from sqlalchemy import inspect, text
|
|
632
|
+
|
|
633
|
+
insp = inspector or inspect(conn)
|
|
634
|
+
try:
|
|
635
|
+
cols = {c["name"] for c in insp.get_columns("design_system", schema=schema)}
|
|
636
|
+
except Exception:
|
|
637
|
+
return
|
|
638
|
+
if not cols or "font_mapping_json" in cols:
|
|
639
|
+
return
|
|
640
|
+
|
|
641
|
+
col_type = "JSON" if is_sqlite else "JSONB"
|
|
642
|
+
logger.info("Migration: adding font_mapping_json column to design_system")
|
|
643
|
+
conn.execute(text(
|
|
644
|
+
f"ALTER TABLE {_qual('design_system')} ADD COLUMN font_mapping_json {col_type} NULL"
|
|
645
|
+
))
|
|
646
|
+
|
|
647
|
+
|
|
602
648
|
def _reassign_new_objects_to_shared_owner(
|
|
603
649
|
conn, is_sqlite: bool, owning_role: str = "tellr_app_owners"
|
|
604
650
|
) -> None:
|