databricks-tellr-app 0.4.3.dev21__tar.gz → 0.4.3.dev23__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.dev21 → databricks_tellr_app-0.4.3.dev23}/PKG-INFO +1 -1
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/databricks_tellr_app.egg-info/PKG-INFO +1 -1
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/pyproject.toml +1 -1
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/src/api/services/chat_service.py +228 -46
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/src/services/graph/builder.py +19 -2
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/src/services/graph/event_emitter.py +40 -0
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/src/services/graph/nodes.py +134 -21
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/README.md +0 -0
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/databricks_tellr_app/__init__.py +0 -0
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/databricks_tellr_app/run.py +0 -0
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/databricks_tellr_app.egg-info/SOURCES.txt +0 -0
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/databricks_tellr_app.egg-info/dependency_links.txt +0 -0
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/databricks_tellr_app.egg-info/requires.txt +0 -0
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/databricks_tellr_app.egg-info/top_level.txt +0 -0
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/setup.cfg +0 -0
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/setup.py +0 -0
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/src/__init__.py +0 -0
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/src/api/__init__.py +0 -0
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/src/api/_otel_bootstrap.py +0 -0
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/src/api/fixtures/tour_demo_deck.json +0 -0
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/src/api/main.py +0 -0
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/src/api/mcp_auth.py +0 -0
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/src/api/mcp_server.py +0 -0
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/src/api/middleware/__init__.py +0 -0
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/src/api/middleware/csrf.py +0 -0
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/src/api/middleware/request_logging.py +0 -0
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/src/api/middleware/security_headers.py +0 -0
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/src/api/routes/__init__.py +0 -0
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/src/api/routes/_authz.py +0 -0
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/src/api/routes/admin.py +0 -0
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/src/api/routes/admin_usage.py +0 -0
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/src/api/routes/agent_config.py +0 -0
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/src/api/routes/chat.py +0 -0
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/src/api/routes/deck_contributors.py +0 -0
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/src/api/routes/export.py +0 -0
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/src/api/routes/feedback.py +0 -0
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/src/api/routes/google_slides.py +0 -0
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/src/api/routes/images.py +0 -0
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/src/api/routes/local_version.py +0 -0
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/src/api/routes/profiles.py +0 -0
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/src/api/routes/sessions.py +0 -0
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/src/api/routes/settings/__init__.py +0 -0
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/src/api/routes/settings/contributors.py +0 -0
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/src/api/routes/settings/deck_prompts.py +0 -0
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/src/api/routes/settings/design_systems.py +0 -0
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/src/api/routes/settings/identities.py +0 -0
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/src/api/routes/settings/slide_styles.py +0 -0
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/src/api/routes/setup.py +0 -0
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/src/api/routes/slides.py +0 -0
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/src/api/routes/tools.py +0 -0
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/src/api/routes/tour.py +0 -0
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/src/api/routes/verification.py +0 -0
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/src/api/routes/version.py +0 -0
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/src/api/schemas/__init__.py +0 -0
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/src/api/schemas/agent_config.py +0 -0
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/src/api/schemas/feedback.py +0 -0
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/src/api/schemas/requests.py +0 -0
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/src/api/schemas/responses.py +0 -0
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/src/api/schemas/settings/__init__.py +0 -0
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/src/api/schemas/settings/requests.py +0 -0
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/src/api/schemas/settings/responses.py +0 -0
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/src/api/schemas/streaming.py +0 -0
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/src/api/services/__init__.py +0 -0
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/src/api/services/deck_level_writer.py +0 -0
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/src/api/services/export_job_queue.py +0 -0
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/src/api/services/feedback_service.py +0 -0
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/src/api/services/job_queue.py +0 -0
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/src/api/services/session_manager.py +0 -0
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/src/api/services/session_naming.py +0 -0
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/src/api/services/slide_repository.py +0 -0
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/src/api/services/usage_events.py +0 -0
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/src/api/services/usage_service.py +0 -0
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/src/api/utils/__init__.py +0 -0
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/src/api/utils/validation.py +0 -0
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/src/core/__init__.py +0 -0
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/src/core/backfill_session_slides_startup.py +0 -0
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/src/core/checkpointer.py +0 -0
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/src/core/config_loader.py +0 -0
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/src/core/context_utils.py +0 -0
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/src/core/database.py +0 -0
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/src/core/databricks_client.py +0 -0
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/src/core/defaults.py +0 -0
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/src/core/encryption.py +0 -0
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/src/core/init_default_profile.py +0 -0
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/src/core/lakebase.py +0 -0
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/src/core/migrate_profiles_to_agent_config.py +0 -0
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/src/core/mlflow_agent_spans.py +0 -0
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/src/core/mlflow_tracing.py +0 -0
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/src/core/otel_logging.py +0 -0
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/src/core/permission_context.py +0 -0
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/src/core/prompt_modules.py +0 -0
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/src/core/settings_db.py +0 -0
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/src/core/skills/__init__.py +0 -0
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/src/core/skills/architect.py +0 -0
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/src/core/skills/build_reviewer.py +0 -0
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/src/core/skills/builder.py +0 -0
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/src/core/skills/data_analyst.py +0 -0
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/src/core/skills/deck_reviewer.py +0 -0
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/src/core/skills/fix_reviewer.py +0 -0
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/src/core/skills/fixer.py +0 -0
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/src/core/strip_retired_prompt_keys.py +0 -0
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/src/core/user_context.py +0 -0
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/src/database/__init__.py +0 -0
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/src/database/models/__init__.py +0 -0
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/src/database/models/deck_contributor.py +0 -0
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/src/database/models/deck_review.py +0 -0
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/src/database/models/design_system.py +0 -0
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/src/database/models/encryption_key.py +0 -0
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/src/database/models/feedback.py +0 -0
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/src/database/models/genie_space.py +0 -0
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/src/database/models/google_global_credentials.py +0 -0
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/src/database/models/google_oauth_token.py +0 -0
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/src/database/models/graph_checkpoint.py +0 -0
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/src/database/models/identity.py +0 -0
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/src/database/models/image.py +0 -0
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/src/database/models/profile.py +0 -0
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/src/database/models/profile_contributor.py +0 -0
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/src/database/models/prompts.py +0 -0
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/src/database/models/request_log.py +0 -0
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/src/database/models/session.py +0 -0
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/src/database/models/slide_deck_prompt.py +0 -0
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/src/database/models/slide_style_library.py +0 -0
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/src/database/models/usage_event.py +0 -0
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/src/database/models/user_preference.py +0 -0
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/src/database/types.py +0 -0
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/src/domain/__init__.py +0 -0
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/src/domain/deck_spec.py +0 -0
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/src/domain/finding.py +0 -0
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/src/domain/skill_io.py +0 -0
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/src/domain/slide.py +0 -0
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/src/domain/slide_deck.py +0 -0
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/src/services/__init__.py +0 -0
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/src/services/agent.py +0 -0
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/src/services/agent_factory.py +0 -0
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/src/services/agent_resolution.py +0 -0
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/src/services/config_service.py +0 -0
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/src/services/config_validator.py +0 -0
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/src/services/converter_jail/__init__.py +0 -0
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/src/services/converter_jail/ast_guard.py +0 -0
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/src/services/converter_jail/codeprep.py +0 -0
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/src/services/converter_jail/gslides_runner.py +0 -0
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/src/services/converter_jail/jail.py +0 -0
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/src/services/converter_jail/pptx_runner.py +0 -0
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/src/services/converter_jail/protocol.py +0 -0
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/src/services/deck_css_aggregator.py +0 -0
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/src/services/deck_review_store.py +0 -0
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/src/services/design_system_compiler.py +0 -0
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/src/services/design_system_service.py +0 -0
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/src/services/design_system_templates.py +0 -0
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/src/services/drive_uploader.py +0 -0
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/src/services/evaluation/__init__.py +0 -0
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/src/services/evaluation/llm_judge.py +0 -0
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/src/services/foreman_service.py +0 -0
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/src/services/genie_service.py +0 -0
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/src/services/google_slides_auth.py +0 -0
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/src/services/google_slides_prompts_defaults.py +0 -0
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/src/services/graph/__init__.py +0 -0
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/src/services/graph/routers.py +0 -0
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/src/services/graph/state.py +0 -0
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/src/services/html_to_google_slides.py +0 -0
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/src/services/html_to_pptx.py +0 -0
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/src/services/identity_provider.py +0 -0
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/src/services/identity_providers/__init__.py +0 -0
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/src/services/identity_providers/account_provider.py +0 -0
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/src/services/identity_providers/local_provider.py +0 -0
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/src/services/identity_providers/workspace_provider.py +0 -0
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/src/services/image_service.py +0 -0
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/src/services/image_tools.py +0 -0
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/src/services/permission_service.py +0 -0
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/src/services/pptx_from_html_huashu.py +0 -0
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/src/services/pptx_from_records.py +0 -0
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/src/services/pptx_prompts_defaults.py +0 -0
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/src/services/profile_service.py +0 -0
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/src/services/spec_sync.py +0 -0
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/src/services/streaming_callback.py +0 -0
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/src/services/template_sections.py +0 -0
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/src/services/tools/__init__.py +0 -0
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/src/services/tools/agent_bricks_tool.py +0 -0
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/src/services/tools/ds_asset_tool.py +0 -0
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/src/services/tools/genie_tool.py +0 -0
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/src/services/tools/mcp_tool.py +0 -0
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/src/services/tools/model_endpoint_tool.py +0 -0
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/src/services/tools/vector_tool.py +0 -0
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/src/services/validator.py +0 -0
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/src/utils/__init__.py +0 -0
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/src/utils/css_utils.py +0 -0
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/src/utils/ds_asset_utils.py +0 -0
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/src/utils/error_handling.py +0 -0
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/src/utils/graph_safety.py +0 -0
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/src/utils/html_safety.py +0 -0
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/src/utils/html_utils.py +0 -0
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/src/utils/image_utils.py +0 -0
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/src/utils/js_validator.py +0 -0
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/src/utils/logging_config.py +0 -0
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/src/utils/pi_filter.py +0 -0
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/src/utils/slide_hash.py +0 -0
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/src/utils/spotlight.py +0 -0
- {databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/src/utils/text_caps.py +0 -0
|
@@ -14,7 +14,7 @@ import re
|
|
|
14
14
|
import threading
|
|
15
15
|
import uuid
|
|
16
16
|
from datetime import datetime
|
|
17
|
-
from typing import Any, Dict, Generator, List, Optional
|
|
17
|
+
from typing import Any, Callable, Dict, Generator, List, Optional
|
|
18
18
|
|
|
19
19
|
from langchain_community.chat_message_histories import ChatMessageHistory
|
|
20
20
|
from langchain_core.messages import AIMessage, HumanMessage, ToolMessage
|
|
@@ -51,6 +51,18 @@ logger = logging.getLogger(__name__)
|
|
|
51
51
|
#: own CSS styles it, and any content here would be content nobody asked for.
|
|
52
52
|
BLANK_SLIDE_HTML = '<div class="slide"></div>'
|
|
53
53
|
|
|
54
|
+
# The deck-spec entry a newly inserted slide gets: contiguous with its neighbours
|
|
55
|
+
# (`DeckSpec.slide_at` depends on that) and deliberately undescribed. The route
|
|
56
|
+
# that inserts fires the spec-dirty marker and the arc-review sweeper writes the
|
|
57
|
+
# prose; inventing a purpose here would put words in the architect's mouth.
|
|
58
|
+
_BLANK_DECK_SPEC_ENTRY: Dict[str, Any] = {
|
|
59
|
+
"purpose": "",
|
|
60
|
+
"content_brief": "",
|
|
61
|
+
"assumes": "",
|
|
62
|
+
"hands_off": "",
|
|
63
|
+
"data_references": [],
|
|
64
|
+
}
|
|
65
|
+
|
|
54
66
|
|
|
55
67
|
def resolve_active_design_system_id(session_id: Optional[str]) -> Optional[int]:
|
|
56
68
|
"""The session's pinned/active design-system id, or None.
|
|
@@ -1117,6 +1129,7 @@ class ChatService:
|
|
|
1117
1129
|
session_id,
|
|
1118
1130
|
message,
|
|
1119
1131
|
is_first_message=is_first_message,
|
|
1132
|
+
request_id=request_id,
|
|
1120
1133
|
)
|
|
1121
1134
|
return
|
|
1122
1135
|
|
|
@@ -1788,6 +1801,7 @@ class ChatService:
|
|
|
1788
1801
|
message: str,
|
|
1789
1802
|
*,
|
|
1790
1803
|
is_first_message: bool = False,
|
|
1804
|
+
request_id: Optional[str] = None,
|
|
1791
1805
|
) -> Generator[StreamEvent, None, None]:
|
|
1792
1806
|
"""Run one turn on the LangGraph engine, yielding events as they arrive.
|
|
1793
1807
|
|
|
@@ -1835,6 +1849,14 @@ class ChatService:
|
|
|
1835
1849
|
``architect_node`` is the sole resolver of the design contract
|
|
1836
1850
|
and the template bytes.
|
|
1837
1851
|
is_first_message: Whether to generate a session title this turn.
|
|
1852
|
+
request_id: The async transport's ``ChatRequest.request_id``, handed
|
|
1853
|
+
to ``invoke_graph`` so a node persisting a chat message tags the
|
|
1854
|
+
row with it. ``GET /chat/poll`` reads assistant text through
|
|
1855
|
+
``get_messages_for_request``, which filters on that column, so
|
|
1856
|
+
without it the architect's reply is written but no polling client
|
|
1857
|
+
can see it — and polling is the transport the deployed app uses.
|
|
1858
|
+
``None`` on the SSE path, which persists no request row: that
|
|
1859
|
+
client is reading the yielded events instead.
|
|
1838
1860
|
|
|
1839
1861
|
Yields:
|
|
1840
1862
|
Every ``StreamEvent`` the graph emits, then ``COMPLETE`` carrying
|
|
@@ -1858,6 +1880,7 @@ class ChatService:
|
|
|
1858
1880
|
session_id,
|
|
1859
1881
|
{"architect_message": message},
|
|
1860
1882
|
emitter=event_queue,
|
|
1883
|
+
request_id=request_id,
|
|
1861
1884
|
)
|
|
1862
1885
|
except Exception as e:
|
|
1863
1886
|
logger.error(
|
|
@@ -3343,6 +3366,13 @@ class ChatService:
|
|
|
3343
3366
|
)
|
|
3344
3367
|
self._record_deck_version(session_id, save_result)
|
|
3345
3368
|
|
|
3369
|
+
# The deck spec's slide list has to move with the deck's. AFTER
|
|
3370
|
+
# save_slide_deck, never before: the spec write bumps deck.version, which
|
|
3371
|
+
# would make the caller's expected_version stale and 409 a legitimate save.
|
|
3372
|
+
spec_result = self._reorder_deck_spec_slides(session_id, new_order)
|
|
3373
|
+
if spec_result:
|
|
3374
|
+
self._record_deck_version(session_id, spec_result)
|
|
3375
|
+
|
|
3346
3376
|
# Create save point
|
|
3347
3377
|
try:
|
|
3348
3378
|
self.create_save_point(
|
|
@@ -3518,6 +3548,13 @@ class ChatService:
|
|
|
3518
3548
|
)
|
|
3519
3549
|
self._record_deck_version(session_id, save_result)
|
|
3520
3550
|
|
|
3551
|
+
# The deck spec's slide list has to move with the deck's. AFTER
|
|
3552
|
+
# save_slide_deck, never before: the spec write bumps deck.version, which
|
|
3553
|
+
# would make the caller's expected_version stale and 409 a legitimate save.
|
|
3554
|
+
spec_result = self._duplicate_deck_spec_slide(session_id, index)
|
|
3555
|
+
if spec_result:
|
|
3556
|
+
self._record_deck_version(session_id, spec_result)
|
|
3557
|
+
|
|
3521
3558
|
# Create save point
|
|
3522
3559
|
try:
|
|
3523
3560
|
self.create_save_point(
|
|
@@ -3660,33 +3697,66 @@ class ChatService:
|
|
|
3660
3697
|
deck_dict, _ = self._substitute_images_for_response(deck_dict, session_id=session_id)
|
|
3661
3698
|
return deck_dict
|
|
3662
3699
|
|
|
3663
|
-
def
|
|
3700
|
+
def _rewrite_deck_spec_slides(
|
|
3664
3701
|
self,
|
|
3665
3702
|
session_id: str,
|
|
3666
|
-
|
|
3703
|
+
reorder: Callable[[List[Dict[str, Any]]], Optional[List[Dict[str, Any]]]],
|
|
3704
|
+
*,
|
|
3705
|
+
label: str,
|
|
3667
3706
|
) -> Optional[Dict[str, Any]]:
|
|
3668
|
-
"""
|
|
3707
|
+
"""Re-order the deck spec's slide entries and re-stamp their positions.
|
|
3669
3708
|
|
|
3670
3709
|
A deck spec describes the deck slide by slide, keyed on ``position``
|
|
3671
3710
|
(``SlideSpec.position`` is the canonical identity, looked up by
|
|
3672
|
-
``DeckSpec.slide_at`` and never by list index).
|
|
3673
|
-
|
|
3674
|
-
its neighbour.
|
|
3675
|
-
|
|
3676
|
-
|
|
3677
|
-
|
|
3678
|
-
|
|
3679
|
-
|
|
3680
|
-
|
|
3711
|
+
``DeckSpec.slide_at`` and never by list index). Any mutation that moves,
|
|
3712
|
+
adds or removes a slide must move its spec entry with it, or every entry
|
|
3713
|
+
past the change describes its neighbour.
|
|
3714
|
+
|
|
3715
|
+
Before this existed, ``deck_spec_json`` was renumbered on INSERT and on
|
|
3716
|
+
nothing else (final review C1), so after a delete, a duplicate or a reorder
|
|
3717
|
+
the deck-level spec and the per-row ``deck_spec_slide`` fragments — two
|
|
3718
|
+
representations of the same thing — disagreed, and three consumers read the
|
|
3719
|
+
deck-level one: the builder brief, §4.6's re-review and ``architect_node``'s
|
|
3720
|
+
edit-turn fallback. Measured: an edit turn dispatched a builder for a
|
|
3721
|
+
position with no slide and the deck gained one nobody asked for.
|
|
3722
|
+
|
|
3723
|
+
WHY ONE PRIMITIVE AND NOT FOUR WRITERS
|
|
3724
|
+
--------------------------------------
|
|
3725
|
+
Four hand-copied row-writers diverging is the defect class the previous
|
|
3726
|
+
workstream's final review found, and C1 is itself an instance of it — insert
|
|
3727
|
+
renumbered, its three siblings did not. So the four routes supply only
|
|
3728
|
+
*which entries end up in which order*; the position stamping, the raw-dict
|
|
3729
|
+
handling, the author stamp and the failure policy live here once.
|
|
3730
|
+
|
|
3731
|
+
*reorder* receives the positioned entries **in position order** and returns
|
|
3732
|
+
the new list in the new order — it does not touch ``position`` at all, which
|
|
3733
|
+
this method re-stamps contiguously from 0. Returning ``None`` leaves the
|
|
3734
|
+
spec untouched.
|
|
3735
|
+
|
|
3736
|
+
Operates on the RAW spec dict rather than parsing a ``DeckSpec``: a spec that
|
|
3737
|
+
no longer validates (an older shape, a hand-edited column) must not be
|
|
3738
|
+
destroyed by a renumber, and nothing here reads a field other than
|
|
3739
|
+
``position``. Entries that are not dicts, or whose ``position`` is not an
|
|
3740
|
+
int, cannot be identified with a slide, so they are carried through
|
|
3741
|
+
**unchanged and last** rather than silently permuted.
|
|
3742
|
+
|
|
3743
|
+
A spec write that fails must never fail the mutation: the slide change is
|
|
3744
|
+
already committed by the time this runs, and the route's spec-dirty marker
|
|
3745
|
+
still fires, so the sweeper re-describes the deck either way.
|
|
3681
3746
|
|
|
3682
|
-
|
|
3683
|
-
|
|
3684
|
-
|
|
3747
|
+
Args:
|
|
3748
|
+
session_id: The mutating session. Resolves to the deck owner in the
|
|
3749
|
+
layers below, exactly as every other mutation does.
|
|
3750
|
+
reorder: Takes the positioned entries in position order, returns them in
|
|
3751
|
+
their new order, or ``None`` to leave the spec alone. Raises
|
|
3752
|
+
``IndexError`` when the spec cannot answer the mutation.
|
|
3753
|
+
label: Route name, for the log lines only.
|
|
3685
3754
|
|
|
3686
3755
|
Returns:
|
|
3687
3756
|
The writer's result dict (carrying the new deck ``version``), or None
|
|
3688
|
-
when there was no spec to
|
|
3689
|
-
is
|
|
3757
|
+
when there was no spec to renumber, the spec was unreadable, or
|
|
3758
|
+
*reorder* declined — none of which is an error. A deck built before the
|
|
3759
|
+
spec column existed has no spec.
|
|
3690
3760
|
"""
|
|
3691
3761
|
# Imported here, not at module scope: src.api.services.__init__ imports
|
|
3692
3762
|
# chat_service, so a module-level import of a sibling service is circular.
|
|
@@ -3698,7 +3768,7 @@ class ChatService:
|
|
|
3698
3768
|
try:
|
|
3699
3769
|
spec = read_deck_spec(session_id)
|
|
3700
3770
|
except Exception as e:
|
|
3701
|
-
logger.warning(f"Failed to read deck spec (
|
|
3771
|
+
logger.warning(f"Failed to read deck spec ({label}): {e}")
|
|
3702
3772
|
return None
|
|
3703
3773
|
|
|
3704
3774
|
if not spec:
|
|
@@ -3708,37 +3778,48 @@ class ChatService:
|
|
|
3708
3778
|
if not isinstance(slides, list):
|
|
3709
3779
|
logger.warning(
|
|
3710
3780
|
"deck_spec_json has no slides list; leaving it untouched",
|
|
3711
|
-
extra={"session_id": session_id},
|
|
3781
|
+
extra={"session_id": session_id, "route": label},
|
|
3712
3782
|
)
|
|
3713
3783
|
return None
|
|
3714
3784
|
|
|
3715
|
-
|
|
3785
|
+
# One pass, and identity is never inferred from equality: two entries with
|
|
3786
|
+
# the same content are still two entries.
|
|
3787
|
+
positioned: List[Dict[str, Any]] = []
|
|
3788
|
+
unpositioned: List[Any] = []
|
|
3716
3789
|
for entry in slides:
|
|
3717
|
-
if
|
|
3718
|
-
|
|
3719
|
-
|
|
3790
|
+
if isinstance(entry, dict) and isinstance(entry.get("position"), int):
|
|
3791
|
+
positioned.append(entry)
|
|
3792
|
+
else:
|
|
3793
|
+
unpositioned.append(entry)
|
|
3794
|
+
positioned.sort(key=lambda e: e["position"])
|
|
3795
|
+
|
|
3796
|
+
try:
|
|
3797
|
+
rewritten = reorder(list(positioned))
|
|
3798
|
+
except IndexError:
|
|
3799
|
+
# The route validated its index against the DECK, so an index the spec
|
|
3800
|
+
# cannot answer means the two have already drifted. Leaving a drifted
|
|
3801
|
+
# spec alone is recoverable; renumbering it against the wrong slides is
|
|
3802
|
+
# not.
|
|
3803
|
+
logger.warning(
|
|
3804
|
+
"deck spec has %s entries and cannot answer this mutation; "
|
|
3805
|
+
"leaving it untouched",
|
|
3806
|
+
len(positioned),
|
|
3807
|
+
extra={"session_id": session_id, "route": label},
|
|
3808
|
+
)
|
|
3809
|
+
return None
|
|
3810
|
+
|
|
3811
|
+
if rewritten is None:
|
|
3812
|
+
return None
|
|
3813
|
+
|
|
3814
|
+
renumbered: List[Any] = []
|
|
3815
|
+
for index, entry in enumerate(rewritten):
|
|
3720
3816
|
entry = dict(entry)
|
|
3721
|
-
|
|
3722
|
-
|
|
3723
|
-
|
|
3724
|
-
shifted.append(entry)
|
|
3725
|
-
|
|
3726
|
-
shifted.append(
|
|
3727
|
-
{
|
|
3728
|
-
"position": position,
|
|
3729
|
-
"purpose": "",
|
|
3730
|
-
"content_brief": "",
|
|
3731
|
-
"assumes": "",
|
|
3732
|
-
"hands_off": "",
|
|
3733
|
-
"data_references": [],
|
|
3734
|
-
}
|
|
3735
|
-
)
|
|
3736
|
-
shifted.sort(
|
|
3737
|
-
key=lambda e: e.get("position", 0) if isinstance(e, dict) else 0
|
|
3738
|
-
)
|
|
3817
|
+
entry["position"] = index
|
|
3818
|
+
renumbered.append(entry)
|
|
3819
|
+
renumbered.extend(unpositioned)
|
|
3739
3820
|
|
|
3740
3821
|
spec = dict(spec)
|
|
3741
|
-
spec["slides"] =
|
|
3822
|
+
spec["slides"] = renumbered
|
|
3742
3823
|
|
|
3743
3824
|
try:
|
|
3744
3825
|
_user = get_current_username()
|
|
@@ -3752,11 +3833,105 @@ class ChatService:
|
|
|
3752
3833
|
modified_by=_user,
|
|
3753
3834
|
)
|
|
3754
3835
|
except Exception as e:
|
|
3755
|
-
|
|
3756
|
-
# write that fails must not fail the insert.
|
|
3757
|
-
logger.warning(f"Failed to shift deck spec (insert_slide): {e}")
|
|
3836
|
+
logger.warning(f"Failed to write deck spec ({label}): {e}")
|
|
3758
3837
|
return None
|
|
3759
3838
|
|
|
3839
|
+
def _insert_deck_spec_slide(
|
|
3840
|
+
self,
|
|
3841
|
+
session_id: str,
|
|
3842
|
+
position: int,
|
|
3843
|
+
) -> Optional[Dict[str, Any]]:
|
|
3844
|
+
"""Give the deck spec a placeholder entry at *position*, shifting the rest up.
|
|
3845
|
+
|
|
3846
|
+
*position* is where the slide ACTUALLY landed, which the caller computes
|
|
3847
|
+
before the insert: ``SlideDeck.insert_slide`` delegates to ``list.insert``,
|
|
3848
|
+
which clamps a too-large position into an append, so the requested position
|
|
3849
|
+
and the real one diverge on an append. The clamp is re-applied here against
|
|
3850
|
+
the spec's own length rather than trusted, because a spec shorter than the
|
|
3851
|
+
deck would otherwise leave a hole.
|
|
3852
|
+
"""
|
|
3853
|
+
|
|
3854
|
+
def _splice(entries: List[Dict[str, Any]]) -> List[Dict[str, Any]]:
|
|
3855
|
+
landed = min(position, len(entries))
|
|
3856
|
+
return (
|
|
3857
|
+
entries[:landed]
|
|
3858
|
+
+ [dict(_BLANK_DECK_SPEC_ENTRY)]
|
|
3859
|
+
+ entries[landed:]
|
|
3860
|
+
)
|
|
3861
|
+
|
|
3862
|
+
return self._rewrite_deck_spec_slides(
|
|
3863
|
+
session_id, _splice, label="insert_slide"
|
|
3864
|
+
)
|
|
3865
|
+
|
|
3866
|
+
def _delete_deck_spec_slide(
|
|
3867
|
+
self,
|
|
3868
|
+
session_id: str,
|
|
3869
|
+
index: int,
|
|
3870
|
+
) -> Optional[Dict[str, Any]]:
|
|
3871
|
+
"""Drop the deleted slide's spec entry and shift every higher entry down."""
|
|
3872
|
+
|
|
3873
|
+
def _drop(entries: List[Dict[str, Any]]) -> List[Dict[str, Any]]:
|
|
3874
|
+
if index >= len(entries):
|
|
3875
|
+
raise IndexError(index)
|
|
3876
|
+
return entries[:index] + entries[index + 1 :]
|
|
3877
|
+
|
|
3878
|
+
return self._rewrite_deck_spec_slides(
|
|
3879
|
+
session_id, _drop, label="delete_slide"
|
|
3880
|
+
)
|
|
3881
|
+
|
|
3882
|
+
def _duplicate_deck_spec_slide(
|
|
3883
|
+
self,
|
|
3884
|
+
session_id: str,
|
|
3885
|
+
index: int,
|
|
3886
|
+
) -> Optional[Dict[str, Any]]:
|
|
3887
|
+
"""Copy the duplicated slide's spec entry in beside it.
|
|
3888
|
+
|
|
3889
|
+
The clone gets its SOURCE's brief, not a blank one: a duplicate is a copy of
|
|
3890
|
+
a slide we already have a description for, so that description is known to be
|
|
3891
|
+
right, where insert's newcomer has none. The route's spec-dirty marker fires
|
|
3892
|
+
either way, so the sweeper re-describes both — this decides which starting
|
|
3893
|
+
point is true, not who writes the final prose.
|
|
3894
|
+
"""
|
|
3895
|
+
|
|
3896
|
+
def _clone(entries: List[Dict[str, Any]]) -> List[Dict[str, Any]]:
|
|
3897
|
+
if index >= len(entries):
|
|
3898
|
+
raise IndexError(index)
|
|
3899
|
+
return (
|
|
3900
|
+
entries[: index + 1]
|
|
3901
|
+
+ [dict(entries[index])]
|
|
3902
|
+
+ entries[index + 1 :]
|
|
3903
|
+
)
|
|
3904
|
+
|
|
3905
|
+
return self._rewrite_deck_spec_slides(
|
|
3906
|
+
session_id, _clone, label="duplicate_slide"
|
|
3907
|
+
)
|
|
3908
|
+
|
|
3909
|
+
def _reorder_deck_spec_slides(
|
|
3910
|
+
self,
|
|
3911
|
+
session_id: str,
|
|
3912
|
+
new_order: List[int],
|
|
3913
|
+
) -> Optional[Dict[str, Any]]:
|
|
3914
|
+
"""Permute the spec entries by the same permutation the slides took.
|
|
3915
|
+
|
|
3916
|
+
``new_order[j]`` is the OLD index of the slide that now sits at *j* — the
|
|
3917
|
+
route validates it is a true permutation of ``range(len(slides))`` before
|
|
3918
|
+
reaching here.
|
|
3919
|
+
|
|
3920
|
+
This is the case the consumer-side guard structurally cannot cover: a reorder
|
|
3921
|
+
preserves the position SET, so ``_persisted_spec_describes_these_rows`` reads
|
|
3922
|
+
a reordered deck as aligned while every entry describes a different slide.
|
|
3923
|
+
Renumbering in the route is the only thing that closes it.
|
|
3924
|
+
"""
|
|
3925
|
+
|
|
3926
|
+
def _permute(entries: List[Dict[str, Any]]) -> List[Dict[str, Any]]:
|
|
3927
|
+
if len(new_order) != len(entries):
|
|
3928
|
+
raise IndexError(len(new_order))
|
|
3929
|
+
return [entries[old] for old in new_order]
|
|
3930
|
+
|
|
3931
|
+
return self._rewrite_deck_spec_slides(
|
|
3932
|
+
session_id, _permute, label="reorder_slides"
|
|
3933
|
+
)
|
|
3934
|
+
|
|
3760
3935
|
def delete_slide(self, session_id: str, index: int, *, expected_version: Optional[int] = None) -> Dict[str, Any]:
|
|
3761
3936
|
"""Delete a slide.
|
|
3762
3937
|
|
|
@@ -3799,6 +3974,13 @@ class ChatService:
|
|
|
3799
3974
|
)
|
|
3800
3975
|
self._record_deck_version(session_id, save_result)
|
|
3801
3976
|
|
|
3977
|
+
# The deck spec's slide list has to move with the deck's. AFTER
|
|
3978
|
+
# save_slide_deck, never before: the spec write bumps deck.version, which
|
|
3979
|
+
# would make the caller's expected_version stale and 409 a legitimate save.
|
|
3980
|
+
spec_result = self._delete_deck_spec_slide(session_id, index)
|
|
3981
|
+
if spec_result:
|
|
3982
|
+
self._record_deck_version(session_id, spec_result)
|
|
3983
|
+
|
|
3802
3984
|
# Create save point
|
|
3803
3985
|
try:
|
|
3804
3986
|
self.create_save_point(
|
{databricks_tellr_app-0.4.3.dev21 → databricks_tellr_app-0.4.3.dev23}/src/services/graph/builder.py
RENAMED
|
@@ -73,7 +73,10 @@ from langgraph.graph import END, START, StateGraph
|
|
|
73
73
|
from src.core.checkpointer import get_checkpointer
|
|
74
74
|
from src.core.user_context import get_current_user
|
|
75
75
|
from src.services.foreman_service import CAP
|
|
76
|
-
from src.services.graph.event_emitter import
|
|
76
|
+
from src.services.graph.event_emitter import (
|
|
77
|
+
set_chat_request_id,
|
|
78
|
+
set_event_emitter,
|
|
79
|
+
)
|
|
77
80
|
from src.services.graph.nodes import (
|
|
78
81
|
architect_node,
|
|
79
82
|
build_reviewer_node,
|
|
@@ -188,6 +191,7 @@ def invoke_graph(
|
|
|
188
191
|
emitter: Any = None,
|
|
189
192
|
principal: Optional[str] = None,
|
|
190
193
|
describe_only: bool = False,
|
|
194
|
+
request_id: Optional[str] = None,
|
|
191
195
|
) -> Dict[str, Any]:
|
|
192
196
|
"""Run one turn of the graph for *session_id*.
|
|
193
197
|
|
|
@@ -210,6 +214,14 @@ def invoke_graph(
|
|
|
210
214
|
arc-review sweeper passes it: that turn exists to re-describe the
|
|
211
215
|
deck a human hand-edited, and dispatching builders would overwrite
|
|
212
216
|
the very edits that scheduled it.
|
|
217
|
+
request_id: The ``ChatRequest.request_id`` this turn is answering, or
|
|
218
|
+
``None``. Bound in a ``ContextVar`` so a node persisting a chat
|
|
219
|
+
message can TAG it: ``GET /chat/poll`` reads a turn's assistant text
|
|
220
|
+
through ``get_messages_for_request``, which filters on that column,
|
|
221
|
+
so an untagged row is invisible to every polling client — the
|
|
222
|
+
transport the deployed app uses. ``None`` is correct on the SSE path
|
|
223
|
+
(no request row exists) and on the sweeper (no request at all); those
|
|
224
|
+
rows are still durable and still reach ``_conversation``.
|
|
213
225
|
|
|
214
226
|
``describe_only`` is turn-scoped, and it is wrapped HERE because this is the
|
|
215
227
|
only place that knows ``turn_id``. It is written on **every** invocation,
|
|
@@ -231,12 +243,17 @@ def invoke_graph(
|
|
|
231
243
|
|
|
232
244
|
The emitter is set on every invocation, ``None`` included: that is the reset
|
|
233
245
|
a turn resumed in a new process needs, or events queue into the first
|
|
234
|
-
process's queue forever.
|
|
246
|
+
process's queue forever. ``request_id`` is set unconditionally for the same
|
|
247
|
+
reason, and the failure it prevents is worse than a lost event: a stale id
|
|
248
|
+
left in the var would tag turn 2's chat rows with turn 1's request, so turn
|
|
249
|
+
1's poll would receive turn 2's reply and turn 2's own poll would never see
|
|
250
|
+
it.
|
|
235
251
|
"""
|
|
236
252
|
turn_id = uuid.uuid4().hex
|
|
237
253
|
initiated_by = principal or get_current_user()
|
|
238
254
|
|
|
239
255
|
set_event_emitter(emitter)
|
|
256
|
+
set_chat_request_id(request_id)
|
|
240
257
|
|
|
241
258
|
state: Dict[str, Any] = dict(initial or {})
|
|
242
259
|
state.update(
|
|
@@ -69,6 +69,46 @@ slide_cursor_var: ContextVar[Optional[list]] = ContextVar(
|
|
|
69
69
|
"graph_slide_cursor", default=None
|
|
70
70
|
)
|
|
71
71
|
|
|
72
|
+
#: The ``ChatRequest.request_id`` this turn is running under, or ``None``.
|
|
73
|
+
#:
|
|
74
|
+
#: **This is not an emission concern, and it is here because the LIFECYCLE is the
|
|
75
|
+
#: same one.** ``GET /chat/poll`` reads a turn's assistant text out of
|
|
76
|
+
#: ``get_messages_for_request``, which filters ``SessionMessage.request_id ==
|
|
77
|
+
#: request_id`` — so a chat row written by a node with no request id is invisible
|
|
78
|
+
#: to every polling client, which is the transport the deployed app uses. The id
|
|
79
|
+
#: cannot travel in ``GraphState`` (undeclared keys are silently dropped and
|
|
80
|
+
#: declaring it would put a transport detail in the graph's contract) and it
|
|
81
|
+
#: cannot be re-derived inside a node, so it rides the same ``ContextVar``
|
|
82
|
+
#: mechanism the emitter does, is set by ``invoke_graph`` on **every** invocation,
|
|
83
|
+
#: and is ``None`` on the SSE path (which has no request row) and on the sweeper.
|
|
84
|
+
chat_request_id_var: ContextVar[Optional[str]] = ContextVar(
|
|
85
|
+
"graph_chat_request_id", default=None
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def set_chat_request_id(request_id: Optional[str]) -> None:
|
|
90
|
+
"""Bind *request_id* for this turn's context; ``None`` clears it.
|
|
91
|
+
|
|
92
|
+
Called by ``invoke_graph`` unconditionally, for the reason
|
|
93
|
+
:func:`set_event_emitter` is: a value left over from an earlier turn in the
|
|
94
|
+
same process would tag turn 2's rows with turn 1's request, and turn 1's
|
|
95
|
+
poll — which a client may still be draining — would then receive turn 2's
|
|
96
|
+
reply while turn 2's own poll never saw it.
|
|
97
|
+
"""
|
|
98
|
+
chat_request_id_var.set(request_id)
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def get_chat_request_id() -> Optional[str]:
|
|
102
|
+
"""The request id this turn runs under, or ``None`` when there is no request.
|
|
103
|
+
|
|
104
|
+
``None`` is a normal, correct answer on two paths: the SSE transport (which
|
|
105
|
+
persists no ``ChatRequest`` row at all) and the arc-review sweeper (which has
|
|
106
|
+
no request). A row written with ``None`` is still a durable transcript row and
|
|
107
|
+
still reaches ``_conversation``; it is only the *polling* projection that
|
|
108
|
+
needs the id.
|
|
109
|
+
"""
|
|
110
|
+
return chat_request_id_var.get()
|
|
111
|
+
|
|
72
112
|
|
|
73
113
|
def set_event_emitter(emitter: Optional[queue.Queue]) -> None:
|
|
74
114
|
"""Install *emitter* as this context's event queue, and reset the slide cursor.
|