databricks-tellr-app 0.3.13.dev2__tar.gz → 0.3.13.dev3__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 (157) hide show
  1. {databricks_tellr_app-0.3.13.dev2 → databricks_tellr_app-0.3.13.dev3}/PKG-INFO +1 -1
  2. {databricks_tellr_app-0.3.13.dev2 → databricks_tellr_app-0.3.13.dev3}/databricks_tellr_app.egg-info/PKG-INFO +1 -1
  3. {databricks_tellr_app-0.3.13.dev2 → databricks_tellr_app-0.3.13.dev3}/databricks_tellr_app.egg-info/SOURCES.txt +14 -4
  4. {databricks_tellr_app-0.3.13.dev2 → databricks_tellr_app-0.3.13.dev3}/pyproject.toml +1 -1
  5. {databricks_tellr_app-0.3.13.dev2 → databricks_tellr_app-0.3.13.dev3}/src/api/main.py +0 -18
  6. {databricks_tellr_app-0.3.13.dev2 → databricks_tellr_app-0.3.13.dev3}/src/api/services/export_job_queue.py +10 -23
  7. databricks_tellr_app-0.3.13.dev3/src/services/converter_jail/__init__.py +10 -0
  8. databricks_tellr_app-0.3.13.dev3/src/services/converter_jail/ast_guard.py +58 -0
  9. databricks_tellr_app-0.3.13.dev3/src/services/converter_jail/codeprep.py +220 -0
  10. databricks_tellr_app-0.3.13.dev3/src/services/converter_jail/gslides_runner.py +78 -0
  11. databricks_tellr_app-0.3.13.dev3/src/services/converter_jail/jail.py +230 -0
  12. databricks_tellr_app-0.3.13.dev3/src/services/converter_jail/pptx_runner.py +124 -0
  13. databricks_tellr_app-0.3.13.dev3/src/services/converter_jail/protocol.py +41 -0
  14. {databricks_tellr_app-0.3.13.dev2 → databricks_tellr_app-0.3.13.dev3}/src/services/google_slides_prompts_defaults.py +28 -18
  15. {databricks_tellr_app-0.3.13.dev2 → databricks_tellr_app-0.3.13.dev3}/src/services/html_to_google_slides.py +246 -185
  16. {databricks_tellr_app-0.3.13.dev2 → databricks_tellr_app-0.3.13.dev3}/src/services/html_to_pptx.py +73 -453
  17. databricks_tellr_app-0.3.13.dev2/src/api/middleware/csrf.py +0 -109
  18. databricks_tellr_app-0.3.13.dev2/src/api/middleware/security_headers.py +0 -97
  19. {databricks_tellr_app-0.3.13.dev2 → databricks_tellr_app-0.3.13.dev3}/README.md +0 -0
  20. {databricks_tellr_app-0.3.13.dev2 → databricks_tellr_app-0.3.13.dev3}/databricks_tellr_app/__init__.py +0 -0
  21. {databricks_tellr_app-0.3.13.dev2 → databricks_tellr_app-0.3.13.dev3}/databricks_tellr_app/run.py +0 -0
  22. {databricks_tellr_app-0.3.13.dev2 → databricks_tellr_app-0.3.13.dev3}/databricks_tellr_app.egg-info/dependency_links.txt +0 -0
  23. {databricks_tellr_app-0.3.13.dev2 → databricks_tellr_app-0.3.13.dev3}/databricks_tellr_app.egg-info/requires.txt +0 -0
  24. {databricks_tellr_app-0.3.13.dev2 → databricks_tellr_app-0.3.13.dev3}/databricks_tellr_app.egg-info/top_level.txt +0 -0
  25. {databricks_tellr_app-0.3.13.dev2 → databricks_tellr_app-0.3.13.dev3}/setup.cfg +0 -0
  26. {databricks_tellr_app-0.3.13.dev2 → databricks_tellr_app-0.3.13.dev3}/setup.py +0 -0
  27. {databricks_tellr_app-0.3.13.dev2 → databricks_tellr_app-0.3.13.dev3}/src/__init__.py +0 -0
  28. {databricks_tellr_app-0.3.13.dev2 → databricks_tellr_app-0.3.13.dev3}/src/api/__init__.py +0 -0
  29. {databricks_tellr_app-0.3.13.dev2 → databricks_tellr_app-0.3.13.dev3}/src/api/_otel_bootstrap.py +0 -0
  30. {databricks_tellr_app-0.3.13.dev2 → databricks_tellr_app-0.3.13.dev3}/src/api/fixtures/tour_demo_deck.json +0 -0
  31. {databricks_tellr_app-0.3.13.dev2 → databricks_tellr_app-0.3.13.dev3}/src/api/mcp_auth.py +0 -0
  32. {databricks_tellr_app-0.3.13.dev2 → databricks_tellr_app-0.3.13.dev3}/src/api/mcp_server.py +0 -0
  33. {databricks_tellr_app-0.3.13.dev2 → databricks_tellr_app-0.3.13.dev3}/src/api/middleware/__init__.py +0 -0
  34. {databricks_tellr_app-0.3.13.dev2 → databricks_tellr_app-0.3.13.dev3}/src/api/middleware/request_logging.py +0 -0
  35. {databricks_tellr_app-0.3.13.dev2 → databricks_tellr_app-0.3.13.dev3}/src/api/routes/__init__.py +0 -0
  36. {databricks_tellr_app-0.3.13.dev2 → databricks_tellr_app-0.3.13.dev3}/src/api/routes/admin.py +0 -0
  37. {databricks_tellr_app-0.3.13.dev2 → databricks_tellr_app-0.3.13.dev3}/src/api/routes/admin_usage.py +0 -0
  38. {databricks_tellr_app-0.3.13.dev2 → databricks_tellr_app-0.3.13.dev3}/src/api/routes/agent_config.py +0 -0
  39. {databricks_tellr_app-0.3.13.dev2 → databricks_tellr_app-0.3.13.dev3}/src/api/routes/chat.py +0 -0
  40. {databricks_tellr_app-0.3.13.dev2 → databricks_tellr_app-0.3.13.dev3}/src/api/routes/deck_contributors.py +0 -0
  41. {databricks_tellr_app-0.3.13.dev2 → databricks_tellr_app-0.3.13.dev3}/src/api/routes/export.py +0 -0
  42. {databricks_tellr_app-0.3.13.dev2 → databricks_tellr_app-0.3.13.dev3}/src/api/routes/feedback.py +0 -0
  43. {databricks_tellr_app-0.3.13.dev2 → databricks_tellr_app-0.3.13.dev3}/src/api/routes/google_slides.py +0 -0
  44. {databricks_tellr_app-0.3.13.dev2 → databricks_tellr_app-0.3.13.dev3}/src/api/routes/images.py +0 -0
  45. {databricks_tellr_app-0.3.13.dev2 → databricks_tellr_app-0.3.13.dev3}/src/api/routes/local_version.py +0 -0
  46. {databricks_tellr_app-0.3.13.dev2 → databricks_tellr_app-0.3.13.dev3}/src/api/routes/profiles.py +0 -0
  47. {databricks_tellr_app-0.3.13.dev2 → databricks_tellr_app-0.3.13.dev3}/src/api/routes/sessions.py +0 -0
  48. {databricks_tellr_app-0.3.13.dev2 → databricks_tellr_app-0.3.13.dev3}/src/api/routes/settings/__init__.py +0 -0
  49. {databricks_tellr_app-0.3.13.dev2 → databricks_tellr_app-0.3.13.dev3}/src/api/routes/settings/contributors.py +0 -0
  50. {databricks_tellr_app-0.3.13.dev2 → databricks_tellr_app-0.3.13.dev3}/src/api/routes/settings/deck_prompts.py +0 -0
  51. {databricks_tellr_app-0.3.13.dev2 → databricks_tellr_app-0.3.13.dev3}/src/api/routes/settings/identities.py +0 -0
  52. {databricks_tellr_app-0.3.13.dev2 → databricks_tellr_app-0.3.13.dev3}/src/api/routes/settings/slide_styles.py +0 -0
  53. {databricks_tellr_app-0.3.13.dev2 → databricks_tellr_app-0.3.13.dev3}/src/api/routes/setup.py +0 -0
  54. {databricks_tellr_app-0.3.13.dev2 → databricks_tellr_app-0.3.13.dev3}/src/api/routes/slides.py +0 -0
  55. {databricks_tellr_app-0.3.13.dev2 → databricks_tellr_app-0.3.13.dev3}/src/api/routes/tools.py +0 -0
  56. {databricks_tellr_app-0.3.13.dev2 → databricks_tellr_app-0.3.13.dev3}/src/api/routes/tour.py +0 -0
  57. {databricks_tellr_app-0.3.13.dev2 → databricks_tellr_app-0.3.13.dev3}/src/api/routes/verification.py +0 -0
  58. {databricks_tellr_app-0.3.13.dev2 → databricks_tellr_app-0.3.13.dev3}/src/api/routes/version.py +0 -0
  59. {databricks_tellr_app-0.3.13.dev2 → databricks_tellr_app-0.3.13.dev3}/src/api/schemas/__init__.py +0 -0
  60. {databricks_tellr_app-0.3.13.dev2 → databricks_tellr_app-0.3.13.dev3}/src/api/schemas/agent_config.py +0 -0
  61. {databricks_tellr_app-0.3.13.dev2 → databricks_tellr_app-0.3.13.dev3}/src/api/schemas/feedback.py +0 -0
  62. {databricks_tellr_app-0.3.13.dev2 → databricks_tellr_app-0.3.13.dev3}/src/api/schemas/requests.py +0 -0
  63. {databricks_tellr_app-0.3.13.dev2 → databricks_tellr_app-0.3.13.dev3}/src/api/schemas/responses.py +0 -0
  64. {databricks_tellr_app-0.3.13.dev2 → databricks_tellr_app-0.3.13.dev3}/src/api/schemas/settings/__init__.py +0 -0
  65. {databricks_tellr_app-0.3.13.dev2 → databricks_tellr_app-0.3.13.dev3}/src/api/schemas/settings/requests.py +0 -0
  66. {databricks_tellr_app-0.3.13.dev2 → databricks_tellr_app-0.3.13.dev3}/src/api/schemas/settings/responses.py +0 -0
  67. {databricks_tellr_app-0.3.13.dev2 → databricks_tellr_app-0.3.13.dev3}/src/api/schemas/streaming.py +0 -0
  68. {databricks_tellr_app-0.3.13.dev2 → databricks_tellr_app-0.3.13.dev3}/src/api/services/__init__.py +0 -0
  69. {databricks_tellr_app-0.3.13.dev2 → databricks_tellr_app-0.3.13.dev3}/src/api/services/chat_service.py +0 -0
  70. {databricks_tellr_app-0.3.13.dev2 → databricks_tellr_app-0.3.13.dev3}/src/api/services/feedback_service.py +0 -0
  71. {databricks_tellr_app-0.3.13.dev2 → databricks_tellr_app-0.3.13.dev3}/src/api/services/job_queue.py +0 -0
  72. {databricks_tellr_app-0.3.13.dev2 → databricks_tellr_app-0.3.13.dev3}/src/api/services/session_manager.py +0 -0
  73. {databricks_tellr_app-0.3.13.dev2 → databricks_tellr_app-0.3.13.dev3}/src/api/services/session_naming.py +0 -0
  74. {databricks_tellr_app-0.3.13.dev2 → databricks_tellr_app-0.3.13.dev3}/src/api/services/usage_events.py +0 -0
  75. {databricks_tellr_app-0.3.13.dev2 → databricks_tellr_app-0.3.13.dev3}/src/api/services/usage_service.py +0 -0
  76. {databricks_tellr_app-0.3.13.dev2 → databricks_tellr_app-0.3.13.dev3}/src/api/utils/__init__.py +0 -0
  77. {databricks_tellr_app-0.3.13.dev2 → databricks_tellr_app-0.3.13.dev3}/src/api/utils/validation.py +0 -0
  78. {databricks_tellr_app-0.3.13.dev2 → databricks_tellr_app-0.3.13.dev3}/src/core/__init__.py +0 -0
  79. {databricks_tellr_app-0.3.13.dev2 → databricks_tellr_app-0.3.13.dev3}/src/core/config_loader.py +0 -0
  80. {databricks_tellr_app-0.3.13.dev2 → databricks_tellr_app-0.3.13.dev3}/src/core/context_utils.py +0 -0
  81. {databricks_tellr_app-0.3.13.dev2 → databricks_tellr_app-0.3.13.dev3}/src/core/database.py +0 -0
  82. {databricks_tellr_app-0.3.13.dev2 → databricks_tellr_app-0.3.13.dev3}/src/core/databricks_client.py +0 -0
  83. {databricks_tellr_app-0.3.13.dev2 → databricks_tellr_app-0.3.13.dev3}/src/core/defaults.py +0 -0
  84. {databricks_tellr_app-0.3.13.dev2 → databricks_tellr_app-0.3.13.dev3}/src/core/encryption.py +0 -0
  85. {databricks_tellr_app-0.3.13.dev2 → databricks_tellr_app-0.3.13.dev3}/src/core/init_default_profile.py +0 -0
  86. {databricks_tellr_app-0.3.13.dev2 → databricks_tellr_app-0.3.13.dev3}/src/core/lakebase.py +0 -0
  87. {databricks_tellr_app-0.3.13.dev2 → databricks_tellr_app-0.3.13.dev3}/src/core/migrate_profiles_to_agent_config.py +0 -0
  88. {databricks_tellr_app-0.3.13.dev2 → databricks_tellr_app-0.3.13.dev3}/src/core/mlflow_agent_spans.py +0 -0
  89. {databricks_tellr_app-0.3.13.dev2 → databricks_tellr_app-0.3.13.dev3}/src/core/mlflow_tracing.py +0 -0
  90. {databricks_tellr_app-0.3.13.dev2 → databricks_tellr_app-0.3.13.dev3}/src/core/otel_logging.py +0 -0
  91. {databricks_tellr_app-0.3.13.dev2 → databricks_tellr_app-0.3.13.dev3}/src/core/permission_context.py +0 -0
  92. {databricks_tellr_app-0.3.13.dev2 → databricks_tellr_app-0.3.13.dev3}/src/core/prompt_modules.py +0 -0
  93. {databricks_tellr_app-0.3.13.dev2 → databricks_tellr_app-0.3.13.dev3}/src/core/settings_db.py +0 -0
  94. {databricks_tellr_app-0.3.13.dev2 → databricks_tellr_app-0.3.13.dev3}/src/core/user_context.py +0 -0
  95. {databricks_tellr_app-0.3.13.dev2 → databricks_tellr_app-0.3.13.dev3}/src/database/__init__.py +0 -0
  96. {databricks_tellr_app-0.3.13.dev2 → databricks_tellr_app-0.3.13.dev3}/src/database/models/__init__.py +0 -0
  97. {databricks_tellr_app-0.3.13.dev2 → databricks_tellr_app-0.3.13.dev3}/src/database/models/deck_contributor.py +0 -0
  98. {databricks_tellr_app-0.3.13.dev2 → databricks_tellr_app-0.3.13.dev3}/src/database/models/feedback.py +0 -0
  99. {databricks_tellr_app-0.3.13.dev2 → databricks_tellr_app-0.3.13.dev3}/src/database/models/genie_space.py +0 -0
  100. {databricks_tellr_app-0.3.13.dev2 → databricks_tellr_app-0.3.13.dev3}/src/database/models/google_global_credentials.py +0 -0
  101. {databricks_tellr_app-0.3.13.dev2 → databricks_tellr_app-0.3.13.dev3}/src/database/models/google_oauth_token.py +0 -0
  102. {databricks_tellr_app-0.3.13.dev2 → databricks_tellr_app-0.3.13.dev3}/src/database/models/identity.py +0 -0
  103. {databricks_tellr_app-0.3.13.dev2 → databricks_tellr_app-0.3.13.dev3}/src/database/models/image.py +0 -0
  104. {databricks_tellr_app-0.3.13.dev2 → databricks_tellr_app-0.3.13.dev3}/src/database/models/profile.py +0 -0
  105. {databricks_tellr_app-0.3.13.dev2 → databricks_tellr_app-0.3.13.dev3}/src/database/models/profile_contributor.py +0 -0
  106. {databricks_tellr_app-0.3.13.dev2 → databricks_tellr_app-0.3.13.dev3}/src/database/models/prompts.py +0 -0
  107. {databricks_tellr_app-0.3.13.dev2 → databricks_tellr_app-0.3.13.dev3}/src/database/models/request_log.py +0 -0
  108. {databricks_tellr_app-0.3.13.dev2 → databricks_tellr_app-0.3.13.dev3}/src/database/models/session.py +0 -0
  109. {databricks_tellr_app-0.3.13.dev2 → databricks_tellr_app-0.3.13.dev3}/src/database/models/slide_deck_prompt.py +0 -0
  110. {databricks_tellr_app-0.3.13.dev2 → databricks_tellr_app-0.3.13.dev3}/src/database/models/slide_style_library.py +0 -0
  111. {databricks_tellr_app-0.3.13.dev2 → databricks_tellr_app-0.3.13.dev3}/src/database/models/usage_event.py +0 -0
  112. {databricks_tellr_app-0.3.13.dev2 → databricks_tellr_app-0.3.13.dev3}/src/database/models/user_preference.py +0 -0
  113. {databricks_tellr_app-0.3.13.dev2 → databricks_tellr_app-0.3.13.dev3}/src/domain/__init__.py +0 -0
  114. {databricks_tellr_app-0.3.13.dev2 → databricks_tellr_app-0.3.13.dev3}/src/domain/slide.py +0 -0
  115. {databricks_tellr_app-0.3.13.dev2 → databricks_tellr_app-0.3.13.dev3}/src/domain/slide_deck.py +0 -0
  116. {databricks_tellr_app-0.3.13.dev2 → databricks_tellr_app-0.3.13.dev3}/src/services/__init__.py +0 -0
  117. {databricks_tellr_app-0.3.13.dev2 → databricks_tellr_app-0.3.13.dev3}/src/services/agent.py +0 -0
  118. {databricks_tellr_app-0.3.13.dev2 → databricks_tellr_app-0.3.13.dev3}/src/services/agent_factory.py +0 -0
  119. {databricks_tellr_app-0.3.13.dev2 → databricks_tellr_app-0.3.13.dev3}/src/services/config_service.py +0 -0
  120. {databricks_tellr_app-0.3.13.dev2 → databricks_tellr_app-0.3.13.dev3}/src/services/config_validator.py +0 -0
  121. {databricks_tellr_app-0.3.13.dev2 → databricks_tellr_app-0.3.13.dev3}/src/services/drive_uploader.py +0 -0
  122. {databricks_tellr_app-0.3.13.dev2 → databricks_tellr_app-0.3.13.dev3}/src/services/evaluation/__init__.py +0 -0
  123. {databricks_tellr_app-0.3.13.dev2 → databricks_tellr_app-0.3.13.dev3}/src/services/evaluation/llm_judge.py +0 -0
  124. {databricks_tellr_app-0.3.13.dev2 → databricks_tellr_app-0.3.13.dev3}/src/services/genie_service.py +0 -0
  125. {databricks_tellr_app-0.3.13.dev2 → databricks_tellr_app-0.3.13.dev3}/src/services/google_slides_auth.py +0 -0
  126. {databricks_tellr_app-0.3.13.dev2 → databricks_tellr_app-0.3.13.dev3}/src/services/identity_provider.py +0 -0
  127. {databricks_tellr_app-0.3.13.dev2 → databricks_tellr_app-0.3.13.dev3}/src/services/identity_providers/__init__.py +0 -0
  128. {databricks_tellr_app-0.3.13.dev2 → databricks_tellr_app-0.3.13.dev3}/src/services/identity_providers/account_provider.py +0 -0
  129. {databricks_tellr_app-0.3.13.dev2 → databricks_tellr_app-0.3.13.dev3}/src/services/identity_providers/local_provider.py +0 -0
  130. {databricks_tellr_app-0.3.13.dev2 → databricks_tellr_app-0.3.13.dev3}/src/services/identity_providers/workspace_provider.py +0 -0
  131. {databricks_tellr_app-0.3.13.dev2 → databricks_tellr_app-0.3.13.dev3}/src/services/image_service.py +0 -0
  132. {databricks_tellr_app-0.3.13.dev2 → databricks_tellr_app-0.3.13.dev3}/src/services/image_tools.py +0 -0
  133. {databricks_tellr_app-0.3.13.dev2 → databricks_tellr_app-0.3.13.dev3}/src/services/permission_service.py +0 -0
  134. {databricks_tellr_app-0.3.13.dev2 → databricks_tellr_app-0.3.13.dev3}/src/services/pptx_from_html_huashu.py +0 -0
  135. {databricks_tellr_app-0.3.13.dev2 → databricks_tellr_app-0.3.13.dev3}/src/services/pptx_from_records.py +0 -0
  136. {databricks_tellr_app-0.3.13.dev2 → databricks_tellr_app-0.3.13.dev3}/src/services/pptx_prompts_defaults.py +0 -0
  137. {databricks_tellr_app-0.3.13.dev2 → databricks_tellr_app-0.3.13.dev3}/src/services/profile_service.py +0 -0
  138. {databricks_tellr_app-0.3.13.dev2 → databricks_tellr_app-0.3.13.dev3}/src/services/streaming_callback.py +0 -0
  139. {databricks_tellr_app-0.3.13.dev2 → databricks_tellr_app-0.3.13.dev3}/src/services/tools/__init__.py +0 -0
  140. {databricks_tellr_app-0.3.13.dev2 → databricks_tellr_app-0.3.13.dev3}/src/services/tools/agent_bricks_tool.py +0 -0
  141. {databricks_tellr_app-0.3.13.dev2 → databricks_tellr_app-0.3.13.dev3}/src/services/tools/genie_tool.py +0 -0
  142. {databricks_tellr_app-0.3.13.dev2 → databricks_tellr_app-0.3.13.dev3}/src/services/tools/mcp_tool.py +0 -0
  143. {databricks_tellr_app-0.3.13.dev2 → databricks_tellr_app-0.3.13.dev3}/src/services/tools/model_endpoint_tool.py +0 -0
  144. {databricks_tellr_app-0.3.13.dev2 → databricks_tellr_app-0.3.13.dev3}/src/services/tools/vector_tool.py +0 -0
  145. {databricks_tellr_app-0.3.13.dev2 → databricks_tellr_app-0.3.13.dev3}/src/services/validator.py +0 -0
  146. {databricks_tellr_app-0.3.13.dev2 → databricks_tellr_app-0.3.13.dev3}/src/utils/__init__.py +0 -0
  147. {databricks_tellr_app-0.3.13.dev2 → databricks_tellr_app-0.3.13.dev3}/src/utils/css_utils.py +0 -0
  148. {databricks_tellr_app-0.3.13.dev2 → databricks_tellr_app-0.3.13.dev3}/src/utils/error_handling.py +0 -0
  149. {databricks_tellr_app-0.3.13.dev2 → databricks_tellr_app-0.3.13.dev3}/src/utils/html_safety.py +0 -0
  150. {databricks_tellr_app-0.3.13.dev2 → databricks_tellr_app-0.3.13.dev3}/src/utils/html_utils.py +0 -0
  151. {databricks_tellr_app-0.3.13.dev2 → databricks_tellr_app-0.3.13.dev3}/src/utils/image_utils.py +0 -0
  152. {databricks_tellr_app-0.3.13.dev2 → databricks_tellr_app-0.3.13.dev3}/src/utils/js_validator.py +0 -0
  153. {databricks_tellr_app-0.3.13.dev2 → databricks_tellr_app-0.3.13.dev3}/src/utils/logging_config.py +0 -0
  154. {databricks_tellr_app-0.3.13.dev2 → databricks_tellr_app-0.3.13.dev3}/src/utils/pi_filter.py +0 -0
  155. {databricks_tellr_app-0.3.13.dev2 → databricks_tellr_app-0.3.13.dev3}/src/utils/slide_hash.py +0 -0
  156. {databricks_tellr_app-0.3.13.dev2 → databricks_tellr_app-0.3.13.dev3}/src/utils/spotlight.py +0 -0
  157. {databricks_tellr_app-0.3.13.dev2 → databricks_tellr_app-0.3.13.dev3}/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.dev2
