databricks-tellr-app 0.3.13.dev4__tar.gz → 0.3.13.dev11__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.
Files changed (150) hide show
  1. {databricks_tellr_app-0.3.13.dev4 → databricks_tellr_app-0.3.13.dev11}/PKG-INFO +8 -8
  2. {databricks_tellr_app-0.3.13.dev4 → databricks_tellr_app-0.3.13.dev11}/databricks_tellr_app/run.py +0 -12
  3. {databricks_tellr_app-0.3.13.dev4 → databricks_tellr_app-0.3.13.dev11}/databricks_tellr_app.egg-info/PKG-INFO +8 -8
  4. {databricks_tellr_app-0.3.13.dev4 → databricks_tellr_app-0.3.13.dev11}/databricks_tellr_app.egg-info/SOURCES.txt +0 -2
  5. {databricks_tellr_app-0.3.13.dev4 → databricks_tellr_app-0.3.13.dev11}/databricks_tellr_app.egg-info/requires.txt +7 -7
  6. {databricks_tellr_app-0.3.13.dev4 → databricks_tellr_app-0.3.13.dev11}/pyproject.toml +8 -10
  7. {databricks_tellr_app-0.3.13.dev4 → databricks_tellr_app-0.3.13.dev11}/src/api/services/chat_service.py +75 -19
  8. {databricks_tellr_app-0.3.13.dev4 → databricks_tellr_app-0.3.13.dev11}/src/api/services/session_manager.py +27 -0
  9. databricks_tellr_app-0.3.13.dev11/src/core/encryption.py +86 -0
  10. {databricks_tellr_app-0.3.13.dev4 → databricks_tellr_app-0.3.13.dev11}/src/core/lakebase.py +114 -0
  11. {databricks_tellr_app-0.3.13.dev4 → databricks_tellr_app-0.3.13.dev11}/src/database/models/__init__.py +0 -2
  12. databricks_tellr_app-0.3.13.dev4/src/core/encryption.py +0 -125
  13. databricks_tellr_app-0.3.13.dev4/src/database/models/encryption_key.py +0 -30
  14. {databricks_tellr_app-0.3.13.dev4 → databricks_tellr_app-0.3.13.dev11}/README.md +0 -0
  15. {databricks_tellr_app-0.3.13.dev4 → databricks_tellr_app-0.3.13.dev11}/databricks_tellr_app/__init__.py +0 -0
  16. {databricks_tellr_app-0.3.13.dev4 → databricks_tellr_app-0.3.13.dev11}/databricks_tellr_app.egg-info/dependency_links.txt +0 -0
  17. {databricks_tellr_app-0.3.13.dev4 → databricks_tellr_app-0.3.13.dev11}/databricks_tellr_app.egg-info/top_level.txt +0 -0
  18. {databricks_tellr_app-0.3.13.dev4 → databricks_tellr_app-0.3.13.dev11}/setup.cfg +0 -0
  19. {databricks_tellr_app-0.3.13.dev4 → databricks_tellr_app-0.3.13.dev11}/setup.py +0 -0
  20. {databricks_tellr_app-0.3.13.dev4 → databricks_tellr_app-0.3.13.dev11}/src/__init__.py +0 -0
  21. {databricks_tellr_app-0.3.13.dev4 → databricks_tellr_app-0.3.13.dev11}/src/api/__init__.py +0 -0
  22. {databricks_tellr_app-0.3.13.dev4 → databricks_tellr_app-0.3.13.dev11}/src/api/_otel_bootstrap.py +0 -0
  23. {databricks_tellr_app-0.3.13.dev4 → databricks_tellr_app-0.3.13.dev11}/src/api/fixtures/tour_demo_deck.json +0 -0
  24. {databricks_tellr_app-0.3.13.dev4 → databricks_tellr_app-0.3.13.dev11}/src/api/main.py +0 -0
  25. {databricks_tellr_app-0.3.13.dev4 → databricks_tellr_app-0.3.13.dev11}/src/api/mcp_auth.py +0 -0
  26. {databricks_tellr_app-0.3.13.dev4 → databricks_tellr_app-0.3.13.dev11}/src/api/mcp_server.py +0 -0
  27. {databricks_tellr_app-0.3.13.dev4 → databricks_tellr_app-0.3.13.dev11}/src/api/middleware/__init__.py +0 -0
  28. {databricks_tellr_app-0.3.13.dev4 → databricks_tellr_app-0.3.13.dev11}/src/api/middleware/request_logging.py +0 -0
  29. {databricks_tellr_app-0.3.13.dev4 → databricks_tellr_app-0.3.13.dev11}/src/api/routes/__init__.py +0 -0
  30. {databricks_tellr_app-0.3.13.dev4 → databricks_tellr_app-0.3.13.dev11}/src/api/routes/admin.py +0 -0
  31. {databricks_tellr_app-0.3.13.dev4 → databricks_tellr_app-0.3.13.dev11}/src/api/routes/admin_usage.py +0 -0
  32. {databricks_tellr_app-0.3.13.dev4 → databricks_tellr_app-0.3.13.dev11}/src/api/routes/agent_config.py +0 -0
  33. {databricks_tellr_app-0.3.13.dev4 → databricks_tellr_app-0.3.13.dev11}/src/api/routes/chat.py +0 -0
  34. {databricks_tellr_app-0.3.13.dev4 → databricks_tellr_app-0.3.13.dev11}/src/api/routes/deck_contributors.py +0 -0
  35. {databricks_tellr_app-0.3.13.dev4 → databricks_tellr_app-0.3.13.dev11}/src/api/routes/export.py +0 -0
  36. {databricks_tellr_app-0.3.13.dev4 → databricks_tellr_app-0.3.13.dev11}/src/api/routes/feedback.py +0 -0
  37. {databricks_tellr_app-0.3.13.dev4 → databricks_tellr_app-0.3.13.dev11}/src/api/routes/google_slides.py +0 -0
  38. {databricks_tellr_app-0.3.13.dev4 → databricks_tellr_app-0.3.13.dev11}/src/api/routes/images.py +0 -0
  39. {databricks_tellr_app-0.3.13.dev4 → databricks_tellr_app-0.3.13.dev11}/src/api/routes/local_version.py +0 -0
  40. {databricks_tellr_app-0.3.13.dev4 → databricks_tellr_app-0.3.13.dev11}/src/api/routes/profiles.py +0 -0
  41. {databricks_tellr_app-0.3.13.dev4 → databricks_tellr_app-0.3.13.dev11}/src/api/routes/sessions.py +0 -0
  42. {databricks_tellr_app-0.3.13.dev4 → databricks_tellr_app-0.3.13.dev11}/src/api/routes/settings/__init__.py +0 -0
  43. {databricks_tellr_app-0.3.13.dev4 → databricks_tellr_app-0.3.13.dev11}/src/api/routes/settings/contributors.py +0 -0
  44. {databricks_tellr_app-0.3.13.dev4 → databricks_tellr_app-0.3.13.dev11}/src/api/routes/settings/deck_prompts.py +0 -0
  45. {databricks_tellr_app-0.3.13.dev4 → databricks_tellr_app-0.3.13.dev11}/src/api/routes/settings/identities.py +0 -0
  46. {databricks_tellr_app-0.3.13.dev4 → databricks_tellr_app-0.3.13.dev11}/src/api/routes/settings/slide_styles.py +0 -0
  47. {databricks_tellr_app-0.3.13.dev4 → databricks_tellr_app-0.3.13.dev11}/src/api/routes/setup.py +0 -0
  48. {databricks_tellr_app-0.3.13.dev4 → databricks_tellr_app-0.3.13.dev11}/src/api/routes/slides.py +0 -0
  49. {databricks_tellr_app-0.3.13.dev4 → databricks_tellr_app-0.3.13.dev11}/src/api/routes/tools.py +0 -0
  50. {databricks_tellr_app-0.3.13.dev4 → databricks_tellr_app-0.3.13.dev11}/src/api/routes/tour.py +0 -0
  51. {databricks_tellr_app-0.3.13.dev4 → databricks_tellr_app-0.3.13.dev11}/src/api/routes/verification.py +0 -0
  52. {databricks_tellr_app-0.3.13.dev4 → databricks_tellr_app-0.3.13.dev11}/src/api/routes/version.py +0 -0
  53. {databricks_tellr_app-0.3.13.dev4 → databricks_tellr_app-0.3.13.dev11}/src/api/schemas/__init__.py +0 -0
  54. {databricks_tellr_app-0.3.13.dev4 → databricks_tellr_app-0.3.13.dev11}/src/api/schemas/agent_config.py +0 -0
  55. {databricks_tellr_app-0.3.13.dev4 → databricks_tellr_app-0.3.13.dev11}/src/api/schemas/feedback.py +0 -0
  56. {databricks_tellr_app-0.3.13.dev4 → databricks_tellr_app-0.3.13.dev11}/src/api/schemas/requests.py +0 -0
  57. {databricks_tellr_app-0.3.13.dev4 → databricks_tellr_app-0.3.13.dev11}/src/api/schemas/responses.py +0 -0
  58. {databricks_tellr_app-0.3.13.dev4 → databricks_tellr_app-0.3.13.dev11}/src/api/schemas/settings/__init__.py +0 -0
  59. {databricks_tellr_app-0.3.13.dev4 → databricks_tellr_app-0.3.13.dev11}/src/api/schemas/settings/requests.py +0 -0
  60. {databricks_tellr_app-0.3.13.dev4 → databricks_tellr_app-0.3.13.dev11}/src/api/schemas/settings/responses.py +0 -0
  61. {databricks_tellr_app-0.3.13.dev4 → databricks_tellr_app-0.3.13.dev11}/src/api/schemas/streaming.py +0 -0
  62. {databricks_tellr_app-0.3.13.dev4 → databricks_tellr_app-0.3.13.dev11}/src/api/services/__init__.py +0 -0
  63. {databricks_tellr_app-0.3.13.dev4 → databricks_tellr_app-0.3.13.dev11}/src/api/services/export_job_queue.py +0 -0
  64. {databricks_tellr_app-0.3.13.dev4 → databricks_tellr_app-0.3.13.dev11}/src/api/services/feedback_service.py +0 -0
  65. {databricks_tellr_app-0.3.13.dev4 → databricks_tellr_app-0.3.13.dev11}/src/api/services/job_queue.py +0 -0
  66. {databricks_tellr_app-0.3.13.dev4 → databricks_tellr_app-0.3.13.dev11}/src/api/services/session_naming.py +0 -0
  67. {databricks_tellr_app-0.3.13.dev4 → databricks_tellr_app-0.3.13.dev11}/src/api/services/usage_events.py +0 -0
  68. {databricks_tellr_app-0.3.13.dev4 → databricks_tellr_app-0.3.13.dev11}/src/api/services/usage_service.py +0 -0
  69. {databricks_tellr_app-0.3.13.dev4 → databricks_tellr_app-0.3.13.dev11}/src/api/utils/__init__.py +0 -0
  70. {databricks_tellr_app-0.3.13.dev4 → databricks_tellr_app-0.3.13.dev11}/src/api/utils/validation.py +0 -0
  71. {databricks_tellr_app-0.3.13.dev4 → databricks_tellr_app-0.3.13.dev11}/src/core/__init__.py +0 -0
  72. {databricks_tellr_app-0.3.13.dev4 → databricks_tellr_app-0.3.13.dev11}/src/core/config_loader.py +0 -0
  73. {databricks_tellr_app-0.3.13.dev4 → databricks_tellr_app-0.3.13.dev11}/src/core/context_utils.py +0 -0
  74. {databricks_tellr_app-0.3.13.dev4 → databricks_tellr_app-0.3.13.dev11}/src/core/database.py +0 -0
  75. {databricks_tellr_app-0.3.13.dev4 → databricks_tellr_app-0.3.13.dev11}/src/core/databricks_client.py +0 -0
  76. {databricks_tellr_app-0.3.13.dev4 → databricks_tellr_app-0.3.13.dev11}/src/core/defaults.py +0 -0
  77. {databricks_tellr_app-0.3.13.dev4 → databricks_tellr_app-0.3.13.dev11}/src/core/init_default_profile.py +0 -0
  78. {databricks_tellr_app-0.3.13.dev4 → databricks_tellr_app-0.3.13.dev11}/src/core/migrate_profiles_to_agent_config.py +0 -0
  79. {databricks_tellr_app-0.3.13.dev4 → databricks_tellr_app-0.3.13.dev11}/src/core/mlflow_agent_spans.py +0 -0
  80. {databricks_tellr_app-0.3.13.dev4 → databricks_tellr_app-0.3.13.dev11}/src/core/mlflow_tracing.py +0 -0
  81. {databricks_tellr_app-0.3.13.dev4 → databricks_tellr_app-0.3.13.dev11}/src/core/otel_logging.py +0 -0
  82. {databricks_tellr_app-0.3.13.dev4 → databricks_tellr_app-0.3.13.dev11}/src/core/permission_context.py +0 -0
  83. {databricks_tellr_app-0.3.13.dev4 → databricks_tellr_app-0.3.13.dev11}/src/core/prompt_modules.py +0 -0
  84. {databricks_tellr_app-0.3.13.dev4 → databricks_tellr_app-0.3.13.dev11}/src/core/settings_db.py +0 -0
  85. {databricks_tellr_app-0.3.13.dev4 → databricks_tellr_app-0.3.13.dev11}/src/core/user_context.py +0 -0
  86. {databricks_tellr_app-0.3.13.dev4 → databricks_tellr_app-0.3.13.dev11}/src/database/__init__.py +0 -0
  87. {databricks_tellr_app-0.3.13.dev4 → databricks_tellr_app-0.3.13.dev11}/src/database/models/deck_contributor.py +0 -0
  88. {databricks_tellr_app-0.3.13.dev4 → databricks_tellr_app-0.3.13.dev11}/src/database/models/feedback.py +0 -0
  89. {databricks_tellr_app-0.3.13.dev4 → databricks_tellr_app-0.3.13.dev11}/src/database/models/genie_space.py +0 -0
  90. {databricks_tellr_app-0.3.13.dev4 → databricks_tellr_app-0.3.13.dev11}/src/database/models/google_global_credentials.py +0 -0
  91. {databricks_tellr_app-0.3.13.dev4 → databricks_tellr_app-0.3.13.dev11}/src/database/models/google_oauth_token.py +0 -0
  92. {databricks_tellr_app-0.3.13.dev4 → databricks_tellr_app-0.3.13.dev11}/src/database/models/identity.py +0 -0
  93. {databricks_tellr_app-0.3.13.dev4 → databricks_tellr_app-0.3.13.dev11}/src/database/models/image.py +0 -0
  94. {databricks_tellr_app-0.3.13.dev4 → databricks_tellr_app-0.3.13.dev11}/src/database/models/profile.py +0 -0
  95. {databricks_tellr_app-0.3.13.dev4 → databricks_tellr_app-0.3.13.dev11}/src/database/models/profile_contributor.py +0 -0
  96. {databricks_tellr_app-0.3.13.dev4 → databricks_tellr_app-0.3.13.dev11}/src/database/models/prompts.py +0 -0
  97. {databricks_tellr_app-0.3.13.dev4 → databricks_tellr_app-0.3.13.dev11}/src/database/models/request_log.py +0 -0
  98. {databricks_tellr_app-0.3.13.dev4 → databricks_tellr_app-0.3.13.dev11}/src/database/models/session.py +0 -0
  99. {databricks_tellr_app-0.3.13.dev4 → databricks_tellr_app-0.3.13.dev11}/src/database/models/slide_deck_prompt.py +0 -0
  100. {databricks_tellr_app-0.3.13.dev4 → databricks_tellr_app-0.3.13.dev11}/src/database/models/slide_style_library.py +0 -0
  101. {databricks_tellr_app-0.3.13.dev4 → databricks_tellr_app-0.3.13.dev11}/src/database/models/usage_event.py +0 -0
  102. {databricks_tellr_app-0.3.13.dev4 → databricks_tellr_app-0.3.13.dev11}/src/database/models/user_preference.py +0 -0
  103. {databricks_tellr_app-0.3.13.dev4 → databricks_tellr_app-0.3.13.dev11}/src/domain/__init__.py +0 -0
  104. {databricks_tellr_app-0.3.13.dev4 → databricks_tellr_app-0.3.13.dev11}/src/domain/slide.py +0 -0
  105. {databricks_tellr_app-0.3.13.dev4 → databricks_tellr_app-0.3.13.dev11}/src/domain/slide_deck.py +0 -0
  106. {databricks_tellr_app-0.3.13.dev4 → databricks_tellr_app-0.3.13.dev11}/src/services/__init__.py +0 -0
  107. {databricks_tellr_app-0.3.13.dev4 → databricks_tellr_app-0.3.13.dev11}/src/services/agent.py +0 -0
  108. {databricks_tellr_app-0.3.13.dev4 → databricks_tellr_app-0.3.13.dev11}/src/services/agent_factory.py +0 -0
  109. {databricks_tellr_app-0.3.13.dev4 → databricks_tellr_app-0.3.13.dev11}/src/services/config_service.py +0 -0
  110. {databricks_tellr_app-0.3.13.dev4 → databricks_tellr_app-0.3.13.dev11}/src/services/config_validator.py +0 -0
  111. {databricks_tellr_app-0.3.13.dev4 → databricks_tellr_app-0.3.13.dev11}/src/services/drive_uploader.py +0 -0
  112. {databricks_tellr_app-0.3.13.dev4 → databricks_tellr_app-0.3.13.dev11}/src/services/evaluation/__init__.py +0 -0
  113. {databricks_tellr_app-0.3.13.dev4 → databricks_tellr_app-0.3.13.dev11}/src/services/evaluation/llm_judge.py +0 -0
  114. {databricks_tellr_app-0.3.13.dev4 → databricks_tellr_app-0.3.13.dev11}/src/services/genie_service.py +0 -0
  115. {databricks_tellr_app-0.3.13.dev4 → databricks_tellr_app-0.3.13.dev11}/src/services/google_slides_auth.py +0 -0
  116. {databricks_tellr_app-0.3.13.dev4 → databricks_tellr_app-0.3.13.dev11}/src/services/google_slides_prompts_defaults.py +0 -0
  117. {databricks_tellr_app-0.3.13.dev4 → databricks_tellr_app-0.3.13.dev11}/src/services/html_to_google_slides.py +0 -0
  118. {databricks_tellr_app-0.3.13.dev4 → databricks_tellr_app-0.3.13.dev11}/src/services/html_to_pptx.py +0 -0
  119. {databricks_tellr_app-0.3.13.dev4 → databricks_tellr_app-0.3.13.dev11}/src/services/identity_provider.py +0 -0
  120. {databricks_tellr_app-0.3.13.dev4 → databricks_tellr_app-0.3.13.dev11}/src/services/identity_providers/__init__.py +0 -0
  121. {databricks_tellr_app-0.3.13.dev4 → databricks_tellr_app-0.3.13.dev11}/src/services/identity_providers/account_provider.py +0 -0
  122. {databricks_tellr_app-0.3.13.dev4 → databricks_tellr_app-0.3.13.dev11}/src/services/identity_providers/local_provider.py +0 -0
  123. {databricks_tellr_app-0.3.13.dev4 → databricks_tellr_app-0.3.13.dev11}/src/services/identity_providers/workspace_provider.py +0 -0
  124. {databricks_tellr_app-0.3.13.dev4 → databricks_tellr_app-0.3.13.dev11}/src/services/image_service.py +0 -0
  125. {databricks_tellr_app-0.3.13.dev4 → databricks_tellr_app-0.3.13.dev11}/src/services/image_tools.py +0 -0
  126. {databricks_tellr_app-0.3.13.dev4 → databricks_tellr_app-0.3.13.dev11}/src/services/permission_service.py +0 -0
  127. {databricks_tellr_app-0.3.13.dev4 → databricks_tellr_app-0.3.13.dev11}/src/services/pptx_from_html_huashu.py +0 -0
  128. {databricks_tellr_app-0.3.13.dev4 → databricks_tellr_app-0.3.13.dev11}/src/services/pptx_from_records.py +0 -0
  129. {databricks_tellr_app-0.3.13.dev4 → databricks_tellr_app-0.3.13.dev11}/src/services/pptx_prompts_defaults.py +0 -0
  130. {databricks_tellr_app-0.3.13.dev4 → databricks_tellr_app-0.3.13.dev11}/src/services/profile_service.py +0 -0
  131. {databricks_tellr_app-0.3.13.dev4 → databricks_tellr_app-0.3.13.dev11}/src/services/streaming_callback.py +0 -0
  132. {databricks_tellr_app-0.3.13.dev4 → databricks_tellr_app-0.3.13.dev11}/src/services/tools/__init__.py +0 -0
  133. {databricks_tellr_app-0.3.13.dev4 → databricks_tellr_app-0.3.13.dev11}/src/services/tools/agent_bricks_tool.py +0 -0
  134. {databricks_tellr_app-0.3.13.dev4 → databricks_tellr_app-0.3.13.dev11}/src/services/tools/genie_tool.py +0 -0
  135. {databricks_tellr_app-0.3.13.dev4 → databricks_tellr_app-0.3.13.dev11}/src/services/tools/mcp_tool.py +0 -0
  136. {databricks_tellr_app-0.3.13.dev4 → databricks_tellr_app-0.3.13.dev11}/src/services/tools/model_endpoint_tool.py +0 -0
  137. {databricks_tellr_app-0.3.13.dev4 → databricks_tellr_app-0.3.13.dev11}/src/services/tools/vector_tool.py +0 -0
  138. {databricks_tellr_app-0.3.13.dev4 → databricks_tellr_app-0.3.13.dev11}/src/services/validator.py +0 -0
  139. {databricks_tellr_app-0.3.13.dev4 → databricks_tellr_app-0.3.13.dev11}/src/utils/__init__.py +0 -0
  140. {databricks_tellr_app-0.3.13.dev4 → databricks_tellr_app-0.3.13.dev11}/src/utils/css_utils.py +0 -0
  141. {databricks_tellr_app-0.3.13.dev4 → databricks_tellr_app-0.3.13.dev11}/src/utils/error_handling.py +0 -0
  142. {databricks_tellr_app-0.3.13.dev4 → databricks_tellr_app-0.3.13.dev11}/src/utils/html_safety.py +0 -0
  143. {databricks_tellr_app-0.3.13.dev4 → databricks_tellr_app-0.3.13.dev11}/src/utils/html_utils.py +0 -0
  144. {databricks_tellr_app-0.3.13.dev4 → databricks_tellr_app-0.3.13.dev11}/src/utils/image_utils.py +0 -0
  145. {databricks_tellr_app-0.3.13.dev4 → databricks_tellr_app-0.3.13.dev11}/src/utils/js_validator.py +0 -0
  146. {databricks_tellr_app-0.3.13.dev4 → databricks_tellr_app-0.3.13.dev11}/src/utils/logging_config.py +0 -0
  147. {databricks_tellr_app-0.3.13.dev4 → databricks_tellr_app-0.3.13.dev11}/src/utils/pi_filter.py +0 -0
  148. {databricks_tellr_app-0.3.13.dev4 → databricks_tellr_app-0.3.13.dev11}/src/utils/slide_hash.py +0 -0
  149. {databricks_tellr_app-0.3.13.dev4 → databricks_tellr_app-0.3.13.dev11}/src/utils/spotlight.py +0 -0
  150. {databricks_tellr_app-0.3.13.dev4 → databricks_tellr_app-0.3.13.dev11}/src/utils/text_caps.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: databricks-tellr-app
