databricks-tellr-app 0.4.3.dev20__tar.gz → 0.4.3.dev21__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.4.3.dev20 → databricks_tellr_app-0.4.3.dev21}/PKG-INFO +1 -1
- {databricks_tellr_app-0.4.3.dev20 → databricks_tellr_app-0.4.3.dev21}/databricks_tellr_app.egg-info/PKG-INFO +1 -1
- {databricks_tellr_app-0.4.3.dev20 → databricks_tellr_app-0.4.3.dev21}/databricks_tellr_app.egg-info/SOURCES.txt +38 -0
- {databricks_tellr_app-0.4.3.dev20 → databricks_tellr_app-0.4.3.dev21}/pyproject.toml +1 -1
- {databricks_tellr_app-0.4.3.dev20 → databricks_tellr_app-0.4.3.dev21}/src/api/fixtures/tour_demo_deck.json +52 -1
- {databricks_tellr_app-0.4.3.dev20 → databricks_tellr_app-0.4.3.dev21}/src/api/main.py +20 -1
- {databricks_tellr_app-0.4.3.dev20 → databricks_tellr_app-0.4.3.dev21}/src/api/routes/chat.py +68 -5
- {databricks_tellr_app-0.4.3.dev20 → databricks_tellr_app-0.4.3.dev21}/src/api/routes/sessions.py +52 -0
- {databricks_tellr_app-0.4.3.dev20 → databricks_tellr_app-0.4.3.dev21}/src/api/routes/slides.py +129 -0
- {databricks_tellr_app-0.4.3.dev20 → databricks_tellr_app-0.4.3.dev21}/src/api/routes/tour.py +28 -0
- {databricks_tellr_app-0.4.3.dev20 → databricks_tellr_app-0.4.3.dev21}/src/api/schemas/streaming.py +23 -0
- {databricks_tellr_app-0.4.3.dev20 → databricks_tellr_app-0.4.3.dev21}/src/api/services/chat_service.py +852 -13
- {databricks_tellr_app-0.4.3.dev20 → databricks_tellr_app-0.4.3.dev21}/src/api/services/deck_level_writer.py +81 -6
- {databricks_tellr_app-0.4.3.dev20 → databricks_tellr_app-0.4.3.dev21}/src/api/services/job_queue.py +15 -0
- {databricks_tellr_app-0.4.3.dev20 → databricks_tellr_app-0.4.3.dev21}/src/api/services/session_manager.py +332 -24
- databricks_tellr_app-0.4.3.dev21/src/core/skills/__init__.py +225 -0
- databricks_tellr_app-0.4.3.dev21/src/core/skills/architect.py +64 -0
- databricks_tellr_app-0.4.3.dev21/src/core/skills/build_reviewer.py +80 -0
- databricks_tellr_app-0.4.3.dev21/src/core/skills/builder.py +54 -0
- databricks_tellr_app-0.4.3.dev21/src/core/skills/data_analyst.py +38 -0
- databricks_tellr_app-0.4.3.dev21/src/core/skills/deck_reviewer.py +24 -0
- databricks_tellr_app-0.4.3.dev21/src/core/skills/fix_reviewer.py +22 -0
- databricks_tellr_app-0.4.3.dev21/src/core/skills/fixer.py +52 -0
- {databricks_tellr_app-0.4.3.dev20 → databricks_tellr_app-0.4.3.dev21}/src/domain/slide_deck.py +10 -4
- databricks_tellr_app-0.4.3.dev21/src/services/agent_factory.py +150 -0
- databricks_tellr_app-0.4.3.dev20/src/services/agent_factory.py → databricks_tellr_app-0.4.3.dev21/src/services/agent_resolution.py +253 -145
- {databricks_tellr_app-0.4.3.dev20 → databricks_tellr_app-0.4.3.dev21}/src/services/deck_css_aggregator.py +29 -8
- databricks_tellr_app-0.4.3.dev21/src/services/foreman_service.py +307 -0
- databricks_tellr_app-0.4.3.dev21/src/services/graph/__init__.py +6 -0
- databricks_tellr_app-0.4.3.dev21/src/services/graph/builder.py +265 -0
- databricks_tellr_app-0.4.3.dev21/src/services/graph/event_emitter.py +186 -0
- databricks_tellr_app-0.4.3.dev21/src/services/graph/nodes.py +2682 -0
- databricks_tellr_app-0.4.3.dev21/src/services/graph/routers.py +203 -0
- databricks_tellr_app-0.4.3.dev21/src/services/graph/state.py +307 -0
- {databricks_tellr_app-0.4.3.dev20 → databricks_tellr_app-0.4.3.dev21}/src/services/html_to_google_slides.py +1 -1
- {databricks_tellr_app-0.4.3.dev20 → databricks_tellr_app-0.4.3.dev21}/src/services/html_to_pptx.py +2 -2
- databricks_tellr_app-0.4.3.dev21/src/services/spec_sync.py +756 -0
- databricks_tellr_app-0.4.3.dev21/src/services/template_sections.py +512 -0
- databricks_tellr_app-0.4.3.dev21/src/utils/graph_safety.py +100 -0
- {databricks_tellr_app-0.4.3.dev20 → databricks_tellr_app-0.4.3.dev21}/README.md +0 -0
- {databricks_tellr_app-0.4.3.dev20 → databricks_tellr_app-0.4.3.dev21}/databricks_tellr_app/__init__.py +0 -0
- {databricks_tellr_app-0.4.3.dev20 → databricks_tellr_app-0.4.3.dev21}/databricks_tellr_app/run.py +0 -0
- {databricks_tellr_app-0.4.3.dev20 → databricks_tellr_app-0.4.3.dev21}/databricks_tellr_app.egg-info/dependency_links.txt +0 -0
- {databricks_tellr_app-0.4.3.dev20 → databricks_tellr_app-0.4.3.dev21}/databricks_tellr_app.egg-info/requires.txt +0 -0
- {databricks_tellr_app-0.4.3.dev20 → databricks_tellr_app-0.4.3.dev21}/databricks_tellr_app.egg-info/top_level.txt +0 -0
- {databricks_tellr_app-0.4.3.dev20 → databricks_tellr_app-0.4.3.dev21}/setup.cfg +0 -0
- {databricks_tellr_app-0.4.3.dev20 → databricks_tellr_app-0.4.3.dev21}/setup.py +0 -0
- {databricks_tellr_app-0.4.3.dev20 → databricks_tellr_app-0.4.3.dev21}/src/__init__.py +0 -0
- {databricks_tellr_app-0.4.3.dev20 → databricks_tellr_app-0.4.3.dev21}/src/api/__init__.py +0 -0
- {databricks_tellr_app-0.4.3.dev20 → databricks_tellr_app-0.4.3.dev21}/src/api/_otel_bootstrap.py +0 -0
- {databricks_tellr_app-0.4.3.dev20 → databricks_tellr_app-0.4.3.dev21}/src/api/mcp_auth.py +0 -0
- {databricks_tellr_app-0.4.3.dev20 → databricks_tellr_app-0.4.3.dev21}/src/api/mcp_server.py +0 -0
- {databricks_tellr_app-0.4.3.dev20 → databricks_tellr_app-0.4.3.dev21}/src/api/middleware/__init__.py +0 -0
- {databricks_tellr_app-0.4.3.dev20 → databricks_tellr_app-0.4.3.dev21}/src/api/middleware/csrf.py +0 -0
- {databricks_tellr_app-0.4.3.dev20 → databricks_tellr_app-0.4.3.dev21}/src/api/middleware/request_logging.py +0 -0
- {databricks_tellr_app-0.4.3.dev20 → databricks_tellr_app-0.4.3.dev21}/src/api/middleware/security_headers.py +0 -0
- {databricks_tellr_app-0.4.3.dev20 → databricks_tellr_app-0.4.3.dev21}/src/api/routes/__init__.py +0 -0
- {databricks_tellr_app-0.4.3.dev20 → databricks_tellr_app-0.4.3.dev21}/src/api/routes/_authz.py +0 -0
- {databricks_tellr_app-0.4.3.dev20 → databricks_tellr_app-0.4.3.dev21}/src/api/routes/admin.py +0 -0
- {databricks_tellr_app-0.4.3.dev20 → databricks_tellr_app-0.4.3.dev21}/src/api/routes/admin_usage.py +0 -0
- {databricks_tellr_app-0.4.3.dev20 → databricks_tellr_app-0.4.3.dev21}/src/api/routes/agent_config.py +0 -0
- {databricks_tellr_app-0.4.3.dev20 → databricks_tellr_app-0.4.3.dev21}/src/api/routes/deck_contributors.py +0 -0
- {databricks_tellr_app-0.4.3.dev20 → databricks_tellr_app-0.4.3.dev21}/src/api/routes/export.py +0 -0
- {databricks_tellr_app-0.4.3.dev20 → databricks_tellr_app-0.4.3.dev21}/src/api/routes/feedback.py +0 -0
- {databricks_tellr_app-0.4.3.dev20 → databricks_tellr_app-0.4.3.dev21}/src/api/routes/google_slides.py +0 -0
- {databricks_tellr_app-0.4.3.dev20 → databricks_tellr_app-0.4.3.dev21}/src/api/routes/images.py +0 -0
- {databricks_tellr_app-0.4.3.dev20 → databricks_tellr_app-0.4.3.dev21}/src/api/routes/local_version.py +0 -0
- {databricks_tellr_app-0.4.3.dev20 → databricks_tellr_app-0.4.3.dev21}/src/api/routes/profiles.py +0 -0
- {databricks_tellr_app-0.4.3.dev20 → databricks_tellr_app-0.4.3.dev21}/src/api/routes/settings/__init__.py +0 -0
- {databricks_tellr_app-0.4.3.dev20 → databricks_tellr_app-0.4.3.dev21}/src/api/routes/settings/contributors.py +0 -0
- {databricks_tellr_app-0.4.3.dev20 → databricks_tellr_app-0.4.3.dev21}/src/api/routes/settings/deck_prompts.py +0 -0
- {databricks_tellr_app-0.4.3.dev20 → databricks_tellr_app-0.4.3.dev21}/src/api/routes/settings/design_systems.py +0 -0
- {databricks_tellr_app-0.4.3.dev20 → databricks_tellr_app-0.4.3.dev21}/src/api/routes/settings/identities.py +0 -0
- {databricks_tellr_app-0.4.3.dev20 → databricks_tellr_app-0.4.3.dev21}/src/api/routes/settings/slide_styles.py +0 -0
- {databricks_tellr_app-0.4.3.dev20 → databricks_tellr_app-0.4.3.dev21}/src/api/routes/setup.py +0 -0
- {databricks_tellr_app-0.4.3.dev20 → databricks_tellr_app-0.4.3.dev21}/src/api/routes/tools.py +0 -0
- {databricks_tellr_app-0.4.3.dev20 → databricks_tellr_app-0.4.3.dev21}/src/api/routes/verification.py +0 -0
- {databricks_tellr_app-0.4.3.dev20 → databricks_tellr_app-0.4.3.dev21}/src/api/routes/version.py +0 -0
- {databricks_tellr_app-0.4.3.dev20 → databricks_tellr_app-0.4.3.dev21}/src/api/schemas/__init__.py +0 -0
- {databricks_tellr_app-0.4.3.dev20 → databricks_tellr_app-0.4.3.dev21}/src/api/schemas/agent_config.py +0 -0
- {databricks_tellr_app-0.4.3.dev20 → databricks_tellr_app-0.4.3.dev21}/src/api/schemas/feedback.py +0 -0
- {databricks_tellr_app-0.4.3.dev20 → databricks_tellr_app-0.4.3.dev21}/src/api/schemas/requests.py +0 -0
- {databricks_tellr_app-0.4.3.dev20 → databricks_tellr_app-0.4.3.dev21}/src/api/schemas/responses.py +0 -0
- {databricks_tellr_app-0.4.3.dev20 → databricks_tellr_app-0.4.3.dev21}/src/api/schemas/settings/__init__.py +0 -0
- {databricks_tellr_app-0.4.3.dev20 → databricks_tellr_app-0.4.3.dev21}/src/api/schemas/settings/requests.py +0 -0
- {databricks_tellr_app-0.4.3.dev20 → databricks_tellr_app-0.4.3.dev21}/src/api/schemas/settings/responses.py +0 -0
- {databricks_tellr_app-0.4.3.dev20 → databricks_tellr_app-0.4.3.dev21}/src/api/services/__init__.py +0 -0
- {databricks_tellr_app-0.4.3.dev20 → databricks_tellr_app-0.4.3.dev21}/src/api/services/export_job_queue.py +0 -0
- {databricks_tellr_app-0.4.3.dev20 → databricks_tellr_app-0.4.3.dev21}/src/api/services/feedback_service.py +0 -0
- {databricks_tellr_app-0.4.3.dev20 → databricks_tellr_app-0.4.3.dev21}/src/api/services/session_naming.py +0 -0
- {databricks_tellr_app-0.4.3.dev20 → databricks_tellr_app-0.4.3.dev21}/src/api/services/slide_repository.py +0 -0
- {databricks_tellr_app-0.4.3.dev20 → databricks_tellr_app-0.4.3.dev21}/src/api/services/usage_events.py +0 -0
- {databricks_tellr_app-0.4.3.dev20 → databricks_tellr_app-0.4.3.dev21}/src/api/services/usage_service.py +0 -0
- {databricks_tellr_app-0.4.3.dev20 → databricks_tellr_app-0.4.3.dev21}/src/api/utils/__init__.py +0 -0
- {databricks_tellr_app-0.4.3.dev20 → databricks_tellr_app-0.4.3.dev21}/src/api/utils/validation.py +0 -0
- {databricks_tellr_app-0.4.3.dev20 → databricks_tellr_app-0.4.3.dev21}/src/core/__init__.py +0 -0
- {databricks_tellr_app-0.4.3.dev20 → databricks_tellr_app-0.4.3.dev21}/src/core/backfill_session_slides_startup.py +0 -0
- {databricks_tellr_app-0.4.3.dev20 → databricks_tellr_app-0.4.3.dev21}/src/core/checkpointer.py +0 -0
- {databricks_tellr_app-0.4.3.dev20 → databricks_tellr_app-0.4.3.dev21}/src/core/config_loader.py +0 -0
- {databricks_tellr_app-0.4.3.dev20 → databricks_tellr_app-0.4.3.dev21}/src/core/context_utils.py +0 -0
- {databricks_tellr_app-0.4.3.dev20 → databricks_tellr_app-0.4.3.dev21}/src/core/database.py +0 -0
- {databricks_tellr_app-0.4.3.dev20 → databricks_tellr_app-0.4.3.dev21}/src/core/databricks_client.py +0 -0
- {databricks_tellr_app-0.4.3.dev20 → databricks_tellr_app-0.4.3.dev21}/src/core/defaults.py +0 -0
- {databricks_tellr_app-0.4.3.dev20 → databricks_tellr_app-0.4.3.dev21}/src/core/encryption.py +0 -0
- {databricks_tellr_app-0.4.3.dev20 → databricks_tellr_app-0.4.3.dev21}/src/core/init_default_profile.py +0 -0
- {databricks_tellr_app-0.4.3.dev20 → databricks_tellr_app-0.4.3.dev21}/src/core/lakebase.py +0 -0
- {databricks_tellr_app-0.4.3.dev20 → databricks_tellr_app-0.4.3.dev21}/src/core/migrate_profiles_to_agent_config.py +0 -0
- {databricks_tellr_app-0.4.3.dev20 → databricks_tellr_app-0.4.3.dev21}/src/core/mlflow_agent_spans.py +0 -0
- {databricks_tellr_app-0.4.3.dev20 → databricks_tellr_app-0.4.3.dev21}/src/core/mlflow_tracing.py +0 -0
- {databricks_tellr_app-0.4.3.dev20 → databricks_tellr_app-0.4.3.dev21}/src/core/otel_logging.py +0 -0
- {databricks_tellr_app-0.4.3.dev20 → databricks_tellr_app-0.4.3.dev21}/src/core/permission_context.py +0 -0
- {databricks_tellr_app-0.4.3.dev20 → databricks_tellr_app-0.4.3.dev21}/src/core/prompt_modules.py +0 -0
- {databricks_tellr_app-0.4.3.dev20 → databricks_tellr_app-0.4.3.dev21}/src/core/settings_db.py +0 -0
- {databricks_tellr_app-0.4.3.dev20 → databricks_tellr_app-0.4.3.dev21}/src/core/strip_retired_prompt_keys.py +0 -0
- {databricks_tellr_app-0.4.3.dev20 → databricks_tellr_app-0.4.3.dev21}/src/core/user_context.py +0 -0
- {databricks_tellr_app-0.4.3.dev20 → databricks_tellr_app-0.4.3.dev21}/src/database/__init__.py +0 -0
- {databricks_tellr_app-0.4.3.dev20 → databricks_tellr_app-0.4.3.dev21}/src/database/models/__init__.py +0 -0
- {databricks_tellr_app-0.4.3.dev20 → databricks_tellr_app-0.4.3.dev21}/src/database/models/deck_contributor.py +0 -0
- {databricks_tellr_app-0.4.3.dev20 → databricks_tellr_app-0.4.3.dev21}/src/database/models/deck_review.py +0 -0
- {databricks_tellr_app-0.4.3.dev20 → databricks_tellr_app-0.4.3.dev21}/src/database/models/design_system.py +0 -0
- {databricks_tellr_app-0.4.3.dev20 → databricks_tellr_app-0.4.3.dev21}/src/database/models/encryption_key.py +0 -0
- {databricks_tellr_app-0.4.3.dev20 → databricks_tellr_app-0.4.3.dev21}/src/database/models/feedback.py +0 -0
- {databricks_tellr_app-0.4.3.dev20 → databricks_tellr_app-0.4.3.dev21}/src/database/models/genie_space.py +0 -0
- {databricks_tellr_app-0.4.3.dev20 → databricks_tellr_app-0.4.3.dev21}/src/database/models/google_global_credentials.py +0 -0
- {databricks_tellr_app-0.4.3.dev20 → databricks_tellr_app-0.4.3.dev21}/src/database/models/google_oauth_token.py +0 -0
- {databricks_tellr_app-0.4.3.dev20 → databricks_tellr_app-0.4.3.dev21}/src/database/models/graph_checkpoint.py +0 -0
- {databricks_tellr_app-0.4.3.dev20 → databricks_tellr_app-0.4.3.dev21}/src/database/models/identity.py +0 -0
- {databricks_tellr_app-0.4.3.dev20 → databricks_tellr_app-0.4.3.dev21}/src/database/models/image.py +0 -0
- {databricks_tellr_app-0.4.3.dev20 → databricks_tellr_app-0.4.3.dev21}/src/database/models/profile.py +0 -0
- {databricks_tellr_app-0.4.3.dev20 → databricks_tellr_app-0.4.3.dev21}/src/database/models/profile_contributor.py +0 -0
- {databricks_tellr_app-0.4.3.dev20 → databricks_tellr_app-0.4.3.dev21}/src/database/models/prompts.py +0 -0
- {databricks_tellr_app-0.4.3.dev20 → databricks_tellr_app-0.4.3.dev21}/src/database/models/request_log.py +0 -0
- {databricks_tellr_app-0.4.3.dev20 → databricks_tellr_app-0.4.3.dev21}/src/database/models/session.py +0 -0
- {databricks_tellr_app-0.4.3.dev20 → databricks_tellr_app-0.4.3.dev21}/src/database/models/slide_deck_prompt.py +0 -0
- {databricks_tellr_app-0.4.3.dev20 → databricks_tellr_app-0.4.3.dev21}/src/database/models/slide_style_library.py +0 -0
- {databricks_tellr_app-0.4.3.dev20 → databricks_tellr_app-0.4.3.dev21}/src/database/models/usage_event.py +0 -0
- {databricks_tellr_app-0.4.3.dev20 → databricks_tellr_app-0.4.3.dev21}/src/database/models/user_preference.py +0 -0
- {databricks_tellr_app-0.4.3.dev20 → databricks_tellr_app-0.4.3.dev21}/src/database/types.py +0 -0
- {databricks_tellr_app-0.4.3.dev20 → databricks_tellr_app-0.4.3.dev21}/src/domain/__init__.py +0 -0
- {databricks_tellr_app-0.4.3.dev20 → databricks_tellr_app-0.4.3.dev21}/src/domain/deck_spec.py +0 -0
- {databricks_tellr_app-0.4.3.dev20 → databricks_tellr_app-0.4.3.dev21}/src/domain/finding.py +0 -0
- {databricks_tellr_app-0.4.3.dev20 → databricks_tellr_app-0.4.3.dev21}/src/domain/skill_io.py +0 -0
- {databricks_tellr_app-0.4.3.dev20 → databricks_tellr_app-0.4.3.dev21}/src/domain/slide.py +0 -0
- {databricks_tellr_app-0.4.3.dev20 → databricks_tellr_app-0.4.3.dev21}/src/services/__init__.py +0 -0
- {databricks_tellr_app-0.4.3.dev20 → databricks_tellr_app-0.4.3.dev21}/src/services/agent.py +0 -0
- {databricks_tellr_app-0.4.3.dev20 → databricks_tellr_app-0.4.3.dev21}/src/services/config_service.py +0 -0
- {databricks_tellr_app-0.4.3.dev20 → databricks_tellr_app-0.4.3.dev21}/src/services/config_validator.py +0 -0
- {databricks_tellr_app-0.4.3.dev20 → databricks_tellr_app-0.4.3.dev21}/src/services/converter_jail/__init__.py +0 -0
- {databricks_tellr_app-0.4.3.dev20 → databricks_tellr_app-0.4.3.dev21}/src/services/converter_jail/ast_guard.py +0 -0
- {databricks_tellr_app-0.4.3.dev20 → databricks_tellr_app-0.4.3.dev21}/src/services/converter_jail/codeprep.py +0 -0
- {databricks_tellr_app-0.4.3.dev20 → databricks_tellr_app-0.4.3.dev21}/src/services/converter_jail/gslides_runner.py +0 -0
- {databricks_tellr_app-0.4.3.dev20 → databricks_tellr_app-0.4.3.dev21}/src/services/converter_jail/jail.py +0 -0
- {databricks_tellr_app-0.4.3.dev20 → databricks_tellr_app-0.4.3.dev21}/src/services/converter_jail/pptx_runner.py +0 -0
- {databricks_tellr_app-0.4.3.dev20 → databricks_tellr_app-0.4.3.dev21}/src/services/converter_jail/protocol.py +0 -0
- {databricks_tellr_app-0.4.3.dev20 → databricks_tellr_app-0.4.3.dev21}/src/services/deck_review_store.py +0 -0
- {databricks_tellr_app-0.4.3.dev20 → databricks_tellr_app-0.4.3.dev21}/src/services/design_system_compiler.py +0 -0
- {databricks_tellr_app-0.4.3.dev20 → databricks_tellr_app-0.4.3.dev21}/src/services/design_system_service.py +0 -0
- {databricks_tellr_app-0.4.3.dev20 → databricks_tellr_app-0.4.3.dev21}/src/services/design_system_templates.py +0 -0
- {databricks_tellr_app-0.4.3.dev20 → databricks_tellr_app-0.4.3.dev21}/src/services/drive_uploader.py +0 -0
- {databricks_tellr_app-0.4.3.dev20 → databricks_tellr_app-0.4.3.dev21}/src/services/evaluation/__init__.py +0 -0
- {databricks_tellr_app-0.4.3.dev20 → databricks_tellr_app-0.4.3.dev21}/src/services/evaluation/llm_judge.py +0 -0
- {databricks_tellr_app-0.4.3.dev20 → databricks_tellr_app-0.4.3.dev21}/src/services/genie_service.py +0 -0
- {databricks_tellr_app-0.4.3.dev20 → databricks_tellr_app-0.4.3.dev21}/src/services/google_slides_auth.py +0 -0
- {databricks_tellr_app-0.4.3.dev20 → databricks_tellr_app-0.4.3.dev21}/src/services/google_slides_prompts_defaults.py +0 -0
- {databricks_tellr_app-0.4.3.dev20 → databricks_tellr_app-0.4.3.dev21}/src/services/identity_provider.py +0 -0
- {databricks_tellr_app-0.4.3.dev20 → databricks_tellr_app-0.4.3.dev21}/src/services/identity_providers/__init__.py +0 -0
- {databricks_tellr_app-0.4.3.dev20 → databricks_tellr_app-0.4.3.dev21}/src/services/identity_providers/account_provider.py +0 -0
- {databricks_tellr_app-0.4.3.dev20 → databricks_tellr_app-0.4.3.dev21}/src/services/identity_providers/local_provider.py +0 -0
- {databricks_tellr_app-0.4.3.dev20 → databricks_tellr_app-0.4.3.dev21}/src/services/identity_providers/workspace_provider.py +0 -0
- {databricks_tellr_app-0.4.3.dev20 → databricks_tellr_app-0.4.3.dev21}/src/services/image_service.py +0 -0
- {databricks_tellr_app-0.4.3.dev20 → databricks_tellr_app-0.4.3.dev21}/src/services/image_tools.py +0 -0
- {databricks_tellr_app-0.4.3.dev20 → databricks_tellr_app-0.4.3.dev21}/src/services/permission_service.py +0 -0
- {databricks_tellr_app-0.4.3.dev20 → databricks_tellr_app-0.4.3.dev21}/src/services/pptx_from_html_huashu.py +0 -0
- {databricks_tellr_app-0.4.3.dev20 → databricks_tellr_app-0.4.3.dev21}/src/services/pptx_from_records.py +0 -0
- {databricks_tellr_app-0.4.3.dev20 → databricks_tellr_app-0.4.3.dev21}/src/services/pptx_prompts_defaults.py +0 -0
- {databricks_tellr_app-0.4.3.dev20 → databricks_tellr_app-0.4.3.dev21}/src/services/profile_service.py +0 -0
- {databricks_tellr_app-0.4.3.dev20 → databricks_tellr_app-0.4.3.dev21}/src/services/streaming_callback.py +0 -0
- {databricks_tellr_app-0.4.3.dev20 → databricks_tellr_app-0.4.3.dev21}/src/services/tools/__init__.py +0 -0
- {databricks_tellr_app-0.4.3.dev20 → databricks_tellr_app-0.4.3.dev21}/src/services/tools/agent_bricks_tool.py +0 -0
- {databricks_tellr_app-0.4.3.dev20 → databricks_tellr_app-0.4.3.dev21}/src/services/tools/ds_asset_tool.py +0 -0
- {databricks_tellr_app-0.4.3.dev20 → databricks_tellr_app-0.4.3.dev21}/src/services/tools/genie_tool.py +0 -0
- {databricks_tellr_app-0.4.3.dev20 → databricks_tellr_app-0.4.3.dev21}/src/services/tools/mcp_tool.py +0 -0
- {databricks_tellr_app-0.4.3.dev20 → databricks_tellr_app-0.4.3.dev21}/src/services/tools/model_endpoint_tool.py +0 -0
- {databricks_tellr_app-0.4.3.dev20 → databricks_tellr_app-0.4.3.dev21}/src/services/tools/vector_tool.py +0 -0
- {databricks_tellr_app-0.4.3.dev20 → databricks_tellr_app-0.4.3.dev21}/src/services/validator.py +0 -0
- {databricks_tellr_app-0.4.3.dev20 → databricks_tellr_app-0.4.3.dev21}/src/utils/__init__.py +0 -0
- {databricks_tellr_app-0.4.3.dev20 → databricks_tellr_app-0.4.3.dev21}/src/utils/css_utils.py +0 -0
- {databricks_tellr_app-0.4.3.dev20 → databricks_tellr_app-0.4.3.dev21}/src/utils/ds_asset_utils.py +0 -0
- {databricks_tellr_app-0.4.3.dev20 → databricks_tellr_app-0.4.3.dev21}/src/utils/error_handling.py +0 -0
- {databricks_tellr_app-0.4.3.dev20 → databricks_tellr_app-0.4.3.dev21}/src/utils/html_safety.py +0 -0
- {databricks_tellr_app-0.4.3.dev20 → databricks_tellr_app-0.4.3.dev21}/src/utils/html_utils.py +0 -0
- {databricks_tellr_app-0.4.3.dev20 → databricks_tellr_app-0.4.3.dev21}/src/utils/image_utils.py +0 -0
- {databricks_tellr_app-0.4.3.dev20 → databricks_tellr_app-0.4.3.dev21}/src/utils/js_validator.py +0 -0
- {databricks_tellr_app-0.4.3.dev20 → databricks_tellr_app-0.4.3.dev21}/src/utils/logging_config.py +0 -0
- {databricks_tellr_app-0.4.3.dev20 → databricks_tellr_app-0.4.3.dev21}/src/utils/pi_filter.py +0 -0
- {databricks_tellr_app-0.4.3.dev20 → databricks_tellr_app-0.4.3.dev21}/src/utils/slide_hash.py +0 -0
- {databricks_tellr_app-0.4.3.dev20 → databricks_tellr_app-0.4.3.dev21}/src/utils/spotlight.py +0 -0
- {databricks_tellr_app-0.4.3.dev20 → databricks_tellr_app-0.4.3.dev21}/src/utils/text_caps.py +0 -0
|
@@ -82,6 +82,14 @@ setup.py
|
|
|
82
82
|
./src/core/settings_db.py
|
|
83
83
|
./src/core/strip_retired_prompt_keys.py
|
|
84
84
|
./src/core/user_context.py
|
|
85
|
+
./src/core/skills/__init__.py
|
|
86
|
+
./src/core/skills/architect.py
|
|
87
|
+
./src/core/skills/build_reviewer.py
|
|
88
|
+
./src/core/skills/builder.py
|
|
89
|
+
./src/core/skills/data_analyst.py
|
|
90
|
+
./src/core/skills/deck_reviewer.py
|
|
91
|
+
./src/core/skills/fix_reviewer.py
|
|
92
|
+
./src/core/skills/fixer.py
|
|
85
93
|
./src/database/__init__.py
|
|
86
94
|
./src/database/types.py
|
|
87
95
|
./src/database/models/__init__.py
|
|
@@ -114,6 +122,7 @@ setup.py
|
|
|
114
122
|
./src/services/__init__.py
|
|
115
123
|
./src/services/agent.py
|
|
116
124
|
./src/services/agent_factory.py
|
|
125
|
+
./src/services/agent_resolution.py
|
|
117
126
|
./src/services/config_service.py
|
|
118
127
|
./src/services/config_validator.py
|
|
119
128
|
./src/services/deck_css_aggregator.py
|
|
@@ -122,6 +131,7 @@ setup.py
|
|
|
122
131
|
./src/services/design_system_service.py
|
|
123
132
|
./src/services/design_system_templates.py
|
|
124
133
|
./src/services/drive_uploader.py
|
|
134
|
+
./src/services/foreman_service.py
|
|
125
135
|
./src/services/genie_service.py
|
|
126
136
|
./src/services/google_slides_auth.py
|
|
127
137
|
./src/services/google_slides_prompts_defaults.py
|
|
@@ -135,7 +145,9 @@ setup.py
|
|
|
135
145
|
./src/services/pptx_from_records.py
|
|
136
146
|
./src/services/pptx_prompts_defaults.py
|
|
137
147
|
./src/services/profile_service.py
|
|
148
|
+
./src/services/spec_sync.py
|
|
138
149
|
./src/services/streaming_callback.py
|
|
150
|
+
./src/services/template_sections.py
|
|
139
151
|
./src/services/validator.py
|
|
140
152
|
./src/services/converter_jail/__init__.py
|
|
141
153
|
./src/services/converter_jail/ast_guard.py
|
|
@@ -146,6 +158,12 @@ setup.py
|
|
|
146
158
|
./src/services/converter_jail/protocol.py
|
|
147
159
|
./src/services/evaluation/__init__.py
|
|
148
160
|
./src/services/evaluation/llm_judge.py
|
|
161
|
+
./src/services/graph/__init__.py
|
|
162
|
+
./src/services/graph/builder.py
|
|
163
|
+
./src/services/graph/event_emitter.py
|
|
164
|
+
./src/services/graph/nodes.py
|
|
165
|
+
./src/services/graph/routers.py
|
|
166
|
+
./src/services/graph/state.py
|
|
149
167
|
./src/services/identity_providers/__init__.py
|
|
150
168
|
./src/services/identity_providers/account_provider.py
|
|
151
169
|
./src/services/identity_providers/local_provider.py
|
|
@@ -161,6 +179,7 @@ setup.py
|
|
|
161
179
|
./src/utils/css_utils.py
|
|
162
180
|
./src/utils/ds_asset_utils.py
|
|
163
181
|
./src/utils/error_handling.py
|
|
182
|
+
./src/utils/graph_safety.py
|
|
164
183
|
./src/utils/html_safety.py
|
|
165
184
|
./src/utils/html_utils.py
|
|
166
185
|
./src/utils/image_utils.py
|
|
@@ -256,6 +275,14 @@ src/core/prompt_modules.py
|
|
|
256
275
|
src/core/settings_db.py
|
|
257
276
|
src/core/strip_retired_prompt_keys.py
|
|
258
277
|
src/core/user_context.py
|
|
278
|
+
src/core/skills/__init__.py
|
|
279
|
+
src/core/skills/architect.py
|
|
280
|
+
src/core/skills/build_reviewer.py
|
|
281
|
+
src/core/skills/builder.py
|
|
282
|
+
src/core/skills/data_analyst.py
|
|
283
|
+
src/core/skills/deck_reviewer.py
|
|
284
|
+
src/core/skills/fix_reviewer.py
|
|
285
|
+
src/core/skills/fixer.py
|
|
259
286
|
src/database/__init__.py
|
|
260
287
|
src/database/types.py
|
|
261
288
|
src/database/models/__init__.py
|
|
@@ -288,6 +315,7 @@ src/domain/slide_deck.py
|
|
|
288
315
|
src/services/__init__.py
|
|
289
316
|
src/services/agent.py
|
|
290
317
|
src/services/agent_factory.py
|
|
318
|
+
src/services/agent_resolution.py
|
|
291
319
|
src/services/config_service.py
|
|
292
320
|
src/services/config_validator.py
|
|
293
321
|
src/services/deck_css_aggregator.py
|
|
@@ -296,6 +324,7 @@ src/services/design_system_compiler.py
|
|
|
296
324
|
src/services/design_system_service.py
|
|
297
325
|
src/services/design_system_templates.py
|
|
298
326
|
src/services/drive_uploader.py
|
|
327
|
+
src/services/foreman_service.py
|
|
299
328
|
src/services/genie_service.py
|
|
300
329
|
src/services/google_slides_auth.py
|
|
301
330
|
src/services/google_slides_prompts_defaults.py
|
|
@@ -309,7 +338,9 @@ src/services/pptx_from_html_huashu.py
|
|
|
309
338
|
src/services/pptx_from_records.py
|
|
310
339
|
src/services/pptx_prompts_defaults.py
|
|
311
340
|
src/services/profile_service.py
|
|
341
|
+
src/services/spec_sync.py
|
|
312
342
|
src/services/streaming_callback.py
|
|
343
|
+
src/services/template_sections.py
|
|
313
344
|
src/services/validator.py
|
|
314
345
|
src/services/converter_jail/__init__.py
|
|
315
346
|
src/services/converter_jail/ast_guard.py
|
|
@@ -320,6 +351,12 @@ src/services/converter_jail/pptx_runner.py
|
|
|
320
351
|
src/services/converter_jail/protocol.py
|
|
321
352
|
src/services/evaluation/__init__.py
|
|
322
353
|
src/services/evaluation/llm_judge.py
|
|
354
|
+
src/services/graph/__init__.py
|
|
355
|
+
src/services/graph/builder.py
|
|
356
|
+
src/services/graph/event_emitter.py
|
|
357
|
+
src/services/graph/nodes.py
|
|
358
|
+
src/services/graph/routers.py
|
|
359
|
+
src/services/graph/state.py
|
|
323
360
|
src/services/identity_providers/__init__.py
|
|
324
361
|
src/services/identity_providers/account_provider.py
|
|
325
362
|
src/services/identity_providers/local_provider.py
|
|
@@ -335,6 +372,7 @@ src/utils/__init__.py
|
|
|
335
372
|
src/utils/css_utils.py
|
|
336
373
|
src/utils/ds_asset_utils.py
|
|
337
374
|
src/utils/error_handling.py
|
|
375
|
+
src/utils/graph_safety.py
|
|
338
376
|
src/utils/html_safety.py
|
|
339
377
|
src/utils/html_utils.py
|
|
340
378
|
src/utils/image_utils.py
|
|
@@ -24,5 +24,56 @@
|
|
|
24
24
|
}
|
|
25
25
|
],
|
|
26
26
|
"chat_prompt": "Create 3 slides about the benefits of AI in modern healthcare, with a title slide, key advantages, and future outlook.",
|
|
27
|
-
"chat_reply": "Here's your deck with 3 slides! I've created:\n\n1. **Title Slide** — introducing AI in healthcare\n2. **Key Advantages** — early detection, personalized treatment, operational efficiency, and drug discovery\n3. **Future Outlook** — predictive monitoring, AI-assisted surgery, and global health equity\n\nFeel free to ask me to refine any slide, add new ones, or change the style."
|
|
27
|
+
"chat_reply": "Here's your deck with 3 slides! I've created:\n\n1. **Title Slide** — introducing AI in healthcare\n2. **Key Advantages** — early detection, personalized treatment, operational efficiency, and drug discovery\n3. **Future Outlook** — predictive monitoring, AI-assisted surgery, and global health equity\n\nFeel free to ask me to refine any slide, add new ones, or change the style.",
|
|
28
|
+
"deck_spec": {
|
|
29
|
+
"title": "Example Deck — AI in Healthcare",
|
|
30
|
+
"audience": "A first-time Tellr user taking the product tour: a general business audience, with no clinical or machine-learning background assumed.",
|
|
31
|
+
"purpose": "Show a new user what a finished Tellr deck looks like, on a subject that needs no domain briefing, so they can see structure and styling before they write their own prompt.",
|
|
32
|
+
"argument": "AI already delivers concrete benefits across diagnosis, treatment and research, and its near-term trajectory points at prevention and wider access, so it is worth engaging with now rather than watching.",
|
|
33
|
+
"call_to_action": "Ask the assistant to refine a slide, add a new one, or change the style: the next step of the tour.",
|
|
34
|
+
"narrative_arc": [
|
|
35
|
+
"Frame the subject: AI is already changing patient care, diagnostics and medical research, a transformation under way rather than a forecast.",
|
|
36
|
+
"Ground that claim in four advantages visible today: earlier disease detection, personalized treatment, operational efficiency, and faster drug discovery.",
|
|
37
|
+
"Extend today's gains into the near future and land on access: predictive monitoring, AI-assisted surgery, and specialist-level care reaching underserved communities."
|
|
38
|
+
],
|
|
39
|
+
"design_contract": {
|
|
40
|
+
"design_system_id": null,
|
|
41
|
+
"template_id": null,
|
|
42
|
+
"slide_style_id": null
|
|
43
|
+
},
|
|
44
|
+
"resolved_data": {
|
|
45
|
+
"synthesis": "No live data was resolved for this deck. The tour ships fixed illustrative bytes so every user sees the same three slides; the claims are widely reported general benefits of AI in healthcare, not figures drawn from a source system.",
|
|
46
|
+
"figures": [],
|
|
47
|
+
"gaps": [
|
|
48
|
+
"No quantitative source: the deck cites no measured figure, so no claim in it is traceable to a dataset.",
|
|
49
|
+
"No organisation context: the tour deck is deliberately generic and names no customer, hospital or vendor."
|
|
50
|
+
]
|
|
51
|
+
},
|
|
52
|
+
"slides": [
|
|
53
|
+
{
|
|
54
|
+
"position": 0,
|
|
55
|
+
"purpose": "Open the deck: name the subject and frame AI in healthcare as a transformation already under way.",
|
|
56
|
+
"content_brief": "Title slide. Badge reading 'AI & Healthcare', headline 'The Benefits of AI in Modern Healthcare', and a one-line standfirst naming the three areas the deck covers: patient care, diagnostics, and medical research.",
|
|
57
|
+
"assumes": "Nothing. This is the deck's first slide.",
|
|
58
|
+
"hands_off": "The reader knows the subject and expects concrete advantages next.",
|
|
59
|
+
"data_references": []
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
"position": 1,
|
|
63
|
+
"purpose": "Make the case with four concrete present-day advantages, so the benefit claim is specific rather than atmospheric.",
|
|
64
|
+
"content_brief": "Four bolded items, one explanatory clause each: early disease detection (imaging patterns a human reader can miss), personalized treatment plans (genetics, lifestyle, history), operational efficiency (scheduling, documentation, triage), and drug discovery acceleration (molecular simulation at scale).",
|
|
65
|
+
"assumes": "The reader has accepted the framing from the title slide.",
|
|
66
|
+
"hands_off": "The present-tense case is made; the reader is ready to look forward.",
|
|
67
|
+
"data_references": []
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
"position": 2,
|
|
71
|
+
"purpose": "Extend the argument from today's gains to the near future and close on access, so the deck ends on consequence rather than a list.",
|
|
72
|
+
"content_brief": "Three bolded forward-looking items: predictive health monitoring (wearables shifting medicine from reactive to proactive), AI-assisted surgery (robotics guided by real-time analysis), and global health equity (mobile diagnostics for underserved communities). Close on the italic line about healthcare becoming intelligent, personalized and accessible to all.",
|
|
73
|
+
"assumes": "The reader accepts the four present-day advantages from the previous slide.",
|
|
74
|
+
"hands_off": "The deck ends; the reader leaves with the equity claim as the takeaway.",
|
|
75
|
+
"data_references": []
|
|
76
|
+
}
|
|
77
|
+
]
|
|
78
|
+
}
|
|
28
79
|
}
|
|
@@ -51,13 +51,14 @@ _worker_task = None
|
|
|
51
51
|
_export_worker_task = None
|
|
52
52
|
_cleanup_task = None
|
|
53
53
|
_timeout_task = None
|
|
54
|
+
_spec_sweeper_task = None
|
|
54
55
|
_frontend_assets_stack: ExitStack | None = None
|
|
55
56
|
|
|
56
57
|
|
|
57
58
|
@asynccontextmanager
|
|
58
59
|
async def lifespan(app: FastAPI):
|
|
59
60
|
"""Lifespan context manager for startup/shutdown events."""
|
|
60
|
-
global _worker_task, _export_worker_task, _cleanup_task, _timeout_task, _frontend_assets_stack
|
|
61
|
+
global _worker_task, _export_worker_task, _cleanup_task, _timeout_task, _spec_sweeper_task, _frontend_assets_stack
|
|
61
62
|
|
|
62
63
|
# Startup
|
|
63
64
|
logger.info(f"Starting AI Slide Generator API (environment: {ENVIRONMENT})")
|
|
@@ -134,6 +135,16 @@ async def lifespan(app: FastAPI):
|
|
|
134
135
|
_cleanup_task = asyncio.create_task(request_log_cleanup_loop())
|
|
135
136
|
logger.info("Request log cleanup task started")
|
|
136
137
|
|
|
138
|
+
# Start the spec-dirty arc-review sweeper. A PERIODIC LOOP, so it belongs
|
|
139
|
+
# here beside the two above and NOT in run.py::init_database: the
|
|
140
|
+
# pre-fork rule there is for migrations and backfills, which must run
|
|
141
|
+
# exactly once, and a loop started in that step would die with it. It
|
|
142
|
+
# runs in every uvicorn worker (UVICORN_WORKERS defaults to 4), which is
|
|
143
|
+
# why claim_due_marker takes an atomic lease rather than trusting a read.
|
|
144
|
+
from src.services.spec_sync import spec_review_sweeper_loop
|
|
145
|
+
_spec_sweeper_task = asyncio.create_task(spec_review_sweeper_loop())
|
|
146
|
+
logger.info("Spec-review arc sweeper started")
|
|
147
|
+
|
|
137
148
|
# Recover any stuck requests from previous crashes
|
|
138
149
|
try:
|
|
139
150
|
recovered = await recover_stuck_requests()
|
|
@@ -185,6 +196,14 @@ async def lifespan(app: FastAPI):
|
|
|
185
196
|
pass
|
|
186
197
|
logger.info("MCP job timeout sweeper stopped")
|
|
187
198
|
|
|
199
|
+
if _spec_sweeper_task:
|
|
200
|
+
_spec_sweeper_task.cancel()
|
|
201
|
+
try:
|
|
202
|
+
await _spec_sweeper_task
|
|
203
|
+
except asyncio.CancelledError:
|
|
204
|
+
pass
|
|
205
|
+
logger.info("Spec-review arc sweeper stopped")
|
|
206
|
+
|
|
188
207
|
# Tear down the FastMCP session manager's task group. Safe to call
|
|
189
208
|
# unconditionally — the stack was entered unconditionally at startup.
|
|
190
209
|
await mcp_lifespan_stack.aclose()
|
{databricks_tellr_app-0.4.3.dev20 → databricks_tellr_app-0.4.3.dev21}/src/api/routes/chat.py
RENAMED
|
@@ -26,7 +26,7 @@ from src.api.schemas.requests import ChatRequest
|
|
|
26
26
|
from src.api.schemas.responses import ChatResponse
|
|
27
27
|
from src.api.schemas.streaming import StreamEvent, StreamEventType
|
|
28
28
|
from src.api.routes._authz import _check_deck_permission_for_session
|
|
29
|
-
from src.api.services.chat_service import get_chat_service
|
|
29
|
+
from src.api.services.chat_service import get_chat_service, resolve_engine_mode_or
|
|
30
30
|
from src.api.services.job_queue import enqueue_job
|
|
31
31
|
from src.api.services.session_manager import SessionNotFoundError, get_session_manager
|
|
32
32
|
from src.core.context_utils import run_in_thread_with_context
|
|
@@ -454,6 +454,15 @@ async def send_message_streaming(
|
|
|
454
454
|
detail="Session is currently processing another request. Please wait.",
|
|
455
455
|
)
|
|
456
456
|
|
|
457
|
+
# ws4d D2 — the engine for this turn is resolved HERE, in the chat route,
|
|
458
|
+
# and passed down. Not in send_message_streaming and not in the job queue:
|
|
459
|
+
# MCP's enqueue_create_job drains through the same worker as POST
|
|
460
|
+
# /chat/async, so a resolution downstream of enqueue_job would put MCP on
|
|
461
|
+
# the graph. Resolution reads the database, so it runs off the event loop.
|
|
462
|
+
engine_mode = await asyncio.to_thread(
|
|
463
|
+
resolve_engine_mode_or, request.session_id
|
|
464
|
+
)
|
|
465
|
+
|
|
457
466
|
async def generate_events() -> AsyncGenerator[str, None]:
|
|
458
467
|
"""Generate SSE events from the chat service."""
|
|
459
468
|
import queue
|
|
@@ -484,6 +493,7 @@ async def send_message_streaming(
|
|
|
484
493
|
if request.slide_context
|
|
485
494
|
else None,
|
|
486
495
|
image_ids=request.image_ids,
|
|
496
|
+
engine_mode=engine_mode,
|
|
487
497
|
):
|
|
488
498
|
event_queue.put(event)
|
|
489
499
|
except Exception as e:
|
|
@@ -637,6 +647,14 @@ async def submit_chat_async(
|
|
|
637
647
|
request_id=request_id,
|
|
638
648
|
)
|
|
639
649
|
|
|
650
|
+
# ws4d D2 — resolved AFTER the user message is persisted just above,
|
|
651
|
+
# because resolve_engine_mode reads the deck's EARLIEST role='user'
|
|
652
|
+
# message: on turn 1 that row has to exist already or the phrase in the
|
|
653
|
+
# very first message would resolve to monolith.
|
|
654
|
+
engine_mode = await asyncio.to_thread(
|
|
655
|
+
resolve_engine_mode_or, request.session_id
|
|
656
|
+
)
|
|
657
|
+
|
|
640
658
|
# Queue for processing
|
|
641
659
|
await enqueue_job(
|
|
642
660
|
request_id,
|
|
@@ -648,6 +666,10 @@ async def submit_chat_async(
|
|
|
648
666
|
),
|
|
649
667
|
"is_first_message": is_first_message,
|
|
650
668
|
"image_ids": request.image_ids,
|
|
669
|
+
# `engine_mode`, NEVER `mode`: `mode` is already MCP's
|
|
670
|
+
# generate/edit flag in this same payload (mcp_server.py), whose
|
|
671
|
+
# values are neither "graph" nor "monolith".
|
|
672
|
+
"engine_mode": engine_mode,
|
|
651
673
|
},
|
|
652
674
|
)
|
|
653
675
|
|
|
@@ -669,18 +691,36 @@ async def submit_chat_async(
|
|
|
669
691
|
async def poll_chat(
|
|
670
692
|
request_id: str,
|
|
671
693
|
after_message_id: int = Query(default=0, description="Return messages after this ID"),
|
|
694
|
+
slide_cursor: int = Query(
|
|
695
|
+
default=0, description="Lowest slide position not yet delivered"
|
|
696
|
+
),
|
|
672
697
|
):
|
|
673
|
-
"""Poll for chat request status and
|
|
698
|
+
"""Poll for chat request status, new messages, and newly released slides.
|
|
674
699
|
|
|
675
|
-
Returns the current request status
|
|
676
|
-
|
|
700
|
+
Returns the current request status, any new messages since the last poll
|
|
701
|
+
(based on after_message_id), and any slides the reorder buffer has released
|
|
702
|
+
since ``slide_cursor``.
|
|
703
|
+
|
|
704
|
+
**The slides do NOT come through ``msg_to_stream_event``.** ws4d D3 does not
|
|
705
|
+
persist slide-ready as a ``SessionMessage``: that converter returns a fixed
|
|
706
|
+
six-key dict with nowhere to put ``position``/``html``/``scripts``, so a
|
|
707
|
+
converter that learned the type still could not carry the data — and build
|
|
708
|
+
mechanics in the transcript pollute a view that is now user-visible and
|
|
709
|
+
clearable. This reads **committed rows** instead, via
|
|
710
|
+
``SessionManager.slides_since_cursor``, which is the same reorder-buffer prefix
|
|
711
|
+
rule the graph applies to state on the SSE transport.
|
|
677
712
|
|
|
678
713
|
Args:
|
|
679
714
|
request_id: Request ID from submit_chat_async
|
|
680
715
|
after_message_id: Return messages with ID greater than this
|
|
716
|
+
slide_cursor: Lowest slide position the caller has NOT been sent. ``0``
|
|
717
|
+
(the default) asks for every released slide. Positions are 0-based,
|
|
718
|
+
so this is inclusive — unlike ``after_message_id``, whose ids start
|
|
719
|
+
at 1. Hand back the ``slide_cursor`` from the previous response.
|
|
681
720
|
|
|
682
721
|
Returns:
|
|
683
|
-
Dictionary with status, events, last_message_id,
|
|
722
|
+
Dictionary with status, events, last_message_id, result, error, slides
|
|
723
|
+
and slide_cursor
|
|
684
724
|
|
|
685
725
|
Raises:
|
|
686
726
|
HTTPException: 404 if request not found
|
|
@@ -721,10 +761,33 @@ async def poll_chat(
|
|
|
721
761
|
if m["role"] != "user"
|
|
722
762
|
]
|
|
723
763
|
|
|
764
|
+
# ws4d D3 — incremental slides on the polling transport. Projected into an
|
|
765
|
+
# explicit response shape rather than returned as-is: the row query knows
|
|
766
|
+
# about columns this endpoint has no business shipping, and the four keys
|
|
767
|
+
# below are the contract ws4e consumes.
|
|
768
|
+
released_slides = [
|
|
769
|
+
{
|
|
770
|
+
"position": row["position"],
|
|
771
|
+
"html": row["html"],
|
|
772
|
+
"scripts": row["scripts"],
|
|
773
|
+
"agent": row["agent"],
|
|
774
|
+
}
|
|
775
|
+
for row in await asyncio.to_thread(
|
|
776
|
+
session_manager.slides_since_cursor, session_id, slide_cursor
|
|
777
|
+
)
|
|
778
|
+
]
|
|
779
|
+
|
|
724
780
|
return {
|
|
725
781
|
"status": chat_request["status"],
|
|
726
782
|
"events": events,
|
|
727
783
|
"last_message_id": messages[-1]["id"] if messages else after_message_id,
|
|
728
784
|
"result": chat_request.get("result") if chat_request["status"] == "completed" else None,
|
|
729
785
|
"error": chat_request.get("error_message") if chat_request["status"] == "error" else None,
|
|
786
|
+
"slides": released_slides,
|
|
787
|
+
# The next position NOT yet delivered. Unchanged when nothing was
|
|
788
|
+
# released, so a client that keeps handing this back never re-receives a
|
|
789
|
+
# slide it already has.
|
|
790
|
+
"slide_cursor": (
|
|
791
|
+
released_slides[-1]["position"] + 1 if released_slides else slide_cursor
|
|
792
|
+
),
|
|
730
793
|
}
|
{databricks_tellr_app-0.4.3.dev20 → databricks_tellr_app-0.4.3.dev21}/src/api/routes/sessions.py
RENAMED
|
@@ -22,6 +22,7 @@ from src.api.routes._authz import (
|
|
|
22
22
|
_require_session_access,
|
|
23
23
|
)
|
|
24
24
|
from src.api.schemas.requests import CreateSessionRequest, DuplicateSessionRequest
|
|
25
|
+
from src.api.services.chat_service import get_chat_service
|
|
25
26
|
from src.api.services.session_manager import (
|
|
26
27
|
SessionAccessDeniedError,
|
|
27
28
|
SessionNotFoundError,
|
|
@@ -704,6 +705,57 @@ async def add_message(session_id: str, request: AddMessageRequest):
|
|
|
704
705
|
) from e
|
|
705
706
|
|
|
706
707
|
|
|
708
|
+
@router.delete("/{session_id}/context")
|
|
709
|
+
async def clear_session_context(session_id: str):
|
|
710
|
+
"""Clear the conversation context for a session, keeping the deck and its spec.
|
|
711
|
+
|
|
712
|
+
Drops the transcript and the graph checkpoint thread; keeps the slide deck,
|
|
713
|
+
``deck_spec_json`` (the structured compaction of what was decided) and the
|
|
714
|
+
earliest user message, which is what engine-mode resolution reads.
|
|
715
|
+
|
|
716
|
+
Requires CAN_EDIT: the helper's default is CAN_VIEW, and taking the default
|
|
717
|
+
would let a **viewer** wipe another user's transcript and graph thread.
|
|
718
|
+
|
|
719
|
+
Args:
|
|
720
|
+
session_id: Session whose context to clear
|
|
721
|
+
|
|
722
|
+
Returns:
|
|
723
|
+
Clear confirmation with the number of messages deleted
|
|
724
|
+
"""
|
|
725
|
+
# Permission check: require CAN_EDIT on the deck — NOT the helper's default.
|
|
726
|
+
await asyncio.to_thread(
|
|
727
|
+
_check_deck_permission_for_session, session_id, PermissionLevel.CAN_EDIT
|
|
728
|
+
)
|
|
729
|
+
|
|
730
|
+
try:
|
|
731
|
+
chat_service = get_chat_service()
|
|
732
|
+
result = await asyncio.to_thread(chat_service.clear_context, session_id)
|
|
733
|
+
|
|
734
|
+
logger.info(
|
|
735
|
+
"Session context cleared via API",
|
|
736
|
+
extra={
|
|
737
|
+
"session_id": session_id,
|
|
738
|
+
"deleted_messages": result.get("deleted_messages"),
|
|
739
|
+
},
|
|
740
|
+
)
|
|
741
|
+
|
|
742
|
+
return result
|
|
743
|
+
|
|
744
|
+
except SessionNotFoundError:
|
|
745
|
+
raise HTTPException(
|
|
746
|
+
status_code=404,
|
|
747
|
+
detail=f"Session not found: {session_id}",
|
|
748
|
+
)
|
|
749
|
+
except HTTPException:
|
|
750
|
+
raise
|
|
751
|
+
except Exception as e:
|
|
752
|
+
logger.error(f"Failed to clear session context: {e}", exc_info=True)
|
|
753
|
+
raise HTTPException(
|
|
754
|
+
status_code=500,
|
|
755
|
+
detail="Failed to clear session context",
|
|
756
|
+
) from e
|
|
757
|
+
|
|
758
|
+
|
|
707
759
|
@router.get("/{session_id}/slides")
|
|
708
760
|
async def get_session_slides(session_id: str):
|
|
709
761
|
"""Get slide deck for a session.
|
{databricks_tellr_app-0.4.3.dev20 → databricks_tellr_app-0.4.3.dev21}/src/api/routes/slides.py
RENAMED
|
@@ -7,6 +7,26 @@ Slide access is controlled by session permissions:
|
|
|
7
7
|
- CAN_VIEW: Can view slides (via get_slides)
|
|
8
8
|
- CAN_EDIT: Can modify slides (reorder, update, duplicate, delete)
|
|
9
9
|
- CAN_MANAGE: Full control (same as owner)
|
|
10
|
+
|
|
11
|
+
The spec-dirty trigger lives in THIS module and nowhere else
|
|
12
|
+
------------------------------------------------------------
|
|
13
|
+
Five handlers below call ``spec_sync.mark_dirty`` after their mutation commits:
|
|
14
|
+
insert, reorder, update, duplicate and delete. That is the whole of the trigger surface —
|
|
15
|
+
the LangGraph deck build calls the ``chat_service`` / ``slide_repository`` /
|
|
16
|
+
``deck_level_writer`` methods these handlers wrap, and never issues an HTTP
|
|
17
|
+
request, so "arrived via a route" *means* "a human did this" by construction. No
|
|
18
|
+
``origin=`` parameter to forget, no ContextVar to leak.
|
|
19
|
+
``tests/unit/test_spec_sync_placement.py`` fails if any other module under ``src/``
|
|
20
|
+
references the name, and pins which routes do and do not trigger. Read
|
|
21
|
+
``src/services/spec_sync.py``'s module docstring before adding or moving a call.
|
|
22
|
+
|
|
23
|
+
``get_current_user()`` is read in the handler's own request context and the author
|
|
24
|
+
passed explicitly into the worker thread — never re-read inside the thread.
|
|
25
|
+
|
|
26
|
+
Deliberately NOT triggers: the two verification routes and the three version
|
|
27
|
+
routes (a version save does not change the live deck; a restore cancels the
|
|
28
|
+
pending review instead), and session-duplicate in ``sessions.py`` (a copied spec
|
|
29
|
+
is already correct for the HTML it carries).
|
|
10
30
|
"""
|
|
11
31
|
|
|
12
32
|
import asyncio
|
|
@@ -28,6 +48,7 @@ from src.core.database import get_db
|
|
|
28
48
|
from src.core.permission_context import get_permission_context # noqa: F401 — patched by integration-test fixtures
|
|
29
49
|
from src.core.user_context import get_current_user # noqa: F401 — patched by integration-test fixtures
|
|
30
50
|
from src.database.models.profile_contributor import PermissionLevel
|
|
51
|
+
from src.services.spec_sync import mark_dirty
|
|
31
52
|
|
|
32
53
|
logger = logging.getLogger(__name__)
|
|
33
54
|
|
|
@@ -57,6 +78,27 @@ class SlideActionRequest(BaseModel):
|
|
|
57
78
|
expected_version: Optional[int] = None
|
|
58
79
|
|
|
59
80
|
|
|
81
|
+
class InsertSlideRequest(BaseModel):
|
|
82
|
+
"""Request to insert a new slide at a position.
|
|
83
|
+
|
|
84
|
+
``position`` is 0-based and NOT bounded above: a position past the end appends,
|
|
85
|
+
which is `SlideDeck.insert_slide`'s own behaviour and is deliberately not
|
|
86
|
+
re-implemented here. The lower bound IS enforced — not by a pydantic
|
|
87
|
+
constraint, but by `chat_service.insert_slide` raising ValueError, which this
|
|
88
|
+
handler turns into a 400 — because `list.insert` would read a negative position
|
|
89
|
+
as counting backwards from the end and land the slide somewhere the caller did
|
|
90
|
+
not ask for.
|
|
91
|
+
|
|
92
|
+
``html`` is optional: omitted, an empty slide is inserted for a human or the
|
|
93
|
+
architect to fill.
|
|
94
|
+
"""
|
|
95
|
+
|
|
96
|
+
session_id: str
|
|
97
|
+
position: int
|
|
98
|
+
html: Optional[str] = None
|
|
99
|
+
expected_version: Optional[int] = None
|
|
100
|
+
|
|
101
|
+
|
|
60
102
|
class UpdateVerificationRequest(BaseModel):
|
|
61
103
|
"""Request to update a slide's verification result."""
|
|
62
104
|
|
|
@@ -108,6 +150,83 @@ async def get_slides(
|
|
|
108
150
|
raise HTTPException(status_code=500, detail="Internal server error")
|
|
109
151
|
|
|
110
152
|
|
|
153
|
+
@router.post("")
|
|
154
|
+
async def insert_slide(request: InsertSlideRequest, db: Session = Depends(get_db)):
|
|
155
|
+
"""Insert a new slide, shifting every slide above the insertion point up.
|
|
156
|
+
|
|
157
|
+
Requires CAN_EDIT permission — the level is pinned by
|
|
158
|
+
``tests/unit/test_authz_slides_insert.py``, not by
|
|
159
|
+
``test_route_authz_coverage.py``, which checks that a route has *a* check and
|
|
160
|
+
cannot detect a wrong LEVEL.
|
|
161
|
+
Uses session locking to prevent concurrent modifications.
|
|
162
|
+
|
|
163
|
+
Args:
|
|
164
|
+
request: InsertSlideRequest with session_id, position and optional html
|
|
165
|
+
|
|
166
|
+
Returns:
|
|
167
|
+
Updated slide deck
|
|
168
|
+
|
|
169
|
+
Raises:
|
|
170
|
+
HTTPException: 403 if no permission, 400 for validation errors, 409 if
|
|
171
|
+
session busy or version conflict, 423 if another user holds the
|
|
172
|
+
editing lock, 500 on error
|
|
173
|
+
"""
|
|
174
|
+
_require_slide_permission(request.session_id, db, PermissionLevel.CAN_EDIT)
|
|
175
|
+
|
|
176
|
+
session_manager = get_session_manager()
|
|
177
|
+
|
|
178
|
+
try:
|
|
179
|
+
await run_in_thread_with_context(session_manager.require_editing_lock, request.session_id)
|
|
180
|
+
except PermissionError as e:
|
|
181
|
+
raise HTTPException(status_code=423, detail=str(e))
|
|
182
|
+
|
|
183
|
+
locked = await asyncio.to_thread(
|
|
184
|
+
session_manager.acquire_session_lock,
|
|
185
|
+
request.session_id,
|
|
186
|
+
)
|
|
187
|
+
if not locked:
|
|
188
|
+
raise HTTPException(
|
|
189
|
+
status_code=409,
|
|
190
|
+
detail="Session is currently processing another request. Please wait.",
|
|
191
|
+
)
|
|
192
|
+
|
|
193
|
+
try:
|
|
194
|
+
chat_service = get_chat_service()
|
|
195
|
+
result = await asyncio.to_thread(
|
|
196
|
+
chat_service.insert_slide,
|
|
197
|
+
request.session_id,
|
|
198
|
+
request.position,
|
|
199
|
+
html=request.html,
|
|
200
|
+
expected_version=request.expected_version,
|
|
201
|
+
)
|
|
202
|
+
|
|
203
|
+
logger.info(
|
|
204
|
+
"Inserted slide",
|
|
205
|
+
extra={"position": request.position, "session_id": request.session_id},
|
|
206
|
+
)
|
|
207
|
+
# A slide was added: the deck's slide list no longer matches the spec's.
|
|
208
|
+
# The service gave the new position a PLACEHOLDER spec entry with no
|
|
209
|
+
# purpose, so this is also what schedules the description of it.
|
|
210
|
+
await asyncio.to_thread(mark_dirty, request.session_id, get_current_user())
|
|
211
|
+
return result
|
|
212
|
+
|
|
213
|
+
except VersionConflictError as e:
|
|
214
|
+
raise HTTPException(status_code=409, detail=str(e))
|
|
215
|
+
except PermissionError as e:
|
|
216
|
+
raise HTTPException(status_code=423, detail=str(e))
|
|
217
|
+
except ValueError as e:
|
|
218
|
+
logger.warning(f"Validation error in insert_slide: {e}")
|
|
219
|
+
raise HTTPException(status_code=400, detail=str(e))
|
|
220
|
+
except Exception as e:
|
|
221
|
+
logger.error(f"Failed to insert slide: {e}", exc_info=True)
|
|
222
|
+
raise HTTPException(status_code=500, detail="Internal server error")
|
|
223
|
+
finally:
|
|
224
|
+
await asyncio.to_thread(
|
|
225
|
+
session_manager.release_session_lock,
|
|
226
|
+
request.session_id,
|
|
227
|
+
)
|
|
228
|
+
|
|
229
|
+
|
|
111
230
|
@router.put("/reorder")
|
|
112
231
|
async def reorder_slides(request: ReorderRequest, db: Session = Depends(get_db)):
|
|
113
232
|
"""Reorder slides.
|
|
@@ -156,6 +275,9 @@ async def reorder_slides(request: ReorderRequest, db: Session = Depends(get_db))
|
|
|
156
275
|
"Reordered slides",
|
|
157
276
|
extra={"new_order": request.new_order, "session_id": request.session_id},
|
|
158
277
|
)
|
|
278
|
+
# A reorder mutates the narrative arc with NO HTML change, so a
|
|
279
|
+
# content-hash trigger would miss it entirely.
|
|
280
|
+
await asyncio.to_thread(mark_dirty, request.session_id, get_current_user())
|
|
159
281
|
return result
|
|
160
282
|
|
|
161
283
|
except VersionConflictError as e:
|
|
@@ -224,6 +346,8 @@ async def update_slide(index: int, request: UpdateSlideRequest, db: Session = De
|
|
|
224
346
|
"Updated slide",
|
|
225
347
|
extra={"index": index, "session_id": request.session_id},
|
|
226
348
|
)
|
|
349
|
+
# The human HTML edit — the reason the marker exists.
|
|
350
|
+
await asyncio.to_thread(mark_dirty, request.session_id, get_current_user())
|
|
227
351
|
return result
|
|
228
352
|
|
|
229
353
|
except VersionConflictError as e:
|
|
@@ -292,6 +416,8 @@ async def duplicate_slide(index: int, request: SlideActionRequest, db: Session =
|
|
|
292
416
|
"Duplicated slide",
|
|
293
417
|
extra={"index": index, "session_id": request.session_id},
|
|
294
418
|
)
|
|
419
|
+
# A slide was added: the deck's slide list no longer matches the spec's.
|
|
420
|
+
await asyncio.to_thread(mark_dirty, request.session_id, get_current_user())
|
|
295
421
|
return result
|
|
296
422
|
|
|
297
423
|
except VersionConflictError as e:
|
|
@@ -366,6 +492,9 @@ async def delete_slide(
|
|
|
366
492
|
"Deleted slide",
|
|
367
493
|
extra={"index": index, "session_id": session_id},
|
|
368
494
|
)
|
|
495
|
+
# A slide was removed. Note this handler takes session_id from the query
|
|
496
|
+
# string, not a request body.
|
|
497
|
+
await asyncio.to_thread(mark_dirty, session_id, get_current_user())
|
|
369
498
|
return result
|
|
370
499
|
|
|
371
500
|
except VersionConflictError as e:
|