3
+ Version: 0.3.13.dev3
4
4
  Summary: Tellr application package for Databricks Apps
5
5
  Requires-Python: >=3.10
6
6
  Description-Content-Type: text/markdown
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: databricks-tellr-app
3
- Version: 0.3.13.dev2
3
+ Version: 0.3.13.dev3
4
4
  Summary: Tellr application package for Databricks Apps
5
5
  Requires-Python: >=3.10
6
6
  Description-Content-Type: text/markdown
@@ -11,9 +11,7 @@ setup.py
11
11
  ./src/api/mcp_server.py
12
12
  ./src/api/fixtures/tour_demo_deck.json
13
13
  ./src/api/middleware/__init__.py
14
- ./src/api/middleware/csrf.py
15
14
  ./src/api/middleware/request_logging.py
16
- ./src/api/middleware/security_headers.py
17
15
  ./src/api/routes/__init__.py
18
16
  ./src/api/routes/admin.py
19
17
  ./src/api/routes/admin_usage.py
@@ -117,6 +115,13 @@ setup.py
117
115
  ./src/services/profile_service.py
118
116
  ./src/services/streaming_callback.py
119
117
  ./src/services/validator.py
118
+ ./src/services/converter_jail/__init__.py
119
+ ./src/services/converter_jail/ast_guard.py
120
+ ./src/services/converter_jail/codeprep.py
121
+ ./src/services/converter_jail/gslides_runner.py
122
+ ./src/services/converter_jail/jail.py
123
+ ./src/services/converter_jail/pptx_runner.py
124
+ ./src/services/converter_jail/protocol.py
120
125
  ./src/services/evaluation/__init__.py