3
- Version: 0.3.13.dev4
3
+ Version: 0.3.13.dev11
4
4
  Summary: Tellr application package for Databricks Apps
5
5
  Requires-Python: >=3.10
6
6
  Description-Content-Type: text/markdown
@@ -24,7 +24,7 @@ Requires-Dist: tinycss2==1.3.0
24
24
  Requires-Dist: esprima==4.0.1
25
25
  Requires-Dist: sqlalchemy==2.0.36
26
26
  Requires-Dist: psycopg2-binary==2.9.10
27
- Requires-Dist: databricks-sql-connector==3.0.1
27
+ Requires-Dist: databricks-sql-connector>=3.0.0
28
28
  Requires-Dist: httpx==0.28.1
29
29
  Requires-Dist: jinja2==3.1.2
30
30
  Requires-Dist: python-dotenv==1.2.1
@@ -32,12 +32,12 @@ Requires-Dist: pyyaml==6.0.1
32
32
  Requires-Dist: pandas==2.3.3
33
33
  Requires-Dist: python-pptx==0.6.23
34
34
  Requires-Dist: playwright==1.48.0
35
- Requires-Dist: Pillow==11.3.0
36
- Requires-Dist: google-api-python-client==2.190.0
37
- Requires-Dist: google-auth-oauthlib==1.2.4
38
- Requires-Dist: google-auth-httplib2==0.3.0
39
- Requires-Dist: cryptography==46.0.3
40
- Requires-Dist: sse-starlette==3.0.3
35
+ Requires-Dist: Pillow>=10.0.0
36
+ Requires-Dist: google-api-python-client>=2.100.0
37
+ Requires-Dist: google-auth-oauthlib>=1.2.0
38
+ Requires-Dist: google-auth-httplib2>=0.2.0
39
+ Requires-Dist: cryptography>=42.0.0
40
+ Requires-Dist: sse-starlette>=3.0.0
41
41
  Requires-Dist: databricks-sdk==0.120.0
