Add AI SDK recipes: health report, LangChain template and AI agent no…#27506
Add AI SDK recipes: health report, LangChain template and AI agent no…#27506prateekbaibhav wants to merge 6 commits intoopen-metadata:mainfrom
Conversation
…tebooks Three Jupyter notebooks for OpenMetadata Hackathon 2026: 1) Metadata Health Report : analyzes table documentation quality 2) LangChain Template : connects AI to OpenMetadata 3) AI Agent : intelligent agent that autodecides how to search
|
Hi there 👋 Thanks for your contribution! The OpenMetadata team will review the PR shortly! Once it has been labeled as Let us know if you need any help! |
| " decision_prompt = f\"\"\"You are a data catalog agent. \n", | ||
| "You have these tools available:\n", | ||
| "1. get_tables - fetches list of tables\n", | ||
| "2. search_tables - searches tables by keyword\n", | ||
| "3. get_databases - fetches all databases\n", | ||
| "\n", | ||
| "User question: {user_question}\n", | ||
| "\n", | ||
| "Which tool should you use first? Reply with ONLY one of:\n", | ||
| "get_tables\n", | ||
| "search_tables: <keyword>\n", | ||
| "get_databases\"\"\"\n", |
There was a problem hiding this comment.
⚠️ Security: Prompt injection via unsanitized user input and API data
In openmetadata_ai_agent.ipynb, user_question is interpolated directly into the LLM prompt (line 147 decision_prompt and line 183 final_prompt), and raw API response data is also injected into prompts (line 186 Data retrieved: {data}). A malicious table name or description in OpenMetadata could manipulate the LLM's behavior (indirect prompt injection). Similarly in langchain_openmetadata_template.ipynb, table names from the API are injected into the prompt at line 141.
For example recipes this may be acceptable, but consider adding a note about sanitization for production use, or at minimum escaping/truncating the data before prompt injection.
Was this helpful? React with 👍 / 👎 | Reply gitar fix to apply this suggestion
| "print(f\" ✅ Have Description : {has_description} ({round(has_description/total*100)}%)\")\n", | ||
| "print(f\" ❌ Missing Description : {missing_description} ({round(missing_description/total*100)}%)\")\n", | ||
| "print(f\" ✅ Have Owner : {has_owner} ({round(has_owner/total*100)}%)\")\n", | ||
| "print(f\" ❌ Missing Owner : {missing_owner} ({round(missing_owner/total*100)}%)\")\n", |
There was a problem hiding this comment.
⚠️ Bug: Division by zero when no tables are returned
In metadata_health_report.ipynb, the health score calculation uses total * 2 as a divisor (line 259/321) and the percentage calculations use total as a divisor (lines 250-253/312-315). If the API returns zero tables, total will be 0 and all these expressions will raise ZeroDivisionError. An example/recipe notebook should handle this gracefully since users may run it against an empty or misconfigured instance.
Suggested fix:
Add a guard before the summary block:
if total == 0:
print("No tables found. Check your connection and token.")
else:
# ... existing summary code ...
Was this helpful? React with 👍 / 👎 | Reply gitar fix to apply this suggestion
| "# Print a nice summary report\n", | ||
| "total = len(df)\n", | ||
| "has_description = (df[\"Has Description\"] == \"✅\").sum()\n", | ||
| "missing_description = (df[\"Has Description\"] == \"❌\").sum()\n", | ||
| "has_owner = (df[\"Has Owner\"] == \"✅\").sum()\n", | ||
| "missing_owner = (df[\"Has Owner\"] == \"❌\").sum()\n", | ||
| "total_columns = df[\"Total Columns\"].sum()\n", | ||
| "columns_missing_desc = df[\"Columns Missing Desc\"].sum()\n", | ||
| "\n", | ||
| "print(\"=\" * 50)\n", | ||
| "print(\" 📊 MY OPENMETADATA HEALTH REPORT\")\n", | ||
| "print(\" Built for OpenMetadata Hackathon 2026\")\n", | ||
| "print(\"=\" * 50)\n", | ||
| "print(f\" Total Tables Analyzed : {total}\")\n", | ||
| "print(f\" ✅ Have Description : {has_description} ({round(has_description/total*100)}%)\")\n", |
There was a problem hiding this comment.
⚠️ Quality: Duplicate notebook cell: summary report appears twice
In metadata_health_report.ipynb, cells at lines 236-268 and 298-330 contain nearly identical code for printing the health report summary. The first version includes "Built for OpenMetadata Hackathon 2026" text while the second doesn't, but otherwise they are duplicates. This appears to be an editing artifact — one of them should be removed.
Was this helpful? React with 👍 / 👎 | Reply gitar fix to apply this suggestion
There was a problem hiding this comment.
Pull request overview
This PR adds three example Jupyter notebooks (plus a README) intended to serve as “AI SDK recipes” demonstrating OpenMetadata + AI workflows (health reporting, a LangChain-style template, and an AI agent).
Changes:
- Added a metadata health report notebook that fetches tables and computes simple documentation/ownership coverage plus a chart export.
- Added two AI-assisted notebooks: a Groq-backed Q&A template and a simple tool-choosing “AI agent” over OpenMetadata REST endpoints.
- Added an
ingestion/examples/README.mddescribing how to run the notebooks and install dependencies.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 16 comments.
| File | Description |
|---|---|
| ingestion/examples/openmetadata_ai_agent.ipynb | New notebook: Groq-based agent that chooses between a few REST “tools” (tables/search/databases). |
| ingestion/examples/metadata_health_report.ipynb | New notebook: basic metadata health scoring + CSV/chart export based on /api/v1/tables. |
| ingestion/examples/langchain_openmetadata_template.ipynb | New notebook: Groq-based Q&A template over OpenMetadata REST endpoints (despite the “LangChain” name). |
| ingestion/examples/README.md | New README describing the three notebooks and installation/setup steps. |
| "def get_tables(limit=10):\n", | ||
| " response = requests.get(\n", | ||
| " f\"{BASE_URL}/api/v1/tables\",\n", | ||
| " headers=HEADERS,\n", | ||
| " params={\"limit\": limit}\n", | ||
| " )\n", | ||
| " return response.json().get(\"data\", [])\n", | ||
| "\n", | ||
| "def get_databases():\n", | ||
| " response = requests.get(\n", | ||
| " f\"{BASE_URL}/api/v1/databases\",\n", | ||
| " headers=HEADERS,\n", | ||
| " params={\"limit\": 20}\n", | ||
| " )\n", | ||
| " return response.json().get(\"data\", [])\n", | ||
| "\n", | ||
| "def search_assets(query):\n", | ||
| " response = requests.get(\n", | ||
| " f\"{BASE_URL}/api/v1/search/query\",\n", | ||
| " headers=HEADERS,\n", | ||
| " params={\"q\": query, \"index\": \"table_search_index\", \"limit\": 5}\n", | ||
| " )\n", | ||
| " return response.json().get(\"hits\", {}).get(\"hits\", [])\n", |
There was a problem hiding this comment.
This recipe makes HTTP calls to OpenMetadata but does not check for non-2xx responses before calling response.json(). If authentication fails or the server returns an error, this will raise confusing exceptions. Please add response.raise_for_status() (or explicit status checks) and surface a clear error message when the API call fails.
| "def get_tables(limit=10):\n", | |
| " response = requests.get(\n", | |
| " f\"{BASE_URL}/api/v1/tables\",\n", | |
| " headers=HEADERS,\n", | |
| " params={\"limit\": limit}\n", | |
| " )\n", | |
| " return response.json().get(\"data\", [])\n", | |
| "\n", | |
| "def get_databases():\n", | |
| " response = requests.get(\n", | |
| " f\"{BASE_URL}/api/v1/databases\",\n", | |
| " headers=HEADERS,\n", | |
| " params={\"limit\": 20}\n", | |
| " )\n", | |
| " return response.json().get(\"data\", [])\n", | |
| "\n", | |
| "def search_assets(query):\n", | |
| " response = requests.get(\n", | |
| " f\"{BASE_URL}/api/v1/search/query\",\n", | |
| " headers=HEADERS,\n", | |
| " params={\"q\": query, \"index\": \"table_search_index\", \"limit\": 5}\n", | |
| " )\n", | |
| " return response.json().get(\"hits\", {}).get(\"hits\", [])\n", | |
| "def openmetadata_get(path, params=None):\n", | |
| " url = f\"{BASE_URL}{path}\"\n", | |
| " response = requests.get(url, headers=HEADERS, params=params)\n", | |
| " try:\n", | |
| " response.raise_for_status()\n", | |
| " except requests.HTTPError as exc:\n", | |
| " error_body = response.text.strip()\n", | |
| " raise RuntimeError(\n", | |
| " f\"OpenMetadata API request failed for {url} with status \"\n", | |
| " f\"{response.status_code}: {error_body or 'No response body returned.'}\"\n", | |
| " ) from exc\n", | |
| "\n", | |
| " try:\n", | |
| " return response.json()\n", | |
| " except ValueError as exc:\n", | |
| " raise RuntimeError(\n", | |
| " f\"OpenMetadata API request to {url} returned a non-JSON response.\"\n", | |
| " ) from exc\n", | |
| "\n", | |
| "def get_tables(limit=10):\n", | |
| " response_json = openmetadata_get(\n", | |
| " \"/api/v1/tables\",\n", | |
| " params={\"limit\": limit}\n", | |
| " )\n", | |
| " return response_json.get(\"data\", [])\n", | |
| "\n", | |
| "def get_databases():\n", | |
| " response_json = openmetadata_get(\n", | |
| " \"/api/v1/databases\",\n", | |
| " params={\"limit\": 20}\n", | |
| " )\n", | |
| " return response_json.get(\"data\", [])\n", | |
| "\n", | |
| "def search_assets(query):\n", | |
| " response_json = openmetadata_get(\n", | |
| " \"/api/v1/search/query\",\n", | |
| " params={\"q\": query, \"index\": \"table_search_index\", \"limit\": 5}\n", | |
| " )\n", | |
| " return response_json.get(\"hits\", {}).get(\"hits\", [])\n", |
| "cell_type": "code", | ||
| "execution_count": 11, | ||
| "id": "cfa44929-6991-432b-8455-071cf8a12fe0", | ||
| "metadata": {}, | ||
| "outputs": [ | ||
| { | ||
| "name": "stdout", | ||
| "output_type": "stream", | ||
| "text": [ | ||
| "✅ Helper functions ready!\n" | ||
| ] | ||
| } |
There was a problem hiding this comment.
The committed cells include execution outputs and non-null execution_count values. This makes diffs noisy and can go stale quickly (especially for API-driven responses). Please clear outputs/reset execution counts before committing, or ensure the outputs are intentionally kept and match the current code.
| "name": "python", | ||
| "nbconvert_exporter": "python", | ||
| "pygments_lexer": "ipython3", | ||
| "version": "3.13.9" |
There was a problem hiding this comment.
Notebook metadata indicates it was created with Python 3.13.9. OpenMetadata ingestion/examples are expected to run on supported Python versions (e.g., other repo notebooks use 3.11.x), so this kernel/version metadata is likely to mislead users and can break dependencies. Please re-save the notebook using a supported Python kernel (3.9–3.11) so language_info.version matches the supported runtime.
| "version": "3.13.9" | |
| "version": "3.11.9" |
| "# Print a nice summary report\n", | ||
| "total = len(df)\n", | ||
| "has_description = (df[\"Has Description\"] == \"✅\").sum()\n", | ||
| "missing_description = (df[\"Has Description\"] == \"❌\").sum()\n", | ||
| "has_owner = (df[\"Has Owner\"] == \"✅\").sum()\n", | ||
| "missing_owner = (df[\"Has Owner\"] == \"❌\").sum()\n", | ||
| "total_columns = df[\"Total Columns\"].sum()\n", | ||
| "columns_missing_desc = df[\"Columns Missing Desc\"].sum()\n", | ||
| "\n", | ||
| "print(\"=\" * 50)\n", | ||
| "print(\" 📊 OPENMETADATA HEALTH REPORT\")\n", | ||
| "print(\"=\" * 50)\n", | ||
| "print(f\" Total Tables Analyzed : {total}\")\n", | ||
| "print(f\" ✅ Have Description : {has_description} ({round(has_description/total*100)}%)\")\n", | ||
| "print(f\" ❌ Missing Description : {missing_description} ({round(missing_description/total*100)}%)\")\n", | ||
| "print(f\" ✅ Have Owner : {has_owner} ({round(has_owner/total*100)}%)\")\n", | ||
| "print(f\" ❌ Missing Owner : {missing_owner} ({round(missing_owner/total*100)}%)\")\n", | ||
| "print(f\" Total Columns : {total_columns}\")\n", | ||
| "print(f\" Columns Missing Desc : {columns_missing_desc}\")\n", | ||
| "print(\"=\" * 50)\n", | ||
| "\n", | ||
| "# Health Score\n", | ||
| "score = round(((has_description + has_owner) / (total * 2)) * 100)\n", | ||
| "print(f\"\\n 🏥 Overall Health Score: {score}/100\")\n", | ||
| "if score >= 70:\n", | ||
| " print(\" Status: 🟢 HEALTHY\")\n", | ||
| "elif score >= 40:\n", | ||
| " print(\" Status: 🟡 NEEDS ATTENTION\")\n", | ||
| "else:\n", | ||
| " print(\" Status: 🔴 CRITICAL\")\n", | ||
| "print(\"=\" * 50)" | ||
| ] |
There was a problem hiding this comment.
The health report summary block is duplicated (same calculations and prints appear twice). This adds noise and increases maintenance cost. Please remove one of the duplicated summary cells and keep a single canonical summary section.
| "def get_all_tables():\n", | ||
| " response = requests.get(\n", | ||
| " f\"{BASE_URL}/api/v1/tables\",\n", | ||
| " headers=HEADERS,\n", | ||
| " params={\"limit\": 50}\n", | ||
| " )\n", | ||
| " data = response.json()\n", | ||
| " tables = data.get(\"data\", [])\n", |
There was a problem hiding this comment.
get_all_tables() hard-codes limit=50, so the report will only analyze the first page of tables and can significantly under-report health in larger catalogs. Please add pagination (iterate using the API paging mechanism) or make limit configurable and clearly document that this is a partial report.
| "def get_all_tables():\n", | |
| " response = requests.get(\n", | |
| " f\"{BASE_URL}/api/v1/tables\",\n", | |
| " headers=HEADERS,\n", | |
| " params={\"limit\": 50}\n", | |
| " )\n", | |
| " data = response.json()\n", | |
| " tables = data.get(\"data\", [])\n", | |
| "def get_all_tables(page_size=50):\n", | |
| " tables = []\n", | |
| " after = None\n", | |
| "\n", | |
| " while True:\n", | |
| " params = {\"limit\": page_size}\n", | |
| " if after:\n", | |
| " params[\"after\"] = after\n", | |
| "\n", | |
| " response = requests.get(\n", | |
| " f\"{BASE_URL}/api/v1/tables\",\n", | |
| " headers=HEADERS,\n", | |
| " params=params\n", | |
| " )\n", | |
| " response.raise_for_status()\n", | |
| "\n", | |
| " data = response.json()\n", | |
| " tables.extend(data.get(\"data\", []))\n", | |
| "\n", | |
| " after = data.get(\"paging\", {}).get(\"after\")\n", | |
| " if not after:\n", | |
| " break\n", | |
| "\n", |
| "file_extension": ".py", | ||
| "mimetype": "text/x-python", | ||
| "name": "python", | ||
| "nbconvert_exporter": "python", | ||
| "pygments_lexer": "ipython3", | ||
| "version": "3.13.9" | ||
| } |
There was a problem hiding this comment.
Notebook metadata indicates it was created with Python 3.13.9. OpenMetadata ingestion/examples are expected to run on supported Python versions (e.g., repo notebooks under examples/python-sdk/... use 3.11.x), so this kernel/version metadata is likely to mislead users and can break dependencies. Please re-save the notebook using a supported Python kernel (3.9–3.11) so language_info.version matches the supported runtime.
| "data": { | ||
| "image/png": "iVBORw0KGgoAAAANSUhEUgAABdAAAAJRCAYAAABFgJViAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjYsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvq6yFwwAAAAlwSFlzAAAPYQAAD2EBqD+naQAA59VJREFUeJzs3Xd4FFX//vF7k5BGJ5QQpAvSm6BSFBQQKdYHEWwURR6xIfKAgFIVBAVRpCgCUak2EFB6E6T33qRDQknvye7O7w9+2S+bbEJCApPyfl3XXrozZ2bumV02u589e47FMAxDAAAAAAAAAADAiZvZAQAAAAAAAAAAyIkooAMAAAAAAAAA4AIFdAAAAAAAAAAAXKCADgAAAAAAAACACxTQAQAAAAAAAABwgQI6AAAAAAAAAAAuUEAHAAAAAAAAAMAFCugAAAAAAAAAALhAAR0AAAAAAAAAABcooAMAACDf2rBhgywWi+PWo0cPsyMhhR49ejg9Rhs2bLit/QQGBjrtZ8SIEdmaEwAAAHmTh9kBAAAA7pYTJ05o5syZWr9+vc6ePavw8HAVLlxY5cuX1yOPPKLu3bvr/vvvNztmtjt79qwqV66cavmUKVPUt29fl9t07txZv/32m9OyihUr6uzZs9mWa9KkSQoPD3fcz2sFzX379mnx4sWO+61atVKrVq3uagZXj71hGC7btmrVShs3bnTcnz17dq74QiEwMNDpedmvXz8VK1bsrufo0aOHfvjhB5frfH19VaZMGTVq1EgvvfSSnn322bucLucIDw/XpEmTHPcrVaqUK55nAAAg/6KADgAAcoRKlSrp3LlzGWq7adMmtWjRIsP7TkpK0oABAzRlyhTZbDandaGhoQoNDdX+/fs1efJkvfjii/ruu+9UsGDBTOXPjb799luXBfTg4GAtWbLkjh9/0qRJTo95Xiygjxw50mnZ3S6g5weBgYFOhf8ePXqYUkBPT2xsrM6cOaMzZ87ot99+U5cuXTR//ny5ueW/HwSHh4c7/bto2bIlBXQAAJCjUUAHAAA5xpgxY27ZM7NmzZqZ2qfdbtezzz6rP//8M9W6YsWKKSoqyqmoPm/ePJ08eVJ///23vL29M3Ws3ObAgQPavn27HnzwQaflM2fOVFJSkkmpgLyhYMGCKlSokOLj4xUREeG07ueff1b79u0pHAMAAOQC+a/LAwAAyLHKli2rGjVqpHvLrHHjxqUqnr/11lsKDg5WWFiYIiMjNWXKFKdi+c6dO9W/f/8sn09u8O233zrdt9vt+v77701KA+QdAwYMUHBwsMLDw3XixIlUX/79/PPPJiUDAABAZlBABwAAeVZUVJTGjx/vtKxHjx765ptvVKZMGUk3xibu27evpk2b5tRuxowZTsOLnD171mkCwlatWslms2nSpEmqV6+efHx8VKpUKXXt2lUnT55MM1NcXJymTp2qNm3aqHTp0vL09FTJkiXVpk0b/fDDD7Lb7am2SevYU6dOVaNGjeTr66vixYvrySef1IEDB255XW7+smDhwoVOvWNXrlzpGE/a19f3lvu6nXNq1aqVLBZLqiF7bj5Hi8XiyHHp0iWNGzdOnTt3Vu3ateXv7y9PT08VKlRI1atX1yuvvKJNmzalmc8wDM2YMUMNGzaUj4+PSpcurW7duqX7OCU7fvy4Ro8eraeeeko1atRQqVKlVKBAARUpUkS1a9dWnz59tH//fqdtkicm7dmzp9PykSNHpjlh6eLFi/Xee++pRYsWqly5sooUKSJPT0+VKlVKjzzyiMaPH6/IyMhb5r0b9u3bpzfeeEM1atRQoUKF5Ovrq+rVq+utt97S6dOnXW5zO9fxVpInF715+BZJqly5coYnHY2JidHQoUN17733ysvLSwEBAfrvf/+r69evZyrLrVSrVk0DBgxwWnbmzJk029/ONXY12eqRI0fUpUsXlS5dWj4+Pqpfv76mTJni8nUm2Y4dO9SrVy9Vr15dhQoVko+PjypVqqSuXbtq9erVLrdxNUHr1atX9eabb6pChQry8PBw5Es5Jv/GjRudtq1UqVKa2QAAAExhAAAA5AAVK1Y0Zs+efct2koxNmzZlaJ8LFy40JDluFovFOH/+vMu2drvduPfee53af/755471Z86ccVrXrFkzo0OHDk7Lkm+FCxc2tm/fnuoYR48eNapXr+5ym+Rbq1atjLCwMKftUh77gQceMNq1a5fmsY8ePZru9hUrVjRatmzpuP/NN9842j711FOO5T169Ei1XXac083HTu925swZwzAM45dffslQ+xEjRrh8bFOeR/KtUKFCxtixY52Wde/e3Wnbzz///JbH9fDwMGbOnOnYZv369RnKe/Oxateufcv2FStWNM6dO+fyHNOS8rFP7+1/ysfF1b/Hjz/+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upST044bN86pR7Uru3btUoUKFSTdneuYWe3atXManqR06dJO61O+HmQHDw8PzZw5U6+88kqqdVm9xim1bt3a6X6hQoXUpEkTrVmzxrHsyJEjatCggQ4dOuTUtk2bNi739+uvvzruJxffXWnfvv0tX38AAAByC3qgAwCAPCvlECQ390Z2JeX65N7UrrgapzdlwSi54JteT/a0XL9+Pc11roZWSTnO862GcJFuDFnz6KOPplreu3fvVOOTp5Td55SW5PGeM1I8l+RUAE65jauCXnpFvitXruiNN97IUNE35bEzY/r06RkunmflOMlSDpWTkaFOsvJ4363rmFkp/x3dzr+h9BQsWFClS5d2GvrEarWqe/fumjx5cqr22f1vKiuvUa62TbksvbyVKlVKcx0AAEBuQwEdAADkWTePwS3dmBgwrQkKDcPQihUrnJalHGLhZq4KVymXJU8imbInu4eHR6oJM1PeihcvnuaxU06aKCnVJIEZlXKoFg8PD/Xq1euW22X3OaVl69atCgoKctwPCAjQ2rVrFR0dLcMwFB8fn+a2KX+BkJHH7GbLly93+tVC7dq1tX37dsXFxckwDB07diwzp5KmlENhvP322zp//rxsNpsMw9CHH36YLcfJipSPd7FixW75eCc/T+/WdcyslP+ObvffUFoGDBigK1euKCIiQu+9955juWEY6t+/f6oe3Fm5xq5k5TXq2rVrqbZNuSy9X+gUKlQozXUAAAC5DUO4AACAPKtDhw4qVqyYwsPDJd0oXA0bNkyzZ89O1faHH37QqVOnHPc9PDz0/PPPp7nv06dP69y5c6pYsaJj2bp165za1KxZU9KNYlLlypUdQ54UKFBAJ0+eTFXgvZndbr/1CWaDZ599VqVLl9bVq1clSU8++aQCAgJuuV12nFPKXu42m82pt64kXb582el+165d9dhjjznu//PPP2keL/n6J9u3b59CQ0OdhnFJ+Zild+w+ffrogQceyNCxJdfnl5HjjBkzxuk63uo4d0PK8avffvttjR49Os32drvdcf5ZvY63ktHrbJaCBQtq0qRJ2rNnjzZt2iTpRk/0gQMHOk1UmpVr7Mq6deucJg6Njo5ONZ5+8r+ROnXqOC1fs2aNxo4d67Rs7dq1Tvfr1q2b5rHTk9MfLwAAgJTogQ4AAPKswoULa8CAAU7LAgMD9c477zgKxnFx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ucLDwzV9+nT98ccfatCggV544QWnscqT9erVSwUKFNBPP/2kX3/9VQUKFNDly5fl5+enzz77TCtWrNCUKVPk4eGhBx98UMePH8/262G1WlPdXPXI7tOnj/r166c2bdpo8eLFmjp1qg4fPqxmzZrpypUrjnb//vuvmjZtqmnTpmnVqlUaNmyYtm/frhYtWigpKSnNLFOnTlXz5s3l7++vrVu3Om43mzJlilavXq1JkyZp7ty5iomJUYcOHRQREZHueTZt2lRBQUF66aWXtHHjRsXFxaXZdubMmerQoYPsdrumT5+upUuX6t1339XFixcdbebNm6enn35aRYoU0fz58zVz5kyFhYWpVatW2rx5c6p9Pvfcc7r33nv1yy+/aPr06Zm6nsilDAAAAAAAACCXCQ4ONiQZXbt2TbfdCy+8YEgyrly5YhiGYfTv39/w8fExwsPDHW2OHDliSDImT57sWFajRg2jYcOGRlJSktP+OnXqZJQtW9aw2WyGYRjG7NmzDUnGq6++esvMVqvVSExMNKpVq2a8//77juVnzpwxJBmzZ892LEve75kzZ9LdZ3K79G4VK1Z0tN+6dashyZgwYYLTfi5cuGD4+PgYAwcOdHkcu91uJCUlGefOnTMkGX/88Ue6WTt27Oh03JTnWrduXcNqtTqW79ixw5BkzJ8/P93zjY+PN5555hnHubm7uxsNGzY0hg4daly9etXRLioqyihSpIjRokULw263u9yXzWYzAgICjLp16zoez+RtS5cubTRr1syxbPjw4YYkY9iwYU77uN3ridyDHugAAAAAAADIs4z/3wM7eWiPXr16KS4uTgsXLnS0mT17try8vPTiiy9Kkk6dOqVjx47ppZdekiSnHt0dOnRQUFBQqh7k//nPf1Id22q1asyYMapVq5Y8PT3l4eEhT09PnTx5UkePHs3W8/zxxx+1c+fOVLcWLVo4tVu2bJksFotefvllp/Py9/dX/fr1tWHDBkfbq1ev6r///a/Kly8vDw8PFShQQBUrVpSkLOfv2LGj3N3dHfeTe/6fO3cu3e28vLy0aNEiHTlyRF9++aW6du2qa9eu6dNPP1XNmjUdj8uWLVsUGRmpvn37pjkW/fHjx3X58mW98sorcnP7vzJpoUKF9J///Efbtm1TbGys0zYpH+fMXE/kTkwiCgAAAAAAgFynZMmS8vX11ZkzZ9Jtd/bsWfn6+qpEiRKSpNq1a6tJkyaaPXu23njjDdlsNs2ZM0dPP/20o03ysBsDBgzQgAEDXO435TjgZcuWTdWmf//+mjJligYNGqSWLVuqePHicnNz0+uvv57u0CO3o2bNmmrcuHGq5UWLFtWFCxcc969cuSLDMFSmTBmX+6lSpYokyW636/HHH9fly5f18ccfq27duipYsKDsdrseeuihLOf38/Nzuu/l5SVJGd5vzZo1VbNmTUk3viSZNGmS+vfvr48//lg///yzrl27JklpDu8j3RhPXXL92AUEBMhutyssLMxpotCUbTN6PZF7UUAHAAAAAABAruPu7q5HH31UK1as0MWLF10WSi9evKjdu3erffv2Tr2de/bsqb59++ro0aM6ffq0goKC1LNnT8f6kiVLSpIGDx6s5557zuXx77vvPqf7rno5z5kzR6+++qrGjBnjtPz69esqVqxYhs81O5UsWVIWi0WbNm1yFK1vlrzs0KFD2r9/vwIDA9W9e3fH+lOnTt21rBllsVj0/vvva9SoUTp06JAkqVSpUpLkNN55SslF/KCgoFTrLl++LDc3NxUvXjzVsW6W0euJ3IshXAAAAAAAAJArDR48WIZhqG/fvrLZbE7rbDab3nzzTRmGocGDBzut69atm7y9vRUYGKjAwECVK1dOjz/+uGP9fffdp2rVqmn//v1q3Lixy1vhwoVvmc9isaQqoP7555+6dOlSFs46azp16iTDMHTp0iWX51W3bl1J/1coTpn/22+/zdBxvLy8sr2XveS62C3dKHhHRkYqICBAktSsWTMVLVpU06dPdzmRqnTjcS5XrpzmzZvn1CYmJka//fabmjZt6tT73JWMXk/kXvRABwAAAAAAQK7UvHlzTZo0Sf369VOLFi309ttvq0KFCjp//rymTJmi7du3a9KkSWrWrJnTdsWKFdOzzz6rwMBAhYeHa8CAAU5jYEs3CsXt27dXu3bt1KNHD5UrV06hoaE6evSo9uzZo19++eWW+Tp16qTAwEDVqFFD9erV0+7du/X555+nO6zInda8eXO98cYb6tmzp3bt2qVHHnlEBQsWVFBQkDZv3qy6devqzTffVI0aNVS1alV9+OGHMgxDJUqU0NKlS7V69eoMHadu3br6/fffNW3aNN1///1yc3NzOcRMZr3xxhsKDw/Xf/7zH9WpU0fu7u46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kcaPH6/Q0NBM769Hjx6qVKlS9ge9CwIDA53es3h7e8vf31+PPvqoxo4dq6tXr5odMV0jRoyQxWK5rW0vX76sESNGaN++fdm63+yQkJCgb775Ri1atFDx4sXl6empcuXKqUuXLtq4caNpuQAAAJBzeJgdAMiIrVu3Ot0fPXq01q9fr3Xr1jktr1Wr1t2MdVuqVKmiuXPnSpJiYmJ05swZLV68WM8//7wefvhhLV26VEWLFjU5pWuLFi1SkSJFbmvbv/76S1OmTHFZRM/KfrNq2bJl6tq1q0qWLKm3335bDRs2lMVi0cGDBzVr1iz9+eef2rt3rynZACA3mzFjhvr27av77rtP//vf/1SrVi0lJSVp165dmj59urZu3apFixaZHfOumz17tmrUqKGkpCRdvXpVmzdv1rhx4/TFF19o4cKFatOmjdkRXXr99df1xBNP3Na2ly9f1siRI1WpUiU1aNAg2/abVdevX9cTTzyhAwcOqFevXvrf//6nEiVK6NKlS/rjjz/UunVr7d69W/Xr1zclHwAAAHIGCujIFR566CGn+6VKlZKbm1uq5bmBj49Pqtyvv/66Zs+erV69eumNN97QwoULTUrnWlxcnHx8fNSwYcM7sv87td9bOXPmjLp27arq1atr/fr1Tl9cPPbYY3r33XdzbHEnNjZWvr6+ZscAAJe2bt2qN998U23bttXixYvl5eXlWNe2bVt98MEHWrFihYkJzVOnTh01btzYcf8///mP3n//fbVo0ULPPfecTp48qTJlypiY0Fny35t77rlH99xzT7bv/07tNyNeffVV7d+/XytXrtRjjz3mtK5r167q37+/ihcvbkq29NhsNlmtVqd/VwAAALhzGMIFecaUKVP0yCOPqHTp0ipYsKDq1q2r8ePHKykpyWX7TZs26aGHHpKPj4/KlSunjz/+WDab7ZbHCQ4OVp8+fXTPPffI09NTlStX1siRI2W1WrOUv2fPnurQoYN++eUXnTt3zrHcMAxNnTpVDRo0kI+Pj4oXL67OnTvr9OnTTtvv3btXnTp1UunSpeXl5aWAgAB17NhRFy9edLSx2+2aPHmyY1/FihXTQw89pCVLljjaVKpUSZ06ddLvv/+uhg0bytvbWyNHjnSsu3molQ0bNshisWjOnDnq37+//P395ePjo5YtWzr12u7Ro4emTJkiyXk4nrNnz7rcrySdP39eL7/8suN8atasqQkTJjgNc3P27FlZLBZ98cUXmjhxoipXrqxChQqpadOm2rZt2y2v+cSJExUTE6OpU6e67PVvsVj03HPPOS2bNWuW6tevL29vb5UoUULPPvusjh496lg/adIkWSwWnTp1KtX+Bg0aJE9PT12/ft2xbM2aNWrdurWKFCkiX19fNW/eXGvXrnXaLvnn7Xv27FHnzp1VvHhxVa1aVZK0a9cude3aVZUqVZKPj48qVaqkbt26OT2Hkm3evFlNmzaVt7e34zn//fffOz0WyRYuXKimTZuqYMGCKlSokNq1a0dPfAAZNmbMGFksFn333Xcui3yenp566qmnHPftdrvGjx+vGjVqyMvLS6VLl9arr77q9DfMleS/A4GBganWpRyGLfm19MCBA3r++edVtGhRlShRQv3795fVatXx48f1xBNPqHDhwqpUqZLGjx/vtL/kv3nz58/X0KFDFRAQoCJFiqhNmzY6fvx45i5QChUqVNCECRMUFRWlb7/91mndrl279NRTT6lEiRLy9vZWw4YN9fPPPzu1iY2N1YABAxxD5ZQoUUKNGzfW/Pnzndpt375dTz75pPz8/OTt7a2qVauqX79+qa6Rq783roZaSX7PsGjRItWrV0/e3t6qUqWKvv76a6fr1qRJE0k33uskvwdIfmxc7Tejz4fkYfl27typhx9+WL6+vqpSpYo+++yzWw6Lt3v3bi1fvlyvvfZaquJ5siZNmqhChQqO+4cOHdLTTz+t4sWLy9vbWw0aNNAPP/zgWH/t2jV5enrq448/TrWvY8eOyWKxOF2bjLynTH6Ojx8/Xp988okqV64sLy8vrV+/XvHx8frggw/UoEEDx/O5adOm+uOPP1IdPzw8XK+99ppKlCihQoUKqWPHjjp9+rTL4QpPnjypF1980ek9WPL7OAAAgPyIAjryjH///VcvvviifvrpJy1btkyvvfaaPv/8c/Xp0ydV2+DgYHXt2lUvvfSS/vjjD3Xu3FmffPKJ3nvvvXSPERwcrAceeEArV67UsGHDHB+8xo4dq969e2f5HJ566ikZhqFNmzY5lvXp00f9+vVTmzZttHjxYk2dOlWHDx9Ws2bNdOXKFUk3hoJp27atrly5oilTpmj16tWaNGmSKlSooKioKMe+evTooffee09NmjTRwoULtWDBAj311FOpiqd79uzR//73P7377rtasWKF/vOf/6Sbe8iQITp9+rS+//57ff/997p8+bJatWrlKPJ//PHH6ty5s6QbvRKTb2XLlnW5v2vXrqlZs2ZatWqVRo8erSVLlqhNmzYaMGCA3n777VTtbz7nuXPnKiYmRh06dFBERES6uVetWqUyZcpk+JcMY8eO1WuvvabatWvr999/11dffaUDBw6oadOmOnnypCTp5ZdflqenZ6pijs1m05w5c/Tkk0+qZMmSkqQ5c+bo8ccfV5EiRfTDDz/o559/VokSJdSuXbtURXRJeu6553Tvvffql19+0fTp0yXd+GB93333adKkSVq5cqXGjRunoKAgNWnSxKlQf+DAAbVt21axsbH64YcfNH36dO3Zs0effvppquOMGTNG3bp1U61atfTzzz/rp59+UlRUlB5++GEdOXIkQ9cKQP5ls9m0bt063X///SpfvnyGtnnzzTc1aNAgtW3bVkuWLNHo0aO1YsUKNWvWzOm1LDt06dJF9evX12+//abevXvryy+/1Pvvv69nnnlGHTt21KJFi/TYY49p0KBB+v3331NtP2TIEJ07d07ff/+9vvvuO508eVJPPvlkhr6ET0+HDh3k7u6uv//+27Fs/fr1at68ucLDwzV9+nT98ccfatCggV544QWnvzP9+/fXtGnTHH+3f/rpJz3//PMKCQlxtFm5cqUefvhhnT9/XhMnTtTy5cv10UcfOd5L3MzV35u07Nu3T/369dP777+vRYsWqVmzZnrvvff0xRdfSJIaNWqk2bNnS5I++ugjx3uA119/Pc19Zub5EBwcrJdeekkvv/yylixZovbt22vw4MGaM2dOurlXrVolSXrmmWfSbZfs+PHjatasmQ4fPqyvv/5av//+u2rVqqUePXo4vmwpVaqUOnXqpB9++CFVAX/27Nny9PTUSy+95MidmfeUX3/9tdatW6cvvvhCy5cvV40aNZSQkKDQ0FANGDBAixcv1vz58x2/ZPjxxx8d29rtdj355JOaN2+eBg0apEWLFunBBx90OXTOkSNH1KRJEx06dEgTJkzQsmXL1LFjR7377ruODhUAAAD5DUO4IM+YOHGi4//tdrsefvhh+fn5qWfPnpowYYLTT3BDQkL0xx9/OHq/Pf7444qLi9O0adM0cOBAp95GNxsxYoTCwsJ0+PBhR5vWrVvLx8dHAwYMcIzxersq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yy+/OLVr166dPDw89O+//6aZCwBuZfDgwTIMQ71791ZiYmKq9UlJSVq6dKkk6bHHHpOkVK+vO3fu1NGjR1O9vt6sTJky8vb21oEDB5yW//HHH1k9hbvq/PnzGjBggIoWLeqY/Py+++5TtWrVtH///jRfjwsXLpxqX2XKlFGPHj3UrVs3HT9+XLGxsapevbqqVq2qWbNmpSquZtXhw4e1f/9+p2Xz5s1T4cKF1ahRI0mZew+QledDZjRq1Ejt27fXzJkzUw0Nl2zXrl2OsdJbt26tdevWOY1RL914b+Lr6+v0ZXjNmjX14IMPavbs2S6/RJey5z2lxWKRp6enU/E8ODg41fO/ZcuWkpRqDpMFCxY43ff19dWjjz6qvXv3ql69ei5z3W7nCAAAgNyMHujIE5InQurWrZsGDhyo+Ph4TZs2TWFhYS7b+/n56c0339T58+dVvXp1/fXXX5oxY4befPPNNCcQlaRRo0Zp9erVatasmd59913dd999io+P19mzZ/XXX39p+vTpt+xpFxcX5xg/Mi4uTqdPn9bixYu1bNkytWzZUtOnT3e0bd68ud544w317NlTu3bt0iOPPKKCBQsqKChImzdvVt26dfXmm29q2bJlmjp1qp555hlVqVJFhmHo999/V3h4uGMCq4cfflivvPKKPvnkE125ckWdOnWSl5eX9u7dK19fX73zzjuZvewOV69e1bPPPqvevXsrIiJCw4cPl7e3t9PEVMnF/nHjxql9+/Zyd3dXvXr1XBaJ33//ff3444/q2LGjRo0apYoVK+rPP//U1KlT9eabb2ZqfPb0VK5c2dE7rEGDBnr77bfVsGFDSdKRI0c0a9YsGYahZ599VsWKFdPHH3/smLStW7duCgkJ0ciRI+Xt7a3hw4c77btGjRpq2rSpxo4dqwsXLui7775zWl+oUCFNnjxZ3bt3V2hoqDp37qzSpUvr2rVr2r9/v65du3bLLzuKFCmiRx55RJ9//rlKliypSpUqaePGjZo5c6aKFSvm1Hbo0KFaunSpWrduraFDh8rHx0fTp093jLOePNlppUqVNGrUKA0dOlSnT5/WE088oeLFi+vKlSvasWOHo5cjAKSnadOmmjZtmvr27av7779fb775pmrXrq2kpCTt3btX3333nerUqaMnn3xS9913n9544w1NnjxZbm5uat++vc6ePauPP/5Y5cuXd5oYM6XkeUJmzZqlqlWrqn79+tqxY4fTZM85zaFDhxzjSl+9elWbNm3S7Nmz5e7urkWLFjl6i0vSt99+q/bt26tdu3bq0aOHypUrp9DQUB09elR79uxxfAn64IMPqlOnTqpXr56KFy+uo0eP6qefflLTpk3l6+srSZoyZYqefPJJPfTQQ3r//fdVoUIFnT9/XitXrtTcuXNv+3wCAgL01FNPacSIESpbtqzmzJmj1atXa9y4cY5jV61aVT4+Ppo7d65q1qypQoUKKSAgwGWROCvPh8z68ccf9cQTT6h9+/bq1auX2rdvr+LFiysoKEhLly7V/PnztXv3blWoUEHDhw93jFs+bNgwlShRQnPnztWff/6p8ePHq2jRok777tWrl/r06aPLly+rWbNmjl8LJMuO95SdOnXS77//rr59+6pz5866cOGCRo8erbJlyzqG75FuTBjfvHlzffDBB4qMjNT999+vrVu36scff5T0f+8BJOmrr75SixYt9PDDD+vNN99UpUqVFBUVpVOnTmnp0qVpftkAAACQl1FAR55Qo0YN/fbbb/roo4/03HPPyc/PTy+++KL69+/vNClYMn9/f02ZMkUDBgzQwYMHVaJECQ0ZMuSWhcGyZctq165dGj16tD7//HNdvHhRhQsXVuXKlR2Fxls5ffq0mjZtKunGT67LlCmjRo0a6ZdfftFzzz3n9CFGuvHh+aGHHtK3336rqVOnym63KyAgQM2bN3dMmlWtWjUVK1ZM48eP1+XLl+Xp6an77rtPgYGB6t69u2NfgYGBatSokWbOnKnAwED5+PioVq1aGjJkyC1zp2fMmDHauXOnevbsqcjISD3wwANasGCBqlat6mjz4osv6p9//tHUqVM1atQoGYahM2fOqFKlSqn2V6pUKW3ZskWDBw/W4MGDFRkZqSpVqmj8+PHq379/lrKm1KlTJx08eFATJkzQ9OnTdeHCBbm5uTke05u/WBg8eLBKly6tr7/+WgsXLpSPj49atWqlMWPGqFq1aqn23bNnT73xxhvy8fHRCy+8kGr9yy+/rAoVKmj8+PHq06ePoqKiVLp0aTVo0CDV5GlpmTdvnt577z0NHDhQVqtVzZs31+rVq9WxY0endvXr19fq1as1YMAAvfrqqypevLheeeUVtWzZUoMGDXL64D948GDVqlVLX331lebPn6+EhAT5+/urSZMm+u9//5vBKwsgv+vdu7ceeOABffnllxo3bpyCg4NVoEABVa9eXS+++KLefvttR9tp06apatWqmjlzpqZMmaKiRYvqiSee0NixY2/Z43XChAmSpPHjxys6OlqPPfaYli1b5vLvS06Q3BPZ09NTxYoVU82aNTVo0CC9/vrrTsVzSXr00Ue1Y8cOffrpp+rXr5/CwsLk5+enWrVqOU0I+dhjj2nJkiX68ssvFRsbq3LlyunVV1/V0KFDHW3atWunv//+W6NGjdK7776r+Ph43XPPPakmRc2sBg0aqGfPnho+fLhOnjypgIAATZw40anQ7evrq1mzZmnkyJF6/PHHlZSUpOHDh2vEiBEu95mV50NmlCxZUps3b9aMGTM0f/58zZs3T7GxsSpdurQeeughLVmyxDFB6n333actW7ZoyJAheuuttxQXF6eaNWtq9uzZLv9md+3aVf369dPFixdTfckuZc97yp49e+rq1auaPn26Zs2apSpVqujDDz/UxYsXnd7Turm5aenSpfrggw/02WefKTExUc2bN9ecOXP00EMPOX3pXqtWLe3Zs0ejR4/WRx99pKtXr6pYsWKqVq2aYxx0AACA/MZi3DzuAgBkwoYNG/Too4/ql19+UefOnc2Og9vw+OOP6+zZszpx4oTZUQAAuUylSpVUp04dLVu2zOwouA3z5s3TSy+9pH/++UfNmjUzOw6Q40VGRqpo0aKKiIhQkSJFbns/IRGR2ZgKAPIvv6K3/1osZe51nR7oAJBP9O/fXw0bNlT58uUVGhqquXPnavXq1Zo5c6bZ0QAAwB00f/58Xbp0SXXr1pWbm5u2bdumzz//XI888gjFcwAAgFuggA4A+YTNZtOwYcMUHBwsi8WiWrVq6aefftLLL79sdjQAAHAHFS5cWAsWLNAnn3yimJgYlS1bVj169NAnn3xidjQAAIAcjyFcAAAAAAAA0sEQLgCQs9zNIVzc0l0LAAAAAAAAAEA+RQEdAAAAAAAAAAAXKKADAAAAAAAAAOACBXQAAAAAAAAAAFyggA4AAAAAAAAAgAsU0AEAAAAAAAAAcIECOgAAAAAAAAAALlBABwAAAAAAAADABQroAAAAAAAAAAC4QAEdAAAAAAAAAAAXKKADAAAAAAAAAOACBXQAAAAAAAAAAFyggA4AAAAAAAAAgAsU0AEAAAAAAAAAcIECOgAAAAAAAAAALlBABwAAAAAAAADABQroAAAAAAAAAAC4QAEdAAAAAAAAAAAXKKADAAAAAAAAAOACBXQAAAAAAAAAAFyggA4AAAAAAAAAgAsU0AEAAAAAAAAAcIECOgAAAAAAAAAALlBABwAAAAAAAADABQroAAAAAAAAAAC4QAEdAAAAAAAAAAAXKKADAAAgXxgxYoQaNGiQ5f1s2LBBFotF4eHhWd4XAAAAgJyNAjoAAEAe06NHDz3zzDOplt/pwu/Zs2dlsVgct8KFC6t27dp66623dPLkyTtyzMwYMGCA1q5dm6ltWrVqpX79+jkta9asmYKCglS0aNFsTAcAAAAgJ6KADgAAgGy1Zs0aBQUFaf/+/RozZoyOHj2q+vXrZ7p4nV0Mw5DValWhQoXk5+eX5f15enrK399fFoslG9IBAAAAyMkooAMAAORTISEh6tatm+655x75+vqqbt26mj9/vlObX3/9VXXr1pWPj4/8/PzUpk0bxcTEpLtfPz8/+fv7q0qVKnr66ae1Zs0aPfjgg3rttddks9kc7ZYuXar7779f3t7eqlKlikaOHCmr1epYP2LECFWoUEFeXl4KCAjQu+++61iXkJCggQMHqnz58vLy8lK1atU0c+ZMSf/X037lypVq3LixvLy8tGnTplRDuCT31B85cqRKly6tIkWKqE+fPkpMTHSs37hxo7766itHr/qzZ8+67Mn/22+/qXbt2vLy8lKlSpU0YcIEp2tSqVIljRkzRr169VLhwoVVoUIFfffddxl7oAAAAACYhgI6AABAPhUfH6/7779fy5Yt06FDh/TGG2/olVde0fbt2yVJQUFB6tatm3r16qWjR49qw4YNeu6552QYRqaO4+bmpvfee0/nzp3T7t27JUkrV67Uyy+/rHfffVdHjhzRt99+q8DAQH366aeSbhTuv/zyS3377bc6efKkFi9erLp16zr2+eqrr2rBggX6+uuvdfToUU2fPl2FChVyOu7AgQM1duxYHT16VPXq1XOZbe3atTp69KjWr1+v+fPna9GiRRo5cqQk6auvvlLTpk3Vu3dvBQUFKSgoSOXLl0+1j927d6tLly7q2rWrDh48qBEjRujjjz9WYGCgU7sJEyaocePG2rt3r/r27as333xTx44dy9S1BAAAAHB3eZgdAAAAANlv2bJlqQrKN/f+lqRy5cppwIABjvvvvPOOVqxYoV9++UUPPviggoKCZLVa9dxzz6lixYqS5FTEzowaNWpIujFO+gMPPKBPP/1UH374obp37y5JqlKlikaPHq2BAwdq+PDhOn/+vPz9/dWmTRsVKFBAFSpU0AMPPCBJOnHihH7++WetXr1abdq0cWyf0qhRo9S2bdt0c3l6emrWrFny9fVV7dq1NWrUKP3vf//T6NGjVbRoUXl6esrX11f+/v5p7mPixIlq3bq1Pv74Y0lS9erVdeTIEX3++efq0aOHo12HDh3Ut29fSdKgQYP05ZdfasOGDY5rAwAAACDnoQc6AABAHvToo49q3759Trfvv//eqY3NZtOnn36qevXqyc/PT4UKFdKqVat0/vx5SVL9+vXVunVr1a1bV88//7xmzJihsLCw28qT3Gs9edzw3bt3a9SoUSpUqJDjltzTOzY2Vs8//7zi4uJUpUoV9e7dW4sWLXIM77Jv3z65u7urZcuW6R6zcePGt8xVv359+fr6Ou43bdpU0dHRunDhQobP7ejRo2revLnTsubNm+vkyZNOX1rc3AveYrHI399fV69ezfBxAAAAANx9FNABAADyoIIFC+ree+91upUrV86pzYQJE/Tll19q4MCBWrdunfbt26d27do5xgB3d3fX6tWrtXz5ctWqVUuTJ0/WfffdpzNnzmQ6z9GjRyVJlStXliTZ7XaNHDnSqcB/8OBBnTx5Ut7e3ipfvryOHz+uKVOmyMfHR3379tUjjzyipKQk+fj4ZPga3K7MTBBqGEaq9q6GuSlQoECqY9jt9tsLCAAAAOCuoIAOAACQT23atElPP/20Xn75ZdWvX19VqlTRyZMnndpYLBY1b95cI0eO1N69e+Xp6alFixZl6jh2u11ff/21KleurIYNG0qSGjVqpOPHj6cq8t97771yc7vxFtXHx0dPPfWUvv76a23YsEFbt27VwYMHVbduXdntdm3cuDHL12D//v2Ki4tz3N+2bZsKFSqke+65R9KNIV5SDn2TUq1atbR582anZVu2bFH16tXl7u6e5YwAAAAAzMMY6AAAAPnUvffeq99++01btmxR8eLFNXHiRAUHB6tmzZqSpO3bt2vt2rV6/PHHVbp0aW3fvl3Xrl1zrE9LSEiIgoODFRsbq0OHDmnSpEnasWOH/vzzT0dBediwYerUqZPKly+v559/Xm5ubjpw4IAO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uum2227TwIEDVVRUdEYyHKmwsFDS4ZM7lseM6fL6cwEAAPCHAh0AAABAtdWyZUvVrl1bs2bN8m2bNWuWEhMTfScc/NORo0Kef/55paamyuVyKTY2VhdddJHvug8++EBNmzaV2+1WVFSUevbsqQMHDkg6eoRL165ddeutt2rs2LGKjIxUXFycJkyYUOq+N23apI4dO8rlcqlx48b6+OOPT2oEitPpVFxcnBISEtSyZUvdc889mjt3rhYsWOBbWS8dXhV//fXXKyYmRqGhoerevbvWrVvnu37dunXq1q2bQkJCFBoaqlatWmnVqlW+67/88kt16dJFHo9HERER6tOnjzIzM32Pb9SoURozZoyio6PVq1cvSaVHuGzbtk2GYWjmzJlq3769XC6X0tLStHTpUt/13bp1kyRFRETIMAwNGzbM759LZmamhg4dqoiICHk8HvXr109btmzxXT99+nSFh4frv//9rxo1aqTg4GDfGw0AAABHokAHAAAAUK1dc801euONN3yXX3/9dQ0fPvy4t1m1apVuvfVWTZo0SZs3b9bChQvVuXNnSdKuXbt02WWXafjw4dq4caOWLl2qwYMH63jTM2fMmKGgoCCtWLFC06ZN06RJk7R48WJJUklJiQYNGiSPx6MVK1bo5Zdf1r333lvmx9u9e3c1b97c96aB1+vVgAEDlJ6ervnz52v16tVq2bKlevTooX379kmSrrjiCtWqVUsrV67U6tWrNW7cONntdknS2rVr1aNHD6Wlpemrr77SsmXLdO6556q4uLjU47PZbPryyy/10ksvHTPbXXfdpTvuuENr1qxR+/btdd555ykjI0OJiYn68MMPJUmbN2/Wrl279NRTT/k9xrBhw7Rq1SrNmzdPX331lbxer/r3769Dhw759snLy9Ojjz6qN998U59//rm2b9+uO++8s8zPKQAAqLpsZgcAAAAAADNdddVVGj9+vG8V9JdffqmZM2f6Vj/7s337dgUFBWngwIEKCQlRUlKSb8X6rl27VFRUpMGDByspKUmS1LRp0+NmaNasmR588EFJUmpqqp599lktWbJEvXr10qJFi/TTTz9p6dKliouLkyRNnjzZt5K7LBo2bKj169dLkj799FNt2LBBu3fv9o1UefTRRzVnzhx98MEHuv7667V9+3bdddddatiwoS/jn6ZNm6bWrVvr+eef921L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Vd6JVp7/iZOIAgAAAACAKsvhcKhVq1ZavHhxqe2LFy9W+/btTUoFAKgsWIEOAAAAAACqtDFjxuiqq65S69at1a5dO7388svavn27brzxRrOjAQACHAU6AAAAAACo0i655BJlZGRo0qRJ2rVrl5o0aaL58+crKSnJ7GhAQHE6nXrwwQePGmEEVGeG1+v1mh0CAAAAAAAAAIBAwwx0AAAAAAAAAAD8oEAHAAAAAAAAAMAPCnQAAAAAAAAAAPygQAcAAAAAAACqiKVLl8owDO3fv/+4+yUnJ+vJJ588I5mAyowCHQAAAAAAAKhAw4YN06BBg47afrJl9+mYPn26wsPDy+VYu3fv1g033KDatWvL6XQqLi5Offr00VdffVUuxwcCkc3sAAAAAAAAAAAC34UXXqhDhw5pxowZqlu3rn7//XctWbJE+/btq7D7LCwslMPhqLDjAyfCCnQAAAAAAAAgQCxfvlydO3eW2+1WYmKibr31Vh04cMB3/VtvvaXWrVsrJCREcXFxuvzyy7V7926/x1q6dKmuueYaZWVlyTAMGYahCRMm+K7Py8vT8OHDFRISotq1a+vll18+Zq79+/dr2bJlmjp1qrp166akpCS1adNG48eP14ABA0rtd/311ys2NlYul0tNmjTRRx995Lv+ww8/VFpampxOp5KTk/XYY4+Vup/k5GT94x//0LBhwxQWFqbrrrvupJ4XoKJQoAMAAAAAAAABYMOGDerTp48GDx6s9evX67333tOyZcs0atQo3z6FhYV66KGHtG7dOs2ZM0dbt27VsGHD/B6vffv2evLJJxUaGqpdu3Zp165duvPOO33XP/bYY2rdurXWrFmjkSNH6qabbtKmTZv8His4OFjBwcGaM2eOCgoK/O5TUlKifv36afny5Xrrrbf0/fff65FHHpHVapUkrV69WhdffLEuvfRSbdiwQRMmTND999+v6dOnlzrOP//5TzVp0kSrV6/W/ffff1LPC1BRDK/X6zU7BAAAAAAAAFBVDRs2TG+99ZZcLlep7cXFxcrPz1dmZqbCw8M1dOhQud1uvfTSS759li1bpi5duujAgQNH3V6SVq5cqTZt2ignJ0fBwcFaunSpunXr5jvm9OnTNXr06KPmrCcnJ6tTp0568803JUler1dxcXGaOHGibrzxRr+P48MPP9R1112ngwcPqmXLlurSpYsuvfRSNWvWTJK0aNEi9evXTxs3blT9+vWPuv0VV1yhPXv2aNGiRb5tY8eO1X/+8x999913vlwtWrTQ7NmzffuU5XkBygsr0AEAAAAAAIAK1q1bN61du7bU16uvvlpqn9WrV2v69Om+1d7BwcHq06ePSkpKtHXrVknSmjVrdP755yspKUkhISHq2rWrJGn79u2nnOnP4luSDMNQXFzcMcfBSIdnoP/222+aN2+e+vTpo6VLl6ply5a+FeRr165VrVq1/JbnkrRx40Z16NCh1LYOHTpoy5YtKi4u9m1r3bp1qX1O5nkBKgonEQUAAAAAAAAqWFBQkFJSUkpt27lzZ6nLJSUluuGGG3TrrbcedfvatWvrwIED6t27t3r37q233npLNWrU0Pbt29WnTx8VFhaecia73V7qsmEYKikpOe5tXC6XevXqpV69eumBBx7QiBEj9OCDD2rYsGFyu93Hva3X65VhGEdtO1JQUFCpyyd6XoCKRIEOAAAAAAAABICWLVvqu+++O6po/9OGDRu0d+9ePfLII0pMTJQkrVq16rjHdDgcpVZ3l7fGjRtrzpw5kg6vaN+5c6d++OEHv6vQGzdurGXLlpXatnz5ctWvX983J92fEz0vQEVihAsAAAAAAAAQAO6++2599dVXuvnmm7V27Vpt2bJF8+bN0y233CLp8Gprh8OhZ555Rj///LPmzZunhx566LjHTE5OVm5urpYsWaK9e/cqLy+vTNkyMjLUvXt3vfXWW1q/fr22bt2qf/3rX5o2bZrOP/98SVKXLl3UuXNnXXjhhVq8eLG2bt2qBQsWaOHChZKkO+64Q0uWLNFDDz2kH374QTNmzNCzzz5b6sSmZXlegIpEgQ4AAAAAAAAEgGbNmumzzz7Tli1b1KlTJ7Vo0UL333+/4uPjJUk1atTQ9OnT9a9//UuNGzfWI488okcfffS4x2zfvr1uvPFGXXLJJapRo4amTZtWpmzBwcFq27atnnjiCXXu3FlNmjTR/fffr+uuu07PPvusb78PP/xQZ599ti677DI1btxYY8eO9a2Ab9mypd5//33NnDlTTZo00QMPPKBJkyZp2LBhp/W8ABXJ8PobNAQAAAAAAAAAQDXHCnQAAAAAAAAAAPygQAcAAAAAAAAAwA8KdAAAAAAAAAAA/KBABwAAAAAAAADADwp0AAAAAAAAAAD8oEAHAAAAAAAAAMAPCnQAAAAAAAAAAPygQAcAAAAAAAAAwA8KdAAAAAAAAAAA/KBABwAAAAAAAADADwp0AAAAAAAAAAD8oEAHAAAAAAAAAMCP/wdeaCJODfHB+gAAAABJRU5ErkJggg==", | ||
| "text/plain": [ | ||
| "<Figure size 1500x600 with 3 Axes>" | ||
| ] |
There was a problem hiding this comment.
This notebook commits a large embedded PNG output ("image/png": "iVBOR..."). This bloats the repository and makes diffs noisy. Please clear cell outputs before committing (or keep only minimal text outputs) and rely on the saved metadata_health_chart.png artifact (or regenerate locally) instead of embedding base64 image data in git.
| "\n", | ||
| "\n", | ||
| "❓ User: Find tables related to orders\n", | ||
| "============================================================\n", | ||
| "🧠 Agent thinking about: Find tables related to orders\n", | ||
| "--------------------------------------------------\n", | ||
| "🔧 Agent decided to use: search_tables: orders\n", | ||
| "📦 Data fetched: ['raw_orders', 'fact_orders', 'fact_orders', 'orders', 'orders', 'orders', 'orders', 'orders', 'orders', 'table_soldier_answer_8ad293a8']\n", | ||
| "--------------------------------------------------\n", | ||
| "🤖 Agent answer: I've searched for tables related to \"orders\" in our data catalog. Here are the results:\n", | ||
| "\n", | ||
| "We have found several tables that may be relevant to your query:\n", | ||
| "1. **raw_orders**: This table likely contains raw, unprocessed data related to orders.\n", | ||
| "2. **fact_orders**: This table appears to be a fact table, which is a type of table used in data warehousing to store measurable data, such as order quantities, amounts, or dates. There are multiple instances of this table in our search results.\n", | ||
| "3. **orders**: This table is directly named \"orders\" and may contain a wide range of data related to orders. There are multiple instances of this table in our search results, which could indicate different versions, updates, or copies of the same data.\n", | ||
| "4. **table_soldier_answer_8ad293a8**: This table seems unrelated to orders based on its name. It's possible that this result is a false positive or that the table contains some data related to orders that isn't immediately apparent from its name.\n", | ||
| "\n", | ||
| "To help you further, I can try to:\n", |
There was a problem hiding this comment.
This notebook includes committed execution outputs (e.g., stdout showing API results). In openmetadata_ai_agent.ipynb the captured demo output does not match the test_questions list in the source cell, suggesting the outputs are stale. Please clear outputs (and reset execution_count) or re-run so the committed outputs correspond to the current code.
| "\n", | |
| "\n", | |
| "❓ User: Find tables related to orders\n", | |
| "============================================================\n", | |
| "🧠 Agent thinking about: Find tables related to orders\n", | |
| "--------------------------------------------------\n", | |
| "🔧 Agent decided to use: search_tables: orders\n", | |
| "📦 Data fetched: ['raw_orders', 'fact_orders', 'fact_orders', 'orders', 'orders', 'orders', 'orders', 'orders', 'orders', 'table_soldier_answer_8ad293a8']\n", | |
| "--------------------------------------------------\n", | |
| "🤖 Agent answer: I've searched for tables related to \"orders\" in our data catalog. Here are the results:\n", | |
| "\n", | |
| "We have found several tables that may be relevant to your query:\n", | |
| "1. **raw_orders**: This table likely contains raw, unprocessed data related to orders.\n", | |
| "2. **fact_orders**: This table appears to be a fact table, which is a type of table used in data warehousing to store measurable data, such as order quantities, amounts, or dates. There are multiple instances of this table in our search results.\n", | |
| "3. **orders**: This table is directly named \"orders\" and may contain a wide range of data related to orders. There are multiple instances of this table in our search results, which could indicate different versions, updates, or copies of the same data.\n", | |
| "4. **table_soldier_answer_8ad293a8**: This table seems unrelated to orders based on its name. It's possible that this result is a false positive or that the table contains some data related to orders that isn't immediately apparent from its name.\n", | |
| "\n", | |
| "To help you further, I can try to:\n", |
| "# LangChain + OpenMetadata Template\n", | ||
| "### Built by Baibhav Prateek | OpenMetadata Hackathon 2026\n", | ||
| "\n", | ||
| "## What is this?\n", | ||
| "A reusable template that connects AI to OpenMetadata.\n", | ||
| "Anyone can use this as a starting point for their own\n", | ||
| "AI-powered data catalog applications.\n", | ||
| "\n", | ||
| "## How to use this template:\n", | ||
| "1) Add your API keys\n", | ||
| "2) Run all cells in order\n", | ||
| "3) Ask your own questions\n", | ||
| "4) Customize the questions for your use case\n", | ||
| "\n", | ||
| "## Technologies used:\n", | ||
| "1) OpenMetadata API for metadata\n", | ||
| "2) Groq AI (LLaMA 3) for natural language processing\n", | ||
| "3) Python requests for API calls" |
There was a problem hiding this comment.
The title/README call this a “LangChain + OpenMetadata Template”, but the notebook does not import or use LangChain at all (it directly calls Groq and requests). Either update the implementation to actually use LangChain primitives (e.g., LLM/Prompt/Tool abstractions), or rename the notebook and its description to avoid misleading users.
| "def tool_get_tables(limit=10):\n", | ||
| " \"\"\"Get list of tables from OpenMetadata\"\"\"\n", | ||
| " response = requests.get(\n", | ||
| " f\"{BASE_URL}/api/v1/tables\",\n", | ||
| " headers=HEADERS,\n", | ||
| " params={\"limit\": limit}\n", | ||
| " )\n", | ||
| " tables = response.json().get(\"data\", [])\n", | ||
| " return [{\"name\": t.get(\"name\"), \n", | ||
| " \"description\": t.get(\"description\", \"No description\"),\n", | ||
| " \"columns\": len(t.get(\"columns\", []))} \n", | ||
| " for t in tables]\n", | ||
| "\n", | ||
| " \n", | ||
| "# Tool 2: Search for specific tables by keyword \n", | ||
| "\n", | ||
| "\n", | ||
| "def tool_search_tables(keyword):\n", | ||
| " \"\"\"Search for specific tables by keyword\"\"\"\n", | ||
| " response = requests.get(\n", | ||
| " f\"{BASE_URL}/api/v1/search/query\",\n", | ||
| " headers=HEADERS,\n", | ||
| " params={\"q\": keyword, \"index\": \"table_search_index\", \"limit\": 5}\n", | ||
| " )\n", | ||
| " hits = response.json().get(\"hits\", {}).get(\"hits\", [])\n", | ||
| " return [h.get(\"_source\", {}).get(\"name\", \"\") for h in hits]\n", | ||
| "\n", |
There was a problem hiding this comment.
The agent tool functions call the OpenMetadata REST API but do not validate the HTTP response status before parsing JSON. This can lead to confusing failures on auth/network errors. Please add response.raise_for_status() (or explicit status handling) and provide a clear error message when requests fail.
- Cleared all cell outputs from notebooks - Removed pre-executed data for security - Fixes security concern raised in code review
|
Hi there 👋 Thanks for your contribution! The OpenMetadata team will review the PR shortly! Once it has been labeled as Let us know if you need any help! |
|
Hi there 👋 Thanks for your contribution! The OpenMetadata team will review the PR shortly! Once it has been labeled as Let us know if you need any help! |
| "name": "python", | ||
| "nbconvert_exporter": "python", | ||
| "pygments_lexer": "ipython3", | ||
| "version": "3.13.9" |
There was a problem hiding this comment.
Notebook metadata declares Python 3.13.9, but this repository explicitly supports Python 3.9–3.11 for ingestion/SDK code. Please update the notebook kernel/language metadata to a supported version (e.g., 3.11) to avoid misleading users and compatibility issues.
| "version": "3.13.9" | |
| "version": "3.11" |
| " response = requests.get(\n", | ||
| " f\"{BASE_URL}/api/v1/tables\",\n", | ||
| " headers=HEADERS,\n", | ||
| " params={\"limit\": 50}\n", | ||
| " )\n", | ||
| " data = response.json()\n", | ||
| " tables = data.get(\"data\", [])\n", |
There was a problem hiding this comment.
get_all_tables() only fetches a single page (limit=50), but the name implies it returns the full catalog. Either rename it to reflect that it’s a single page, or implement pagination using the API cursor (paging.after) so larger catalogs are handled correctly.
| " response = requests.get(\n", | |
| " f\"{BASE_URL}/api/v1/tables\",\n", | |
| " headers=HEADERS,\n", | |
| " params={\"limit\": 50}\n", | |
| " )\n", | |
| " data = response.json()\n", | |
| " tables = data.get(\"data\", [])\n", | |
| " tables = []\n", | |
| " after = None\n", | |
| "\n", | |
| " while True:\n", | |
| " params = {\"limit\": 50}\n", | |
| " if after:\n", | |
| " params[\"after\"] = after\n", | |
| "\n", | |
| " response = requests.get(\n", | |
| " f\"{BASE_URL}/api/v1/tables\",\n", | |
| " headers=HEADERS,\n", | |
| " params=params\n", | |
| " )\n", | |
| " data = response.json()\n", | |
| " tables.extend(data.get(\"data\", []))\n", | |
| "\n", | |
| " paging = data.get(\"paging\", {})\n", | |
| " after = paging.get(\"after\")\n", | |
| " if not after:\n", | |
| " break\n", | |
| "\n", |
| @@ -0,0 +1,7 @@ | |||
| openmetadata-ingestion | |||
| groq | |||
| google-genai | |||
There was a problem hiding this comment.
google-genai is listed as a dependency but it is not referenced anywhere in the notebooks/README in this PR. Please remove it from requirements.txt (or add a concrete usage example so the dependency is justified).
| google-genai |
| " if \"search_tables\" in decision:\n", | ||
| " keyword = decision.split(\":\")[-1].strip()\n", | ||
| " data = tool_search_tables(keyword)\n", | ||
| " tool_used = f\"search_tables('{keyword}')\"\n", | ||
| " elif \"get_databases\" in decision:\n", | ||
| " data = tool_get_databases()\n", | ||
| " tool_used = \"get_databases()\"\n", | ||
| " else:\n", | ||
| " data = tool_get_tables()\n", | ||
| " tool_used = \"get_tables()\"\n", |
There was a problem hiding this comment.
Tool selection parsing is brittle: if the model returns search_tables without a colon/keyword, decision.split(":")[-1] will produce search_tables as the keyword and run an unintended search. Please validate the decision format (exact match vs search_tables: <keyword> with non-empty keyword) and handle invalid outputs explicitly.
| " if \"search_tables\" in decision:\n", | |
| " keyword = decision.split(\":\")[-1].strip()\n", | |
| " data = tool_search_tables(keyword)\n", | |
| " tool_used = f\"search_tables('{keyword}')\"\n", | |
| " elif \"get_databases\" in decision:\n", | |
| " data = tool_get_databases()\n", | |
| " tool_used = \"get_databases()\"\n", | |
| " else:\n", | |
| " data = tool_get_tables()\n", | |
| " tool_used = \"get_tables()\"\n", | |
| " normalized_decision = decision.strip()\n", | |
| " if normalized_decision == \"get_tables\":\n", | |
| " data = tool_get_tables()\n", | |
| " tool_used = \"get_tables()\"\n", | |
| " elif normalized_decision == \"get_databases\":\n", | |
| " data = tool_get_databases()\n", | |
| " tool_used = \"get_databases()\"\n", | |
| " elif normalized_decision.startswith(\"search_tables:\"):\n", | |
| " keyword = normalized_decision.partition(\":\")[2].strip()\n", | |
| " if not keyword:\n", | |
| " raise ValueError(\n", | |
| " \"Invalid tool decision: 'search_tables' requires a non-empty keyword.\"\n", | |
| " )\n", | |
| " data = tool_search_tables(keyword)\n", | |
| " tool_used = f\"search_tables('{keyword}')\"\n", | |
| " else:\n", | |
| " raise ValueError(\n", | |
| " \"Invalid tool decision. Expected one of 'get_tables', 'get_databases', or 'search_tables: <keyword>'.\"\n", | |
| " )\n", |
| "total = len(df)\n", | ||
| "has_description = (df[\"Has Description\"] == \"✅\").sum()\n", | ||
| "missing_description = (df[\"Has Description\"] == \"❌\").sum()\n", | ||
| "has_owner = (df[\"Has Owner\"] == \"✅\").sum()\n", | ||
| "missing_owner = (df[\"Has Owner\"] == \"❌\").sum()\n", | ||
| "total_columns = df[\"Total Columns\"].sum()\n", | ||
| "columns_missing_desc = df[\"Columns Missing Desc\"].sum()\n", | ||
| "\n", | ||
| "print(\"=\" * 50)\n", | ||
| "print(\" 📊 OPENMETADATA HEALTH REPORT\")\n", | ||
| "print(\"=\" * 50)\n", | ||
| "print(f\" Total Tables Analyzed : {total}\")\n", | ||
| "print(f\" ✅ Have Description : {has_description} ({round(has_description/total*100)}%)\")\n", | ||
| "print(f\" ❌ Missing Description : {missing_description} ({round(missing_description/total*100)}%)\")\n", | ||
| "print(f\" ✅ Have Owner : {has_owner} ({round(has_owner/total*100)}%)\")\n", | ||
| "print(f\" ❌ Missing Owner : {missing_owner} ({round(missing_owner/total*100)}%)\")\n", | ||
| "print(f\" Total Columns : {total_columns}\")\n", | ||
| "print(f\" Columns Missing Desc : {columns_missing_desc}\")\n", | ||
| "print(\"=\" * 50)\n", | ||
| "\n", | ||
| "# Health Score\n", | ||
| "score = round(((has_description + has_owner) / (total * 2)) * 100)\n", | ||
| "print(f\"\\n 🏥 Overall Health Score: {score}/100\")\n", | ||
| "if score >= 70:\n", | ||
| " print(\" Status: 🟢 HEALTHY\")\n", | ||
| "elif score >= 40:\n", | ||
| " print(\" Status: 🟡 NEEDS ATTENTION\")\n", | ||
| "else:\n", | ||
| " print(\" Status: 🔴 CRITICAL\")\n", | ||
| "print(\"=\" * 50)" |
There was a problem hiding this comment.
The summary report logic is duplicated across multiple consecutive cells, which makes maintenance harder and risks inconsistencies. Please keep a single summary implementation (or refactor into a helper) and remove the duplicated cell.
| "total = len(df)\n", | |
| "has_description = (df[\"Has Description\"] == \"✅\").sum()\n", | |
| "missing_description = (df[\"Has Description\"] == \"❌\").sum()\n", | |
| "has_owner = (df[\"Has Owner\"] == \"✅\").sum()\n", | |
| "missing_owner = (df[\"Has Owner\"] == \"❌\").sum()\n", | |
| "total_columns = df[\"Total Columns\"].sum()\n", | |
| "columns_missing_desc = df[\"Columns Missing Desc\"].sum()\n", | |
| "\n", | |
| "print(\"=\" * 50)\n", | |
| "print(\" 📊 OPENMETADATA HEALTH REPORT\")\n", | |
| "print(\"=\" * 50)\n", | |
| "print(f\" Total Tables Analyzed : {total}\")\n", | |
| "print(f\" ✅ Have Description : {has_description} ({round(has_description/total*100)}%)\")\n", | |
| "print(f\" ❌ Missing Description : {missing_description} ({round(missing_description/total*100)}%)\")\n", | |
| "print(f\" ✅ Have Owner : {has_owner} ({round(has_owner/total*100)}%)\")\n", | |
| "print(f\" ❌ Missing Owner : {missing_owner} ({round(missing_owner/total*100)}%)\")\n", | |
| "print(f\" Total Columns : {total_columns}\")\n", | |
| "print(f\" Columns Missing Desc : {columns_missing_desc}\")\n", | |
| "print(\"=\" * 50)\n", | |
| "\n", | |
| "# Health Score\n", | |
| "score = round(((has_description + has_owner) / (total * 2)) * 100)\n", | |
| "print(f\"\\n 🏥 Overall Health Score: {score}/100\")\n", | |
| "if score >= 70:\n", | |
| " print(\" Status: 🟢 HEALTHY\")\n", | |
| "elif score >= 40:\n", | |
| " print(\" Status: 🟡 NEEDS ATTENTION\")\n", | |
| "else:\n", | |
| " print(\" Status: 🔴 CRITICAL\")\n", | |
| "print(\"=\" * 50)" | |
| "def print_summary_report(df):\n", | |
| " total = len(df)\n", | |
| " has_description = (df[\"Has Description\"] == \"✅\").sum()\n", | |
| " missing_description = (df[\"Has Description\"] == \"❌\").sum()\n", | |
| " has_owner = (df[\"Has Owner\"] == \"✅\").sum()\n", | |
| " missing_owner = (df[\"Has Owner\"] == \"❌\").sum()\n", | |
| " total_columns = df[\"Total Columns\"].sum()\n", | |
| " columns_missing_desc = df[\"Columns Missing Desc\"].sum()\n", | |
| "\n", | |
| " description_pct = round(has_description / total * 100) if total else 0\n", | |
| " missing_description_pct = round(missing_description / total * 100) if total else 0\n", | |
| " owner_pct = round(has_owner / total * 100) if total else 0\n", | |
| " missing_owner_pct = round(missing_owner / total * 100) if total else 0\n", | |
| " score = round(((has_description + has_owner) / (total * 2)) * 100) if total else 0\n", | |
| "\n", | |
| " print(\"=\" * 50)\n", | |
| " print(\" 📊 OPENMETADATA HEALTH REPORT\")\n", | |
| " print(\"=\" * 50)\n", | |
| " print(f\" Total Tables Analyzed : {total}\")\n", | |
| " print(f\" ✅ Have Description : {has_description} ({description_pct}%)\")\n", | |
| " print(f\" ❌ Missing Description : {missing_description} ({missing_description_pct}%)\")\n", | |
| " print(f\" ✅ Have Owner : {has_owner} ({owner_pct}%)\")\n", | |
| " print(f\" ❌ Missing Owner : {missing_owner} ({missing_owner_pct}%)\")\n", | |
| " print(f\" Total Columns : {total_columns}\")\n", | |
| " print(f\" Columns Missing Desc : {columns_missing_desc}\")\n", | |
| " print(\"=\" * 50)\n", | |
| " print(f\"\\n 🏥 Overall Health Score: {score}/100\")\n", | |
| " if score >= 70:\n", | |
| " print(\" Status: 🟢 HEALTHY\")\n", | |
| " elif score >= 40:\n", | |
| " print(\" Status: 🟡 NEEDS ATTENTION\")\n", | |
| " else:\n", | |
| " print(\" Status: 🔴 CRITICAL\")\n", | |
| " print(\"=\" * 50)\n", | |
| "\n", | |
| "print_summary_report(df)" |
| "name": "python", | ||
| "nbconvert_exporter": "python", | ||
| "pygments_lexer": "ipython3", | ||
| "version": "3.13.9" |
There was a problem hiding this comment.
Notebook metadata declares Python 3.13.9, but this repository explicitly supports Python 3.9–3.11 for ingestion/SDK code. Please update the notebook kernel/language metadata to a supported version (e.g., 3.11) to avoid misleading users and compatibility issues.
| "version": "3.13.9" | |
| "version": "3.11" |
| " response = requests.get(\n", | ||
| " f\"{BASE_URL}/api/v1/tables\",\n", | ||
| " headers=HEADERS,\n", | ||
| " params={\"limit\": limit}\n", | ||
| " )\n", |
There was a problem hiding this comment.
All OpenMetadata API calls use response.json() without checking response.status_code / raise_for_status(). If auth fails or the server returns non-JSON, this will throw and obscure the real error. Please add status checking (and a clearer error message) before parsing JSON in these tool functions.
| "print(f\" ✅ Have Description : {has_description} ({round(has_description/total*100)}%)\")\n", | ||
| "print(f\" ❌ Missing Description : {missing_description} ({round(missing_description/total*100)}%)\")\n", | ||
| "print(f\" ✅ Have Owner : {has_owner} ({round(has_owner/total*100)}%)\")\n", | ||
| "print(f\" ❌ Missing Owner : {missing_owner} ({round(missing_owner/total*100)}%)\")\n", | ||
| "print(f\" Total Columns : {total_columns}\")\n", | ||
| "print(f\" Columns Missing Desc : {columns_missing_desc}\")\n", | ||
| "print(\"=\" * 50)\n", | ||
| "\n", | ||
| "# Health Score\n", | ||
| "score = round(((has_description + has_owner) / (total * 2)) * 100)\n", |
There was a problem hiding this comment.
If the catalog has zero tables, total will be 0 and the percentage calculations (has_description/total*100, etc.) will raise ZeroDivisionError. Please guard for total == 0 and print a sensible empty-catalog summary (and ensure score is defined before it’s used later).
| "print(f\" ✅ Have Description : {has_description} ({round(has_description/total*100)}%)\")\n", | |
| "print(f\" ❌ Missing Description : {missing_description} ({round(missing_description/total*100)}%)\")\n", | |
| "print(f\" ✅ Have Owner : {has_owner} ({round(has_owner/total*100)}%)\")\n", | |
| "print(f\" ❌ Missing Owner : {missing_owner} ({round(missing_owner/total*100)}%)\")\n", | |
| "print(f\" Total Columns : {total_columns}\")\n", | |
| "print(f\" Columns Missing Desc : {columns_missing_desc}\")\n", | |
| "print(\"=\" * 50)\n", | |
| "\n", | |
| "# Health Score\n", | |
| "score = round(((has_description + has_owner) / (total * 2)) * 100)\n", | |
| "\n", | |
| "if total == 0:\n", | |
| " print(\" ✅ Have Description : 0 (0%)\")\n", | |
| " print(\" ❌ Missing Description : 0 (0%)\")\n", | |
| " print(\" ✅ Have Owner : 0 (0%)\")\n", | |
| " print(\" ❌ Missing Owner : 0 (0%)\")\n", | |
| " print(f\" Total Columns : {total_columns}\")\n", | |
| " print(f\" Columns Missing Desc : {columns_missing_desc}\")\n", | |
| " print(\"=\" * 50)\n", | |
| " print(\"\\n No tables found in the catalog.\")\n", | |
| " score = 0\n", | |
| "else:\n", | |
| " print(f\" ✅ Have Description : {has_description} ({round(has_description / total * 100)}%)\")\n", | |
| " print(f\" ❌ Missing Description : {missing_description} ({round(missing_description / total * 100)}%)\")\n", | |
| " print(f\" ✅ Have Owner : {has_owner} ({round(has_owner / total * 100)}%)\")\n", | |
| " print(f\" ❌ Missing Owner : {missing_owner} ({round(missing_owner / total * 100)}%)\")\n", | |
| " print(f\" Total Columns : {total_columns}\")\n", | |
| " print(f\" Columns Missing Desc : {columns_missing_desc}\")\n", | |
| " print(\"=\" * 50)\n", | |
| "\n", | |
| " # Health Score\n", | |
| " score = round(((has_description + has_owner) / (total * 2)) * 100)\n", | |
| "\n", |
| "name": "python", | ||
| "nbconvert_exporter": "python", | ||
| "pygments_lexer": "ipython3", | ||
| "version": "3.13.9" |
There was a problem hiding this comment.
Notebook metadata declares Python 3.13.9, but this repository explicitly supports Python 3.9–3.11 for ingestion/SDK code. Please update the notebook kernel/language metadata to a supported version (e.g., 3.11) to avoid misleading users and compatibility issues.
| "version": "3.13.9" | |
| "version": "3.11" |
| ## Technologies Used | ||
| - OpenMetadata API | ||
| - Groq AI (LLaMA 3.3 70b) | ||
| - Python, Pandas, Matplotlib |
There was a problem hiding this comment.
The PR description/issue mention using the OpenMetadata Python SDK, but the notebooks/README currently use raw REST calls via requests (and don’t demonstrate metadata.sdk). Please either update the examples to use the SDK client APIs, or adjust the README to clearly state these are REST-based examples.
|
Hi there 👋 Thanks for your contribution! The OpenMetadata team will review the PR shortly! Once it has been labeled as Let us know if you need any help! |
| "if tables:\n", | ||
| " print(f\"✅ Found {len(tables)} tables!\")" |
There was a problem hiding this comment.
⚠️ Bug: Division by zero still reachable when no tables returned
The new error handling in get_all_tables() correctly returns [] on failure, but downstream code at line ~183 still computes score = round(((has_description + has_owner) / (total * 2)) * 100) where total = len(df). When tables is empty, total is 0 and this raises ZeroDivisionError. The guard added at line 108 (if tables: print(...)) only suppresses the success message but doesn't prevent the rest of the notebook from executing with an empty list.
Suggested fix:
Guard the downstream cells or add an early exit:
tables = get_all_tables()
if not tables:
print("⚠️ No tables to analyze. Exiting.")
else:
print(f"✅ Found {len(tables)} tables!")
And in the score calculation cell:
if total == 0:
print("No tables to score.")
else:
score = round(((has_description + has_owner) / (total * 2)) * 100)
Was this helpful? React with 👍 / 👎 | Reply gitar fix to apply this suggestion
|
Hi there 👋 Thanks for your contribution! The OpenMetadata team will review the PR shortly! Once it has been labeled as Let us know if you need any help! |
| "import requests\n", | ||
| "import json\n", | ||
| "from groq import Groq\n", | ||
| "\n", | ||
| "# Your credentials\n", | ||
| "GROQ_API_KEY = \"paste api key here\"\n", | ||
| "BASE_URL = \"https://sandbox.open-metadata.org\"\n", | ||
| "TOKEN = \"paste your tokens here\"\n", | ||
| "\n", | ||
| "HEADERS = {\n", | ||
| " \"Authorization\": f\"Bearer {TOKEN}\",\n", | ||
| " \"Content-Type\": \"application/json\"\n", | ||
| "}\n", | ||
| "\n", | ||
| "client = Groq(api_key=GROQ_API_KEY)\n", | ||
| "print(\"✅ Agent setup complete!\")" |
There was a problem hiding this comment.
These recipes are described as “AI SDK recipes”, but this notebook uses direct REST calls via requests and does not use the OpenMetadata Python SDK (metadata.sdk). Since openmetadata-ingestion is included as a dependency, consider switching table listing/search to the SDK APIs (see ingestion/src/metadata/sdk/README.md for configure(), Table.list(), Search.search()) so the example showcases the SDK rather than raw HTTP.
| "import requests\n", | |
| "import json\n", | |
| "from groq import Groq\n", | |
| "\n", | |
| "# Your credentials\n", | |
| "GROQ_API_KEY = \"paste api key here\"\n", | |
| "BASE_URL = \"https://sandbox.open-metadata.org\"\n", | |
| "TOKEN = \"paste your tokens here\"\n", | |
| "\n", | |
| "HEADERS = {\n", | |
| " \"Authorization\": f\"Bearer {TOKEN}\",\n", | |
| " \"Content-Type\": \"application/json\"\n", | |
| "}\n", | |
| "\n", | |
| "client = Groq(api_key=GROQ_API_KEY)\n", | |
| "print(\"✅ Agent setup complete!\")" | |
| "import json\n", | |
| "from groq import Groq\n", | |
| "from metadata.sdk import configure\n", | |
| "\n", | |
| "# Your credentials\n", | |
| "GROQ_API_KEY = \"paste api key here\"\n", | |
| "BASE_URL = \"https://sandbox.open-metadata.org\"\n", | |
| "TOKEN = \"paste your tokens here\"\n", | |
| "\n", | |
| "# Configure the OpenMetadata SDK so the notebook uses SDK APIs\n", | |
| "# such as Table.list() and Search.search() instead of raw REST calls.\n", | |
| "ometa = configure(server=BASE_URL, api_key=TOKEN)\n", | |
| "\n", | |
| "client = Groq(api_key=GROQ_API_KEY)\n", | |
| "print(\"✅ Agent setup complete with OpenMetadata SDK!\")" |
| "version": "3.13.9" | ||
| } |
There was a problem hiding this comment.
Notebook metadata lists Python 3.13.9, which is outside the ingestion module’s supported versions (e.g., ingestion/noxfile.py lists 3.10–3.12). Please update the kernel/language metadata to a supported version to reduce confusion when users run these notebooks.
| "name": "python", | ||
| "nbconvert_exporter": "python", | ||
| "pygments_lexer": "ipython3", | ||
| "version": "3.13.9" |
There was a problem hiding this comment.
Notebook metadata lists Python 3.13.9, which is outside the ingestion module’s supported versions (e.g., ingestion/noxfile.py lists 3.10–3.12). Please update the kernel/language metadata to a supported version to reduce confusion when users run these notebooks.
| "version": "3.13.9" | |
| "version": "3.11.0" |
| @@ -0,0 +1,6 @@ | |||
| openmetadata-ingestion | |||
There was a problem hiding this comment.
openmetadata-ingestion is listed here, but the added notebooks currently use raw requests calls and do not import/use the Python SDK (metadata.sdk). Either update the notebooks to use the SDK (preferred for “SDK recipes”) or remove this dependency to avoid suggesting it’s required.
| openmetadata-ingestion |
| " tables = response.json().get(\"data\", [])\n", | ||
| " return [{\"name\": t.get(\"name\"),\n", | ||
| " \"description\": t.get(\"description\", \"No description\"),\n", | ||
| " \"columns\": len(t.get(\"columns\", []))}\n", | ||
| " for t in tables]\n", |
There was a problem hiding this comment.
/api/v1/tables is called without a fields query param, so columns is not returned by default (empty requested fields). As a result, len(t.get("columns", [])) will be 0 for all tables, making the “columns” info misleading. Either request the columns field explicitly via fields=columns or drop this column count from the tool output.
| "# Health Score\n", | ||
| "score = round(((has_description + has_owner) / (total * 2)) * 100)\n", | ||
| "print(f\"\\n 🏥 Overall Health Score: {score}/100\")\n", | ||
| "if score >= 70:\n", | ||
| " print(\" Status: 🟢 HEALTHY\")\n", |
There was a problem hiding this comment.
In this cell, the health score is computed and printed again after the if total == 0 guard. If total is 0, variables like has_description/has_owner are never set, so this block will raise a NameError (and it also duplicates the output). Consider removing the duplicate block or moving score computation entirely inside the else branch with proper initialization.
| "# Print a nice summary report\n", | ||
| "total = len(df)\n", | ||
| "has_description = (df[\"Has Description\"] == \"✅\").sum()\n", | ||
| "missing_description = (df[\"Has Description\"] == \"❌\").sum()\n", | ||
| "has_owner = (df[\"Has Owner\"] == \"✅\").sum()\n", |
There was a problem hiding this comment.
This second summary cell repeats the same calculations/printing as the previous one but without the total == 0 guard, which can lead to division-by-zero errors when the catalog has no tables. Recommend deleting this duplicate cell (or keeping a single summary path that handles the empty case safely).
| "# Fetch limited tables with error handling\n", | ||
| "def get_all_tables():\n", | ||
| " try:\n", | ||
| " response = requests.get(\n", | ||
| " f\"{BASE_URL}/api/v1/tables\",\n", | ||
| " headers=HEADERS,\n", | ||
| " params={\"limit\": 50}\n", | ||
| " )\n", | ||
| " # Check if request was successful\n", | ||
| " if response.status_code != 200:\n", | ||
| " print(f\"❌ Error: Server returned {response.status_code}\")\n", | ||
| " return []\n", | ||
| " \n", | ||
| " data = response.json()\n", | ||
| " tables = data.get(\"data\", [])\n", | ||
| " \n", | ||
| " if not tables:\n", | ||
| " print(\"⚠️ No tables found!\")\n", | ||
| " return []\n", | ||
| " \n", | ||
| " return tables\n", | ||
| " \n", |
There was a problem hiding this comment.
get_all_tables() currently requests only limit=50 tables and does not paginate, but the name and later reporting imply you are analyzing the whole catalog. Either implement pagination (iterate using after/paging until exhausted) or rename the function/outputs to make the limit explicit.
| "# Fetch limited tables with error handling\n", | |
| "def get_all_tables():\n", | |
| " try:\n", | |
| " response = requests.get(\n", | |
| " f\"{BASE_URL}/api/v1/tables\",\n", | |
| " headers=HEADERS,\n", | |
| " params={\"limit\": 50}\n", | |
| " )\n", | |
| " # Check if request was successful\n", | |
| " if response.status_code != 200:\n", | |
| " print(f\"❌ Error: Server returned {response.status_code}\")\n", | |
| " return []\n", | |
| " \n", | |
| " data = response.json()\n", | |
| " tables = data.get(\"data\", [])\n", | |
| " \n", | |
| " if not tables:\n", | |
| " print(\"⚠️ No tables found!\")\n", | |
| " return []\n", | |
| " \n", | |
| " return tables\n", | |
| " \n", | |
| "# Fetch all tables with pagination and error handling\n", | |
| "def get_all_tables():\n", | |
| " try:\n", | |
| " tables = []\n", | |
| " after = None\n", | |
| "\n", | |
| " while True:\n", | |
| " params = {\"limit\": 50}\n", | |
| " if after:\n", | |
| " params[\"after\"] = after\n", | |
| "\n", | |
| " response = requests.get(\n", | |
| " f\"{BASE_URL}/api/v1/tables\",\n", | |
| " headers=HEADERS,\n", | |
| " params=params\n", | |
| " )\n", | |
| "\n", | |
| " # Check if request was successful\n", | |
| " if response.status_code != 200:\n", | |
| " print(f\"❌ Error: Server returned {response.status_code}\")\n", | |
| " return []\n", | |
| "\n", | |
| " data = response.json()\n", | |
| " page_tables = data.get(\"data\", [])\n", | |
| " tables.extend(page_tables)\n", | |
| "\n", | |
| " after = data.get(\"paging\", {}).get(\"after\")\n", | |
| " if not after:\n", | |
| " break\n", | |
| "\n", | |
| " if not tables:\n", | |
| " print(\"⚠️ No tables found!\")\n", | |
| " return []\n", | |
| "\n", | |
| " return tables\n", | |
| "\n", |
| "name": "python", | ||
| "nbconvert_exporter": "python", | ||
| "pygments_lexer": "ipython3", | ||
| "version": "3.13.9" |
There was a problem hiding this comment.
Notebook metadata lists Python 3.13.9. The ingestion module’s supported versions are lower (e.g., ingestion/noxfile.py lists 3.10–3.12), so this kernel metadata is likely to confuse users and may not be runnable in the supported envs. Please update the notebook kernel/language metadata to a supported version (or omit the patch version).
| "version": "3.13.9" | |
| "version": "3.11" |
| " name = table.get(\"name\", \"Unknown\")\n", | ||
| " description = table.get(\"description\", \"\")\n", | ||
| " owner = table.get(\"owner\", None)\n", | ||
| " followers = table.get(\"followers\", 0)\n", | ||
| " tags = table.get(\"tags\", [])\n", |
There was a problem hiding this comment.
The /api/v1/tables list call (without a fields query param) returns tables with an empty fields set, so columns/owners are typically not included (see EntityUtil.Fields behavior). As a result, columns = table.get("columns", []) will be empty and table.get("owner") will always be None (the API uses owners, not owner), making the health metrics inaccurate. Pass an explicit fields param (e.g., include columns,owners) and read from the correct owners field.
|
Hi there 👋 Thanks for your contribution! The OpenMetadata team will review the PR shortly! Once it has been labeled as Let us know if you need any help! |
Code Review
|
| Compact |
|
Was this helpful? React with 👍 / 👎 | Gitar
Summary
This PR adds three Jupyter notebooks as AI SDK recipes for OpenMetadata.
Notebooks Added
Related Issue
Closes #26646
Technologies Used
Summary by Gitar
ingestion/examples/README.mdwith detailed problem statements, workflow explanations, and sample outputs for the AI recipes.requirements.txtto manage necessary environment dependencies.This will update automatically on new commits.