121
126
  ./src/services/evaluation/llm_judge.py
122
127
  ./src/services/identity_providers/__init__.py
@@ -156,9 +161,7 @@ src/api/mcp_auth.py
156
161
  src/api/mcp_server.py
157
162
  src/api/fixtures/tour_demo_deck.json
158
163
  src/api/middleware/__init__.py
159
- src/api/middleware/csrf.py
160
164
  src/api/middleware/request_logging.py
161
- src/api/middleware/security_headers.py
162
165
  src/api/routes/__init__.py
163
166
  src/api/routes/admin.py
164
167
  src/api/routes/admin_usage.py
@@ -262,6 +265,13 @@ src/services/pptx_prompts_defaults.py
262
265
  src/services/profile_service.py
263
266
  src/services/streaming_callback.py
264
267
  src/services/validator.py
268
+ src/services/converter_jail/__init__.py
269
+ src/services/converter_jail/ast_guard.py
270
+ src/services/converter_jail/codeprep.py
271
+ src/services/converter_jail/gslides_runner.py
272
+ src/services/converter_jail/jail.py
273
+ src/services/converter_jail/pptx_runner.py
274
+ src/services/converter_jail/protocol.py
265
275
  src/services/evaluation/__init__.py
266
276
  src/services/evaluation/llm_judge.py