42
42
  Requires-Dist: mlflow[databricks]==3.14.0
43
43
  Requires-Dist: mlflow-skinny==3.14.0
@@ -62,18 +62,6 @@ def init_database(seed_databricks_defaults: bool = False) -> None:
62
62
  logger.error(f"Failed to seed defaults: {e}\n{tb}")
63
63
  raise SystemExit(1) from e
64
64
 
65
- # Seed/load the Fernet master key pre-fork (SDR-4437 CRITICAL-3) so all
66
- # uvicorn workers find the encryption_keys row already present.
67
- logger.info("Ensuring encryption key...")
68
- try:
69
- from src.core.encryption import ensure_encryption_key
70
- ensure_encryption_key()
71
- logger.info("Encryption key ready")
72
- except Exception as e:
73
- tb = traceback.format_exc()
74
- logger.error(f"Failed to ensure encryption key: {e}\n{tb}")
75
- raise SystemExit(1) from e
76
-
77
65
 
78
66
  def main() -> None:
79
67
  """Start the uvicorn server."""
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: databricks-tellr-app
3
- Version: 0.3.13.dev4
3
+ Version: 0.3.13.dev11
4
4
  Summary: Tellr application package for Databricks Apps
5
5
  Requires-Python: >=3.10
6
6
  Description-Content-Type: text/markdown
