{"id":489729,"date":"2026-08-04T14:15:57","date_gmt":"2026-08-04T14:15:57","guid":{"rendered":"https:\/\/savepearlharbor.com\/?p=489729"},"modified":"-0001-11-30T00:00:00","modified_gmt":"2026-08-04T14:15:57","slug":"","status":"publish","type":"post","link":"https:\/\/savepearlharbor.com\/?p=489729","title":{"rendered":"\u041a\u0430\u043a\u00a0\u044f \u0432\u0441\u0442\u0440\u043e\u0438\u043b AI\u2011\u0430\u0433\u0435\u043d\u0442\u0430 \u043f\u0440\u044f\u043c\u043e \u0432\u00a0\u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441 Apache Superset \u0438 \u043d\u0435\u00a0\u0441\u043b\u043e\u043c\u0430\u043b \u0435\u043c\u0443 CSP"},"content":{"rendered":"<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<p>\u0415\u0441\u0442\u044c Apache Superset. \u0415\u0441\u0442\u044c \u0430\u043d\u0430\u043b\u0438\u0442\u0438\u043a, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043e\u0442\u043a\u0440\u044b\u0432\u0430\u0435\u0442 SQL Lab \u0438 \u0434\u0435\u0441\u044f\u0442\u044c \u043c\u0438\u043d\u0443\u0442 \u0432\u0441\u043f\u043e\u043c\u0438\u043d\u0430\u0435\u0442, \u043a\u0430\u043a\u00a0\u043d\u0430\u0437\u044b\u0432\u0430\u0435\u0442\u0441\u044f \u0442\u0430\u0431\u043b\u0438\u0446\u0430 \u0441\u00a0\u0437\u0430\u043a\u0430\u0437\u0430\u043c\u0438\u00a0\u2014 <code>orders<\/code>, <code>olist_orders<\/code> \u0438\u043b\u0438 <code>fact_orders_v2<\/code>. \u0415\u0441\u0442\u044c \u044f\u0437\u044b\u043a\u043e\u0432\u044b\u0435 \u043c\u043e\u0434\u0435\u043b\u0438, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043e\u0442\u043b\u0438\u0447\u043d\u043e \u043f\u0438\u0448\u0443\u0442 SQL, \u043d\u043e\u00a0\u043f\u043e\u043d\u044f\u0442\u0438\u044f \u043d\u0435\u00a0\u0438\u043c\u0435\u044e\u0442, \u0447\u0442\u043e\u00a0\u043b\u0435\u0436\u0438\u0442 \u0432\u00a0\u0432\u0430\u0448\u0435\u043c \u0445\u0440\u0430\u043d\u0438\u043b\u0438\u0449\u0435.<\/p>\n<p>\u0417\u0430\u0434\u0430\u0447\u0430 \u0437\u0432\u0443\u0447\u0430\u043b\u0430 \u043f\u0440\u043e\u0441\u0442\u043e: \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0432\u00a0\u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441 Superset \u043a\u043d\u043e\u043f\u043a\u0443, \u043f\u043e\u00a0\u043a\u043e\u0442\u043e\u0440\u043e\u0439 \u043e\u0442\u043a\u0440\u044b\u0432\u0430\u0435\u0442\u0441\u044f \u0447\u0430\u0442 \u0441\u00a0\u043c\u043e\u0434\u0435\u043b\u044c\u044e. \u041f\u0440\u0438\u0447\u0451\u043c \u0430\u0433\u0435\u043d\u0442 \u0434\u043e\u043b\u0436\u0435\u043d \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c <strong>\u043f\u043e\u00a0\u043f\u0440\u0438\u043d\u0446\u0438\u043f\u0443 MCP<\/strong>\u00a0\u2014 \u043d\u0435\u00a0\u0444\u0430\u043d\u0442\u0430\u0437\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0438\u043c\u0435\u043d\u0430 \u043a\u043e\u043b\u043e\u043d\u043e\u043a, \u0430\u00a0\u0441\u0445\u043e\u0434\u0438\u0442\u044c \u0438 \u043f\u043e\u0441\u043c\u043e\u0442\u0440\u0435\u0442\u044c \u0438\u0445 \u0432\u00a0\u043c\u0435\u0442\u0430\u0434\u0430\u043d\u043d\u044b\u0445. \u041f\u043e\u0434\u043a\u043b\u044e\u0447\u0430\u0442\u044c\u0441\u044f \u043a\u00a0\u043b\u044e\u0431\u043e\u043c\u0443 OpenAI\u2011\u0441\u043e\u0432\u043c\u0435\u0441\u0442\u0438\u043c\u043e\u043c\u0443 API: \u043b\u043e\u043a\u0430\u043b\u044c\u043d\u0430\u044f Ollama, llm7, OpenAI\u00a0\u2014 \u0447\u0442\u043e\u00a0\u0443\u0433\u043e\u0434\u043d\u043e.<\/p>\n<p>\u0414\u0430\u043b\u044c\u0448\u0435\u00a0\u2014 \u043f\u0440\u043e\u00a0\u0442\u043e, \u043a\u0430\u043a\u00a0\u044d\u0442\u043e \u0434\u0435\u043b\u0430\u0435\u0442\u0441\u044f \u0431\u0435\u0437\u00a0\u0444\u043e\u0440\u043a\u0430 Superset, \u0431\u0435\u0437\u00a0\u043f\u0435\u0440\u0435\u0441\u0431\u043e\u0440\u043a\u0438 \u0444\u0440\u043e\u043d\u0442\u0435\u043d\u0434\u0430 \u0438 \u0431\u0435\u0437\u00a0\u043e\u0441\u043b\u0430\u0431\u043b\u0435\u043d\u0438\u044f \u043f\u043e\u043b\u0438\u0442\u0438\u043a\u0438 \u0431\u0435\u0437\u043e\u043f\u0430\u0441\u043d\u043e\u0441\u0442\u0438. \u0418 \u043f\u0440\u043e\u00a0\u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u043b\u043e\u0432\u0443\u0448\u0435\u043a, \u043a\u0430\u0436\u0434\u0430\u044f \u0438\u0437\u00a0\u043a\u043e\u0442\u043e\u0440\u044b\u0445 \u0441\u0442\u043e\u0438\u043b\u0430 \u043c\u043d\u0435 \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u043e\u0433\u043e \u0440\u0430\u0441\u0441\u043b\u0435\u0434\u043e\u0432\u0430\u043d\u0438\u044f.<\/p>\n<p>\u041f\u0435\u0440\u0435\u0434 \u0442\u0435\u0445\u043d\u0438\u0447\u0435\u0441\u043a\u0438\u043c \u0440\u0430\u0437\u0431\u043e\u0440\u043e\u043c \u043c\u043e\u0436\u043d\u043e \u043f\u043e\u0441\u043c\u043e\u0442\u0440\u0435\u0442\u044c \u043a\u043e\u0440\u043e\u0442\u043a\u0443\u044e \u0434\u0435\u043c\u043e\u043d\u0441\u0442\u0440\u0430\u0446\u0438\u044e \u0442\u043e\u0433\u043e, \u043a\u0430\u043a\u00a0AI\u2011\u0430\u0441\u0441\u0438\u0441\u0442\u0435\u043d\u0442 \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u043d\u0435\u043f\u043e\u0441\u0440\u0435\u0434\u0441\u0442\u0432\u0435\u043d\u043d\u043e \u0432\u00a0\u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u0435 Apache Superset: <a href=\"https:\/\/youtu.be\/zf3-mppsvRE\" rel=\"noopener noreferrer nofollow\">\u0432\u0438\u0434\u0435\u043e \u043d\u0430\u00a0YouTube<\/a>.<\/p>\n<h2>\u041e\u0433\u0440\u0430\u043d\u0438\u0447\u0435\u043d\u0438\u044f, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0438\u043b\u0438 \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u0443<\/h2>\n<p>\u041f\u0440\u0435\u0436\u0434\u0435 \u0447\u0435\u043c \u043f\u0438\u0441\u0430\u0442\u044c \u043a\u043e\u0434, \u044f \u0432\u044b\u043f\u0438\u0441\u0430\u043b \u0443\u0441\u043b\u043e\u0432\u0438\u044f \u0437\u0430\u0434\u0430\u0447\u0438. \u041e\u043d\u0438 \u043e\u043a\u0430\u0437\u0430\u043b\u0438\u0441\u044c \u0436\u0451\u0441\u0442\u0447\u0435, \u0447\u0435\u043c \u0432\u044b\u0433\u043b\u044f\u0434\u0435\u043b\u0438.<\/p>\n<p><strong>Superset \u043d\u0435\u043b\u044c\u0437\u044f \u0444\u043e\u0440\u043a\u0430\u0442\u044c.<\/strong> \u041e\u043d \u0434\u043e\u043b\u0436\u0435\u043d \u043e\u0431\u043d\u043e\u0432\u043b\u044f\u0442\u044c\u0441\u044f \u0448\u0442\u0430\u0442\u043d\u043e, \u0447\u0435\u0440\u0435\u0437 <code>pip install apache-superset==X.Y.Z<\/code>. \u0417\u043d\u0430\u0447\u0438\u0442 \u043f\u0440\u0430\u0432\u043a\u0438 \u0432\u00a0\u0435\u0433\u043e \u0444\u0430\u0439\u043b\u0430\u0445 \u0438\u0441\u043a\u043b\u044e\u0447\u0435\u043d\u044b.<\/p>\n<p><strong>\u0424\u0440\u043e\u043d\u0442\u0435\u043d\u0434 \u043d\u0435\u043b\u044c\u0437\u044f \u043f\u0435\u0440\u0435\u0441\u043e\u0431\u0438\u0440\u0430\u0442\u044c.<\/strong> \u0422\u0435\u0445\u043d\u0438\u0447\u0435\u0441\u043a\u0438 \u043c\u043e\u0436\u043d\u043e: \u0443\u00a0\u043c\u0435\u043d\u044f \u0432\u00a0\u043f\u0440\u043e\u0435\u043a\u0442\u0435 \u0443\u0436\u0435 \u0435\u0441\u0442\u044c \u043c\u043e\u0434\u0443\u043b\u044c, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043a\u043b\u043e\u043d\u0438\u0440\u0443\u0435\u0442 <code>superset-frontend<\/code>, \u0441\u0442\u0430\u0432\u0438\u0442 Node, \u0441\u043e\u0431\u0438\u0440\u0430\u0435\u0442 \u0431\u0430\u043d\u0434\u043b \u0438 \u043f\u043e\u0434\u043c\u0435\u043d\u044f\u0435\u0442 \u0441\u0442\u0430\u0442\u0438\u043a\u0443. \u042d\u0442\u043e \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442, \u043d\u043e\u00a0\u0437\u0430\u043d\u0438\u043c\u0430\u0435\u0442 20\u201340\u00a0\u043c\u0438\u043d\u0443\u0442 \u043d\u0430\u00a0\u0441\u0435\u0440\u0432\u0435\u0440\u0435 \u0441 8\u00a0\u0413\u0411 RAM \u0438 \u043b\u043e\u043c\u0430\u0435\u0442\u0441\u044f \u043f\u0440\u0438\u00a0\u043a\u0430\u0436\u0434\u043e\u043c \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0438 Superset. \u0414\u043b\u044f\u00a0\u043a\u043d\u043e\u043f\u043a\u0438 \u0432\u00a0\u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u0435\u00a0\u2014 \u043d\u0435\u043f\u0440\u0438\u0435\u043c\u043b\u0435\u043c\u0430\u044f \u0446\u0435\u043d\u0430.<\/p>\n<p><strong>\u0421\u0442\u0430\u0432\u0438\u0442\u044c\u0441\u044f \u0434\u043e\u043b\u0436\u043d\u043e \u043d\u0430\u00a0\u0443\u0436\u0435 \u0440\u0430\u0431\u043e\u0442\u0430\u044e\u0449\u0438\u0439 Superset.<\/strong> \u041e\u0434\u043d\u043e\u0439 \u043a\u043d\u043e\u043f\u043a\u043e\u0439, \u0431\u0435\u0437\u00a0\u043f\u0435\u0440\u0435\u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0438.<\/p>\n<p><strong>\u041f\u0440\u0430\u0432\u0430 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439 \u0434\u043e\u043b\u0436\u043d\u044b \u0441\u043e\u0431\u043b\u044e\u0434\u0430\u0442\u044c\u0441\u044f.<\/strong> \u0415\u0441\u043b\u0438 \u0430\u043d\u0430\u043b\u0438\u0442\u0438\u043a \u043d\u0435\u00a0\u0432\u0438\u0434\u0438\u0442 \u0431\u0430\u0437\u0443 \u0441\u00a0\u0437\u0430\u0440\u043f\u043b\u0430\u0442\u0430\u043c\u0438, \u0430\u0433\u0435\u043d\u0442 \u0442\u043e\u0436\u0435 \u043d\u0435\u00a0\u0434\u043e\u043b\u0436\u0435\u043d \u0435\u0451 \u0432\u0438\u0434\u0435\u0442\u044c. \u042d\u0442\u043e \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u043e \u0432\u0430\u0436\u043d\u043e, \u043f\u043e\u0442\u043e\u043c\u0443 \u0447\u0442\u043e\u00a0\u0430\u0433\u0435\u043d\u0442 \u0443\u043c\u0435\u0435\u0442 \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0442\u044c SQL.<\/p>\n<p>\u0418\u0437\u00a0\u044d\u0442\u0438\u0445 \u0447\u0435\u0442\u044b\u0440\u0451\u0445 \u043f\u0443\u043d\u043a\u0442\u043e\u0432 \u0441\u043b\u0435\u0434\u0443\u0435\u0442 \u0440\u043e\u0432\u043d\u043e \u043e\u0434\u0438\u043d \u0441\u043f\u043e\u0441\u043e\u0431 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u0438: \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u0438\u0435, \u043a\u043e\u0442\u043e\u0440\u043e\u0435 \u0436\u0438\u0432\u0451\u0442 \u0441\u0431\u043e\u043a\u0443 \u0438 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0430\u0435\u0442\u0441\u044f \u0447\u0435\u0440\u0435\u0437 \u043e\u0444\u0438\u0446\u0438\u0430\u043b\u044c\u043d\u0443\u044e \u0442\u043e\u0447\u043a\u0443 \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u0438\u044f Flask\u2011\u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f.<\/p>\n<h2>\u0422\u043e\u0447\u043a\u0430 \u0432\u0445\u043e\u0434\u0430: FLASK_APP_MUTATOR<\/h2>\n<p>Superset\u00a0\u2014 \u044d\u0442\u043e Flask\u2011\u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435, \u0438 \u0432\u00a0\u0435\u0433\u043e \u043a\u043e\u043d\u0444\u0438\u0433\u0435 \u0435\u0441\u0442\u044c \u0445\u0443\u043a <code>FLASK_APP_MUTATOR<\/code>. \u0414\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u044f \u043e\u043f\u0438\u0441\u044b\u0432\u0430\u0435\u0442 \u0435\u0433\u043e \u0441\u043a\u0443\u043f\u043e: \u00ab\u0445\u0443\u043a, \u0434\u0430\u044e\u0449\u0438\u0439 \u0430\u0434\u043c\u0438\u043d\u0438\u0441\u0442\u0440\u0430\u0442\u043e\u0440\u0430\u043c \u0434\u043e\u0441\u0442\u0443\u043f \u043a\u00a0Flask APP \u043f\u043e\u0441\u043b\u0435 \u0438\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438\u00bb. \u0412\u043e\u043f\u0440\u043e\u0441 \u0432\u00a0\u0442\u043e\u043c, \u0447\u0442\u043e\u00a0\u0437\u043d\u0430\u0447\u0438\u0442 \u00ab\u043f\u043e\u0441\u043b\u0435 \u0438\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438\u00bb\u00a0\u2014 \u043a\u0430\u043a\u0438\u0435 \u0447\u0430\u0441\u0442\u0438 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0443\u0436\u0435 \u0441\u043e\u0431\u0440\u0430\u043d\u044b, \u0430\u00a0\u043a\u0430\u043a\u0438\u0435 \u043d\u0435\u0442.<\/p>\n<p>\u041f\u043e\u043b\u0435\u0437 \u0432\u00a0\u0438\u0441\u0445\u043e\u0434\u043d\u0438\u043a\u0438 Superset, \u0432 <code>superset\/initialization\/__init__.py<\/code>:<\/p>\n<pre><code class=\"python\">def init_app_in_ctx(self) -&gt; None:    self.configure_fab()    self.configure_subjects()    self.configure_url_map_converters()    self.configure_data_sources()    self.configure_auth_provider()    self.configure_async_queries()    self.configure_ssh_manager()    self.configure_stats_manager()    self.configure_task_manager()    # Hook that provides administrators a handle on the Flask APP    # after initialization    if flask_app_mutator := self.config[\"FLASK_APP_MUTATOR\"]:        flask_app_mutator(self.superset_app)    self.superset_app.sync_config_to_db()    self.init_views()<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:87px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u042d\u0442\u043e \u0432\u0430\u0436\u043d\u0435\u0435, \u0447\u0435\u043c \u043a\u0430\u0436\u0435\u0442\u0441\u044f. \u0425\u0443\u043a \u0432\u044b\u0437\u044b\u0432\u0430\u0435\u0442\u0441\u044f <strong>\u043f\u043e\u0441\u043b\u0435<\/strong> <code>configure_fab()<\/code>\u00a0\u2014 \u0437\u043d\u0430\u0447\u0438\u0442 <code>app.appbuilder<\/code> \u0443\u0436\u0435 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u0435\u0442, \u0438 \u0432\u00a0\u043c\u0435\u043d\u044e \u043c\u043e\u0436\u043d\u043e \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u043f\u0443\u043d\u043a\u0442. \u0418 <strong>\u0434\u043e<\/strong> <code>init_views()<\/code>\u00a0\u2014 \u0437\u043d\u0430\u0447\u0438\u0442 \u043c\u043e\u0438 \u043c\u0430\u0440\u0448\u0440\u0443\u0442\u044b \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0438\u0440\u0443\u044e\u0442\u0441\u044f \u0440\u0430\u043d\u044c\u0448\u0435 \u0448\u0442\u0430\u0442\u043d\u044b\u0445, \u0447\u0442\u043e\u00a0\u0434\u043b\u044f\u00a0\u043e\u0442\u0434\u0435\u043b\u044c\u043d\u043e\u0433\u043e \u043f\u0440\u0435\u0444\u0438\u043a\u0441\u0430 \u0431\u0435\u0437\u0440\u0430\u0437\u043b\u0438\u0447\u043d\u043e.<\/p>\n<p>\u0418\u0442\u043e\u0433\u043e \u0432 <code>superset_<\/code><a href=\"http:\/\/config.py\" rel=\"noopener noreferrer nofollow\"><code>config.py<\/code><\/a> \u0434\u043e\u043f\u0438\u0441\u044b\u0432\u0430\u0435\u0442\u0441\u044f \u043e\u0434\u0438\u043d \u0431\u043b\u043e\u043a \u043c\u0435\u0436\u0434\u0443 \u043c\u0430\u0440\u043a\u0435\u0440\u0430\u043c\u0438\u00a0\u2014 16\u00a0\u0441\u0442\u0440\u043e\u043a \u043a\u043e\u0434\u0430 (27\u00a0\u0432\u043c\u0435\u0441\u0442\u0435 \u0441\u00a0\u043c\u0430\u0440\u043a\u0435\u0440\u0430\u043c\u0438 \u0438 \u043a\u043e\u043c\u043c\u0435\u043d\u0442\u0430\u0440\u0438\u044f\u043c\u0438). \u041d\u0438\u0436\u0435 \u043e\u043d \u0431\u0435\u0437\u00a0\u043a\u043e\u043c\u043c\u0435\u043d\u0442\u0430\u0440\u0438\u0435\u0432:<\/p>\n<pre><code class=\"python\"># &gt;&gt;&gt; superset-ai-assistant &gt;&gt;&gt;import sys as _sai_sys_SAI_PATH = '\/root\/superset_ai'if _SAI_PATH not in _sai_sys.path:    _sai_sys.path.insert(0, _SAI_PATH)AI_ASSISTANT_SETTINGS_PATH = '\/root\/superset_ai\/ai_settings.json'def FLASK_APP_MUTATOR(app):    try:        from superset_ai import init_ai_assistant        init_ai_assistant(app)    except Exception as _sai_error:        import logging        logging.getLogger(__name__).exception(            \"AI-\u0430\u0441\u0441\u0438\u0441\u0442\u0435\u043d\u0442 \u043d\u0435 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0451\u043d: %s\", _sai_error        )# &lt;&lt;&lt; superset-ai-assistant &lt;&lt;&lt;<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u041e\u0431\u0440\u0430\u0442\u0438\u0442\u0435 \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435 \u043d\u0430 <code>try\/except<\/code> \u0432\u043e\u043a\u0440\u0443\u0433 \u0438\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438. \u042d\u0442\u043e \u043d\u0435\u00a0\u043f\u0430\u0440\u0430\u043d\u043e\u0439\u044f: \u0435\u0441\u043b\u0438 \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u0438\u0435 \u0443\u043f\u0430\u0434\u0451\u0442 \u043d\u0430\u00a0\u0438\u043c\u043f\u043e\u0440\u0442\u0435, \u0431\u0435\u0437\u00a0\u043e\u0431\u0451\u0440\u0442\u043a\u0438 \u043d\u0435\u00a0\u043f\u043e\u0434\u043d\u0438\u043c\u0435\u0442\u0441\u044f \u0432\u0435\u0441\u044c Superset. \u0410\u043d\u0430\u043b\u0438\u0442\u0438\u043a\u0438 \u043e\u0441\u0442\u0430\u043d\u0443\u0442\u0441\u044f \u0431\u0435\u0437\u00a0BI \u0438\u0437\u2011\u0437\u0430 \u0441\u043b\u043e\u043c\u0430\u043d\u043d\u043e\u0439 \u043a\u043d\u043e\u043f\u043a\u0438 \u0447\u0430\u0442\u0430\u00a0\u2014 \u043f\u043b\u043e\u0445\u043e\u0439 \u043e\u0431\u043c\u0435\u043d. \u0417\u0434\u0435\u0441\u044c \u0438 \u0434\u0430\u043b\u044c\u0448\u0435 \u044f \u0438\u0441\u0445\u043e\u0434\u0438\u043b \u0438\u0437\u00a0\u043f\u0440\u0438\u043d\u0446\u0438\u043f\u0430: <strong>\u0430\u0441\u0441\u0438\u0441\u0442\u0435\u043d\u0442 \u043d\u0435\u00a0\u0438\u043c\u0435\u0435\u0442 \u043f\u0440\u0430\u0432\u0430 \u043b\u043e\u043c\u0430\u0442\u044c Superset \u043d\u0438 \u043f\u0440\u0438\u00a0\u043a\u0430\u043a\u0438\u0445 \u043e\u0431\u0441\u0442\u043e\u044f\u0442\u0435\u043b\u044c\u0441\u0442\u0432\u0430\u0445<\/strong>.<\/p>\n<p>\u041d\u0438 \u043e\u0434\u043d\u0430 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u044e\u0449\u0430\u044f \u0441\u0442\u0440\u043e\u043a\u0430 \u043a\u043e\u043d\u0444\u0438\u0433\u0430 \u043d\u0435\u00a0\u043c\u0435\u043d\u044f\u0435\u0442\u0441\u044f. <code>FEATURE_FLAGS<\/code>, <code>TALISMAN_CONFIG<\/code>, <code>SECRET_KEY<\/code>, \u0431\u0430\u0437\u0430 \u043c\u0435\u0442\u0430\u0434\u0430\u043d\u043d\u044b\u0445\u00a0\u2014 \u0432\u0441\u0451 \u043e\u0441\u0442\u0430\u0451\u0442\u0441\u044f \u043a\u0430\u043a\u00a0\u0431\u044b\u043b\u043e.<\/p>\n<h2>\u041b\u043e\u0432\u0443\u0448\u043a\u0430 \u043f\u0435\u0440\u0432\u0430\u044f: strict\u2011dynamic<\/h2>\n<p>\u041a\u043d\u043e\u043f\u043a\u0443 \u043d\u0430\u0434\u043e \u043a\u0430\u043a\u2011\u0442\u043e \u043f\u043e\u043a\u0430\u0437\u0430\u0442\u044c. Superset \u043e\u0442\u0434\u0430\u0451\u0442 \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u0443 \u0441\u0435\u0440\u0432\u0435\u0440\u043d\u044b\u0439 HTML\u2011\u043a\u0430\u0440\u043a\u0430\u0441, \u0432\u00a0\u043a\u043e\u0442\u043e\u0440\u044b\u0439 React \u0443\u0436\u0435 \u043c\u043e\u043d\u0442\u0438\u0440\u0443\u0435\u0442 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441. \u0417\u043d\u0430\u0447\u0438\u0442 \u0434\u043e\u0441\u0442\u0430\u0442\u043e\u0447\u043d\u043e \u0434\u043e\u043f\u0438\u0441\u0430\u0442\u044c \u0432\u00a0\u044d\u0442\u043e\u0442 \u043a\u0430\u0440\u043a\u0430\u0441 <code>&lt;script&gt;<\/code>\u00a0\u2014 \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u044d\u0442\u043e \u043c\u043e\u0436\u043d\u043e \u0432\u043e\u00a0Flask\u2011\u0445\u0443\u043a\u0435 <code>after_request<\/code>.<\/p>\n<p>\u041f\u0438\u0448\u0443 \u0438\u043d\u044a\u0435\u043a\u0446\u0438\u044e, \u043e\u0442\u043a\u0440\u044b\u0432\u0430\u044e Superset\u00a0\u2014 \u0441\u043a\u0440\u0438\u043f\u0442 \u0437\u0430\u0431\u043b\u043e\u043a\u0438\u0440\u043e\u0432\u0430\u043d. \u0421\u043c\u043e\u0442\u0440\u044e \u0432\u00a0\u043a\u043e\u043d\u0441\u043e\u043b\u044c: CSP.<\/p>\n<p>\u041f\u043e\u043b\u0438\u0442\u0438\u043a\u0430 \u0432\u00a0\u0433\u0435\u043d\u0435\u0440\u0438\u0440\u0443\u0435\u043c\u043e\u043c \u043a\u043e\u043d\u0444\u0438\u0433\u0435 \u0442\u0430\u043a\u0430\u044f:<\/p>\n<pre><code class=\"python\">TALISMAN_CONFIG = {    \"content_security_policy\": {        \"default-src\": [\"'self'\"],        \"style-src\": [\"'self'\", \"'unsafe-inline'\"],        \"script-src\": [\"'self'\", \"'strict-dynamic'\", \"'unsafe-eval'\"],        ...    },    \"content_security_policy_nonce_in\": [\"script-src\"],}<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u0417\u0434\u0435\u0441\u044c \u043a\u043b\u044e\u0447\u0435\u0432\u043e\u0435\u00a0\u2014 <code>'strict-dynamic'<\/code>. \u042d\u0442\u0430 \u0434\u0438\u0440\u0435\u043a\u0442\u0438\u0432\u0430 \u0433\u043e\u0432\u043e\u0440\u0438\u0442 \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u0443: <strong>\u0438\u0433\u043d\u043e\u0440\u0438\u0440\u0443\u0439 \u0440\u0430\u0437\u0440\u0435\u0448\u0435\u043d\u0438\u044f \u043f\u043e\u00a0\u0438\u0441\u0442\u043e\u0447\u043d\u0438\u043a\u0430\u043c<\/strong>. \u0422\u043e \u0435\u0441\u0442\u044c <code>'self'<\/code> \u0432 <code>script-src<\/code> \u043f\u0440\u0438\u00a0\u043d\u0430\u043b\u0438\u0447\u0438\u0438 <code>strict-dynamic<\/code> \u043f\u0435\u0440\u0435\u0441\u0442\u0430\u0451\u0442 \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u0432\u043e\u043e\u0431\u0449\u0435. \u0412\u044b\u043f\u043e\u043b\u043d\u0438\u0442\u0441\u044f \u0442\u043e\u043b\u044c\u043a\u043e \u0441\u043a\u0440\u0438\u043f\u0442 \u0441\u00a0\u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u044b\u043c \u043e\u0434\u043d\u043e\u0440\u0430\u0437\u043e\u0432\u044b\u043c \u0442\u043e\u043a\u0435\u043d\u043e\u043c\u00a0\u2014 nonce.<\/p>\n<p>\u0421\u043e\u0431\u043b\u0430\u0437\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0439 \u043f\u0443\u0442\u044c: \u0440\u0430\u0437 \u043a\u043e\u043d\u0444\u0438\u0433 \u0433\u0435\u043d\u0435\u0440\u0438\u0440\u0443\u0435\u0442 \u043c\u043e\u0439\u00a0\u0436\u0435 \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u0449\u0438\u043a, \u043c\u043e\u0436\u043d\u043e \u0443\u0431\u0440\u0430\u0442\u044c <code>strict-dynamic<\/code> \u0438 \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c <code>'unsafe-inline'<\/code>. \u0422\u0440\u0438 \u0441\u0442\u0440\u043e\u043a\u0438 \u043f\u0440\u0430\u0432\u043a\u0438. \u041d\u043e\u00a0\u044d\u0442\u043e \u043e\u0441\u043e\u0437\u043d\u0430\u043d\u043d\u043e\u0435 \u043e\u0441\u043b\u0430\u0431\u043b\u0435\u043d\u0438\u0435 \u0437\u0430\u0449\u0438\u0442\u044b \u043e\u0442\u00a0XSS \u0432\u00a0BI\u2011\u0441\u0438\u0441\u0442\u0435\u043c\u0435, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u0445\u043e\u0434\u0438\u0442 \u0432\u00a0\u043f\u0440\u043e\u0434\u043e\u0432\u044b\u0435 \u0431\u0430\u0437\u044b. \u0417\u0430\u00a0\u043a\u043d\u043e\u043f\u043a\u0443 \u0447\u0430\u0442\u0430\u00a0\u2014 \u043d\u0435\u0442.<\/p>\n<p>\u0417\u043d\u0430\u0447\u0438\u0442 \u043d\u0443\u0436\u0435\u043d nonce. \u0415\u0433\u043e \u0432\u044b\u0434\u0430\u0451\u0442 <code>flask-talisman<\/code>, \u0438 \u043d\u0430\u0434\u043e \u043f\u043e\u043d\u044f\u0442\u044c, \u0433\u0434\u0435 \u043e\u043d \u043b\u0435\u0436\u0438\u0442. \u041e\u0442\u043a\u0440\u044b\u0432\u0430\u044e \u0438\u0441\u0445\u043e\u0434\u043d\u0438\u043a <code>flask_talisman\/talisman.py<\/code>:<\/p>\n<pre><code class=\"python\">def init_app(self, app, ...):    ...    app.jinja_env.globals['csp_nonce'] = self._get_nonce    app.before_request(self._force_https)    app.before_request(self._make_nonce)    app.after_request(self._set_response_headers)def _make_nonce(self):    local_options = self._get_local_options()    if (local_options['content_security_policy'] and            self.content_security_policy_nonce_in and            not getattr(flask.request, 'csp_nonce', None)):        flask.request.csp_nonce = get_random_string(NONCE_LENGTH)<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u0412\u043e\u0442 \u0438 \u043e\u0442\u0432\u0435\u0442. Nonce \u0433\u0435\u043d\u0435\u0440\u0438\u0440\u0443\u0435\u0442\u0441\u044f \u0432 <code>before_request<\/code> \u0438 \u0436\u0438\u0432\u0451\u0442 \u0432 <code>flask.request.csp_nonce<\/code>. \u041c\u043e\u0439 <code>after_request<\/code> \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0435\u0442\u0441\u044f \u043f\u043e\u0437\u0436\u0435\u00a0\u2014 \u0437\u043d\u0430\u0447\u0438\u0442 \u0442\u043e\u043a\u0435\u043d \u0443\u0436\u0435 \u0434\u043e\u0441\u0442\u0443\u043f\u0435\u043d:<\/p>\n<pre><code class=\"python\">def _csp_nonce():    \"\"\"    Nonce, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 flask-talisman \u043f\u043e\u043b\u043e\u0436\u0438\u043b \u0432 request \u043d\u0430 \u044d\u0442\u043e\u0442 \u0437\u0430\u043f\u0440\u043e\u0441.    \u0411\u0435\u0437 \u043d\u0435\u0433\u043e \u0431\u0440\u0430\u0443\u0437\u0435\u0440 \u043e\u0442\u043a\u043b\u043e\u043d\u0438\u0442 \u0441\u043a\u0440\u0438\u043f\u0442: \u0432 CSP Superset \u0432\u043a\u043b\u044e\u0447\u0451\u043d strict-dynamic.    \"\"\"    try:        import flask        return getattr(flask.request, \"csp_nonce\", \"\") or \"\"    except Exception:        return \"\"<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u0418 \u0441\u0430\u043c\u0430 \u0438\u043d\u044a\u0435\u043a\u0446\u0438\u044f:<\/p>\n<pre><code class=\"python\">@app.after_requestdef inject_ai_widget(response):    try:        config = ai_settings.load()        if not config.get(\"enabled\") or not config.get(\"inject_widget\"):            return response        if not _is_authenticated() and not config.get(\"allow_anonymous\"):            return response        if not _should_inject(response):            return response        body = response.get_data(as_text=True)        if \"&lt;\/body&gt;\" not in body or _INJECT_MARKER in body:            return response        nonce = _csp_nonce()        nonce_attr = ' nonce=\"{}\"'.format(nonce) if nonce else \"\"        snippet = (            '&lt;link id=\"{marker}-css\" rel=\"stylesheet\" href=\"{prefix}\/static\/widget.css\"&gt;'            '&lt;script id=\"{marker}\" src=\"{prefix}\/static\/widget.js\"'            ' data-ai-base=\"{prefix}\" defer{nonce}&gt;&lt;\/script&gt;'        ).format(marker=_INJECT_MARKER, prefix=URL_PREFIX, nonce=nonce_attr)        body = body.replace(\"&lt;\/body&gt;\", snippet + \"&lt;\/body&gt;\", 1)        response.set_data(body)        response.headers.pop(\"Content-Length\", None)    except Exception as error:        # \u041d\u0438\u043a\u043e\u0433\u0434\u0430 \u043d\u0435 \u043b\u043e\u043c\u0430\u0435\u043c \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0443 Superset \u0438\u0437-\u0437\u0430 \u0432\u0438\u0434\u0436\u0435\u0442\u0430        logger.warning(\"AI-\u0430\u0441\u0441\u0438\u0441\u0442\u0435\u043d\u0442: \u043d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u0432\u0441\u0442\u0440\u043e\u0438\u0442\u044c \u0432\u0438\u0434\u0436\u0435\u0442: %s\", error)    return response<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u0422\u0440\u0438 \u0434\u0435\u0442\u0430\u043b\u0438, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043b\u0435\u0433\u043a\u043e \u0443\u043f\u0443\u0441\u0442\u0438\u0442\u044c:<\/p>\n<p><code><strong>response.headers.pop(\"Content-Length\", None)<\/strong><\/code>\u00a0\u2014 \u0442\u0435\u043b\u043e \u0432\u044b\u0440\u043e\u0441\u043b\u043e, \u0441\u0442\u0430\u0440\u0430\u044f \u0434\u043b\u0438\u043d\u0430 \u0441\u0442\u0430\u043b\u0430 \u043b\u043e\u0436\u044c\u044e. \u0411\u0435\u0437\u00a0\u044d\u0442\u043e\u0433\u043e \u0431\u0440\u0430\u0443\u0437\u0435\u0440 \u043e\u0431\u0440\u0435\u0436\u0435\u0442 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0443.<\/p>\n<p><strong>\u041f\u0440\u043e\u0432\u0435\u0440\u043a\u0430 <\/strong><code><strong>_INJECT_MARKER in body<\/strong><\/code>\u00a0\u2014 \u0437\u0430\u0449\u0438\u0442\u0430 \u043e\u0442\u00a0\u043f\u043e\u0432\u0442\u043e\u0440\u043d\u043e\u0439 \u0432\u0441\u0442\u0430\u0432\u043a\u0438.<\/p>\n<p><strong>\u0424\u0438\u043b\u044c\u0442\u0440 <\/strong><code><strong>_should_inject<\/strong><\/code>\u00a0\u2014 \u0442\u0440\u043e\u0433\u0430\u0435\u043c \u0442\u043e\u043b\u044c\u043a\u043e HTML\u2011\u0441\u0442\u0440\u0430\u043d\u0438\u0446\u044b, \u0438 \u0442\u043e\u043b\u044c\u043a\u043e \u0443\u0441\u043f\u0435\u0448\u043d\u044b\u0435:<\/p>\n<pre><code class=\"python\">def _should_inject(response):    if request.path.startswith(URL_PREFIX):        return False    content_type = (response.headers.get(\"Content-Type\") or \"\").lower()    if \"text\/html\" not in content_type:        return False    if response.status_code != 200 or response.direct_passthrough:        return False    return True<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p><code>direct_passthrough<\/code>\u00a0\u2014 \u043f\u0440\u043e\u00a0\u043e\u0442\u0434\u0430\u0447\u0443 \u0444\u0430\u0439\u043b\u043e\u0432 \u0447\u0435\u0440\u0435\u0437 <code>send_file<\/code>: \u0443\u00a0\u0442\u0430\u043a\u0438\u0445 \u043e\u0442\u0432\u0435\u0442\u043e\u0432 \u0442\u0435\u043b\u043e \u0447\u0438\u0442\u0430\u0442\u044c \u043d\u0435\u043b\u044c\u0437\u044f. \u041f\u0440\u043e\u0432\u0435\u0440\u043a\u0430 \u043f\u043e <code>Content-Type<\/code> \u0437\u0430\u043e\u0434\u043d\u043e \u0441\u043f\u0430\u0441\u0430\u0435\u0442 JSON\u2011\u043e\u0442\u0432\u0435\u0442\u044b API \u043e\u0442\u00a0\u043f\u0440\u0435\u0432\u0440\u0430\u0449\u0435\u043d\u0438\u044f \u0432\u00a0\u043d\u0435\u0432\u0430\u043b\u0438\u0434\u043d\u044b\u0435.<\/p>\n<h2>\u041a\u0430\u043a\u00a0\u044f \u044d\u0442\u043e \u043f\u0440\u043e\u0432\u0435\u0440\u0438\u043b<\/h2>\n<p>\u0423\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043d\u0438\u0435 \u00abnonce \u043f\u043e\u0434\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u043e\u00bb \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u0442\u0441\u044f \u0440\u043e\u0432\u043d\u043e \u043e\u0434\u043d\u0438\u043c \u0441\u043f\u043e\u0441\u043e\u0431\u043e\u043c: \u043f\u043e\u0434\u043d\u044f\u0442\u044c Flask \u0441\u00a0\u0442\u043e\u0439\u00a0\u0436\u0435 \u043f\u043e\u043b\u0438\u0442\u0438\u043a\u043e\u0439, \u0447\u0442\u043e\u00a0\u0443\u00a0Superset, \u0438 \u0441\u0440\u0430\u0432\u043d\u0438\u0442\u044c nonce \u0432\u00a0\u0442\u0435\u0433\u0435 \u0441\u00a0nonce \u0432\u00a0\u0438\u0442\u043e\u0433\u043e\u0432\u043e\u043c \u0437\u0430\u0433\u043e\u043b\u043e\u0432\u043a\u0435.<\/p>\n<pre><code class=\"python\">Talisman(    app, force_https=False,    content_security_policy={        \"default-src\": [\"'self'\"],        \"style-src\": [\"'self'\", \"'unsafe-inline'\"],        \"script-src\": [\"'self'\", \"'strict-dynamic'\", \"'unsafe-eval'\"],    },    content_security_policy_nonce_in=[\"script-src\"],)...csp = re.search(r\"'nonce-([^']+)'\", response.headers[\"Content-Security-Policy\"])tag = re.search(r'id=\"superset-ai-widget-loader\"[^&gt;]*nonce=\"([^\"]+)\"', html)assert csp.group(1) == tag.group(1)<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u0421\u043e\u0432\u043f\u0430\u043b\u043e. CSP \u043d\u0435\u00a0\u0442\u0440\u043e\u043d\u0443\u0442 \u043d\u0438 \u043d\u0430\u00a0\u0441\u0438\u043c\u0432\u043e\u043b, \u0444\u0440\u043e\u043d\u0442\u0435\u043d\u0434 \u043d\u0435\u00a0\u043f\u0435\u0440\u0435\u0441\u043e\u0431\u0440\u0430\u043d.<\/p>\n<p>\u0415\u0441\u0442\u044c \u0442\u043e\u043d\u043a\u043e\u0441\u0442\u044c \u0441\u00a0\u043f\u043e\u0440\u044f\u0434\u043a\u043e\u043c \u0445\u0443\u043a\u043e\u0432. Flask \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0435\u0442 <code>after_request<\/code> <strong>\u0432\u00a0\u043e\u0431\u0440\u0430\u0442\u043d\u043e\u043c \u043f\u043e\u0440\u044f\u0434\u043a\u0435 \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0446\u0438\u0438<\/strong>: \u043c\u043e\u0439 \u0445\u0443\u043a \u0437\u0430\u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0438\u0440\u043e\u0432\u0430\u043d \u043f\u043e\u0437\u0436\u0435, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u0441\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0435\u0442 \u0440\u0430\u043d\u044c\u0448\u0435 talisman&#8217;\u0430. \u041d\u0430\u00a0\u043c\u043e\u043c\u0435\u043d\u0442 \u0438\u043d\u044a\u0435\u043a\u0446\u0438\u0438 \u0437\u0430\u0433\u043e\u043b\u043e\u0432\u043a\u0430 CSP \u0432\u00a0\u043e\u0442\u0432\u0435\u0442\u0435 \u0435\u0449\u0451 \u043d\u0435\u0442\u00a0\u2014 \u043d\u043e\u00a0\u043e\u043d \u043c\u043d\u0435 \u0438 \u043d\u0435\u00a0\u043d\u0443\u0436\u0435\u043d, \u044f \u0431\u0435\u0440\u0443 nonce \u0438\u0437 <code>request<\/code>, \u043a\u0443\u0434\u0430 \u0435\u0433\u043e \u043f\u043e\u043b\u043e\u0436\u0438\u043b\u0438 \u0432 <code>before_request<\/code>. \u0415\u0441\u043b\u0438\u00a0\u0431\u044b \u044f \u043f\u044b\u0442\u0430\u043b\u0441\u044f \u0432\u044b\u0442\u0430\u0449\u0438\u0442\u044c \u0442\u043e\u043a\u0435\u043d \u0438\u0437\u00a0\u0437\u0430\u0433\u043e\u043b\u043e\u0432\u043a\u0430 \u043e\u0442\u0432\u0435\u0442\u0430, \u043d\u0438\u0447\u0435\u0433\u043e\u00a0\u0431\u044b \u043d\u0435\u00a0\u0432\u044b\u0448\u043b\u043e.<\/p>\n<h2>\u041f\u0443\u043d\u043a\u0442 \u043c\u0435\u043d\u044e: \u043f\u043e\u0434\u0432\u043e\u0434\u043d\u044b\u0439 \u043a\u0430\u043c\u0435\u043d\u044c \u0441\u00a0\u043f\u0440\u0430\u0432\u0430\u043c\u0438<\/h2>\n<p>\u041f\u043b\u0430\u0432\u0430\u044e\u0449\u0430\u044f \u043a\u043d\u043e\u043f\u043a\u0430\u00a0\u2014 \u0445\u043e\u0440\u043e\u0448\u043e, \u043d\u043e\u00a0\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0438 \u0438\u0449\u0443\u0442 \u0444\u0443\u043d\u043a\u0446\u0438\u0438 \u0432\u00a0\u043c\u0435\u043d\u044e. <code>FLASK_APP_MUTATOR<\/code> \u0432\u044b\u0437\u044b\u0432\u0430\u0435\u0442\u0441\u044f \u043f\u043e\u0441\u043b\u0435 <code>configure_fab()<\/code>, \u043f\u043e\u044d\u0442\u043e\u043c\u0443:<\/p>\n<pre><code class=\"python\">appbuilder.add_link(    name=\"AI Assistant\",    label=_(\"AI Assistant\"),    href=URL_PREFIX + \"\/\",    icon=\"fa-robot\",    category=\"\",)<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u0421\u0441\u044b\u043b\u043a\u0430 \u043f\u043e\u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u0432\u00a0\u043d\u0430\u0432\u0431\u0430\u0440\u0435 Superset, \u043f\u043e\u0442\u043e\u043c\u0443 \u0447\u0442\u043e\u00a0React\u2011\u043c\u0435\u043d\u044e \u0441\u0442\u0440\u043e\u0438\u0442\u0441\u044f \u0438\u0437\u00a0\u0434\u0430\u043d\u043d\u044b\u0445 FAB.<\/p>\n<p>\u041d\u043e\u00a0\u0435\u0441\u0442\u044c \u043d\u044e\u0430\u043d\u0441. <code>add_link<\/code> \u0441\u043e\u0437\u0434\u0430\u0451\u0442 \u0440\u0430\u0437\u0440\u0435\u0448\u0435\u043d\u0438\u0435 <code>menu_access<\/code> \u043d\u0430\u00a0\u043d\u043e\u0432\u044b\u0439 \u043f\u0443\u043d\u043a\u0442, \u0430\u00a0\u0432\u043e\u0442 <strong>\u0432\u044b\u0434\u0430\u0442\u044c<\/strong> \u044d\u0442\u043e \u0440\u0430\u0437\u0440\u0435\u0448\u0435\u043d\u0438\u0435 \u0440\u043e\u043b\u0438 Admin \u0434\u043e\u043b\u0436\u0435\u043d \u043f\u0440\u043e\u0446\u0435\u0441\u0441 \u0441\u0438\u043d\u0445\u0440\u043e\u043d\u0438\u0437\u0430\u0446\u0438\u0438 \u0440\u043e\u043b\u0435\u0439. \u041e\u043d \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u0442\u0441\u044f \u043a\u043e\u043c\u0430\u043d\u0434\u043e\u0439 <code>superset init<\/code>. \u0415\u0441\u043b\u0438 \u0435\u0451 \u043d\u0435\u00a0\u0432\u044b\u043f\u043e\u043b\u043d\u0438\u0442\u044c, \u043f\u0443\u043d\u043a\u0442 \u043c\u0435\u043d\u044e \u0431\u0443\u0434\u0435\u0442 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u043e\u0432\u0430\u0442\u044c\u00a0\u2014 \u0438 \u043d\u0435\u00a0\u0431\u0443\u0434\u0435\u0442 \u0432\u0438\u0434\u0435\u043d \u043d\u0438\u043a\u043e\u043c\u0443.<\/p>\n<p>\u041f\u043e\u044d\u0442\u043e\u043c\u0443 \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u0449\u0438\u043a \u0434\u0435\u043b\u0430\u0435\u0442 <code>superset init<\/code> \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u043c \u0448\u0430\u0433\u043e\u043c. \u041e\u043d \u0437\u0430\u043d\u0438\u043c\u0430\u0435\u0442 \u043e\u043a\u043e\u043b\u043e \u043f\u043e\u043b\u0443\u043c\u0438\u043d\u0443\u0442\u044b, \u0438 \u044f \u0441\u043e\u0437\u043d\u0430\u0442\u0435\u043b\u044c\u043d\u043e \u043d\u0435\u00a0\u0441\u0447\u0438\u0442\u0430\u044e \u0435\u0433\u043e \u043f\u0440\u043e\u0432\u0430\u043b \u0444\u0430\u0442\u0430\u043b\u044c\u043d\u044b\u043c:<\/p>\n<pre><code class=\"python\">code, _, err = self.run(command, timeout=900)if code != 0:    self.log(        \"\u26a0\ufe0f superset init \u0437\u0430\u0432\u0435\u0440\u0448\u0438\u043b\u0441\u044f \u0441 \u043e\u0448\u0438\u0431\u043a\u043e\u0439 \u2014 \u043f\u043b\u0430\u0432\u0430\u044e\u0449\u0430\u044f \u043a\u043d\u043e\u043f\u043a\u0430 \u0432\u0441\u0451 \u0440\u0430\u0432\u043d\u043e \"        \"\u0431\u0443\u0434\u0435\u0442 \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c, \u043d\u043e \u043f\u0443\u043d\u043a\u0442\u0430 \u043c\u0435\u043d\u044e \u043c\u043e\u0436\u0435\u0442 \u043d\u0435 \u0431\u044b\u0442\u044c: {}\".format(err.strip()[:200])    )    return True<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u041f\u043b\u0430\u0432\u0430\u044e\u0449\u0430\u044f \u043a\u043d\u043e\u043f\u043a\u0430 \u043d\u0435\u00a0\u0437\u0430\u0432\u0438\u0441\u0438\u0442 \u043e\u0442\u00a0\u0440\u0430\u0437\u0440\u0435\u0448\u0435\u043d\u0438\u0439 FAB, \u0442\u0430\u043a \u0447\u0442\u043e\u00a0\u0434\u0435\u0433\u0440\u0430\u0434\u0430\u0446\u0438\u044f \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u0442\u0441\u044f \u0447\u0430\u0441\u0442\u0438\u0447\u043d\u043e\u0439, \u0430\u00a0\u043d\u0435\u00a0\u043f\u043e\u043b\u043d\u043e\u0439.<\/p>\n<h2>\u041f\u043e\u0447\u0435\u043c\u0443 \u00ab\u043f\u043e \u043f\u0440\u0438\u043d\u0446\u0438\u043f\u0443 MCP\u00bb, \u0430\u00a0\u043d\u0435\u00a0MCP<\/h2>\n<p>\u0417\u0434\u0435\u0441\u044c \u043d\u0430\u0434\u043e \u0447\u0435\u0441\u0442\u043d\u043e \u0440\u0430\u0437\u0434\u0435\u043b\u0438\u0442\u044c \u043f\u043e\u043d\u044f\u0442\u0438\u044f, \u043f\u043e\u0442\u043e\u043c\u0443 \u0447\u0442\u043e\u00a0\u043c\u0435\u043d\u044f \u043e\u0431\u00a0\u044d\u0442\u043e\u043c \u0441\u043f\u0440\u0430\u0432\u0435\u0434\u043b\u0438\u0432\u043e \u0441\u043f\u0440\u043e\u0441\u0438\u043b\u0438 \u0443\u0436\u0435 \u043f\u043e\u0441\u043b\u0435 \u0442\u043e\u0433\u043e, \u043a\u0430\u043a\u00a0\u0432\u0441\u0451 \u0437\u0430\u0440\u0430\u0431\u043e\u0442\u0430\u043b\u043e.<\/p>\n<p>\u0412\u00a0Superset 6.x \u0435\u0441\u0442\u044c <strong>\u0432\u0441\u0442\u0440\u043e\u0435\u043d\u043d\u044b\u0439 MCP\u2011\u0441\u0435\u0440\u0432\u0435\u0440<\/strong>: \u043a\u043e\u043c\u0430\u043d\u0434\u0430 <code>superset mcp run<\/code> \u043f\u043e\u0434\u043d\u0438\u043c\u0430\u0435\u0442 \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u0439 \u043f\u0440\u043e\u0446\u0435\u0441\u0441 \u043d\u0430\u00a0\u043f\u043e\u0440\u0442\u0443 5008, \u043a\u00a0\u043a\u043e\u0442\u043e\u0440\u043e\u043c\u0443 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0430\u044e\u0442\u0441\u044f \u0432\u043d\u0435\u0448\u043d\u0438\u0435 \u043a\u043b\u0438\u0435\u043d\u0442\u044b \u0432\u0440\u043e\u0434\u0435 Claude Desktop \u0438\u043b\u0438\u00a0Cursor. \u042d\u0442\u043e \u043d\u0430\u0441\u0442\u043e\u044f\u0449\u0438\u0439 MCP \u043f\u043e\u00a0\u043f\u0440\u043e\u0442\u043e\u043a\u043e\u043b\u0443.<\/p>\n<p>\u041c\u043e\u0439 \u0430\u0433\u0435\u043d\u0442\u00a0\u2014 <strong>\u043d\u0435\u00a0\u043e\u043d<\/strong>. \u041e\u0431\u0449\u0435\u0435 \u0443\u00a0\u043d\u0438\u0445\u00a0\u2014 \u043f\u0440\u0438\u043d\u0446\u0438\u043f \u0440\u0430\u0431\u043e\u0442\u044b: \u043c\u043e\u0434\u0435\u043b\u044c \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u0442 \u0441\u043f\u0438\u0441\u043e\u043a \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u043e\u0432 \u0441\u043e \u0441\u0445\u0435\u043c\u0430\u043c\u0438, \u0441\u0430\u043c\u0430 \u0440\u0435\u0448\u0430\u0435\u0442, \u0447\u0442\u043e\u00a0\u0432\u044b\u0437\u0432\u0430\u0442\u044c, \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u0442 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442 \u0438 \u043f\u0440\u043e\u0434\u043e\u043b\u0436\u0430\u0435\u0442 \u0440\u0430\u0441\u0441\u0443\u0436\u0434\u0435\u043d\u0438\u0435. \u0420\u0430\u0437\u043b\u0438\u0447\u0438\u0435 \u0432\u00a0\u0442\u043e\u043c, \u0447\u0442\u043e\u00a0\u043c\u043e\u0438 \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u044b \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u044e\u0442\u0441\u044f \u043f\u0440\u044f\u043c\u043e \u0432\u043d\u0443\u0442\u0440\u0438 \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u0430 Superset, \u0430\u00a0\u043d\u0435\u00a0\u043f\u043e\u00a0\u0441\u0435\u0442\u0438 \u0447\u0435\u0440\u0435\u0437 MCP\u2011\u043f\u0440\u043e\u0442\u043e\u043a\u043e\u043b.<\/p>\n<p>\u041f\u043e\u0447\u0435\u043c\u0443 \u044f \u0432\u044b\u0431\u0440\u0430\u043b \u0442\u0430\u043a\u043e\u0439 \u043f\u0443\u0442\u044c, \u0445\u043e\u0442\u044f \u043f\u0440\u043e\u043a\u0441\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043a\u00a0\u0432\u0441\u0442\u0440\u043e\u0435\u043d\u043d\u043e\u043c\u0443 MCP\u00a0\u0431\u044b\u043b\u043e\u00a0\u0431\u044b \u043c\u0435\u043d\u044c\u0448\u0435 \u043a\u043e\u0434\u0430:<\/p>\n<div>\n<div class=\"table\">\n<table>\n<tbody>\n<tr>\n<td>\n<p align=\"left\">\n<\/td>\n<td>\n<p align=\"left\">\u041c\u043e\u0439 \u0430\u0433\u0435\u043d\u0442<\/p>\n<\/td>\n<td>\n<p align=\"left\">\u0412\u0441\u0442\u0440\u043e\u0435\u043d\u043d\u044b\u0439 MCP Superset<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p align=\"left\">\u0413\u0434\u0435 \u0436\u0438\u0432\u0451\u0442<\/p>\n<\/td>\n<td>\n<p align=\"left\">\u0432\u043d\u0443\u0442\u0440\u0438 gunicorn<\/p>\n<\/td>\n<td>\n<p align=\"left\">\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u0439 \u043f\u0440\u043e\u0446\u0435\u0441\u0441, \u043f\u043e\u0440\u0442 5008<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p align=\"left\">\u041a\u043b\u0438\u0435\u043d\u0442<\/p>\n<\/td>\n<td>\n<p align=\"left\">\u043a\u043d\u043e\u043f\u043a\u0430 \u0432\u00a0\u0431\u0440\u0430\u0443\u0437\u0435\u0440\u0435<\/p>\n<\/td>\n<td>\n<p align=\"left\">\u0432\u043d\u0435\u0448\u043d\u0438\u0439 \u043a\u043b\u0438\u0435\u043d\u0442<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p align=\"left\">\u041f\u0440\u0430\u0432\u0430<\/p>\n<\/td>\n<td>\n<p align=\"left\">\u0442\u0435\u043a\u0443\u0449\u0435\u0433\u043e \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f Superset<\/p>\n<\/td>\n<td>\n<p align=\"left\">\u043e\u0442 <code>MCP_DEV_USERNAME<\/code>, \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u044f \u0432\u044b\u043a\u043b\u044e\u0447\u0435\u043d\u0430<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<\/div>\n<\/div>\n<p>\u041f\u043e\u0441\u043b\u0435\u0434\u043d\u044f\u044f \u0441\u0442\u0440\u043e\u043a\u0430 \u0438 \u0440\u0435\u0448\u0438\u043b\u0430 \u0434\u0435\u043b\u043e. MCP\u2011\u0441\u0435\u0440\u0432\u0435\u0440 \u0432\u00a0\u0448\u0442\u0430\u0442\u043d\u043e\u0439 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u0438 \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u0432\u00a0dev\u2011\u0440\u0435\u0436\u0438\u043c\u0435: \u0432\u0441\u0435 \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0438 \u043e\u0442\u00a0\u0438\u043c\u0435\u043d\u0438 \u0430\u0434\u043c\u0438\u043d\u0438\u0441\u0442\u0440\u0430\u0442\u043e\u0440\u0430, \u0431\u0435\u0437\u00a0\u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438. \u0415\u0441\u043b\u0438\u00a0\u0431\u044b \u043a\u043d\u043e\u043f\u043a\u0430 \u0432\u00a0\u0431\u0440\u0430\u0443\u0437\u0435\u0440\u0435 \u0445\u043e\u0434\u0438\u043b\u0430 \u0447\u0435\u0440\u0435\u0437 \u043d\u0435\u0433\u043e, \u043b\u044e\u0431\u043e\u0439 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c Superset \u043f\u043e\u043b\u0443\u0447\u0438\u043b\u00a0\u0431\u044b \u0434\u043e\u0441\u0442\u0443\u043f \u043a\u00a0\u0434\u0430\u043d\u043d\u044b\u043c \u0441\u00a0\u043f\u0440\u0430\u0432\u0430\u043c\u0438 \u0430\u0434\u043c\u0438\u043d\u0430. \u042d\u0442\u043e \u043e\u0431\u0445\u043e\u0434 \u043c\u043e\u0434\u0435\u043b\u0438 \u043f\u0440\u0430\u0432, \u0430\u00a0\u043d\u0435\u00a0\u0444\u0438\u0447\u0430.<\/p>\n<p>\u0414\u0432\u0430 \u043c\u0435\u0445\u0430\u043d\u0438\u0437\u043c\u0430 \u043d\u0435\u0437\u0430\u0432\u0438\u0441\u0438\u043c\u044b \u0438 \u043c\u043e\u0433\u0443\u0442 \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u043e\u0434\u043d\u043e\u0432\u0440\u0435\u043c\u0435\u043d\u043d\u043e.<\/p>\n<h2>\u0418\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u044b: \u0441\u0435\u043c\u044c \u0444\u0443\u043d\u043a\u0446\u0438\u0439 \u0432\u043c\u0435\u0441\u0442\u043e \u0434\u043e\u0433\u0430\u0434\u043e\u043a<\/h2>\n<p>\u0420\u0435\u0435\u0441\u0442\u0440 \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u043e\u0432\u00a0\u2014 \u043e\u0431\u044b\u0447\u043d\u044b\u0439 \u0441\u043b\u043e\u0432\u0430\u0440\u044c, \u0438\u0437\u00a0\u043a\u043e\u0442\u043e\u0440\u043e\u0433\u043e \u0433\u0435\u043d\u0435\u0440\u0438\u0440\u0443\u044e\u0442\u0441\u044f \u0441\u0445\u0435\u043c\u044b \u0432\u00a0\u0444\u043e\u0440\u043c\u0430\u0442\u0435 OpenAI function calling:<\/p>\n<pre><code class=\"python\">REGISTRY = {    \"list_databases\": {...},    \"list_datasets\": {...},    \"describe_dataset\": {...},    \"list_charts\": {...},    \"list_dashboards\": {...},    \"run_sql\": {...},    \"get_page_context\": {...},}<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u041e\u043f\u0438\u0441\u0430\u043d\u0438\u044f \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u043e\u0432\u00a0\u2014 \u044d\u0442\u043e \u0442\u043e\u0436\u0435 \u0447\u0430\u0441\u0442\u044c \u043f\u0440\u043e\u043c\u043f\u0442\u0430, \u0438 \u043e\u043d\u0438 \u043d\u0430\u043f\u0438\u0441\u0430\u043d\u044b \u043a\u0430\u043a\u00a0\u0438\u043d\u0441\u0442\u0440\u0443\u043a\u0446\u0438\u0438 \u043c\u043e\u0434\u0435\u043b\u0438:<\/p>\n<pre><code class=\"python\">\"describe_dataset\": {    \"func\": describe_dataset,    \"description\": \"\u041a\u043e\u043b\u043e\u043d\u043a\u0438, \u0442\u0438\u043f\u044b \u0434\u0430\u043d\u043d\u044b\u0445 \u0438 \u043c\u0435\u0442\u0440\u0438\u043a\u0438 \u0434\u0430\u0442\u0430\u0441\u0435\u0442\u0430. \"    \"\u0412\u044b\u0437\u044b\u0432\u0430\u0439 \u043f\u0435\u0440\u0435\u0434 \u043d\u0430\u043f\u0438\u0441\u0430\u043d\u0438\u0435\u043c SQL, \u0447\u0442\u043e\u0431\u044b \u043d\u0435 \u0432\u044b\u0434\u0443\u043c\u044b\u0432\u0430\u0442\u044c \u0438\u043c\u0435\u043d\u0430 \u043a\u043e\u043b\u043e\u043d\u043e\u043a.\",    ...}<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u041e\u0442\u0434\u0435\u043b\u044c\u043d\u043e \u0441\u0442\u043e\u0438\u0442 <code>get_page_context<\/code>\u00a0\u2014 \u043e\u043d \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u0442 \u043e\u0442\u00a0\u0432\u0438\u0434\u0436\u0435\u0442\u0430 URL \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u044b \u0438 \u0440\u0430\u0441\u043f\u043e\u0437\u043d\u0430\u0451\u0442, \u0447\u0442\u043e\u00a0\u043e\u0442\u043a\u0440\u044b\u0442\u043e \u0443\u00a0\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f, \u0430\u00a0\u0437\u0430\u0442\u0435\u043c \u043e\u0431\u043e\u0433\u0430\u0449\u0430\u0435\u0442 \u044d\u0442\u043e \u0434\u0430\u043d\u043d\u044b\u043c\u0438 \u0438\u0437\u00a0\u0411\u0414:<\/p>\n<pre><code class=\"javascript\">var dashboardMatch = path.match(\/\\\/(?:superset\\\/)?dashboard\\\/([^\/]+)\/);if (dashboardMatch) {  context.page = 'dashboard';  context.dashboard_id = dashboardMatch[1];} else if (path.indexOf('\/explore') === 0) {  context.page = 'chart';  if (params.get('slice_id')) context.chart_id = params.get('slice_id');}<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u0411\u043b\u0430\u0433\u043e\u0434\u0430\u0440\u044f \u044d\u0442\u043e\u043c\u0443 \u0432\u043e\u043f\u0440\u043e\u0441 \u00ab\u043e\u0431\u044a\u044f\u0441\u043d\u0438, \u0447\u0442\u043e\u00a0\u0442\u0443\u0442 \u043f\u043e\u043a\u0430\u0437\u0430\u043d\u043e\u00bb \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u0431\u0435\u0437\u00a0\u0443\u0442\u043e\u0447\u043d\u0435\u043d\u0438\u0439: \u0430\u0433\u0435\u043d\u0442 \u043f\u043e\u0434\u0442\u044f\u0433\u0438\u0432\u0430\u0435\u0442 \u0441\u043e\u0441\u0442\u0430\u0432 \u0434\u0430\u0448\u0431\u043e\u0440\u0434\u0430 \u0438 \u0442\u0438\u043f\u044b \u0432\u0438\u0437\u0443\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0439.<\/p>\n<h2>\u0421\u043e\u0432\u043c\u0435\u0441\u0442\u0438\u043c\u043e\u0441\u0442\u044c \u0441\u00a0\u0432\u0435\u0440\u0441\u0438\u044f\u043c\u0438 Superset<\/h2>\n<p>\u0412\u043d\u0443\u0442\u0440\u0435\u043d\u043d\u043e\u0441\u0442\u0438 Superset \u043c\u0435\u0436\u0434\u0443 \u0432\u0435\u0440\u0441\u0438\u044f\u043c\u0438 \u043f\u0435\u0440\u0435\u0435\u0437\u0436\u0430\u044e\u0442. \u0427\u0442\u043e\u0431\u044b \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u0438\u0435 \u043d\u0435\u00a0\u043f\u0440\u0438\u0432\u044f\u0437\u044b\u0432\u0430\u043b\u043e\u0441\u044c \u043a\u00a0\u043e\u0434\u043d\u043e\u0439 \u0432\u0435\u0440\u0441\u0438\u0438, \u043e\u0431\u0440\u0430\u0449\u0435\u043d\u0438\u044f \u043d\u0430\u043f\u0438\u0441\u0430\u043d\u044b \u0441\u00a0\u043f\u0435\u0440\u0435\u0431\u043e\u0440\u043e\u043c \u0432\u0430\u0440\u0438\u0430\u043d\u0442\u043e\u0432:<\/p>\n<pre><code class=\"python\">def _model_dataset():    \"\"\"SqlaTable \u043f\u043e-\u0440\u0430\u0437\u043d\u043e\u043c\u0443 \u0440\u0430\u0441\u043f\u043e\u043b\u043e\u0436\u0435\u043d \u0432 \u0440\u0430\u0437\u043d\u044b\u0445 \u0432\u0435\u0440\u0441\u0438\u044f\u0445 Superset.\"\"\"    for module_path in (        \"superset.connectors.sqla.models\",        \"superset.connectors.sqlatable.models\",    ):        try:            module = __import__(module_path, fromlist=[\"SqlaTable\"])            return getattr(module, \"SqlaTable\")        except (ImportError, AttributeError):            continue    raise ToolError(\"\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043d\u0430\u0439\u0442\u0438 \u043c\u043e\u0434\u0435\u043b\u044c \u0434\u0430\u0442\u0430\u0441\u0435\u0442\u0430 \u0432 \u044d\u0442\u043e\u0439 \u0432\u0435\u0440\u0441\u0438\u0438 Superset\")<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u0422\u0430\u00a0\u0436\u0435 \u0438\u0441\u0442\u043e\u0440\u0438\u044f \u0441\u00a0\u0434\u0432\u0438\u0436\u043a\u043e\u043c SQLAlchemy: \u0432\u00a0Superset 3.x \u043c\u0435\u0442\u043e\u0434 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u043b \u0441\u0430\u043c Engine, \u0432 4.x \u0438 \u043d\u043e\u0432\u0435\u0435\u00a0\u2014 \u043a\u043e\u043d\u0442\u0435\u043a\u0441\u0442\u043d\u044b\u0439 \u043c\u0435\u043d\u0435\u0434\u0436\u0435\u0440:<\/p>\n<pre><code class=\"python\">engine_ctx = database.get_sqla_engine()if hasattr(engine_ctx, \"__enter__\"):    with engine_ctx as engine:        columns, rows = _execute_select(engine, sa_text(limited_sql), limit)else:    columns, rows = _execute_select(engine_ctx, sa_text(limited_sql), limit)<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u041f\u0440\u043e\u0432\u0435\u0440\u044f\u0442\u044c \u0432\u0435\u0440\u0441\u0438\u044e \u0447\u0435\u0440\u0435\u0437 \u0441\u0440\u0430\u0432\u043d\u0435\u043d\u0438\u0435 \u0441\u0442\u0440\u043e\u043a \u044f \u043d\u0435\u00a0\u0441\u0442\u0430\u043b: \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0430 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u0435\u0439 \u043d\u0430\u0434\u0451\u0436\u043d\u0435\u0435 \u0438 \u043d\u0435\u00a0\u043b\u043e\u043c\u0430\u0435\u0442\u0441\u044f \u043d\u0430\u00a0\u0444\u043e\u0440\u043a\u0430\u0445 \u0438 dev\u2011\u0441\u0431\u043e\u0440\u043a\u0430\u0445. \u041e\u0433\u043e\u0432\u043e\u0440\u044e\u0441\u044c: \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043b \u044f \u043d\u0430 6.1, \u043a\u00a0\u043e\u0441\u0442\u0430\u043b\u044c\u043d\u044b\u043c \u0432\u0435\u0440\u0441\u0438\u044f\u043c \u043a\u043e\u0434 \u043f\u043e\u0434\u0433\u043e\u0442\u043e\u0432\u043b\u0435\u043d, \u043d\u043e\u00a0\u043d\u0435\u00a0\u043f\u0440\u043e\u0432\u0435\u0440\u0435\u043d.<\/p>\n<h2>run_sql: \u0441\u0430\u043c\u044b\u0439 \u043e\u043f\u0430\u0441\u043d\u044b\u0439 \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442<\/h2>\n<p>\u0414\u0430\u0442\u044c \u043c\u043e\u0434\u0435\u043b\u0438 \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0442\u044c SQL\u00a0\u2014 \u044d\u0442\u043e \u0434\u0430\u0442\u044c \u0435\u0439 \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0442\u044c SQL. \u0425\u043e\u0440\u043e\u0448\u0430\u044f \u043d\u043e\u0432\u043e\u0441\u0442\u044c: \u0443\u00a0\u043d\u0430\u0441 \u0435\u0441\u0442\u044c \u043c\u043e\u0434\u0435\u043b\u044c \u043f\u0440\u0430\u0432 \u0441\u0430\u043c\u043e\u0433\u043e Superset, \u043d\u0430\u0434\u043e \u0442\u043e\u043b\u044c\u043a\u043e \u0435\u0439 \u0432\u043e\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c\u0441\u044f.<\/p>\n<p><strong>\u041f\u0435\u0440\u0432\u043e\u0435\u00a0\u2014 \u0442\u043e\u043b\u044c\u043a\u043e \u0447\u0442\u0435\u043d\u0438\u0435.<\/strong> \u041f\u0440\u043e\u0432\u0435\u0440\u043a\u0430 \u0434\u0432\u0443\u0445\u0443\u0440\u043e\u0432\u043d\u0435\u0432\u0430\u044f. \u0421\u043d\u0430\u0447\u0430\u043b\u0430 \u0433\u0440\u0443\u0431\u044b\u0439 \u0444\u0438\u043b\u044c\u0442\u0440 \u043f\u043e\u00a0\u043a\u043b\u044e\u0447\u0435\u0432\u044b\u043c \u0441\u043b\u043e\u0432\u0430\u043c \u0438 \u0437\u0430\u043f\u0440\u0435\u0442 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u0438\u0445 \u0438\u043d\u0441\u0442\u0440\u0443\u043a\u0446\u0438\u0439:<\/p>\n<pre><code class=\"python\">_FORBIDDEN_SQL = re.compile(    r\"\\b(\"    r\"insert|update|delete|drop|alter|create|truncate|grant|revoke|\"    r\"vacuum|attach|detach|call|merge|replace|copy|load|\"    r\"pg_read_file|pg_sleep|lo_import|lo_export|dblink\"    r\")\\b\",    re.IGNORECASE,)def _assert_select_only(sql):    stripped = sql.strip().rstrip(\";\").strip()    if \";\" in stripped:        raise ToolError(\"\u0420\u0430\u0437\u0440\u0435\u0448\u0451\u043d \u0442\u043e\u043b\u044c\u043a\u043e \u043e\u0434\u0438\u043d SQL-\u0437\u0430\u043f\u0440\u043e\u0441 \u0431\u0435\u0437 ';'\")    if _FORBIDDEN_SQL.search(stripped):        raise ToolError(\"\u0420\u0430\u0437\u0440\u0435\u0448\u0435\u043d\u044b \u0442\u043e\u043b\u044c\u043a\u043e \u0437\u0430\u043f\u0440\u043e\u0441\u044b \u043d\u0430 \u0447\u0442\u0435\u043d\u0438\u0435 (SELECT)\")    ...<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u0417\u0430\u0442\u0435\u043c\u00a0\u2014 \u0441\u0442\u0440\u043e\u0433\u0430\u044f \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0430 \u043f\u0430\u0440\u0441\u0435\u0440\u043e\u043c \u0441\u0430\u043c\u043e\u0433\u043e Superset, \u0442\u043e\u0436\u0435 \u0441\u00a0\u043f\u0435\u0440\u0435\u0431\u043e\u0440\u043e\u043c \u043f\u0443\u0442\u0435\u0439 \u0438\u043c\u043f\u043e\u0440\u0442\u0430:<\/p>\n<pre><code class=\"python\">for module_path, class_name in (    (\"superset.sql.parse\", \"SQLScript\"),      # \u043d\u043e\u0432\u044b\u0435 \u0432\u0435\u0440\u0441\u0438\u0438    (\"superset.sql_parse\", \"ParsedQuery\"),    # \u0441\u0442\u0430\u0440\u044b\u0435):    ...<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u0415\u0441\u043b\u0438 \u043d\u0438 \u043e\u0434\u0438\u043d \u043f\u0430\u0440\u0441\u0435\u0440 \u043d\u0435\u00a0\u0434\u043e\u0441\u0442\u0443\u043f\u0435\u043d, \u043e\u0441\u0442\u0430\u0451\u0442\u0441\u044f \u0440\u0435\u0433\u0443\u043b\u044f\u0440\u043a\u0430 <code>^\\s*(select|with)\\b<\/code>. \u0422\u0440\u0438 \u0443\u0440\u043e\u0432\u043d\u044f \u0437\u0430\u0449\u0438\u0442\u044b \u0432\u043c\u0435\u0441\u0442\u043e \u043e\u0434\u043d\u043e\u0433\u043e, \u043f\u043e\u0442\u043e\u043c\u0443 \u0447\u0442\u043e\u00a0\u0441\u0442\u043e\u0438\u043c\u043e\u0441\u0442\u044c \u043e\u0448\u0438\u0431\u043a\u0438 \u0437\u0434\u0435\u0441\u044c \u0432\u044b\u0441\u043e\u043a\u0430\u044f.<\/p>\n<p><strong>\u0412\u0442\u043e\u0440\u043e\u0435\u00a0\u2014 \u043f\u0440\u0430\u0432\u0430 \u0442\u0435\u043a\u0443\u0449\u0435\u0433\u043e \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f.<\/strong> \u042d\u0442\u043e \u043f\u0440\u0438\u043d\u0446\u0438\u043f\u0438\u0430\u043b\u044c\u043d\u044b\u0439 \u043c\u043e\u043c\u0435\u043d\u0442: \u0430\u0433\u0435\u043d\u0442 \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u043d\u0435\u00a0\u043e\u0442\u00a0\u0438\u043c\u0435\u043d\u0438 \u0430\u0434\u043c\u0438\u043d\u0438\u0441\u0442\u0440\u0430\u0442\u043e\u0440\u0430, \u0430\u00a0\u043e\u0442\u00a0\u0438\u043c\u0435\u043d\u0438 \u0442\u043e\u0433\u043e, \u043a\u0442\u043e \u043d\u0430\u0436\u0430\u043b \u043a\u043d\u043e\u043f\u043a\u0443:<\/p>\n<pre><code class=\"python\">def _can_access_database(database):    sm = _security_manager()    for method_name in (\"can_access_database\", \"can_access_datasource\"):        method = getattr(sm, method_name, None)        if method is None:            continue        try:            if method(database):                return True        except Exception:            continue    return _is_admin()<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p><strong>\u0422\u0440\u0435\u0442\u044c\u0435\u00a0\u2014\u00a0\u043b\u0438\u043c\u0438\u0442 \u0441\u0442\u0440\u043e\u043a.<\/strong> \u041f\u0440\u0438\u0447\u0451\u043c \u043d\u0430\u043a\u043b\u0430\u0434\u044b\u0432\u0430\u0435\u0442 \u0435\u0433\u043e \u0441\u0430\u043c Superset, \u043f\u043e\u0442\u043e\u043c\u0443 \u0447\u0442\u043e\u00a0\u043e\u043d \u0437\u043d\u0430\u0435\u0442 \u0434\u0438\u0430\u043b\u0435\u043a\u0442 \u0446\u0435\u043b\u0435\u0432\u043e\u0439 \u0431\u0430\u0437\u044b:<\/p>\n<pre><code class=\"python\">apply_limit = getattr(database, \"apply_limit_to_sql\", None)if apply_limit is not None:    try:        limited_sql = apply_limit(safe_sql, effective_limit)    except Exception:        limited_sql = safe_sql<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p><strong>\u0427\u0435\u0442\u0432\u0451\u0440\u0442\u043e\u0435\u00a0\u2014 \u0432\u044b\u043a\u043b\u044e\u0447\u0430\u0442\u0435\u043b\u0438.<\/strong> <code>allow_sql: false<\/code> \u043f\u043e\u043b\u043d\u043e\u0441\u0442\u044c\u044e \u0443\u0431\u0438\u0440\u0430\u0435\u0442 \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442 \u0438\u0437\u00a0\u0441\u0445\u0435\u043c, \u043e\u0442\u0434\u0430\u0432\u0430\u0435\u043c\u044b\u0445 \u043c\u043e\u0434\u0435\u043b\u0438. \u0410 <code>send_sql_rows_to_model: false<\/code> \u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u0435, \u043d\u043e\u00a0\u043d\u0435\u00a0\u043f\u0435\u0440\u0435\u0434\u0430\u0451\u0442 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f \u0441\u0442\u0440\u043e\u043a\u00a0\u2014 \u043c\u043e\u0434\u0435\u043b\u044c \u0432\u0438\u0434\u0438\u0442 \u0442\u043e\u043b\u044c\u043a\u043e \u0438\u043c\u0435\u043d\u0430 \u043a\u043e\u043b\u043e\u043d\u043e\u043a \u0438 \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u0437\u0430\u043f\u0438\u0441\u0435\u0439. \u0414\u043b\u044f\u00a0\u0441\u043b\u0443\u0447\u0430\u0435\u0432, \u043a\u043e\u0433\u0434\u0430 \u0434\u0430\u043d\u043d\u044b\u0435 \u043d\u0435\u00a0\u0434\u043e\u043b\u0436\u043d\u044b \u043f\u043e\u043a\u0438\u0434\u0430\u0442\u044c \u0441\u0435\u0440\u0432\u0435\u0440.<\/p>\n<p>\u0413\u0430\u0440\u0434 \u044f \u043f\u0440\u043e\u0432\u0435\u0440\u0438\u043b \u043d\u0430\u00a0\u043d\u0430\u0431\u043e\u0440\u0435 \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043e\u0431\u044f\u0437\u0430\u043d\u044b\u00a0\u0431\u044b\u0442\u044c \u043e\u0442\u043a\u043b\u043e\u043d\u0435\u043d\u044b:<\/p>\n<pre><code class=\"python\">bad = [\"delete from t\", \"select 1; drop table t\", \"update t set a=1\",       \"drop table t\", \"insert into t values (1)\", \"select * from t; --\",       \"truncate t\", \"call proc()\"]for q in bad:    try:        tools._assert_select_only(q)        raise AssertionError(\"\u041d\u0415 \u0417\u0410\u0411\u041b\u041e\u041a\u0418\u0420\u041e\u0412\u0410\u041d: \" + q)    except tools.ToolError:        pass<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u0412\u0441\u0435 \u0432\u043e\u0441\u0435\u043c\u044c \u0437\u0430\u0431\u043b\u043e\u043a\u0438\u0440\u043e\u0432\u0430\u043d\u044b, \u0442\u0440\u0438 \u043b\u0435\u0433\u0438\u0442\u0438\u043c\u043d\u044b\u0445 (<code>select 1<\/code>, <code>SELECT a FROM t WHERE x=1<\/code>, <code>with q as (select 1) select * from q<\/code>) \u043f\u0440\u043e\u0448\u043b\u0438.<\/p>\n<h2>\u0426\u0438\u043a\u043b \u0430\u0433\u0435\u043d\u0442\u0430 \u0438 \u043c\u043e\u0434\u0435\u043b\u0438<\/h2>\n<p>\u0421\u0430\u043c \u0446\u0438\u043a\u043b\u00a0\u2014 \u043f\u0440\u044f\u043c\u043e\u043b\u0438\u043d\u0435\u0439\u043d\u044b\u0439: \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u043c \u0438\u0441\u0442\u043e\u0440\u0438\u044e \u0441\u043e \u0441\u0445\u0435\u043c\u0430\u043c\u0438 \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u043e\u0432, \u0435\u0441\u043b\u0438 \u043c\u043e\u0434\u0435\u043b\u044c \u043f\u043e\u043f\u0440\u043e\u0441\u0438\u043b\u0430 \u0432\u044b\u0437\u043e\u0432\u00a0\u2014 \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0435\u043c, \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442 \u043a\u043b\u0430\u0434\u0451\u043c \u0432\u00a0\u0438\u0441\u0442\u043e\u0440\u0438\u044e \u0440\u043e\u043b\u044c\u044e <code>tool<\/code>, \u043f\u043e\u0432\u0442\u043e\u0440\u044f\u0435\u043c. \u041c\u0430\u043a\u0441\u0438\u043c\u0443\u043c N \u0438\u0442\u0435\u0440\u0430\u0446\u0438\u0439.<\/p>\n<p>\u0418\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u0435\u0435 \u0442\u043e, \u0447\u0442\u043e\u00a0\u043f\u0440\u043e\u0438\u0441\u0445\u043e\u0434\u0438\u0442 \u0441\u00a0\u043c\u043e\u0434\u0435\u043b\u044f\u043c\u0438 <strong>\u0431\u0435\u0437<\/strong> function calling. \u0418\u0445 \u043c\u043d\u043e\u0433\u043e, \u043e\u0441\u043e\u0431\u0435\u043d\u043d\u043e \u0441\u0440\u0435\u0434\u0438 \u043b\u043e\u043a\u0430\u043b\u044c\u043d\u044b\u0445. \u041d\u0430\u0438\u0432\u043d\u0430\u044f \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f \u043f\u0440\u043e\u0441\u0442\u043e \u0443\u043f\u0430\u0434\u0451\u0442 \u0441\u00a0HTTP 400.<\/p>\n<p>\u042f \u0441\u0434\u0435\u043b\u0430\u043b \u043e\u0442\u043a\u0430\u0442: \u0435\u0441\u043b\u0438 \u043f\u0435\u0440\u0432\u044b\u0439 \u0437\u0430\u043f\u0440\u043e\u0441 \u0441\u00a0\u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u0430\u043c\u0438 \u043f\u0440\u043e\u0432\u0430\u043b\u0438\u043b\u0441\u044f \u0438 \u043e\u0448\u0438\u0431\u043a\u0430 \u043f\u043e\u0445\u043e\u0436\u0430 \u043d\u0430 \u00ab\u043d\u0435 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442\u0441\u044f\u00bb, \u0430\u0433\u0435\u043d\u0442 \u043f\u043e\u0432\u0442\u043e\u0440\u044f\u0435\u0442 \u0437\u0430\u043f\u0440\u043e\u0441 \u0431\u0435\u0437\u00a0\u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u043e\u0432, \u043d\u043e\u00a0\u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u0442 \u0432\u00a0\u0441\u0438\u0441\u0442\u0435\u043c\u043d\u044b\u0439 \u043f\u0440\u043e\u043c\u043f\u0442 \u0441\u0432\u043e\u0434\u043a\u0443 \u043c\u0435\u0442\u0430\u0434\u0430\u043d\u043d\u044b\u0445:<\/p>\n<pre><code class=\"python\">try:    data = _post(settings, _chat_payload(settings, messages, tool_schemas if use_tools else None))except LLMError as error:    if use_tools and iteration == 0 and _looks_like_tools_unsupported(str(error)):        logger.info(\"\u041c\u043e\u0434\u0435\u043b\u044c \u043d\u0435 \u043f\u0440\u0438\u043d\u044f\u043b\u0430 tools, \u043f\u0435\u0440\u0435\u043a\u043b\u044e\u0447\u0430\u044e\u0441\u044c \u043d\u0430 \u0440\u0435\u0436\u0438\u043c \u0431\u0435\u0437 \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u043e\u0432\")        use_tools = False        messages[0][\"content\"] += _tool_summary_for_fallback(settings, page_context)        data = _post(settings, _chat_payload(settings, messages, None))    else:        raise<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u0421\u0432\u043e\u0434\u043a\u0430 \u0441\u043e\u0431\u0438\u0440\u0430\u0435\u0442\u0441\u044f \u0442\u0435\u043c\u0438\u00a0\u0436\u0435 \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u0430\u043c\u0438\u00a0\u2014 \u043f\u0440\u043e\u0441\u0442\u043e \u0432\u044b\u0437\u044b\u0432\u0430\u0435\u0442\u0441\u044f \u0437\u0430\u0440\u0430\u043d\u0435\u0435:<\/p>\n<pre><code class=\"python\">databases, _ = tools.call(\"list_databases\", {}, settings, page_context)if databases and databases.get(\"databases\"):    listed = \", \".join(        \"{} (id={}, {})\".format(item[\"name\"], item[\"id\"], item[\"backend\"])        for item in databases[\"databases\"][:20]    )    parts.append(\"\u0411\u0430\u0437\u044b \u0434\u0430\u043d\u043d\u044b\u0445 Superset: \" + listed)<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u041e\u0442\u0432\u0435\u0442 \u0431\u0443\u0434\u0435\u0442 \u0445\u0443\u0436\u0435, \u0447\u0435\u043c \u0441\u00a0\u043f\u043e\u043b\u043d\u043e\u0446\u0435\u043d\u043d\u044b\u043c tool calling, \u043d\u043e\u00a0\u043e\u043d \u0432\u0441\u0451 \u0440\u0430\u0432\u043d\u043e \u0431\u0443\u0434\u0435\u0442 \u043e\u043f\u0438\u0440\u0430\u0442\u044c\u0441\u044f \u043d\u0430\u00a0\u0440\u0435\u0430\u043b\u044c\u043d\u044b\u0435 \u0438\u043c\u0435\u043d\u0430 \u0434\u0430\u0442\u0430\u0441\u0435\u0442\u043e\u0432. \u0412\u00a0\u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u0435 \u043f\u0440\u0438\u00a0\u044d\u0442\u043e\u043c \u0447\u0435\u0441\u0442\u043d\u043e \u043f\u043e\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u0442\u0441\u044f \u043f\u043b\u0430\u0448\u043a\u0430: \u043c\u043e\u0434\u0435\u043b\u044c \u043d\u0435\u00a0\u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442 \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u044b, \u043e\u0442\u0432\u0435\u0442 \u043f\u043e\u0441\u0442\u0440\u043e\u0435\u043d \u043f\u043e\u00a0\u0441\u0432\u043e\u0434\u043a\u0435.<\/p>\n<p>\u041e\u0448\u0438\u0431\u043a\u0438 \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u043e\u0432 \u0442\u043e\u0436\u0435 \u043d\u0435\u00a0\u043f\u0440\u0435\u0440\u044b\u0432\u0430\u044e\u0442 \u0440\u0430\u0431\u043e\u0442\u0443\u00a0\u2014 \u043e\u043d\u0438 \u0443\u0445\u043e\u0434\u044f\u0442 \u043c\u043e\u0434\u0435\u043b\u0438 \u043a\u0430\u043a\u00a0\u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442, \u0438 \u0442\u0430 \u043c\u043e\u0436\u0435\u0442 \u043f\u043e\u043f\u0440\u043e\u0431\u043e\u0432\u0430\u0442\u044c \u0434\u0440\u0443\u0433\u043e\u0439 \u043f\u0443\u0442\u044c:<\/p>\n<pre><code class=\"python\">result, error = tools.call(name, arguments, settings, page_context)if error:    tool_content = json.dumps({\"error\": error}, ensure_ascii=False)    step_status = \"error\"else:    tool_content = json.dumps(result, ensure_ascii=False, default=str)<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u041f\u044f\u0442\u044c \u0441\u0446\u0435\u043d\u0430\u0440\u0438\u0435\u0432 \u0446\u0438\u043a\u043b\u0430 \u044f \u043f\u0440\u043e\u0433\u043d\u0430\u043b \u043d\u0430\u00a0\u043f\u043e\u0434\u043c\u0435\u043d\u0451\u043d\u043d\u043e\u043c <code>_post<\/code>: \u043e\u0431\u044b\u0447\u043d\u044b\u0439 tool calling, \u043e\u0442\u043a\u0430\u0442 \u0431\u0435\u0437\u00a0\u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u043e\u0432, \u043e\u0448\u0438\u0431\u043a\u0430 \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u0430, \u0441\u0435\u0442\u0435\u0432\u0430\u044f \u043e\u0448\u0438\u0431\u043a\u0430, \u0443\u043f\u043e\u0440 \u0432\u00a0\u043b\u0438\u043c\u0438\u0442 \u0438\u0442\u0435\u0440\u0430\u0446\u0438\u0439. \u0412\u0441\u0435 \u0432\u0435\u0434\u0443\u0442 \u0441\u0435\u0431\u044f \u043a\u0430\u043a\u00a0\u0437\u0430\u0434\u0443\u043c\u0430\u043d\u043e.<\/p>\n<h2>\u041b\u043e\u0432\u0443\u0448\u043a\u0430 \u0432\u0442\u043e\u0440\u0430\u044f: reasoning\u2011\u043c\u043e\u0434\u0435\u043b\u044c \u0441\u044a\u0435\u0434\u0430\u0435\u0442 max_tokens<\/h2>\n<p>\u041a\u043e\u0433\u0434\u0430 \u0432\u0441\u0451\u00a0\u0431\u044b\u043b\u043e \u0433\u043e\u0442\u043e\u0432\u043e, \u044f \u043f\u043e\u0448\u0451\u043b \u0441\u043c\u043e\u0442\u0440\u0435\u0442\u044c, \u043a\u0430\u043a\u0438\u0435 \u043c\u043e\u0434\u0435\u043b\u0438 \u043e\u0442\u0434\u0430\u0451\u0442 <a href=\"https:\/\/api.llm7.io\/v1\/models%60\" rel=\"noopener noreferrer nofollow\"><code>https:\/\/api.llm7.io\/v1\/models<\/code><\/a>. \u041e\u0442\u0432\u0435\u0442 \u043e\u043a\u0430\u0437\u0430\u043b\u0441\u044f \u0431\u043e\u0433\u0430\u0447\u0435, \u0447\u0435\u043c \u043e\u0436\u0438\u0434\u0430\u043b: \u0443\u00a0\u043a\u0430\u0436\u0434\u043e\u0439 \u043c\u043e\u0434\u0435\u043b\u0438 \u0435\u0441\u0442\u044c \u0444\u043b\u0430\u0433 <code>tools_calling<\/code>.<\/p>\n<p>26\u00a0\u043c\u043e\u0434\u0435\u043b\u0435\u0439, \u0438\u0437\u00a0\u043d\u0438\u0445 17\u00a0\u0447\u0430\u0442\u043e\u0432\u044b\u0445, \u0438 <strong>\u0432\u0441\u0435 17\u00a0\u0437\u0430\u044f\u0432\u043b\u044f\u044e\u0442 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0443 \u0432\u044b\u0437\u043e\u0432\u0430 \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u043e\u0432<\/strong>. \u0422\u0430\u0440\u0438\u0444\u044b \u0434\u0435\u043b\u044f\u0442\u0441\u044f \u043d\u0430\u00a0\u0434\u0432\u0430 \u0443\u0440\u043e\u0432\u043d\u044f: <code>turbo<\/code> \u043e\u0442\u0432\u0435\u0447\u0430\u0435\u0442 \u0431\u0435\u0437\u00a0API\u2011\u043a\u043b\u044e\u0447\u0430, <code>pro<\/code> \u0442\u0440\u0435\u0431\u0443\u0435\u0442 \u043a\u043b\u044e\u0447.<\/p>\n<p>\u041f\u0440\u043e\u0432\u0435\u0440\u044f\u044e \u0431\u0435\u0441\u043f\u043b\u0430\u0442\u043d\u0443\u044e <code>gpt-oss:20b<\/code> \u0436\u0438\u0432\u044b\u043c \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u043c \u0441\u00a0\u043e\u043f\u0438\u0441\u0430\u043d\u0438\u0435\u043c \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u0430, <code>max_tokens: 80<\/code>. \u041f\u043e\u043b\u0443\u0447\u0430\u044e:<\/p>\n<pre><code class=\"json\">{\"finish_reason\": \"length\", \"message\": {\"content\": \"\", \"reasoning\": \"The user asks: ...\"}}<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p><code>tool_calls<\/code> \u043d\u0435\u0442. \u041c\u043e\u0434\u0435\u043b\u044c \u043d\u0430\u0447\u0430\u043b\u0430 \u0440\u0430\u0441\u0441\u0443\u0436\u0434\u0430\u0442\u044c \u0432\u00a0\u043f\u043e\u043b\u0435 <code>reasoning<\/code>, \u0438\u0437\u0440\u0430\u0441\u0445\u043e\u0434\u043e\u0432\u0430\u043b\u0430 \u0432\u0441\u0435 80\u00a0\u0442\u043e\u043a\u0435\u043d\u043e\u0432 \u0438 \u0434\u043e\u00a0\u0432\u044b\u0437\u043e\u0432\u0430 \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u0430 \u043d\u0435\u00a0\u0434\u043e\u0448\u043b\u0430.<\/p>\n<p>\u041f\u043e\u0432\u0442\u043e\u0440\u044f\u044e \u0441 <code>max_tokens: 1500<\/code>:<\/p>\n<pre><code class=\"json\">\"tool_calls\": [{\"id\": \"call_4cgrapec\", \"type\": \"function\",  \"function\": {\"name\": \"list_datasets\", \"arguments\": \"{\\\"limit\\\":10}\"}}]<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p><code>finish_reason: tool_calls<\/code>, \u0440\u0435\u0430\u043b\u044c\u043d\u043e \u0438\u0437\u0440\u0430\u0441\u0445\u043e\u0434\u043e\u0432\u0430\u043d\u043e 112\u00a0\u0442\u043e\u043a\u0435\u043d\u043e\u0432.<\/p>\n<p>\u0412\u044b\u0432\u043e\u0434 \u043f\u0440\u0430\u043a\u0442\u0438\u0447\u0435\u0441\u043a\u0438\u0439: <strong>\u0434\u043b\u044f\u00a0reasoning\u2011\u043c\u043e\u0434\u0435\u043b\u0435\u0439 \u043d\u0435\u043b\u044c\u0437\u044f \u0436\u0430\u0434\u043d\u0438\u0447\u0430\u0442\u044c \u0441 <\/strong><code><strong>max_tokens<\/strong><\/code>. \u041c\u043e\u0434\u0435\u043b\u044c \u0441\u043d\u0430\u0447\u0430\u043b\u0430 \u0434\u0443\u043c\u0430\u0435\u0442, \u043f\u043e\u0442\u043e\u043c \u0432\u044b\u0437\u044b\u0432\u0430\u0435\u0442 \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442, \u0438 \u0435\u0441\u043b\u0438\u00a0\u043b\u0438\u043c\u0438\u0442 \u043a\u043e\u043d\u0447\u0438\u0442\u0441\u044f \u043d\u0430\u00a0\u044d\u0442\u0430\u043f\u0435 \u0440\u0430\u0437\u043c\u044b\u0448\u043b\u0435\u043d\u0438\u0439, \u0430\u0433\u0435\u043d\u0442 \u043d\u0438\u043a\u043e\u0433\u0434\u0430 \u043d\u0435\u00a0\u0434\u043e\u0431\u0435\u0440\u0451\u0442\u0441\u044f \u0434\u043e\u00a0\u0434\u0430\u043d\u043d\u044b\u0445. \u0414\u0435\u0444\u043e\u043b\u0442 1500\u00a0\u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u0441\u00a0\u0437\u0430\u043f\u0430\u0441\u043e\u043c, \u043d\u043e\u00a0\u043e\u043f\u0443\u0441\u043a\u0430\u0442\u044c \u043d\u0438\u0436\u0435 ~800\u00a0\u043d\u0435\u043b\u044c\u0437\u044f.<\/p>\n<p>\u0417\u0430\u043e\u0434\u043d\u043e \u0432\u044b\u044f\u0441\u043d\u0438\u043b\u043e\u0441\u044c, \u0447\u0442\u043e\u00a0\u0431\u0435\u0437\u00a0\u043a\u043b\u044e\u0447\u0430 \u0440\u0430\u0431\u043e\u0442\u0430\u044e\u0442 \u0442\u0440\u0438 \u043c\u043e\u0434\u0435\u043b\u0438 (<code>gpt-oss:20b<\/code>, <code>gemini-3.1-flash-lite<\/code>, <code>codestral-latest<\/code>), \u0430\u00a0\u043e\u0441\u0442\u0430\u043b\u044c\u043d\u044b\u0435 14\u00a0\u043e\u0442\u0434\u0430\u044e\u0442 <code>401 Missing API key<\/code>. \u042d\u0442\u043e \u043b\u0435\u0433\u043b\u043e \u0432\u00a0UI: \u0441\u043f\u0438\u0441\u043e\u043a \u043c\u043e\u0434\u0435\u043b\u0435\u0439 \u043f\u0440\u043e\u0432\u0430\u0439\u0434\u0435\u0440\u0430, \u0433\u0434\u0435 \u0431\u0435\u0441\u043f\u043b\u0430\u0442\u043d\u044b\u0435 \u0438\u0434\u0443\u0442 \u043f\u0435\u0440\u0432\u044b\u043c\u0438, \u043f\u043b\u044e\u0441 \u043f\u043e\u044f\u0441\u043d\u0435\u043d\u0438\u0435 \u043f\u043e\u0434\u00a0\u043f\u043e\u043b\u0435\u043c.<\/p>\n<h2>CSRF: POST, \u043a\u0442\u043e\u0440\u044b\u0439 \u043d\u0435\u00a0\u043f\u0440\u043e\u0445\u043e\u0434\u0438\u0442<\/h2>\n<p>Superset \u0432\u043a\u043b\u044e\u0447\u0430\u0435\u0442 <code>WTF_CSRF_ENABLED<\/code>, \u0438 \u043c\u043e\u0439 <code>POST \/superset-ai\/api\/chat<\/code> \u043f\u043e\u043f\u0430\u0434\u0430\u0435\u0442 \u043f\u043e\u0434\u00a0\u0437\u0430\u0449\u0438\u0442\u0443 <code>CSRFProtect<\/code> \u043d\u0430\u0440\u0430\u0432\u043d\u0435 \u0441\u043e \u0448\u0442\u0430\u0442\u043d\u044b\u043c\u0438 \u043c\u0430\u0440\u0448\u0440\u0443\u0442\u0430\u043c\u0438. \u0411\u0435\u0437\u00a0\u0442\u043e\u043a\u0435\u043d\u0430\u00a0\u2014 HTTP 400.<\/p>\n<p>\u0421\u043e\u0431\u043b\u0430\u0437\u043d \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0441\u0432\u043e\u0439 \u044d\u043d\u0434\u043f\u043e\u0438\u043d\u0442 \u0432 <code>WTF_CSRF_EXEMPT_LIST<\/code> \u0435\u0441\u0442\u044c, \u043d\u043e\u00a0\u044d\u0442\u043e \u0434\u044b\u0440\u0430. \u041f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u044b\u0439 \u043f\u0443\u0442\u044c\u00a0\u2014 \u0431\u0440\u0430\u0442\u044c \u0442\u043e\u043a\u0435\u043d \u0442\u0430\u043c, \u0433\u0434\u0435 \u0435\u0433\u043e \u0431\u0435\u0440\u0451\u0442 \u0441\u0430\u043c \u0444\u0440\u043e\u043d\u0442\u0435\u043d\u0434 Superset. \u0412\u0438\u0434\u0436\u0435\u0442 \u043f\u0440\u043e\u0431\u0443\u0435\u0442 \u0442\u0440\u0438 \u0438\u0441\u0442\u043e\u0447\u043d\u0438\u043a\u0430 \u043f\u043e\u00a0\u043f\u043e\u0440\u044f\u0434\u043a\u0443 \u0443\u0431\u044b\u0432\u0430\u043d\u0438\u044f \u0434\u0435\u0448\u0435\u0432\u0438\u0437\u043d\u044b:<\/p>\n<pre><code class=\"javascript\">function ensureCsrfToken() {  if (state.csrfToken) return Promise.resolve(state.csrfToken);  var bootstrap = window.bootstrapData;  var fromBootstrap = (bootstrap &amp;&amp; bootstrap.common &amp;&amp; bootstrap.common.csrf_token)                   || (bootstrap &amp;&amp; bootstrap.csrf_token);  if (fromBootstrap) { state.csrfToken = fromBootstrap; return Promise.resolve(state.csrfToken); }  var input = document.querySelector('input[name=\"csrf_token\"]');  if (input &amp;&amp; input.value) { state.csrfToken = input.value; return Promise.resolve(state.csrfToken); }  return fetch('\/api\/v1\/security\/csrf_token\/', { credentials: 'same-origin' })    .then(function (r) { return r.ok ? r.json() : null; })    .then(function (d) { state.csrfToken = (d &amp;&amp; (d.result || d.csrf_token)) || null; return state.csrfToken; });}<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u0414\u0430\u043b\u044c\u0448\u0435 \u0442\u043e\u043a\u0435\u043d \u0443\u0445\u043e\u0434\u0438\u0442 \u0432\u00a0\u0437\u0430\u0433\u043e\u043b\u043e\u0432\u043a\u0435 <code>X-CSRFToken<\/code>, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 <code>Flask-WTF<\/code> \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u0442 \u043f\u043e\u00a0\u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e. \u041f\u0440\u043e\u0433\u043d\u0430\u043b \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u043c \u0442\u0435\u0441\u0442\u043e\u043c \u0441\u00a0\u043d\u0430\u0441\u0442\u043e\u044f\u0449\u0438\u043c <code>CSRFProtect<\/code>: \u0431\u0435\u0437\u00a0\u0442\u043e\u043a\u0435\u043d\u0430 400, \u0441\u00a0\u0442\u043e\u043a\u0435\u043d\u043e\u043c 200.<\/p>\n<p>\u0415\u0449\u0451 \u043e\u0434\u0438\u043d \u043c\u043e\u043c\u0435\u043d\u0442 \u043f\u0440\u043e\u00a0XSS: \u043e\u0442\u0432\u0435\u0442 \u043c\u043e\u0434\u0435\u043b\u0438\u00a0\u2014 \u044d\u0442\u043e \u043d\u0435\u0434\u043e\u0432\u0435\u0440\u0435\u043d\u043d\u044b\u0439 \u0442\u0435\u043a\u0441\u0442. \u041c\u043e\u0439 markdown\u2011\u0440\u0435\u043d\u0434\u0435\u0440 \u0440\u0430\u0437\u0431\u0438\u0440\u0430\u0435\u0442 \u0431\u043b\u043e\u043a\u0438 \u043a\u043e\u0434\u0430 \u0438 \u0438\u043d\u043b\u0430\u0439\u043d\u2011\u0440\u0430\u0437\u043c\u0435\u0442\u043a\u0443, \u043d\u043e\u00a0\u0442\u0435\u043a\u0441\u0442 \u0432\u0441\u0435\u0433\u0434\u0430 \u043f\u043e\u043f\u0430\u0434\u0430\u0435\u0442 \u0432\u00a0DOM \u0447\u0435\u0440\u0435\u0437 <code>textContent<\/code>, \u043d\u0438\u043a\u043e\u0433\u0434\u0430 \u0447\u0435\u0440\u0435\u0437 <code>innerHTML<\/code>:<\/p>\n<pre><code class=\"javascript\">if (index % 2 === 1) {  var pre = el('pre');  pre.appendChild(el('code', null, code));   \/\/ textContent \u0432\u043d\u0443\u0442\u0440\u0438 el()  container.appendChild(pre);}<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<h2>\u0425\u0440\u0430\u043d\u0435\u043d\u0438\u0435 \u043d\u0430\u0441\u0442\u0440\u043e\u0435\u043a: \u043f\u043e\u0447\u0435\u043c\u0443 \u043d\u0435\u00a0\u0432\u00a0\u043a\u043e\u043d\u0444\u0438\u0433\u0435<\/h2>\n<p>API\u2011\u043a\u043b\u044e\u0447 \u043c\u043e\u0436\u043d\u043e\u00a0\u0431\u044b\u043b\u043e \u043f\u043e\u043b\u043e\u0436\u0438\u0442\u044c \u0432 <code>superset_config.py<\/code>\u00a0\u2014 \u0438 \u044d\u0442\u043e\u00a0\u0431\u044b\u043b\u043e\u00a0\u0431\u044b \u043e\u0448\u0438\u0431\u043a\u043e\u0439. \u041a\u043e\u043d\u0444\u0438\u0433 \u043f\u043e\u043f\u0430\u0434\u0430\u0435\u0442 \u0432\u00a0\u0431\u044d\u043a\u0430\u043f\u044b, \u0435\u0433\u043e \u0447\u0438\u0442\u0430\u044e\u0442 \u0433\u043b\u0430\u0437\u0430\u043c\u0438, \u043e\u043d \u043b\u0435\u0436\u0438\u0442 \u0441\u00a0\u043e\u0431\u044b\u0447\u043d\u044b\u043c\u0438 \u043f\u0440\u0430\u0432\u0430\u043c\u0438.<\/p>\n<p>\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u0436\u0438\u0432\u0443\u0442 \u0432\u00a0\u043e\u0442\u0434\u0435\u043b\u044c\u043d\u043e\u043c \u0444\u0430\u0439\u043b\u0435 <code>~\/superset_ai\/ai_settings.json<\/code> \u0441\u00a0\u043f\u0440\u0430\u0432\u0430\u043c\u0438 600. \u0412\u00a0\u043a\u043e\u043d\u0444\u0438\u0433\u0435\u00a0\u2014 \u0442\u043e\u043b\u044c\u043a\u043e \u043f\u0443\u0442\u044c \u043a\u00a0\u043d\u0435\u043c\u0443. \u041f\u043b\u044e\u0441 \u043f\u0440\u0438\u044f\u0442\u043d\u044b\u0439 \u043f\u043e\u0431\u043e\u0447\u043d\u044b\u0439 \u044d\u0444\u0444\u0435\u043a\u0442: \u0444\u0430\u0439\u043b \u043f\u0435\u0440\u0435\u0447\u0438\u0442\u044b\u0432\u0430\u0435\u0442\u0441\u044f \u043f\u043e\u00a0mtime, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u0441\u043c\u0435\u043d\u0430 \u043a\u043b\u044e\u0447\u0430 \u0438\u043b\u0438\u00a0\u043c\u043e\u0434\u0435\u043b\u0438 \u043f\u0440\u0438\u043c\u0435\u043d\u044f\u0435\u0442\u0441\u044f <strong>\u0431\u0435\u0437\u00a0\u043f\u0435\u0440\u0435\u0437\u0430\u043f\u0443\u0441\u043a\u0430 Superset<\/strong>:<\/p>\n<pre><code class=\"python\">def load(force=False):    path = settings_path()    try:        mtime = os.path.getmtime(path)    except OSError:        mtime = None    with _LOCK:        cached_ok = (not force and _CACHE[\"data\"] is not None                     and _CACHE[\"path\"] == path and _CACHE[\"mtime\"] == mtime)        if cached_ok:            return _CACHE[\"data\"]        ...<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u0411\u0438\u0442\u044b\u0439 JSON \u043e\u0431\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0435\u0442\u0441\u044f \u0441\u043f\u0435\u0446\u0438\u0430\u043b\u044c\u043d\u043e\u00a0\u2014 \u043d\u0435\u00a0\u0434\u0435\u0444\u043e\u043b\u0442\u0430\u043c\u0438, \u0430\u00a0\u0432\u044b\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435\u043c:<\/p>\n<pre><code class=\"python\">except Exception:    # \u0411\u0438\u0442\u044b\u0439 \u0444\u0430\u0439\u043b \u2014 \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u043c \u043d\u0430 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f\u0445 \u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e, \u043d\u043e \u0432\u044b\u043a\u043b\u044e\u0447\u0435\u043d\u043d\u044b\u043c\u0438,    # \u0447\u0442\u043e\u0431\u044b \u043d\u0435 \u0431\u043e\u043c\u0431\u0430\u0440\u0434\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043d\u0435\u0438\u0437\u0432\u0435\u0441\u0442\u043d\u044b\u0439 \u044d\u043d\u0434\u043f\u043e\u0438\u043d\u0442.    data = dict(DEFAULTS)    data[\"enabled\"] = False<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u041b\u043e\u0433\u0438\u043a\u0430 \u043f\u0440\u043e\u0441\u0442\u0430\u044f: \u0435\u0441\u043b\u0438 \u043c\u044b \u043d\u0435\u00a0\u0437\u043d\u0430\u0435\u043c, \u043a\u0443\u0434\u0430 \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u0442\u044c \u0437\u0430\u043f\u0440\u043e\u0441\u044b, \u043b\u0443\u0447\u0448\u0435 \u043d\u0435\u00a0\u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u0442\u044c \u0438\u0445 \u043d\u0438\u043a\u0443\u0434\u0430, \u0447\u0435\u043c \u0441\u0442\u0443\u0447\u0430\u0442\u044c\u0441\u044f \u0432\u00a0\u0434\u0435\u0444\u043e\u043b\u0442\u043d\u044b\u0439 \u0430\u0434\u0440\u0435\u0441.<\/p>\n<p>\u041d\u0430\u0440\u0443\u0436\u0443 \u043e\u0442\u0434\u0430\u0451\u0442\u0441\u044f \u0443\u0440\u0435\u0437\u0430\u043d\u043d\u043e\u0435 \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u0438\u0435, \u0433\u0434\u0435 \u043a\u043b\u044e\u0447\u0430 \u043d\u0435\u0442 \u0432\u043e\u043e\u0431\u0449\u0435:<\/p>\n<pre><code class=\"python\">def public_view(data=None):    return {        \"enabled\": bool(data.get(\"enabled\")),        \"model\": data.get(\"model\", \"\"),        \"api_base\": api_base,        \"has_api_key\": bool(data.get(\"api_key\")),   # \u0442\u043e\u043b\u044c\u043a\u043e \u0444\u0430\u043a\u0442 \u043d\u0430\u043b\u0438\u0447\u0438\u044f        ...    }<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<h2>\u0427\u0442\u043e\u00a0\u043f\u043e\u043b\u0443\u0447\u0438\u043b\u043e\u0441\u044c \u0432\u00a0\u0446\u0438\u0444\u0440\u0430\u0445<\/h2>\n<p>\u0421\u0435\u0440\u0432\u0435\u0440\u043d\u0430\u044f \u0447\u0430\u0441\u0442\u044c\u00a0\u2014 7\u00a0\u0444\u0430\u0439\u043b\u043e\u0432, \u043e\u043a\u043e\u043b\u043e 2000\u00a0\u0441\u0442\u0440\u043e\u043a:<\/p>\n<pre><code>superset_ai\/\u251c\u2500\u2500 __init__.py      343   blueprint, \u0438\u043d\u044a\u0435\u043a\u0446\u0438\u044f \u0432\u0438\u0434\u0436\u0435\u0442\u0430, \u043f\u0443\u043d\u043a\u0442 \u043c\u0435\u043d\u044e\u251c\u2500\u2500 tools.py         610   \u0441\u0435\u043c\u044c \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u043e\u0432 \u0430\u0433\u0435\u043d\u0442\u0430\u251c\u2500\u2500 llm.py           317   OpenAI-\u0441\u043e\u0432\u043c\u0435\u0441\u0442\u0438\u043c\u044b\u0439 \u043a\u043b\u0438\u0435\u043d\u0442 \u0438 \u0446\u0438\u043a\u043b \u0430\u0433\u0435\u043d\u0442\u0430\u251c\u2500\u2500 settings.py      166   \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u0441 \u043a\u0435\u0448\u0435\u043c \u043f\u043e mtime\u251c\u2500\u2500 static\/\u2502   \u251c\u2500\u2500 widget.js    792   \u043a\u043d\u043e\u043f\u043a\u0430, \u043f\u0430\u043d\u0435\u043b\u044c, \u0442\u0435\u043c\u044b, CSRF\u2502   \u2514\u2500\u2500 widget.css   446   29 CSS-\u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0445, \u0441\u0432\u0435\u0442\u043b\u0430\u044f \u0438 \u0442\u0451\u043c\u043d\u0430\u044f \u0442\u0435\u043c\u044b\u2514\u2500\u2500 templates\/superset_ai\/panel.html   158<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u041f\u043b\u044e\u0441 \u0432\u00a0\u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0438\u2011\u0443\u0441\u0442\u0430\u043d\u043e\u0432\u0449\u0438\u043a\u0435: \u043c\u043e\u0434\u0443\u043b\u044c \u0434\u0435\u043f\u043b\u043e\u044f \u043f\u043e\u00a0SSH (905\u00a0\u0441\u0442\u0440\u043e\u043a), \u0432\u043a\u043b\u0430\u0434\u043a\u0430 \u043d\u0430\u0441\u0442\u0440\u043e\u0435\u043a (635) \u0438 49\u00a0\u043a\u043b\u044e\u0447\u0435\u0439 \u043b\u043e\u043a\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 \u0432\u00a0\u043a\u0430\u0436\u0434\u043e\u043c \u0438\u0437\u00a0\u0441\u0435\u043c\u0438 \u044f\u0437\u044b\u043a\u043e\u0432.<\/p>\n<p>\u0427\u0442\u043e\u00a0\u0438\u0437\u043c\u0435\u043d\u044f\u0435\u0442\u0441\u044f \u0432\u00a0\u0441\u0430\u043c\u043e\u043c Superset: <strong>\u043e\u0434\u0438\u043d \u0431\u043b\u043e\u043a \u0432\u00a0\u043a\u043e\u043d\u0446\u0435 <\/strong><code><strong>superset_config.py<\/strong><\/code>\u00a0\u2014 16\u00a0\u0441\u0442\u0440\u043e\u043a \u043a\u043e\u0434\u0430. \u041d\u0438 \u043e\u0434\u043d\u043e\u0433\u043e \u0444\u0430\u0439\u043b\u0430 Superset \u043d\u0435\u00a0\u0442\u0440\u043e\u043d\u0443\u0442\u043e, \u0444\u0440\u043e\u043d\u0442\u0435\u043d\u0434 \u043d\u0435\u00a0\u043f\u0435\u0440\u0435\u0441\u043e\u0431\u0440\u0430\u043d, CSP \u043d\u0435\u00a0\u043e\u0441\u043b\u0430\u0431\u043b\u0435\u043d.<\/p>\n<h2>\u041f\u043e\u043b\u0435\u0437\u043d\u043e\u0435 \u043d\u0430\u00a0\u0432\u044b\u043d\u043e\u0441<\/h2>\n<p>\u041d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0432\u0435\u0449\u0435\u0439 \u0438\u0437\u00a0\u044d\u0442\u043e\u0439 \u0440\u0430\u0431\u043e\u0442\u044b \u043f\u0440\u0438\u043c\u0435\u043d\u0438\u043c\u044b \u043a\u00a0\u043b\u044e\u0431\u043e\u043c\u0443 \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u0438\u044e Superset, \u0430\u00a0\u043d\u0435\u00a0\u0442\u043e\u043b\u044c\u043a\u043e \u043a\u00a0AI\u2011\u0430\u0433\u0435\u043d\u0442\u0443:<\/p>\n<p>1. <code>FLASK_APP_MUTATOR<\/code> \u0432\u044b\u0437\u044b\u0432\u0430\u0435\u0442\u0441\u044f \u043f\u043e\u0441\u043b\u0435 <code>configure_fab()<\/code> \u0438 \u0434\u043e <code>init_views()<\/code>. \u0417\u043d\u0430\u0447\u0438\u0442 <code>app.appbuilder<\/code> \u0434\u043e\u0441\u0442\u0443\u043f\u0435\u043d, \u0430\u00a0\u043c\u0430\u0440\u0448\u0440\u0443\u0442\u044b \u043c\u043e\u0436\u043d\u043e \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0438\u0440\u043e\u0432\u0430\u0442\u044c.<\/p>\n<p>2. <code>flask-talisman<\/code> \u043a\u043b\u0430\u0434\u0451\u0442 CSP\u2011nonce \u0432 <code>flask.request.csp_nonce<\/code> (<code>before_request<\/code>) \u0438 \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0438\u0440\u0443\u0435\u0442 jinja\u2011global <code>csp_nonce()<\/code>. \u041f\u0440\u0438 <code>strict-dynamic<\/code> \u0440\u0430\u0437\u0440\u0435\u0448\u0435\u043d\u0438\u044f \u043f\u043e\u00a0\u0438\u0441\u0442\u043e\u0447\u043d\u0438\u043a\u0430\u043c \u043d\u0435\u00a0\u0440\u0430\u0431\u043e\u0442\u0430\u044e\u0442\u00a0\u2014 \u043d\u0443\u0436\u0435\u043d \u0442\u043e\u043b\u044c\u043a\u043e nonce.<\/p>\n<p>3. \u0412\u043d\u0435\u0434\u0440\u044f\u044f \u0447\u0442\u043e\u2011\u0442\u043e \u0432\u00a0HTML \u0447\u0435\u0440\u0435\u0437 <code>after_request<\/code>, \u043d\u0435\u00a0\u0437\u0430\u0431\u0443\u0434\u044c\u0442\u0435 \u0443\u0431\u0440\u0430\u0442\u044c <code>Content-Length<\/code> \u0438 \u043e\u0442\u0444\u0438\u043b\u044c\u0442\u0440\u043e\u0432\u0430\u0442\u044c <code>direct_passthrough<\/code> \u0438 \u043d\u0435\u2011HTML \u043e\u0442\u0432\u0435\u0442\u044b.<\/p>\n<p>4. <code>appbuilder.add_link<\/code> \u0441\u043e\u0437\u0434\u0430\u0451\u0442 \u0440\u0430\u0437\u0440\u0435\u0448\u0435\u043d\u0438\u0435, \u043d\u043e\u00a0\u043d\u0435\u00a0\u0432\u044b\u0434\u0430\u0451\u0442 \u0435\u0433\u043e. \u041d\u0443\u0436\u0435\u043d <code>superset init<\/code>.<\/p>\n<p>5. CSRF\u2011\u0442\u043e\u043a\u0435\u043d Superset \u0434\u043e\u0441\u0442\u0443\u043f\u0435\u043d \u043d\u0430 <code>\/api\/v1\/security\/csrf_token\/<\/code> \u0438 \u043f\u0440\u0438\u043d\u0438\u043c\u0430\u0435\u0442\u0441\u044f \u0432\u00a0\u0437\u0430\u0433\u043e\u043b\u043e\u0432\u043a\u0435 <code>X-CSRFToken<\/code>.<\/p>\n<p>6. \u0414\u043b\u044f\u00a0reasoning\u2011\u043c\u043e\u0434\u0435\u043b\u0435\u0439 <code>max_tokens<\/code> \u0434\u043e\u043b\u0436\u0435\u043d\u00a0\u0431\u044b\u0442\u044c \u0441\u00a0\u0437\u0430\u043f\u0430\u0441\u043e\u043c\u00a0\u2014 \u0442\u043e\u043a\u0435\u043d\u044b \u0443\u0445\u043e\u0434\u044f\u0442 \u043d\u0430\u00a0\u0440\u0430\u0441\u0441\u0443\u0436\u0434\u0435\u043d\u0438\u044f \u0440\u0430\u043d\u044c\u0448\u0435, \u0447\u0435\u043c \u043d\u0430\u00a0\u0432\u044b\u0437\u043e\u0432 \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u0430.<\/p>\n<p>\u0415\u0441\u043b\u0438 \u0431\u0443\u0434\u0435\u0442\u0435 \u0434\u0435\u043b\u0430\u0442\u044c \u0447\u0442\u043e\u2011\u0442\u043e \u043f\u043e\u0445\u043e\u0436\u0435\u0435\u00a0\u2014 \u0441\u0430\u043c\u043e\u0435 \u0446\u0435\u043d\u043d\u043e\u0435 \u0437\u0434\u0435\u0441\u044c \u043d\u0435\u00a0\u043a\u043e\u0434, \u0430\u00a0\u043f\u043e\u0440\u044f\u0434\u043e\u043a: \u0441\u043d\u0430\u0447\u0430\u043b\u0430 \u0432\u044b\u044f\u0441\u043d\u0438\u0442\u044c, \u043a\u0430\u043a\u0438\u0435 \u0442\u043e\u0447\u043a\u0438 \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u0438\u044f \u0434\u0430\u0451\u0442 \u043f\u043b\u0430\u0442\u0444\u043e\u0440\u043c\u0430, \u0438 \u0442\u043e\u043b\u044c\u043a\u043e \u043f\u043e\u0442\u043e\u043c \u0440\u0435\u0448\u0430\u0442\u044c, \u0447\u0442\u043e\u00a0\u043f\u0438\u0441\u0430\u0442\u044c. \u0421\u043e\u0431\u043b\u0430\u0437\u043d \u00ab\u043f\u0440\u043e\u043f\u0430\u0442\u0447\u0438\u0442\u044c \u0438 \u0437\u0430\u0431\u044b\u0442\u044c\u00bb \u0441\u0438\u043b\u0451\u043d, \u043d\u043e\u00a0\u0437\u0430\u00a0\u043d\u0435\u0433\u043e \u043f\u043b\u0430\u0442\u044f\u0442 \u043f\u0440\u0438\u00a0\u043a\u0430\u0436\u0434\u043e\u043c \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0438.<\/p>\n<p>\u0415\u0441\u043b\u0438 \u0442\u0435\u043c\u0430 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u0438 AI \u0432\u00a0Apache Superset \u0432\u0430\u043c \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u0430, \u043f\u0440\u0438\u0441\u043e\u0435\u0434\u0438\u043d\u044f\u0439\u0442\u0435\u0441\u044c \u043a\u00a0\u043c\u043e\u0435\u043c\u0443 <a href=\"https:\/\/t.me\/apache_superset_russia\" rel=\"noopener noreferrer nofollow\">Telegram\u2011\u043a\u0430\u043d\u0430\u043b\u0443<\/a>. \u0422\u0430\u043c \u044f \u043f\u0443\u0431\u043b\u0438\u043a\u0443\u044e \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044f Superset Installer, \u043d\u043e\u0432\u044b\u0435 \u044d\u043a\u0441\u043f\u0435\u0440\u0438\u043c\u0435\u043d\u0442\u044b, \u0432\u0438\u0434\u0435\u043e \u0438 \u043f\u0440\u0430\u043a\u0442\u0438\u0447\u0435\u0441\u043a\u0438\u0435 \u043c\u0430\u0442\u0435\u0440\u0438\u0430\u043b\u044b \u043f\u043e\u00a0Superset \u0438 \u0430\u043d\u0430\u043b\u0438\u0437\u0443 \u0434\u0430\u043d\u043d\u044b\u0445.<\/p>\n<\/div>\n<p>\u0441\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b \u0441\u0442\u0430\u0442\u044c\u0438 <a href=\"https:\/\/habr.com\/ru\/articles\/1066586\/\">https:\/\/habr.com\/ru\/articles\/1066586\/<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>\u0415\u0441\u0442\u044c Apache Superset. \u0415\u0441\u0442\u044c \u0430\u043d\u0430\u043b\u0438\u0442\u0438\u043a, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043e\u0442\u043a\u0440\u044b\u0432\u0430\u0435\u0442 SQL Lab \u0438 \u0434\u0435\u0441\u044f\u0442\u044c \u043c\u0438\u043d\u0443\u0442 \u0432\u0441\u043f\u043e\u043c\u0438\u043d\u0430\u0435\u0442, \u043a\u0430\u043a\u00a0\u043d\u0430\u0437\u044b\u0432\u0430\u0435\u0442\u0441\u044f \u0442\u0430\u0431\u043b\u0438\u0446\u0430 \u0441\u00a0\u0437\u0430\u043a\u0430\u0437\u0430\u043c\u0438\u00a0\u2014 orders, olist_orders \u0438\u043b\u0438 fact_orders_v2. \u0415\u0441\u0442\u044c \u044f\u0437\u044b\u043a\u043e\u0432\u044b\u0435 \u043c\u043e\u0434\u0435\u043b\u0438, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043e\u0442\u043b\u0438\u0447\u043d\u043e \u043f\u0438\u0448\u0443\u0442 SQL, \u043d\u043e\u00a0\u043f\u043e\u043d\u044f\u0442\u0438\u044f \u043d\u0435\u00a0\u0438\u043c\u0435\u044e\u0442, \u0447\u0442\u043e\u00a0\u043b\u0435\u0436\u0438\u0442 \u0432\u00a0\u0432\u0430\u0448\u0435\u043c \u0445\u0440\u0430\u043d\u0438\u043b\u0438\u0449\u0435.\u0417\u0430\u0434\u0430\u0447\u0430 \u0437\u0432\u0443\u0447\u0430\u043b\u0430 \u043f\u0440\u043e\u0441\u0442\u043e: \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0432\u00a0\u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441 Superset \u043a\u043d\u043e\u043f\u043a\u0443, \u043f\u043e\u00a0\u043a\u043e\u0442\u043e\u0440\u043e\u0439 \u043e\u0442\u043a\u0440\u044b\u0432\u0430\u0435\u0442\u0441\u044f \u0447\u0430\u0442 \u0441\u00a0\u043c\u043e\u0434\u0435\u043b\u044c\u044e. \u041f\u0440\u0438\u0447\u0451\u043c \u0430\u0433\u0435\u043d\u0442 \u0434\u043e\u043b\u0436\u0435\u043d \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u043f\u043e\u00a0\u043f\u0440\u0438\u043d\u0446\u0438\u043f\u0443 MCP\u00a0\u2014 \u043d\u0435\u00a0\u0444\u0430\u043d\u0442\u0430\u0437\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0438\u043c\u0435\u043d\u0430 \u043a\u043e\u043b\u043e\u043d\u043e\u043a, \u0430\u00a0\u0441\u0445\u043e\u0434\u0438\u0442\u044c \u0438 \u043f\u043e\u0441\u043c\u043e\u0442\u0440\u0435\u0442\u044c \u0438\u0445 \u0432\u00a0\u043c\u0435\u0442\u0430\u0434\u0430\u043d\u043d\u044b\u0445. \u041f\u043e\u0434\u043a\u043b\u044e\u0447\u0430\u0442\u044c\u0441\u044f \u043a\u00a0\u043b\u044e\u0431\u043e\u043c\u0443 OpenAI\u2011\u0441\u043e\u0432\u043c\u0435\u0441\u0442\u0438\u043c\u043e\u043c\u0443 API: \u043b\u043e\u043a\u0430\u043b\u044c\u043d\u0430\u044f Ollama, llm7, OpenAI\u00a0\u2014 \u0447\u0442\u043e\u00a0\u0443\u0433\u043e\u0434\u043d\u043e.\u0414\u0430\u043b\u044c\u0448\u0435\u00a0\u2014 \u043f\u0440\u043e\u00a0\u0442\u043e, \u043a\u0430\u043a\u00a0\u044d\u0442\u043e \u0434\u0435\u043b\u0430\u0435\u0442\u0441\u044f \u0431\u0435\u0437\u00a0\u0444\u043e\u0440\u043a\u0430 Superset, \u0431\u0435\u0437\u00a0\u043f\u0435\u0440\u0435\u0441\u0431\u043e\u0440\u043a\u0438 \u0444\u0440\u043e\u043d\u0442\u0435\u043d\u0434\u0430 \u0438 \u0431\u0435\u0437\u00a0\u043e\u0441\u043b\u0430\u0431\u043b\u0435\u043d\u0438\u044f \u043f\u043e\u043b\u0438\u0442\u0438\u043a\u0438 \u0431\u0435\u0437\u043e\u043f\u0430\u0441\u043d\u043e\u0441\u0442\u0438. \u0418 \u043f\u0440\u043e\u00a0\u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u043b\u043e\u0432\u0443\u0448\u0435\u043a, \u043a\u0430\u0436\u0434\u0430\u044f \u0438\u0437\u00a0\u043a\u043e\u0442\u043e\u0440\u044b\u0445 \u0441\u0442\u043e\u0438\u043b\u0430 \u043c\u043d\u0435 \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u043e\u0433\u043e \u0440\u0430\u0441\u0441\u043b\u0435\u0434\u043e\u0432\u0430\u043d\u0438\u044f.\u041f\u0435\u0440\u0435\u0434 \u0442\u0435\u0445\u043d\u0438\u0447\u0435\u0441\u043a\u0438\u043c \u0440\u0430\u0437\u0431\u043e\u0440\u043e\u043c \u043c\u043e\u0436\u043d\u043e \u043f\u043e\u0441\u043c\u043e\u0442\u0440\u0435\u0442\u044c \u043a\u043e\u0440\u043e\u0442\u043a\u0443\u044e \u0434\u0435\u043c\u043e\u043d\u0441\u0442\u0440\u0430\u0446\u0438\u044e \u0442\u043e\u0433\u043e, \u043a\u0430\u043a\u00a0AI\u2011\u0430\u0441\u0441\u0438\u0441\u0442\u0435\u043d\u0442 \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u043d\u0435\u043f\u043e\u0441\u0440\u0435\u0434\u0441\u0442\u0432\u0435\u043d\u043d\u043e \u0432\u00a0\u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u0435 Apache Superset: \u0432\u0438\u0434\u0435\u043e \u043d\u0430\u00a0YouTube.\u041e\u0433\u0440\u0430\u043d\u0438\u0447\u0435\u043d\u0438\u044f, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0438\u043b\u0438 \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u0443\u041f\u0440\u0435\u0436\u0434\u0435 \u0447\u0435\u043c \u043f\u0438\u0441\u0430\u0442\u044c \u043a\u043e\u0434, \u044f \u0432\u044b\u043f\u0438\u0441\u0430\u043b \u0443\u0441\u043b\u043e\u0432\u0438\u044f \u0437\u0430\u0434\u0430\u0447\u0438. \u041e\u043d\u0438 \u043e\u043a\u0430\u0437\u0430\u043b\u0438\u0441\u044c \u0436\u0451\u0441\u0442\u0447\u0435, \u0447\u0435\u043c \u0432\u044b\u0433\u043b\u044f\u0434\u0435\u043b\u0438.Superset \u043d\u0435\u043b\u044c\u0437\u044f \u0444\u043e\u0440\u043a\u0430\u0442\u044c. \u041e\u043d \u0434\u043e\u043b\u0436\u0435\u043d \u043e\u0431\u043d\u043e\u0432\u043b\u044f\u0442\u044c\u0441\u044f \u0448\u0442\u0430\u0442\u043d\u043e, \u0447\u0435\u0440\u0435\u0437 pip install apache-superset==X.Y.Z. \u0417\u043d\u0430\u0447\u0438\u0442 \u043f\u0440\u0430\u0432\u043a\u0438 \u0432\u00a0\u0435\u0433\u043e \u0444\u0430\u0439\u043b\u0430\u0445 \u0438\u0441\u043a\u043b\u044e\u0447\u0435\u043d\u044b.\u0424\u0440\u043e\u043d\u0442\u0435\u043d\u0434 \u043d\u0435\u043b\u044c\u0437\u044f \u043f\u0435\u0440\u0435\u0441\u043e\u0431\u0438\u0440\u0430\u0442\u044c. \u0422\u0435\u0445\u043d\u0438\u0447\u0435\u0441\u043a\u0438 \u043c\u043e\u0436\u043d\u043e: \u0443\u00a0\u043c\u0435\u043d\u044f \u0432\u00a0\u043f\u0440\u043e\u0435\u043a\u0442\u0435 \u0443\u0436\u0435 \u0435\u0441\u0442\u044c \u043c\u043e\u0434\u0443\u043b\u044c, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043a\u043b\u043e\u043d\u0438\u0440\u0443\u0435\u0442 superset-frontend, \u0441\u0442\u0430\u0432\u0438\u0442 Node, \u0441\u043e\u0431\u0438\u0440\u0430\u0435\u0442 \u0431\u0430\u043d\u0434\u043b \u0438 \u043f\u043e\u0434\u043c\u0435\u043d\u044f\u0435\u0442 \u0441\u0442\u0430\u0442\u0438\u043a\u0443. \u042d\u0442\u043e \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442, \u043d\u043e\u00a0\u0437\u0430\u043d\u0438\u043c\u0430\u0435\u0442 20\u201340\u00a0\u043c\u0438\u043d\u0443\u0442 \u043d\u0430\u00a0\u0441\u0435\u0440\u0432\u0435\u0440\u0435 \u0441 8\u00a0\u0413\u0411 RAM \u0438 \u043b\u043e\u043c\u0430\u0435\u0442\u0441\u044f \u043f\u0440\u0438\u00a0\u043a\u0430\u0436\u0434\u043e\u043c \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0438 Superset. \u0414\u043b\u044f\u00a0\u043a\u043d\u043e\u043f\u043a\u0438 \u0432\u00a0\u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u0435\u00a0\u2014 \u043d\u0435\u043f\u0440\u0438\u0435\u043c\u043b\u0435\u043c\u0430\u044f \u0446\u0435\u043d\u0430.\u0421\u0442\u0430\u0432\u0438\u0442\u044c\u0441\u044f \u0434\u043e\u043b\u0436\u043d\u043e \u043d\u0430\u00a0\u0443\u0436\u0435 \u0440\u0430\u0431\u043e\u0442\u0430\u044e\u0449\u0438\u0439 Superset. \u041e\u0434\u043d\u043e\u0439 \u043a\u043d\u043e\u043f\u043a\u043e\u0439, \u0431\u0435\u0437\u00a0\u043f\u0435\u0440\u0435\u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0438.\u041f\u0440\u0430\u0432\u0430 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439 \u0434\u043e\u043b\u0436\u043d\u044b \u0441\u043e\u0431\u043b\u044e\u0434\u0430\u0442\u044c\u0441\u044f. \u0415\u0441\u043b\u0438 \u0430\u043d\u0430\u043b\u0438\u0442\u0438\u043a \u043d\u0435\u00a0\u0432\u0438\u0434\u0438\u0442 \u0431\u0430\u0437\u0443 \u0441\u00a0\u0437\u0430\u0440\u043f\u043b\u0430\u0442\u0430\u043c\u0438, \u0430\u0433\u0435\u043d\u0442 \u0442\u043e\u0436\u0435 \u043d\u0435\u00a0\u0434\u043e\u043b\u0436\u0435\u043d \u0435\u0451 \u0432\u0438\u0434\u0435\u0442\u044c. \u042d\u0442\u043e \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u043e \u0432\u0430\u0436\u043d\u043e, \u043f\u043e\u0442\u043e\u043c\u0443 \u0447\u0442\u043e\u00a0\u0430\u0433\u0435\u043d\u0442 \u0443\u043c\u0435\u0435\u0442 \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0442\u044c SQL.\u0418\u0437\u00a0\u044d\u0442\u0438\u0445 \u0447\u0435\u0442\u044b\u0440\u0451\u0445 \u043f\u0443\u043d\u043a\u0442\u043e\u0432 \u0441\u043b\u0435\u0434\u0443\u0435\u0442 \u0440\u043e\u0432\u043d\u043e \u043e\u0434\u0438\u043d \u0441\u043f\u043e\u0441\u043e\u0431 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u0438: \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u0438\u0435, \u043a\u043e\u0442\u043e\u0440\u043e\u0435 \u0436\u0438\u0432\u0451\u0442 \u0441\u0431\u043e\u043a\u0443 \u0438 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0430\u0435\u0442\u0441\u044f \u0447\u0435\u0440\u0435\u0437 \u043e\u0444\u0438\u0446\u0438\u0430\u043b\u044c\u043d\u0443\u044e \u0442\u043e\u0447\u043a\u0443 \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u0438\u044f Flask\u2011\u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f.\u0422\u043e\u0447\u043a\u0430 \u0432\u0445\u043e\u0434\u0430: FLASK_APP_MUTATORSuperset\u00a0\u2014 \u044d\u0442\u043e Flask\u2011\u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435, \u0438 \u0432\u00a0\u0435\u0433\u043e \u043a\u043e\u043d\u0444\u0438\u0433\u0435 \u0435\u0441\u0442\u044c \u0445\u0443\u043a FLASK_APP_MUTATOR. \u0414\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u044f \u043e\u043f\u0438\u0441\u044b\u0432\u0430\u0435\u0442 \u0435\u0433\u043e \u0441\u043a\u0443\u043f\u043e: \u00ab\u0445\u0443\u043a, \u0434\u0430\u044e\u0449\u0438\u0439 \u0430\u0434\u043c\u0438\u043d\u0438\u0441\u0442\u0440\u0430\u0442\u043e\u0440\u0430\u043c \u0434\u043e\u0441\u0442\u0443\u043f \u043a\u00a0Flask APP \u043f\u043e\u0441\u043b\u0435 \u0438\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438\u00bb. \u0412\u043e\u043f\u0440\u043e\u0441 \u0432\u00a0\u0442\u043e\u043c, \u0447\u0442\u043e\u00a0\u0437\u043d\u0430\u0447\u0438\u0442 \u00ab\u043f\u043e\u0441\u043b\u0435 \u0438\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438\u00bb\u00a0\u2014 \u043a\u0430\u043a\u0438\u0435 \u0447\u0430\u0441\u0442\u0438 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0443\u0436\u0435 \u0441\u043e\u0431\u0440\u0430\u043d\u044b, \u0430\u00a0\u043a\u0430\u043a\u0438\u0435 \u043d\u0435\u0442.\u041f\u043e\u043b\u0435\u0437 \u0432\u00a0\u0438\u0441\u0445\u043e\u0434\u043d\u0438\u043a\u0438 Superset, \u0432 superset\/initialization\/__init__.py:def init_app_in_ctx(self) -&gt; None:    self.configure_fab()    self.configure_subjects()    self.configure_url_map_converters()    self.configure_data_sources()    self.configure_auth_provider()    self.configure_async_queries()    self.configure_ssh_manager()    self.configure_stats_manager()    self.configure_task_manager()    # Hook that provides administrators a handle on the Flask APP    # after initialization    if flask_app_mutator := self.config[&#171;FLASK_APP_MUTATOR&#187;]:        flask_app_mutator(self.superset_app)    self.superset_app.sync_config_to_db()    self.init_views()\u042d\u0442\u043e \u0432\u0430\u0436\u043d\u0435\u0435, \u0447\u0435\u043c \u043a\u0430\u0436\u0435\u0442\u0441\u044f. \u0425\u0443\u043a \u0432\u044b\u0437\u044b\u0432\u0430\u0435\u0442\u0441\u044f \u043f\u043e\u0441\u043b\u0435 configure_fab()\u00a0\u2014 \u0437\u043d\u0430\u0447\u0438\u0442 app.appbuilder \u0443\u0436\u0435 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u0435\u0442, \u0438 \u0432\u00a0\u043c\u0435\u043d\u044e \u043c\u043e\u0436\u043d\u043e \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u043f\u0443\u043d\u043a\u0442. \u0418 \u0434\u043e init_views()\u00a0\u2014 \u0437\u043d\u0430\u0447\u0438\u0442 \u043c\u043e\u0438 \u043c\u0430\u0440\u0448\u0440\u0443\u0442\u044b \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0438\u0440\u0443\u044e\u0442\u0441\u044f \u0440\u0430\u043d\u044c\u0448\u0435 \u0448\u0442\u0430\u0442\u043d\u044b\u0445, \u0447\u0442\u043e\u00a0\u0434\u043b\u044f\u00a0\u043e\u0442\u0434\u0435\u043b\u044c\u043d\u043e\u0433\u043e \u043f\u0440\u0435\u0444\u0438\u043a\u0441\u0430 \u0431\u0435\u0437\u0440\u0430\u0437\u043b\u0438\u0447\u043d\u043e.\u0418\u0442\u043e\u0433\u043e \u0432 superset_config.py \u0434\u043e\u043f\u0438\u0441\u044b\u0432\u0430\u0435\u0442\u0441\u044f \u043e\u0434\u0438\u043d \u0431\u043b\u043e\u043a \u043c\u0435\u0436\u0434\u0443 \u043c\u0430\u0440\u043a\u0435\u0440\u0430\u043c\u0438\u00a0\u2014 16\u00a0\u0441\u0442\u0440\u043e\u043a \u043a\u043e\u0434\u0430 (27\u00a0\u0432\u043c\u0435\u0441\u0442\u0435 \u0441\u00a0\u043c\u0430\u0440\u043a\u0435\u0440\u0430\u043c\u0438 \u0438 \u043a\u043e\u043c\u043c\u0435\u043d\u0442\u0430\u0440\u0438\u044f\u043c\u0438). \u041d\u0438\u0436\u0435 \u043e\u043d \u0431\u0435\u0437\u00a0\u043a\u043e\u043c\u043c\u0435\u043d\u0442\u0430\u0440\u0438\u0435\u0432:# &gt;&gt;&gt; superset-ai-assistant &gt;&gt;&gt;import sys as _sai_sys_SAI_PATH = &#8216;\/root\/superset_ai&#8217;if _SAI_PATH not in _sai_sys.path:    _sai_sys.path.insert(0, _SAI_PATH)AI_ASSISTANT_SETTINGS_PATH = &#8216;\/root\/superset_ai\/ai_settings.json&#8217;def FLASK_APP_MUTATOR(app):    try:        from superset_ai import init_ai_assistant        init_ai_assistant(app)    except Exception as _sai_error:        import logging        logging.getLogger(__name__).exception(            &#171;AI-\u0430\u0441\u0441\u0438\u0441\u0442\u0435\u043d\u0442 \u043d\u0435 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0451\u043d: %s&#187;, _sai_error        )# &lt;&lt;&lt; superset-ai-assistant &lt;&lt;&lt;\u041e\u0431\u0440\u0430\u0442\u0438\u0442\u0435 \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435 \u043d\u0430 try\/except \u0432\u043e\u043a\u0440\u0443\u0433 \u0438\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438. \u042d\u0442\u043e \u043d\u0435\u00a0\u043f\u0430\u0440\u0430\u043d\u043e\u0439\u044f: \u0435\u0441\u043b\u0438 \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u0438\u0435 \u0443\u043f\u0430\u0434\u0451\u0442 \u043d\u0430\u00a0\u0438\u043c\u043f\u043e\u0440\u0442\u0435, \u0431\u0435\u0437\u00a0\u043e\u0431\u0451\u0440\u0442\u043a\u0438 \u043d\u0435\u00a0\u043f\u043e\u0434\u043d\u0438\u043c\u0435\u0442\u0441\u044f \u0432\u0435\u0441\u044c Superset. \u0410\u043d\u0430\u043b\u0438\u0442\u0438\u043a\u0438 \u043e\u0441\u0442\u0430\u043d\u0443\u0442\u0441\u044f \u0431\u0435\u0437\u00a0BI \u0438\u0437\u2011\u0437\u0430 \u0441\u043b\u043e\u043c\u0430\u043d\u043d\u043e\u0439 \u043a\u043d\u043e\u043f\u043a\u0438 \u0447\u0430\u0442\u0430\u00a0\u2014 \u043f\u043b\u043e\u0445\u043e\u0439 \u043e\u0431\u043c\u0435\u043d. \u0417\u0434\u0435\u0441\u044c \u0438 \u0434\u0430\u043b\u044c\u0448\u0435 \u044f \u0438\u0441\u0445\u043e\u0434\u0438\u043b \u0438\u0437\u00a0\u043f\u0440\u0438\u043d\u0446\u0438\u043f\u0430: \u0430\u0441\u0441\u0438\u0441\u0442\u0435\u043d\u0442 \u043d\u0435\u00a0\u0438\u043c\u0435\u0435\u0442 \u043f\u0440\u0430\u0432\u0430 \u043b\u043e\u043c\u0430\u0442\u044c Superset \u043d\u0438 \u043f\u0440\u0438\u00a0\u043a\u0430\u043a\u0438\u0445 \u043e\u0431\u0441\u0442\u043e\u044f\u0442\u0435\u043b\u044c\u0441\u0442\u0432\u0430\u0445.\u041d\u0438 \u043e\u0434\u043d\u0430 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u044e\u0449\u0430\u044f \u0441\u0442\u0440\u043e\u043a\u0430 \u043a\u043e\u043d\u0444\u0438\u0433\u0430 \u043d\u0435\u00a0\u043c\u0435\u043d\u044f\u0435\u0442\u0441\u044f. FEATURE_FLAGS, TALISMAN_CONFIG, SECRET_KEY, \u0431\u0430\u0437\u0430 \u043c\u0435\u0442\u0430\u0434\u0430\u043d\u043d\u044b\u0445\u00a0\u2014 \u0432\u0441\u0451 \u043e\u0441\u0442\u0430\u0451\u0442\u0441\u044f \u043a\u0430\u043a\u00a0\u0431\u044b\u043b\u043e.\u041b\u043e\u0432\u0443\u0448\u043a\u0430 \u043f\u0435\u0440\u0432\u0430\u044f: strict\u2011dynamic\u041a\u043d\u043e\u043f\u043a\u0443 \u043d\u0430\u0434\u043e \u043a\u0430\u043a\u2011\u0442\u043e \u043f\u043e\u043a\u0430\u0437\u0430\u0442\u044c. Superset \u043e\u0442\u0434\u0430\u0451\u0442 \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u0443 \u0441\u0435\u0440\u0432\u0435\u0440\u043d\u044b\u0439 HTML\u2011\u043a\u0430\u0440\u043a\u0430\u0441, \u0432\u00a0\u043a\u043e\u0442\u043e\u0440\u044b\u0439 React \u0443\u0436\u0435 \u043c\u043e\u043d\u0442\u0438\u0440\u0443\u0435\u0442 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441. \u0417\u043d\u0430\u0447\u0438\u0442 \u0434\u043e\u0441\u0442\u0430\u0442\u043e\u0447\u043d\u043e \u0434\u043e\u043f\u0438\u0441\u0430\u0442\u044c \u0432\u00a0\u044d\u0442\u043e\u0442 \u043a\u0430\u0440\u043a\u0430\u0441 &lt;script&gt;\u00a0\u2014 \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u044d\u0442\u043e \u043c\u043e\u0436\u043d\u043e \u0432\u043e\u00a0Flask\u2011\u0445\u0443\u043a\u0435 after_request.\u041f\u0438\u0448\u0443 \u0438\u043d\u044a\u0435\u043a\u0446\u0438\u044e, \u043e\u0442\u043a\u0440\u044b\u0432\u0430\u044e Superset\u00a0\u2014 \u0441\u043a\u0440\u0438\u043f\u0442 \u0437\u0430\u0431\u043b\u043e\u043a\u0438\u0440\u043e\u0432\u0430\u043d. \u0421\u043c\u043e\u0442\u0440\u044e \u0432\u00a0\u043a\u043e\u043d\u0441\u043e\u043b\u044c: CSP.\u041f\u043e\u043b\u0438\u0442\u0438\u043a\u0430 \u0432\u00a0\u0433\u0435\u043d\u0435\u0440\u0438\u0440\u0443\u0435\u043c\u043e\u043c \u043a\u043e\u043d\u0444\u0438\u0433\u0435 \u0442\u0430\u043a\u0430\u044f:TALISMAN_CONFIG = {    &#171;content_security_policy&#187;: {        &#171;default-src&#187;: [&#171;&#8216;self'&#187;],        &#171;style-src&#187;: [&#171;&#8216;self'&#187;, &#171;&#8216;unsafe-inline'&#187;],        &#171;script-src&#187;: [&#171;&#8216;self'&#187;, &#171;&#8216;strict-dynamic'&#187;, &#171;&#8216;unsafe-eval'&#187;],        &#8230;    },    &#171;content_security_policy_nonce_in&#187;: [&#171;script-src&#187;],}\u0417\u0434\u0435\u0441\u044c \u043a\u043b\u044e\u0447\u0435\u0432\u043e\u0435\u00a0\u2014 &#8216;strict-dynamic&#8217;. \u042d\u0442\u0430 \u0434\u0438\u0440\u0435\u043a\u0442\u0438\u0432\u0430 \u0433\u043e\u0432\u043e\u0440\u0438\u0442 \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u0443: \u0438\u0433\u043d\u043e\u0440\u0438\u0440\u0443\u0439 \u0440\u0430\u0437\u0440\u0435\u0448\u0435\u043d\u0438\u044f \u043f\u043e\u00a0\u0438\u0441\u0442\u043e\u0447\u043d\u0438\u043a\u0430\u043c. \u0422\u043e \u0435\u0441\u0442\u044c &#8216;self&#8217; \u0432 script-src \u043f\u0440\u0438\u00a0\u043d\u0430\u043b\u0438\u0447\u0438\u0438 strict-dynamic \u043f\u0435\u0440\u0435\u0441\u0442\u0430\u0451\u0442 \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u0432\u043e\u043e\u0431\u0449\u0435. \u0412\u044b\u043f\u043e\u043b\u043d\u0438\u0442\u0441\u044f \u0442\u043e\u043b\u044c\u043a\u043e \u0441\u043a\u0440\u0438\u043f\u0442 \u0441\u00a0\u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u044b\u043c \u043e\u0434\u043d\u043e\u0440\u0430\u0437\u043e\u0432\u044b\u043c \u0442\u043e\u043a\u0435\u043d\u043e\u043c\u00a0\u2014 nonce.\u0421\u043e\u0431\u043b\u0430\u0437\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0439 \u043f\u0443\u0442\u044c: \u0440\u0430\u0437 \u043a\u043e\u043d\u0444\u0438\u0433 \u0433\u0435\u043d\u0435\u0440\u0438\u0440\u0443\u0435\u0442 \u043c\u043e\u0439\u00a0\u0436\u0435 \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u0449\u0438\u043a, \u043c\u043e\u0436\u043d\u043e \u0443\u0431\u0440\u0430\u0442\u044c strict-dynamic \u0438 \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c &#8216;unsafe-inline&#8217;. \u0422\u0440\u0438 \u0441\u0442\u0440\u043e\u043a\u0438 \u043f\u0440\u0430\u0432\u043a\u0438. \u041d\u043e\u00a0\u044d\u0442\u043e \u043e\u0441\u043e\u0437\u043d\u0430\u043d\u043d\u043e\u0435 \u043e\u0441\u043b\u0430\u0431\u043b\u0435\u043d\u0438\u0435 \u0437\u0430\u0449\u0438\u0442\u044b \u043e\u0442\u00a0XSS \u0432\u00a0BI\u2011\u0441\u0438\u0441\u0442\u0435\u043c\u0435, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u0445\u043e\u0434\u0438\u0442 \u0432\u00a0\u043f\u0440\u043e\u0434\u043e\u0432\u044b\u0435 \u0431\u0430\u0437\u044b. \u0417\u0430\u00a0\u043a\u043d\u043e\u043f\u043a\u0443 \u0447\u0430\u0442\u0430\u00a0\u2014 \u043d\u0435\u0442.\u0417\u043d\u0430\u0447\u0438\u0442 \u043d\u0443\u0436\u0435\u043d nonce. \u0415\u0433\u043e \u0432\u044b\u0434\u0430\u0451\u0442 flask-talisman, \u0438 \u043d\u0430\u0434\u043e \u043f\u043e\u043d\u044f\u0442\u044c, \u0433\u0434\u0435 \u043e\u043d \u043b\u0435\u0436\u0438\u0442. \u041e\u0442\u043a\u0440\u044b\u0432\u0430\u044e \u0438\u0441\u0445\u043e\u0434\u043d\u0438\u043a flask_talisman\/talisman.py:def init_app(self, app, &#8230;):    &#8230;    app.jinja_env.globals[&#8216;csp_nonce&#8217;] = self._get_nonce    app.before_request(self._force_https)    app.before_request(self._make_nonce)    app.after_request(self._set_response_headers)def _make_nonce(self):    local_options = self._get_local_options()    if (local_options[&#8216;content_security_policy&#8217;] and            self.content_security_policy_nonce_in and            not getattr(flask.request, &#8216;csp_nonce&#8217;, None)):        flask.request.csp_nonce = get_random_string(NONCE_LENGTH)\u0412\u043e\u0442 \u0438 \u043e\u0442\u0432\u0435\u0442. Nonce \u0433\u0435\u043d\u0435\u0440\u0438\u0440\u0443\u0435\u0442\u0441\u044f \u0432 before_request \u0438 \u0436\u0438\u0432\u0451\u0442 \u0432 flask.request.csp_nonce. \u041c\u043e\u0439 after_request \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0435\u0442\u0441\u044f \u043f\u043e\u0437\u0436\u0435\u00a0\u2014 \u0437\u043d\u0430\u0447\u0438\u0442 \u0442\u043e\u043a\u0435\u043d \u0443\u0436\u0435 \u0434\u043e\u0441\u0442\u0443\u043f\u0435\u043d:def _csp_nonce():    &#171;&#187;&#187;    Nonce, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 flask-talisman \u043f\u043e\u043b\u043e\u0436\u0438\u043b \u0432 request \u043d\u0430 \u044d\u0442\u043e\u0442 \u0437\u0430\u043f\u0440\u043e\u0441.    \u0411\u0435\u0437 \u043d\u0435\u0433\u043e \u0431\u0440\u0430\u0443\u0437\u0435\u0440 \u043e\u0442\u043a\u043b\u043e\u043d\u0438\u0442 \u0441\u043a\u0440\u0438\u043f\u0442: \u0432 CSP Superset \u0432\u043a\u043b\u044e\u0447\u0451\u043d strict-dynamic.    &#171;&#187;&#187;    try:        import flask        return getattr(flask.request, &#171;csp_nonce&#187;, &#171;&#187;) or &#171;&#187;    except Exception:        return &#171;&#187;\u0418 \u0441\u0430\u043c\u0430 \u0438\u043d\u044a\u0435\u043a\u0446\u0438\u044f:@app.after_requestdef inject_ai_widget(response):    try:        config = ai_settings.load()        if not config.get(&#171;enabled&#187;) or not config.get(&#171;inject_widget&#187;):            return response        if not _is_authenticated() and not config.get(&#171;allow_anonymous&#187;):            return response        if not _should_inject(response):            return response        body = response.get_data(as_text=True)        if &#171;&lt;\/body&gt;&#187; not in body or _INJECT_MARKER in body:            return response        nonce = _csp_nonce()        nonce_attr = &#8216; nonce=&#187;{}&#187;&#8216;.format(nonce) if nonce else &#171;&#187;        snippet = (            &#8216;&lt;link id=&#187;{marker}-css&#187; rel=&#187;stylesheet&#187; href=&#187;{prefix}\/static\/widget.css&#187;&gt;&#8217;            &#8216;&lt;script id=&#187;{marker}&#187; src=&#187;{prefix}\/static\/widget.js&#187;&#8216;            &#8216; data-ai-base=&#187;{prefix}&#187; defer{nonce}&gt;&lt;\/script&gt;&#8217;        ).format(marker=_INJECT_MARKER, prefix=URL_PREFIX, nonce=nonce_attr)        body = body.replace(&#171;&lt;\/body&gt;&#187;, snippet + &#171;&lt;\/body&gt;&#187;, 1)        response.set_data(body)        response.headers.pop(&#171;Content-Length&#187;, None)    except Exception as error:        # \u041d\u0438\u043a\u043e\u0433\u0434\u0430 \u043d\u0435 \u043b\u043e\u043c\u0430\u0435\u043c \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0443 Superset \u0438\u0437-\u0437\u0430 \u0432\u0438\u0434\u0436\u0435\u0442\u0430        logger.warning(&#171;AI-\u0430\u0441\u0441\u0438\u0441\u0442\u0435\u043d\u0442: \u043d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u0432\u0441\u0442\u0440\u043e\u0438\u0442\u044c \u0432\u0438\u0434\u0436\u0435\u0442: %s&#187;, error)    return response\u0422\u0440\u0438 \u0434\u0435\u0442\u0430\u043b\u0438, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043b\u0435\u0433\u043a\u043e \u0443\u043f\u0443\u0441\u0442\u0438\u0442\u044c:response.headers.pop(&#171;Content-Length&#187;, None)\u00a0\u2014 \u0442\u0435\u043b\u043e \u0432\u044b\u0440\u043e\u0441\u043b\u043e, \u0441\u0442\u0430\u0440\u0430\u044f \u0434\u043b\u0438\u043d\u0430 \u0441\u0442\u0430\u043b\u0430 \u043b\u043e\u0436\u044c\u044e. \u0411\u0435\u0437\u00a0\u044d\u0442\u043e\u0433\u043e \u0431\u0440\u0430\u0443\u0437\u0435\u0440 \u043e\u0431\u0440\u0435\u0436\u0435\u0442 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0443.\u041f\u0440\u043e\u0432\u0435\u0440\u043a\u0430 _INJECT_MARKER in body\u00a0\u2014 \u0437\u0430\u0449\u0438\u0442\u0430 \u043e\u0442\u00a0\u043f\u043e\u0432\u0442\u043e\u0440\u043d\u043e\u0439 \u0432\u0441\u0442\u0430\u0432\u043a\u0438.\u0424\u0438\u043b\u044c\u0442\u0440 _should_inject\u00a0\u2014 \u0442\u0440\u043e\u0433\u0430\u0435\u043c \u0442\u043e\u043b\u044c\u043a\u043e HTML\u2011\u0441\u0442\u0440\u0430\u043d\u0438\u0446\u044b, \u0438 \u0442\u043e\u043b\u044c\u043a\u043e \u0443\u0441\u043f\u0435\u0448\u043d\u044b\u0435:def _should_inject(response):    if request.path.startswith(URL_PREFIX):        return False    content_type = (response.headers.get(&#171;Content-Type&#187;) or &#171;&#187;).lower()    if &#171;text\/html&#187; not in content_type:        return False    if response.status_code != 200 or response.direct_passthrough:        return False    return Truedirect_passthrough\u00a0\u2014 \u043f\u0440\u043e\u00a0\u043e\u0442\u0434\u0430\u0447\u0443 \u0444\u0430\u0439\u043b\u043e\u0432 \u0447\u0435\u0440\u0435\u0437 send_file: \u0443\u00a0\u0442\u0430\u043a\u0438\u0445 \u043e\u0442\u0432\u0435\u0442\u043e\u0432 \u0442\u0435\u043b\u043e \u0447\u0438\u0442\u0430\u0442\u044c \u043d\u0435\u043b\u044c\u0437\u044f. \u041f\u0440\u043e\u0432\u0435\u0440\u043a\u0430 \u043f\u043e Content-Type \u0437\u0430\u043e\u0434\u043d\u043e \u0441\u043f\u0430\u0441\u0430\u0435\u0442 JSON\u2011\u043e\u0442\u0432\u0435\u0442\u044b API \u043e\u0442\u00a0\u043f\u0440\u0435\u0432\u0440\u0430\u0449\u0435\u043d\u0438\u044f \u0432\u00a0\u043d\u0435\u0432\u0430\u043b\u0438\u0434\u043d\u044b\u0435.\u041a\u0430\u043a\u00a0\u044f \u044d\u0442\u043e \u043f\u0440\u043e\u0432\u0435\u0440\u0438\u043b\u0423\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043d\u0438\u0435 \u00abnonce \u043f\u043e\u0434\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u043e\u00bb \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u0442\u0441\u044f \u0440\u043e\u0432\u043d\u043e \u043e\u0434\u043d\u0438\u043c \u0441\u043f\u043e\u0441\u043e\u0431\u043e\u043c: \u043f\u043e\u0434\u043d\u044f\u0442\u044c Flask \u0441\u00a0\u0442\u043e\u0439\u00a0\u0436\u0435 \u043f\u043e\u043b\u0438\u0442\u0438\u043a\u043e\u0439, \u0447\u0442\u043e\u00a0\u0443\u00a0Superset, \u0438 \u0441\u0440\u0430\u0432\u043d\u0438\u0442\u044c nonce \u0432\u00a0\u0442\u0435\u0433\u0435 \u0441\u00a0nonce \u0432\u00a0\u0438\u0442\u043e\u0433\u043e\u0432\u043e\u043c \u0437\u0430\u0433\u043e\u043b\u043e\u0432\u043a\u0435.Talisman(    app, force_https=False,    content_security_policy={        &#171;default-src&#187;: [&#171;&#8216;self'&#187;],        &#171;style-src&#187;: [&#171;&#8216;self'&#187;, &#171;&#8216;unsafe-inline'&#187;],        &#171;script-src&#187;: [&#171;&#8216;self'&#187;, &#171;&#8216;strict-dynamic'&#187;, &#171;&#8216;unsafe-eval'&#187;],    },    content_security_policy_nonce_in=[&#171;script-src&#187;],)&#8230;csp = re.search(r&#187;&#8216;nonce-([^&#8217;]+)'&#187;, response.headers[&#171;Content-Security-Policy&#187;])tag = re.search(r&#8217;id=&#187;superset-ai-widget-loader&#187;[^&gt;]*nonce=&#187;([^&#187;]+)&#187;&#8216;, html)assert csp.group(1) == tag.group(1)\u0421\u043e\u0432\u043f\u0430\u043b\u043e. CSP \u043d\u0435\u00a0\u0442\u0440\u043e\u043d\u0443\u0442 \u043d\u0438 \u043d\u0430\u00a0\u0441\u0438\u043c\u0432\u043e\u043b, \u0444\u0440\u043e\u043d\u0442\u0435\u043d\u0434 \u043d\u0435\u00a0\u043f\u0435\u0440\u0435\u0441\u043e\u0431\u0440\u0430\u043d.\u0415\u0441\u0442\u044c \u0442\u043e\u043d\u043a\u043e\u0441\u0442\u044c \u0441\u00a0\u043f\u043e\u0440\u044f\u0434\u043a\u043e\u043c \u0445\u0443\u043a\u043e\u0432. Flask \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0435\u0442 after_request \u0432\u00a0\u043e\u0431\u0440\u0430\u0442\u043d\u043e\u043c \u043f\u043e\u0440\u044f\u0434\u043a\u0435 \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0446\u0438\u0438: \u043c\u043e\u0439 \u0445\u0443\u043a \u0437\u0430\u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0438\u0440\u043e\u0432\u0430\u043d \u043f\u043e\u0437\u0436\u0435, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u0441\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0435\u0442 \u0440\u0430\u043d\u044c\u0448\u0435 talisman&#8217;\u0430. \u041d\u0430\u00a0\u043c\u043e\u043c\u0435\u043d\u0442 \u0438\u043d\u044a\u0435\u043a\u0446\u0438\u0438 \u0437\u0430\u0433\u043e\u043b\u043e\u0432\u043a\u0430 CSP \u0432\u00a0\u043e\u0442\u0432\u0435\u0442\u0435 \u0435\u0449\u0451 \u043d\u0435\u0442\u00a0\u2014 \u043d\u043e\u00a0\u043e\u043d \u043c\u043d\u0435 \u0438 \u043d\u0435\u00a0\u043d\u0443\u0436\u0435\u043d, \u044f \u0431\u0435\u0440\u0443 nonce \u0438\u0437 request, \u043a\u0443\u0434\u0430 \u0435\u0433\u043e \u043f\u043e\u043b\u043e\u0436\u0438\u043b\u0438 \u0432 before_request. \u0415\u0441\u043b\u0438\u00a0\u0431\u044b \u044f \u043f\u044b\u0442\u0430\u043b\u0441\u044f \u0432\u044b\u0442\u0430\u0449\u0438\u0442\u044c \u0442\u043e\u043a\u0435\u043d \u0438\u0437\u00a0\u0437\u0430\u0433\u043e\u043b\u043e\u0432\u043a\u0430 \u043e\u0442\u0432\u0435\u0442\u0430, \u043d\u0438\u0447\u0435\u0433\u043e\u00a0\u0431\u044b \u043d\u0435\u00a0\u0432\u044b\u0448\u043b\u043e.\u041f\u0443\u043d\u043a\u0442 \u043c\u0435\u043d\u044e: \u043f\u043e\u0434\u0432\u043e\u0434\u043d\u044b\u0439 \u043a\u0430\u043c\u0435\u043d\u044c \u0441\u00a0\u043f\u0440\u0430\u0432\u0430\u043c\u0438\u041f\u043b\u0430\u0432\u0430\u044e\u0449\u0430\u044f \u043a\u043d\u043e\u043f\u043a\u0430\u00a0\u2014 \u0445\u043e\u0440\u043e\u0448\u043e, \u043d\u043e\u00a0\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0438 \u0438\u0449\u0443\u0442 \u0444\u0443\u043d\u043a\u0446\u0438\u0438 \u0432\u00a0\u043c\u0435\u043d\u044e. FLASK_APP_MUTATOR \u0432\u044b\u0437\u044b\u0432\u0430\u0435\u0442\u0441\u044f \u043f\u043e\u0441\u043b\u0435 configure_fab(), \u043f\u043e\u044d\u0442\u043e\u043c\u0443:appbuilder.add_link(    name=&#187;AI Assistant&#187;,    label=_(&#171;AI Assistant&#187;),    href=URL_PREFIX + &#171;\/&#187;,    icon=&#187;fa-robot&#187;,&#8230;<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[],"tags":[],"class_list":["post-489729","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/489729","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=489729"}],"version-history":[{"count":0,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/489729\/revisions"}],"wp:attachment":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=489729"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=489729"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=489729"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}