267
277
  src/services/identity_providers/__init__.py
@@ -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.dev2"
7
+ version = "0.3.13.dev3"
8
8
  description = "Tellr application package for Databricks Apps"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
@@ -417,24 +417,6 @@ from src.api.middleware.request_logging import RequestLoggingMiddleware
417
417
 
418
418
  app.add_middleware(RequestLoggingMiddleware)
419
419
 
420
- # === SDR-4437 Track A security middleware (PR-1a/PR-1b) ==================
421
- # Keep ALL security-middleware registration inside this delimited block
422
- # (PR-2 adds an exception handler below the middleware block — do not edit
423
- # outside these markers). Starlette wraps later-added middleware outermost;
424
- # ordering here is deliberate:
425
- # SecurityHeaders (outermost, added last) -> CSRF -> RequestLogging ->
426
- # user_auth -> CORS(dev) -> normalize_mcp_path -> routes
427
- # Headers must stamp EVERY response (including CSRF 403s); CSRF must reject
428
- # forged cross-site requests before OBO auth work and before request_logs
429
- # DB writes. CSRF runs outside normalize_mcp_path, so it sees the raw
430
- # un-rewritten /mcp path (its exemption handles both /mcp and /mcp/).
431
- from src.api.middleware.csrf import CSRFProtectionMiddleware # noqa: E402
432
- from src.api.middleware.security_headers import SecurityHeadersMiddleware # noqa: E402
433
-
434
- app.add_middleware(CSRFProtectionMiddleware)
435
- app.add_middleware(SecurityHeadersMiddleware)
436
- # === end SDR-4437 Track A security middleware =============================
437
-
438
420
  # Include API routers