@@ -24,7 +24,7 @@ Requires-Dist: tinycss2==1.3.0
24
24
  Requires-Dist: esprima==4.0.1
25
25
  Requires-Dist: sqlalchemy==2.0.36
26
26
  Requires-Dist: psycopg2-binary==2.9.10
27
- Requires-Dist: databricks-sql-connector==3.0.1
27
+ Requires-Dist: databricks-sql-connector>=3.0.0
28
28
  Requires-Dist: httpx==0.28.1
29
29
  Requires-Dist: jinja2==3.1.2
30
30
  Requires-Dist: python-dotenv==1.2.1
@@ -32,12 +32,12 @@ Requires-Dist: pyyaml==6.0.1
32
32
  Requires-Dist: pandas==2.3.3
33
33
  Requires-Dist: python-pptx==0.6.23
34
34
  Requires-Dist: playwright==1.48.0
35
- Requires-Dist: Pillow==11.3.0
36
- Requires-Dist: google-api-python-client==2.190.0
37
- Requires-Dist: google-auth-oauthlib==1.2.4
38
- Requires-Dist: google-auth-httplib2==0.3.0
39
- Requires-Dist: cryptography==46.0.3
40
- Requires-Dist: sse-starlette==3.0.3
35
+ Requires-Dist: Pillow>=10.0.0
36
+ Requires-Dist: google-api-python-client>=2.100.0
37
+ Requires-Dist: google-auth-oauthlib>=1.2.0
38
+ Requires-Dist: google-auth-httplib2>=0.2.0
39
+ Requires-Dist: cryptography>=42.0.0
40
+ Requires-Dist: sse-starlette>=3.0.0
41
41
  Requires-Dist: databricks-sdk==0.120.0
42
42
  Requires-Dist: mlflow[databricks]==3.14.0
43
43
  Requires-Dist: mlflow-skinny==3.14.0
@@ -76,7 +76,6 @@ setup.py
76
76
  ./src/database/__init__.py
77
77
  ./src/database/models/__init__.py
78
78
  ./src/database/models/deck_contributor.py
79
- ./src/database/models/encryption_key.py
80
79
  ./src/database/models/feedback.py
81
80
  ./src/database/models/genie_space.py
82
81
  ./src/database/models/google_global_credentials.py
@@ -220,7 +219,6 @@ src/core/user_context.py
220
219
  src/database/__init__.py
221
220
  src/database/models/__init__.py
222
221
  src/database/models/deck_contributor.py
223
- src/database/models/encryption_key.py
224
222
  src/database/models/feedback.py
225
223
  src/database/models/genie_space.py
226
224
  src/database/models/google_global_credentials.py
@@ -18,7 +18,7 @@ tinycss2==1.3.0
18
18
  esprima==4.0.1
19
19
  sqlalchemy==2.0.36
20
20
  psycopg2-binary==2.9.10
21
- databricks-sql-connector==3.0.1
21
+ databricks-sql-connector>=3.0.0
22
22
  httpx==0.28.1
23
23
  jinja2==3.1.2
24
24
  python-dotenv==1.2.1
@@ -26,12 +26,12 @@ pyyaml==6.0.1
26
26
  pandas==2.3.3
27
27
  python-pptx==0.6.23
28
28
  playwright==1.48.0
29
- Pillow==11.3.0
30
- google-api-python-client==2.190.0
31
- google-auth-oauthlib==1.2.4
32
- google-auth-httplib2==0.3.0
33
- cryptography==46.0.3
34
- sse-starlette==3.0.3
29
+ Pillow>=10.0.0
30
+ google-api-python-client>=2.100.0
31
+ google-auth-oauthlib>=1.2.0
32
+ google-auth-httplib2>=0.2.0
33
+ cryptography>=42.0.0
34
+ sse-starlette>=3.0.0
35
35
  databricks-sdk==0.120.0
36
36
  mlflow[databricks]==3.14.0
37
37
  mlflow-skinny==3.14.0
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "databricks-tellr-app"
7
- version = "0.3.13.dev4"
7
+ version = "0.3.13.dev11"
8
8
  description = "Tellr application package for Databricks Apps"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