439
421
  app.include_router(admin.router)
440
422
  app.include_router(admin_usage.router)
@@ -307,9 +307,6 @@ async def convert_slides_with_progress(
307
307
  output_path: Path to save PPTX
308
308
  chart_images_per_slide: Chart images per slide
309
309
  """
310
- from pptx import Presentation
311
- from pptx.util import Inches
312
-
313
310
  total_slides = len(slides_html)
314
311
 
315
312
  # -- Phase 1: Prepare assets + parallel LLM code generation ---------------
@@ -336,27 +333,17 @@ async def convert_slides_with_progress(
336
333
  slide_inputs, on_codegen_progress=_on_codegen_progress,
337
334
  )
338
335
 
339
- # -- Phase 2: Sequential execution ----------------------------------------
340
- prs = Presentation()
341
- prs.slide_width = Inches(10)
342
- prs.slide_height = Inches(7.5)
343
-
344
- for i, (code, (html_str, _chart_files, _content_files, assets_dir)) in enumerate(
345
- zip(codes, slide_inputs), 1,
346
- ):
347
- update_export_progress(
348
- job_id, i, total_slides,
349
- status_message=f"Building slide {i}/{total_slides}…",
350
- )
351
-
352
- converter._execute_slide(code, prs, html_str, assets_dir, i)
353
-
354
- logger.debug(
355
- f"Processed slide {i}/{total_slides}",
356
- extra={"job_id": job_id, "slide": i, "total": total_slides},
357
- )
336
+ # -- Phase 2: Sequential execution INSIDE the subprocess jail -------------
337
+ def _relay(current: int, total: int, message: str) -> None:
338
+ update_export_progress(job_id, current, total, status_message=message)
358
339
 
359
- prs.save(output_path)
340
+ # to_thread: keep the long blocking jail wait off the event loop (this
341
+ # coroutine runs on the loop; _relay is fired from the jail's stdout
342
+ # reader thread and update_export_progress opens its own session per call).
343
+ await asyncio.to_thread(
344
+ converter._run_pptx_conversion,
345
+ codes, slide_inputs, output_path, progress_cb=_relay,
346
+ )
360
347
 
361
348
 
362
349
  async def export_worker() -> None:
@@ -0,0 +1,10 @@
1
+ # src/services/converter_jail/__init__.py
2
+ """Subprocess jail for LLM-generated converter code (SDR-4437 HIGH-5)."""
3
+
4
+ from src.services.converter_jail.jail import ( # noqa: F401
5
+ JailError,
6
+ JailResult,
7
+ ResourceLimits,
8
+ run_gslides_jail,
9
+ run_pptx_jail,
10
+ )
@@ -0,0 +1,58 @@
1
+ # src/services/converter_jail/ast_guard.py
2
+ """Best-effort AST import allowlist for generated converter code.
3
+
4
+ Defense-in-depth ONLY — bypassable by construction (e.g. __import__,
5
+ importlib). The security boundary is the subprocess jail, not this check.
6
+ Runs on the trusted host before launch so obviously-hostile code never
7
+ reaches the child.
8
+ """
9
+
10
+ import ast
11
+
12
+
13
+ class DisallowedImport(Exception):
14
+ """Raised when generated code imports a module outside the allowlist."""
15
+
16
+
17
+ # Top-level module names generated converters legitimately need.
18
+ # Keep this GENEROUS for harmless stdlib: a snippet failing this check is
19
+ # downgraded to a blank placeholder slide (has_code=False in the job-dir
20
+ # builders, Tasks 4/6), so a too-narrow list silently blanks legitimate
21
+ # slides. The prompts only show example imports — they do not restrict the
22
+ # LLM to a fixed set — and the jail, not this list, is the security boundary.
23
+ DEFAULT_ALLOWED = frozenset({
24
+ # PPTX path
25
+ "pptx", "os", "re", "math", "json", "uuid", "base64", "io",
26
+ "datetime", "collections", "itertools", "string", "textwrap",
27
+ # harmless stdlib the LLM commonly emits unprompted
28
+ "sys", "time", "typing", "functools", "random", "copy",
29
+ "html", "unicodedata", "decimal",
30
+ # image handling
31
+ "PIL", "lxml",
32
+ # Google Slides path emits data only; googleapiclient is host-side now,
33
+ # but allow the http helper import in case a snippet still references it
34
+ # (host strips its effect; import alone is harmless).
35
+ "googleapiclient",
36
+ "xml",
37
+ })
38
+
39
+
40
+ def _root(name: str) -> str:
41
+ return name.split(".", 1)[0]
42
+
43
+
44
+ def check_imports(code: str, allowed: frozenset = DEFAULT_ALLOWED) -> None:
45
+ """Raise DisallowedImport if *code* imports a non-allowlisted top-level
46
+ module. Raises SyntaxError if *code* does not parse."""
47
+ tree = ast.parse(code)
48
+ for node in ast.walk(tree):
49
+ if isinstance(node, ast.Import):
50
+ for alias in node.names:
51
+ if _root(alias.name) not in allowed:
52
+ raise DisallowedImport(_root(alias.name))
53
+ elif isinstance(node, ast.ImportFrom):
54
+ # Relative imports (node.level > 0) have no module root to check;
55
+ # they cannot reach outside the (nonexistent) package, so allow.
56
+ if node.level == 0 and node.module is not None:
57
+ if _root(node.module) not in allowed:
58
+ raise DisallowedImport(_root(node.module))
@@ -0,0 +1,220 @@
1
+ # src/services/converter_jail/codeprep.py
2
+ """Dependency-light code-prep helpers shared by the jail runners (SDR-4437 PR-5).
3
+
4
+ STDLIB-ONLY BY DESIGN. This module is imported inside the sandboxed child
5
+ (`python -I`), so it must never pull in `databricks`, `google*`, `bs4`,
6
+ `pptx`, or any `src.services.*` converter module — those graphs drag the
7
+ credential-capable SDK into the jail just to call string transformers.
8
+
9
+ Contents:
10
+ - ``sanitize_code`` — moved verbatim from ``HtmlToPptxConverterV3._sanitize_code``.
11
+ - ``_fix_apostrophe_strings`` / ``_convert_single_to_double_quoted`` — moved
12
+ verbatim from ``html_to_google_slides``.
13
+ - ``prepare_requests_code`` — preps generated Google Slides data-out code
14
+ (wraps bare code into ``build_slide_requests``; request-shape fixes).
15
+ """
16
+
17
+ import ast
18
+ import re
19
+
20
+
21
+ def sanitize_code(code: str) -> str:
22
+ """Fix common LLM-generated code issues before execution.
23
+
24
+ Handles smart quotes, em-dashes, and apostrophes inside string literals
25
+ that cause SyntaxError (e.g. ``Anthony's`` inside a single-quoted string).
26
+ """
27
+ # Smart/curly quotes → straight quotes
28
+ for smart, straight in {
29
+ "‘": "'", "’": "'", # single curly quotes
30
+ "“": '"', "”": '"', # double curly quotes
31
+ }.items():
32
+ code = code.replace(smart, straight)
33
+
34
+ # Em-dash / en-dash → regular hyphen (prevents SyntaxError when
35
+ # they appear outside strings due to broken quoting)
36
+ code = code.replace("—", "-") # em-dash
37
+ code = code.replace("–", "-") # en-dash
38
+
39
+ # Fix apostrophes inside single-quoted strings (e.g. 'Anthony's workflow')
40
+ # by converting affected lines to use double-quoted strings.
41
+ for _ in range(20):
42
+ try:
43
+ ast.parse(code)
44
+ break
45
+ except SyntaxError as exc:
46
+ if exc.lineno is None:
47
+ break
48
+ lines = code.splitlines()
49
+ idx = exc.lineno - 1
50
+ if idx < 0 or idx >= len(lines):
51
+ break
52
+ original = lines[idx]
53
+ # Re-quote single-quoted strings that contain apostrophes
54
+ fixed = re.sub(
55
+ r"'([^']*\w'\w[^']*)'",
56
+ lambda m: '"' + m.group(1) + '"',
57
+ original,
58
+ )
59
+ if fixed == original:
60
+ break
61
+ lines[idx] = fixed
62
+ code = "\n".join(lines)
63
+ return code
64
+
65
+
66
+ # ---------------------------------------------------------------------------
67
+ # Apostrophe-in-string syntax fixer
68
+ # ---------------------------------------------------------------------------
69
+
70
+ def _convert_single_to_double_quoted(line: str) -> str:
71
+ """Re-quote single-quoted strings that contain apostrophes as double-quoted.
72
+
73
+ Scans each single-quote-delimited token on *line*. When an apostrophe is
74
+ surrounded by word characters (``\\w'\\w``) it is treated as part of the
75
+ content (not a closing delimiter), so the scanner continues until it finds a
76
+ genuine closing quote. Those strings are then re-emitted with double-quote
77
+ delimiters so Python can parse them correctly.
78
+
79
+ Example::
80
+
81
+ 'We didn't do it' → "We didn't do it"
82
+ """
83
+ result: list = []
84
+ i = 0
85
+ n = len(line)
86
+
87
+ while i < n:
88
+ if line[i] != "'":
89
+ result.append(line[i])
90
+ i += 1
91
+ continue
92
+
93
+ # Scan for the "real" closing quote, treating word-apostrophe-word as content.
94
+ j = i + 1
95
+ while j < n:
96
+ if line[j] == "'":
97
+ before_word = j > 0 and line[j - 1].isalnum()
98
+ after_word = (j + 1) < n and line[j + 1].isalnum()
99
+ if before_word and after_word:
100
+ j += 1 # it's a contraction apostrophe — keep scanning
101
+ continue
102
+ break # genuine closing quote
103
+ j += 1
104
+
105
+ if j >= n:
106
+ # Never found a real closing quote — rest of line is broken content.
107
+ content = line[i + 1:]
108
+ result.append('"' + content.replace('"', '\\"') + '"')
109
+ break
110
+
111
+ content = line[i + 1: j]
112
+ if re.search(r"\w'\w", content):
113
+ # Content has an apostrophe → rewrite as double-quoted string.
114
+ result.append('"' + content.replace('"', '\\"') + '"')
115
+ else:
116
+ result.append(line[i: j + 1])
117
+
118
+ i = j + 1
119
+
120
+ return "".join(result)
121
+
122
+
123
+ def _fix_apostrophe_strings(code: str) -> str:
124
+ """Fix single-quoted literals whose content contains apostrophes (contractions).
125
+
126
+ The LLM often emits ``'text': 'We don't ...'``. The ``'`` in *don't* ends the
127
+ literal early; the parser may report ``unterminated string``, ``eol while
128
+ scanning``, or ``invalid character`` (e.g. em-dash) on the remainder.
129
+
130
+ Repeatedly ``ast.parse`` and rewrite the error line with
131
+ :func:`_convert_single_to_double_quoted` whenever that changes the line.
132
+ """
133
+ for _ in range(20): # guard against infinite loops
134
+ try:
135
+ ast.parse(code)
136
+ return code
137
+ except SyntaxError as exc:
138
+ if exc.lineno is None:
139
+ return code
140
+
141
+ lines = code.splitlines()
142
+ line_idx = exc.lineno - 1
143
+ if line_idx >= len(lines):
144
+ return code
145
+
146
+ original = lines[line_idx]
147
+ fixed = _convert_single_to_double_quoted(original)
148
+ if fixed == original:
149
+ return code # heuristic cannot improve this error
150
+
151
+ lines[line_idx] = fixed
152
+ code = "\n".join(lines)
153
+
154
+ return code
155
+
156
+
157
+ # ---------------------------------------------------------------------------
158
+ # Google Slides data-out contract prep
159
+ # ---------------------------------------------------------------------------
160
+
161
+ _ALPHA_PAT = re.compile(r"""[,\s]*['"]alpha['"]\s*:\s*[\d.]+""")
162
+
163
+
164
+ def prepare_requests_code(code: str) -> str:
165
+ """Prep generated code that defines build_slide_requests(...).
166
+
167
+ Reuses the request-shape fixers from the legacy ``_prepare_code`` but wraps
168
+ bare code into ``build_slide_requests`` (not ``add_slide_to_presentation``)
169
+ and never references services."""
170
+ if "def build_slide_requests" not in code:
171
+ lines = code.split("\n")
172
+ imports, body, helpers = [], [], []
173
+ in_helper = False
174
+ for line in lines:
175
+ stripped = line.strip()
176
+ if stripped.startswith(("import ", "from ")) and not body:
177
+ imports.append(line)
178
+ elif stripped.startswith(("def emu(", "def hex_to_rgb(")):
179
+ in_helper = True
180
+ helpers.append(line)
181
+ elif in_helper:
182
+ if stripped == "" or (line and line[0] == " "):
183
+ helpers.append(line)
184
+ if stripped == "":
185
+ in_helper = False
186
+ else:
187
+ in_helper = False
188
+ body.append(line)
189
+ else:
190
+ body.append(line)
191
+ indented = [(" " + l if l.strip() else "") for l in body]
192
+ parts = []
193
+ if imports:
194
+ parts.append("\n".join(imports) + "\n")
195
+ if helpers:
196
+ parts.append("\n".join(helpers) + "\n")
197
+ parts.append("def build_slide_requests(html_str, assets_dir, page_id):")
198
+ parts.extend(indented)
199
+ parts.append(" return requests")
200
+ code = "\n".join(parts)
201
+
202
+ if "import os" not in code:
203
+ code = "import os\nimport json\nimport uuid\n\n" + code
204
+
205
+ for smart, straight in {"‘": "'", "’": "'", "“": '"', "”": '"'}.items():
206
+ code = code.replace(smart, straight)
207
+
208
+ if "alpha" in code:
209
+ code = _ALPHA_PAT.sub("", code)
210
+ if "'paragraphStyle'" in code:
211
+ code = code.replace("'paragraphStyle'", "'style'")
212
+ if '"paragraphStyle"' in code:
213
+ code = code.replace('"paragraphStyle"', '"style"')
214
+ if "'textStyle'" in code:
215
+ code = code.replace("'textStyle'", "'style'")
216
+ if '"textStyle"' in code:
217
+ code = code.replace('"textStyle"', '"style"')
218
+
219
+ code = _fix_apostrophe_strings(code)
220
+ return code
@@ -0,0 +1,78 @@
1
+ # src/services/converter_jail/gslides_runner.py
2
+ """In-jail trusted runner for Google Slides conversion (SDR-4437 HIGH-5).
3
+
4
+ Runs each generated build_slide_requests(html_str, assets_dir, page_id)
5
+ snippet and collects the returned request lists into a JSON file. NO network,
6
+ NO credentials. The host validates, uploads assets, substitutes placeholder
7
+ URLs, and executes the requests.
8
+
9
+ CLI: python -I gslides_runner.py <src_parent> <job_dir> <requests_out_path>
10
+ (<src_parent> is part of the launcher's CLI contract; this runner does NOT
11
+ put it on sys.path — importing anything via `src.services` would execute that
12
+ package's heavy __init__ and drag the credential-capable SDK graph into the
13
+ jail child. Siblings are imported as top-level modules instead.)
14
+ """
15
+
16
+ import importlib.util
17
+ import json
18
+ import sys
19
+ import tempfile
20
+ from pathlib import Path
21
+
22
+ _HERE = Path(__file__).resolve().parent
23
+
24
+ if __package__ in (None, ""):
25
+ # Script mode (inside the jail child): import siblings as top-level
26
+ # modules so src/services/__init__.py (SDK/agent graph) never executes.
27
+ sys.path.insert(0, str(_HERE))
28
+ import protocol
29
+ from codeprep import prepare_requests_code
30
+ else:
31
+ # Imported on the trusted host (unit tests): normal package imports.
32
+ from src.services.converter_jail import protocol
33
+ from src.services.converter_jail.codeprep import prepare_requests_code
34
+
35
+
36
+ def _exec_snippet(code: str, html_str: str, assets_dir: str, page_id: str):
37
+ """Run one snippet; return (requests_list_or_None, error_or_None)."""
38
+ code = prepare_requests_code(code)
39
+ tmp = Path(tempfile.mktemp(suffix=".py", prefix="jail_gslide_"))
40
+ tmp.write_text(code, encoding="utf-8")
41
+ try:
42
+ spec = importlib.util.spec_from_file_location("jail_gslide", str(tmp))
43
+ module = importlib.util.module_from_spec(spec)
44
+ spec.loader.exec_module(module)
45
+ requests = module.build_slide_requests(html_str, assets_dir, page_id)
46
+ if not isinstance(requests, list):
47
+ return None, f"build_slide_requests returned {type(requests).__name__}"
48
+ return requests, None
49
+ except Exception as exc:
50
+ return None, repr(exc)
51
+ finally:
52
+ if tmp.exists():
53
+ tmp.unlink()
54
+
55
+
56
+ def run(job_dir: str, requests_out_path: str) -> None:
57
+ manifest = json.loads((Path(job_dir) / protocol.MANIFEST_NAME).read_text())
58
+ out = []
59
+ for entry in manifest["slides"]:
60
+ i = entry["index"]
61
+ page_id = entry["page_id"]
62
+ sdir = Path(job_dir) / entry["dir"]
63
+ assets_dir = str(sdir / protocol.ASSETS_DIR)
64
+ if not entry.get("has_code"):
65
+ out.append({"index": i, "page_id": page_id, "requests": None,
66
+ "error": "no_code"})
67
+ continue
68
+ html_str = (sdir / protocol.HTML_NAME).read_text(encoding="utf-8")
69
+ code = (sdir / protocol.CODE_NAME).read_text(encoding="utf-8")
70
+ requests, error = _exec_snippet(code, html_str, assets_dir, page_id)
71
+ out.append({"index": i, "page_id": page_id,
72
+ "requests": requests, "error": error})
73
+ Path(requests_out_path).write_text(json.dumps(out))
74
+
75
+
76
+ if __name__ == "__main__":
77
+ # argv: [runner, src_parent, job_dir, requests_out_path]
78
+ run(sys.argv[2], sys.argv[3])