@@ -29,7 +29,7 @@ dependencies = [
29
29
  "esprima==4.0.1",
30
30
  "sqlalchemy==2.0.36",
31
31
  "psycopg2-binary==2.9.10",
32
- "databricks-sql-connector==3.0.1",
32
+ "databricks-sql-connector>=3.0.0",
33
33
  "httpx==0.28.1",
34
34
  "jinja2==3.1.2",
35
35
  "python-dotenv==1.2.1",
@@ -37,12 +37,12 @@ dependencies = [
37
37
  "pandas==2.3.3",
38
38
  "python-pptx==0.6.23",
39
39
  "playwright==1.48.0",
40
- "Pillow==11.3.0",
41
- "google-api-python-client==2.190.0",
42
- "google-auth-oauthlib==1.2.4",
43
- "google-auth-httplib2==0.3.0",
44
- "cryptography==46.0.3",
45
- "sse-starlette==3.0.3",
40
+ "Pillow>=10.0.0",
41
+ "google-api-python-client>=2.100.0",
42
+ "google-auth-oauthlib>=1.2.0",
43
+ "google-auth-httplib2>=0.2.0",
44
+ "cryptography>=42.0.0",
45
+ "sse-starlette>=3.0.0",
46
46
  # --- Resolver-speedup pins: the backtracking-driver cluster ---
47
47
  # Databricks Apps installs this wheel in a platform-managed BUILD phase with a
48
48
  # ~10-minute budget. Left ranged, the databricks-langchain ->
@@ -58,8 +58,6 @@ dependencies = [
58
58
  # via a devloop deploy if these constraints change. Formerly injected at
59
59
  # deploy time as _RESOLVER_SPEEDUP_PINS; now carried in the wheel so a bare
60
60
  # `pip install databricks-tellr-app` is safe.
61
- # SDR-4437 MEDIUM-7 (July 2026): the 7 formerly-ranged direct deps above
62
- # this block are now exact-pinned too; same refresh discipline.
63
61
  "databricks-sdk==0.120.0",
64
62
  "mlflow[databricks]==3.14.0",
65
63
  "mlflow-skinny==3.14.0",
@@ -80,6 +80,11 @@ class ChatService:
80
80
  # This avoids re-parsing HTML on every request
81
81
  self._deck_cache: Dict[str, SlideDeck] = {}
82
82
 
83
+ # DB version each cached deck corresponds to. The cache is per-process
84
+ # while prod runs multiple uvicorn workers sharing one database, so a
85
+ # cache hit is only valid if the DB version hasn't moved on.
86
+ self._deck_cache_versions: Dict[str, int] = {}
87
+
83
88
  logger.info("ChatService initialized successfully")
84
89
 
85
90
  def _substitute_images_for_response(self, deck_dict, raw_html=None):
@@ -600,7 +605,7 @@ class ChatService:
600
605
  slide_deck_dict = current_deck.to_dict()
601
606
 
602
607
  try:
603
- session_manager.save_slide_deck(
608
+ save_result = session_manager.save_slide_deck(
604
609
  session_id=session_id,
605
610
  title=current_deck.title,
606
611
  html_content=current_deck.knit(),
@@ -610,6 +615,7 @@ class ChatService:
610
615
  modified_by=_user,
611
616
  expected_version=_deck_version_before_llm,
612
617
  )
618
+ self._record_deck_version(session_id, save_result)
613
619
  except VersionConflictError:
614
620
  logger.warning(
615
621
  "Chat save rejected: deck was edited during LLM call, reloading",
@@ -1385,7 +1391,7 @@ class ChatService:
1385
1391
  slide_deck_dict = current_deck.to_dict()
1386
1392
 
1387
1393
  try:
1388
- session_manager.save_slide_deck(
1394
+ save_result = session_manager.save_slide_deck(
1389
1395
  session_id=session_id,
1390
1396
  title=current_deck.title,
1391
1397
  html_content=current_deck.knit(),
@@ -1395,6 +1401,7 @@ class ChatService:
1395
1401
  modified_by=_user,
1396
1402
  expected_version=_deck_version_before_llm,
1397
1403
  )
1404
+ self._record_deck_version(session_id, save_result)
1398
1405
  except VersionConflictError:
1399
1406
  logger.warning(
1400
1407
  "Chat save rejected: deck was edited during LLM call, reloading",
@@ -1921,18 +1928,63 @@ class ChatService:
1921
1928
  context = "\n\n[Attached images]\n" + "\n".join(image_descriptions)
1922
1929
  return message + context
1923
1930
 
1931
+ def _deck_versions(self) -> Dict[str, int]:
1932
+ """Return the cached-deck version map, creating it if needed.
1933
+
1934
+ Lazy creation keeps instances built without __init__ (test harnesses
1935
+ use ChatService.__new__) working. Callers must hold _cache_lock.
1936
+ """
1937
+ try:
1938
+ return self._deck_cache_versions
1939
+ except AttributeError:
1940
+ self._deck_cache_versions = {}
1941
+ return self._deck_cache_versions
1942
+
1943
+ def _record_deck_version(self, session_id: str, save_result: Optional[Dict[str, Any]]) -> None:
1944
+ """Remember which DB version the cached deck now corresponds to.
1945
+
1946
+ Call after save_slide_deck so the next _get_or_load_deck cache hit
1947
+ validates cleanly instead of reloading from the database.
1948
+ """
1949
+ version = (save_result or {}).get("version")
1950
+ with self._cache_lock:
1951
+ if version is not None:
1952
+ self._deck_versions()[session_id] = version
1953
+ else:
1954
+ self._deck_versions().pop(session_id, None)
1955
+
1924
1956
  def _get_or_load_deck(self, session_id: str) -> Optional[SlideDeck]:
1925
1957
  """Get deck from cache or load from database.
1926
1958
 
1927
1959
  Thread-safe access to deck cache using _cache_lock.
1928
-
1960
+
1961
+ The cache is per-process while prod runs multiple uvicorn workers
1962
+ sharing one database, so a cached deck is only served if its recorded
1963
+ version matches the current DB version; otherwise it is reloaded.
1964
+
1929
1965
  Uses deck_dict (slides array with individual scripts) when available,
1930
1966
  falling back to from_html_string for legacy data.
1931
1967
  """
1932
1968
  # Check cache first (with lock)
1933
1969
  with self._cache_lock:
1934
- if session_id in self._deck_cache:
1935
- return self._deck_cache[session_id]
1970
+ cached_deck = self._deck_cache.get(session_id)
1971
+ cached_version = self._deck_versions().get(session_id)
1972
+
1973
+ if cached_deck is not None:
1974
+ db_version = self._get_deck_version(session_id)
1975
+ if db_version is None or cached_version == db_version:
1976
+ return cached_deck
1977
+ logger.info(
1978
+ "Deck cache stale, reloading from database",
1979
+ extra={
1980
+ "session_id": session_id,
1981
+ "cached_version": cached_version,
1982
+ "db_version": db_version,
1983
+ },
1984
+ )
1985
+ with self._cache_lock:
1986
+ self._deck_cache.pop(session_id, None)
1987
+ self._deck_versions().pop(session_id, None)
1936
1988
 
1937
1989
  # Try to load from database (outside lock to avoid blocking)
1938
1990
  session_manager = get_session_manager()
@@ -1966,6 +2018,10 @@ class ChatService:
1966
2018
  # Store in cache (with lock)
1967
2019
  with self._cache_lock:
1968
2020
  self._deck_cache[session_id] = deck
2021
+ if deck_data.get("version") is not None:
2022
+ self._deck_versions()[session_id] = deck_data["version"]
2023
+ else:
2024
+ self._deck_versions().pop(session_id, None)
1969
2025
  return deck
1970
2026
  except Exception as e:
1971
2027
  logger.warning(f"Failed to load deck from database: {e}")
@@ -1975,23 +2031,18 @@ class ChatService:
1975
2031
  def _get_deck_version(self, session_id: str) -> Optional[int]:
1976
2032
  """Read the current deck version from the database.
1977
2033
 
1978
- Called once before the LLM runs to capture the version for
1979
- optimistic locking. Uses the existing get_slide_deck which
1980
- returns the full deck dict including the version column.
1981
-
1982
- On Lakebase/PostgreSQL this is a single-row indexed lookup (~1-2ms).
2034
+ Used on every deck-cache hit (multi-worker cache validation) and
2035
+ before LLM calls (optimistic locking), so it must stay cheap: a
2036
+ single-column indexed lookup, never a full deck fetch.
1983
2037
 
1984
2038
  Returns:
1985
2039
  Current deck version number, or None if no deck exists.
1986
2040
  """
1987
2041
  session_manager = get_session_manager()
1988
2042
  try:
1989
- deck_data = session_manager.get_slide_deck(session_id)
1990
- if deck_data:
1991
- return deck_data.get("version")
2043
+ return session_manager.get_slide_deck_version(session_id)
1992
2044
  except Exception:
1993
- pass
1994
- return None
2045
+ return None
1995
2046
 
1996
2047
  @staticmethod
1997
2048
  def _reindex_slide_ids(deck: "SlideDeck") -> None:
@@ -2008,6 +2059,7 @@ class ChatService:
2008
2059
  """Remove the cached deck for a session so the next read hits the DB."""
2009
2060
  with self._cache_lock:
2010
2061
  self._deck_cache.pop(session_id, None)
2062
+ self._deck_versions().pop(session_id, None)
2011
2063
 
2012
2064
  def _replace_slide_htmls_from_cache(self, session_id: str, slide_context: Dict[str, Any]) -> Dict[str, Any]:
2013
2065
  """Replace frontend-supplied slide_htmls with backend cache versions.
@@ -2540,7 +2592,7 @@ class ChatService:
2540
2592
  # Persist to database
2541
2593
  deck_dict = current_deck.to_dict()
2542
2594
  session_manager = get_session_manager()
2543
- session_manager.save_slide_deck(
2595
+ save_result = session_manager.save_slide_deck(
2544
2596
  session_id=session_id,
2545
2597
  title=current_deck.title,
2546
2598
  html_content=current_deck.knit(),
@@ -2549,6 +2601,7 @@ class ChatService:
2549
2601
  deck_dict=deck_dict,
2550
2602
  expected_version=expected_version,
2551
2603
  )
2604
+ self._record_deck_version(session_id, save_result)
2552
2605
 
2553
2606
  # Create save point
2554
2607
  try:
@@ -2616,7 +2669,7 @@ class ChatService:
2616
2669
  # Persist to database
2617
2670
  deck_dict = current_deck.to_dict()
2618
2671
  session_manager = get_session_manager()
2619
- session_manager.save_slide_deck(
2672
+ save_result = session_manager.save_slide_deck(
2620
2673
  session_id=session_id,
2621
2674
  title=current_deck.title,
2622
2675
  html_content=current_deck.knit(),
@@ -2625,6 +2678,7 @@ class ChatService:
2625
2678
  deck_dict=deck_dict,
2626
2679
  expected_version=expected_version,
2627
2680
  )
2681
+ self._record_deck_version(session_id, save_result)
2628
2682
 
2629
2683
  # Create save point immediately after persisting
2630
2684
  try:
@@ -2681,7 +2735,7 @@ class ChatService:
2681
2735
  # Persist to database
2682
2736
  deck_dict = current_deck.to_dict()
2683
2737
  session_manager = get_session_manager()
2684
- session_manager.save_slide_deck(
2738
+ save_result = session_manager.save_slide_deck(
2685
2739
  session_id=session_id,
2686
2740
  title=current_deck.title,
2687
2741
  html_content=current_deck.knit(),
@@ -2690,6 +2744,7 @@ class ChatService:
2690
2744
  deck_dict=deck_dict,
2691
2745
  expected_version=expected_version,
2692
2746
  )
2747
+ self._record_deck_version(session_id, save_result)
2693
2748
 
2694
2749
  # Create save point
2695
2750
  try:
@@ -2744,7 +2799,7 @@ class ChatService:
2744
2799
  # Persist to database
2745
2800
  deck_dict = current_deck.to_dict()
2746
2801
  session_manager = get_session_manager()
2747
- session_manager.save_slide_deck(
2802
+ save_result = session_manager.save_slide_deck(
2748
2803
  session_id=session_id,
2749
2804
  title=current_deck.title,
2750
2805
  html_content=current_deck.knit(),
@@ -2753,6 +2808,7 @@ class ChatService:
2753
2808
  deck_dict=deck_dict,
2754
2809
  expected_version=expected_version,
2755
2810
  )
2811
+ self._record_deck_version(session_id, save_result)
2756
2812
 
2757
2813
  # Create save point
2758
2814
  try:
@@ -723,6 +723,33 @@ class SessionManager:
723
723
  "version": deck.version,
724
724
  }
725
725
 
726
+ def get_slide_deck_version(self, session_id: str) -> Optional[int]:
727
+ """Return only the deck's current version number.
728
+
729
+ Selects a single column — no deck_json parse, verification merge, or
730
+ display-name resolution — so it is cheap enough to run on every
731
+ deck-cache hit (multi-worker cache validation) and before LLM calls
732
+ (optimistic locking).
733
+
734
+ Args:
735
+ session_id: Session to check (contributor sessions resolve to the
736
+ owner's deck)
737
+
738
+ Returns:
739
+ Current version number, or None if the session/deck doesn't exist
740
+ """
741
+ with get_db_session() as db:
742
+ try:
743
+ session = self._get_session_or_raise(db, session_id)
744
+ except SessionNotFoundError:
745
+ return None
746
+ deck_owner = self._get_deck_owner_session(db, session)
747
+ return (
748
+ db.query(SessionSlideDeck.version)
749
+ .filter(SessionSlideDeck.session_id == deck_owner.id)
750
+ .scalar()
751
+ )
752
+
726
753
  def get_slide_deck(self, session_id: str) -> Optional[Dict[str, Any]]:
727
754
  """Get slide deck for a session with verification merged by content hash.
728
755
 
@@ -0,0 +1,86 @@
1
+ """Fernet symmetric encryption for sensitive data (OAuth credentials, tokens).
2
+
3
+ Uses AES-128-CBC with HMAC-SHA256 authentication via the ``cryptography`` library.
4
+ The encryption key is read from the ``GOOGLE_OAUTH_ENCRYPTION_KEY`` environment
5
+ variable. If the variable is not set, a key is auto-generated on first access,
6
+ persisted to ``.encryption_key`` in the project root, and a warning is logged.
7
+ """
8
+
9
+ import logging
10
+ import os
11
+ from functools import lru_cache
12
+ from pathlib import Path
13
+
14
+ from cryptography.fernet import Fernet, InvalidToken
15
+
16
+ logger = logging.getLogger(__name__)
17
+
18
+ _ENV_KEY_NAME = "GOOGLE_OAUTH_ENCRYPTION_KEY"
19
+ _KEY_FILE = Path(__file__).resolve().parents[2] / ".encryption_key"
20
+
21
+
22
+ @lru_cache(maxsize=1)
23
+ def get_encryption_key() -> bytes:
24
+ """Return the Fernet encryption key.
25
+
26
+ Resolution order:
27
+ 1. ``GOOGLE_OAUTH_ENCRYPTION_KEY`` env var (production).
28
+ 2. ``.encryption_key`` file in project root (persisted across restarts).
29
+ 3. Generate a new key → write to the file → set the env var.
30
+ """
31
+ # 1. Env var takes precedence
32
+ key = os.getenv(_ENV_KEY_NAME)
33
+ if key:
34
+ return key.encode()
35
+
36
+ # Production guard: fail fast if the env var is not set
37
+ if os.getenv("ENVIRONMENT") == "production":
38
+ raise RuntimeError(
39
+ "GOOGLE_OAUTH_ENCRYPTION_KEY environment variable is required in production. "
40
+ 'Generate one with: python -c "from cryptography.fernet import Fernet; '
41
+ 'print(Fernet.generate_key().decode())"'
42
+ )
43
+
44
+ # 2. Read from persisted key file
45
+ if _KEY_FILE.exists():
46
+ key = _KEY_FILE.read_text().strip()
47
+ if key:
48
+ os.environ[_ENV_KEY_NAME] = key
49
+ logger.info("Loaded encryption key from %s", _KEY_FILE)
50
+ return key.encode()
51
+
52
+ # 3. Generate, persist, and warn
53
+ generated = Fernet.generate_key()
54
+ try:
55
+ _KEY_FILE.write_text(generated.decode())
56
+ _KEY_FILE.chmod(0o600) # owner-only read/write
57
+ logger.warning(
58
+ "GOOGLE_OAUTH_ENCRYPTION_KEY not set — auto-generated and saved to %s. "
59
+ "Set the env var in production.",
60
+ _KEY_FILE,
61
+ )
62
+ except OSError:
63
+ logger.warning(
64
+ "GOOGLE_OAUTH_ENCRYPTION_KEY not set — auto-generated a key but "
65
+ "could not persist to %s. Key will be lost on restart.",
66
+ _KEY_FILE,
67
+ )
68
+
69
+ os.environ[_ENV_KEY_NAME] = generated.decode()
70
+ return generated
71
+
72
+
73
+ def encrypt_data(plaintext: str) -> str:
74
+ """Encrypt *plaintext* and return a base64-encoded ciphertext string."""
75
+ f = Fernet(get_encryption_key())
76
+ return f.encrypt(plaintext.encode()).decode()
77
+
78
+
79
+ def decrypt_data(ciphertext: str) -> str:
80
+ """Decrypt a base64-encoded *ciphertext* string and return the plaintext.
81
+
82
+ Raises:
83
+ cryptography.fernet.InvalidToken: If the key does not match or data is corrupt.
84
+ """
85
+ f = Fernet(get_encryption_key())
86
+ return f.decrypt(ciphertext.encode()).decode()
@@ -280,6 +280,120 @@ def get_lakebase_connection_url(
280
280
  return url
281
281
 
282
282
 
283
+ def setup_lakebase_schema(
284
+ instance_name: str,
285
+ schema: str,
286
+ client_id: str,
287
+ user: Optional[str] = None,
288
+ client: Optional[WorkspaceClient] = None,
289
+ endpoint_name: Optional[str] = None,
290
+ host: Optional[str] = None,
291
+ lakebase_type: str = "provisioned",
292
+ ) -> None:
293
+ """
294
+ Set up schema and grant permissions in Lakebase for an app.
295
+
296
+ Supports both provisioned and autoscaling Lakebase.
297
+
298
+ This function connects to the Lakebase instance and:
299
+ 1. Creates the schema if it doesn't exist
300
+ 2. Grants necessary permissions to the app's service principal (client_id)
301
+
302
+ Note: This should be called after the app is created and the database
303
+ resource is attached, as the client_id comes from the app's service principal.
304
+
305
+ Args:
306
+ instance_name: Name of the Lakebase instance
307
+ schema: Schema name to create
308
+ client_id: App's service principal client ID (becomes Postgres role)
309
+ user: Postgres username for connection (uses default if not provided)
310
+ client: Optional WorkspaceClient
311
+ endpoint_name: Full endpoint resource name (autoscaling only)
312
+ host: Pre-resolved host (autoscaling only)
313
+ lakebase_type: 'autoscaling' or 'provisioned'
314
+
315
+ Raises:
316
+ LakebaseError: If schema setup fails
317
+ """
318
+ logger.info(
319
+ "Setting up Lakebase schema",
320
+ extra={
321
+ "instance_name": instance_name, "schema": schema,
322
+ "client_id": client_id, "lakebase_type": lakebase_type,
323
+ },
324
+ )
325
+
326
+ try:
327
+ # Get connection info
328
+ conn_info = get_lakebase_connection_info(
329
+ instance_name=instance_name,
330
+ user=user,
331
+ client=client,
332
+ endpoint_name=endpoint_name,
333
+ host=host,
334
+ lakebase_type=lakebase_type,
335
+ )
336
+
337
+ # Use psycopg2 directly for DDL operations
338
+ import psycopg2
339
+
340
+ conn = psycopg2.connect(
341
+ host=conn_info["host"],
342
+ port=conn_info["port"],
343
+ user=conn_info["user"],
344
+ password=conn_info["password"],
345
+ dbname=conn_info["database"],
346
+ sslmode="require",
347
+ )
348
+ conn.autocommit = True
349
+
350
+ with conn.cursor() as cur:
351
+ # Create schema if not exists
352
+ cur.execute(f'CREATE SCHEMA IF NOT EXISTS "{schema}"')
353
+ logger.info(f"Schema created or verified: {schema}")
354
+
355
+ # Grant permissions to app's service principal
356
+ # The client_id is automatically available as a Postgres role when
357
+ # the database is added as an app resource
358
+ cur.execute(f'GRANT USAGE ON SCHEMA "{schema}" TO "{client_id}"')
359
+ cur.execute(f'GRANT CREATE ON SCHEMA "{schema}" TO "{client_id}"')
360
+
361
+ # Grant permissions on all tables in schema (for existing tables)
362
+ cur.execute(
363
+ f'GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA "{schema}" TO "{client_id}"'
364
+ )
365
+
366
+ # Grant permissions on all sequences in schema (for auto-increment columns)
367
+ cur.execute(
368
+ f'GRANT USAGE, SELECT ON ALL SEQUENCES IN SCHEMA "{schema}" TO "{client_id}"'
369
+ )
370
+
371
+ # Set default privileges for future tables
372
+ cur.execute(
373
+ f'ALTER DEFAULT PRIVILEGES IN SCHEMA "{schema}" '
374
+ f'GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO "{client_id}"'
375
+ )
376
+
377
+ # Set default privileges for future sequences
378
+ cur.execute(
379
+ f'ALTER DEFAULT PRIVILEGES IN SCHEMA "{schema}" '
380
+ f'GRANT USAGE, SELECT ON SEQUENCES TO "{client_id}"'
381
+ )
382
+
383
+ logger.info(f"Permissions granted to {client_id} on schema {schema}")
384
+
385
+ conn.close()
386
+
387
+ except ImportError:
388
+ raise LakebaseError(
389
+ "psycopg2 is required for Lakebase schema setup. "
390
+ "Install with: pip install psycopg2-binary"
391
+ )
392
+ except Exception as e:
393
+ logger.error(f"Failed to setup Lakebase schema: {e}", exc_info=True)
394
+ raise LakebaseError(f"Lakebase schema setup failed: {e}") from e
395
+
396
+
283
397
  def initialize_lakebase_tables(
284
398
  instance_name: str,
285
399
  schema: str,
@@ -1,7 +1,6 @@
1
1
  """Database models."""
2
2
 
3
3
  from src.database.models.deck_contributor import DeckContributor
4
- from src.database.models.encryption_key import EncryptionKey
5
4
  from src.database.models.feedback import FeedbackConversation, SurveyResponse
6
5
  from src.database.models.genie_space import ConfigGenieSpace
7
6
  from src.database.models.google_global_credentials import GoogleGlobalCredentials
@@ -35,7 +34,6 @@ __all__ = [
35
34
  "ChatRequest",
36
35
  "ConfigGenieSpace",
37
36
  "DeckContributor",
38
- "EncryptionKey",
39
37
  "ConfigProfile",
40
38
  "ConfigProfileContributor",
41
39
  "ConfigPrompts",