-
Notifications
You must be signed in to change notification settings - Fork 0
add changes #27
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
add changes #27
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,54 @@ | ||
| import pytest | ||
| from unittest.mock import patch, MagicMock | ||
|
|
||
| from agent.nodes.schema_explorer import hybrid_search_tables | ||
| from core.models.models import Table | ||
|
|
||
| def test_hybrid_search_tables_strict_mode(): | ||
| session_mock = MagicMock() | ||
|
|
||
| table1 = Table(id="t1", name="table_1", schema_name="public", catalog="cat", status="production") | ||
| table2 = Table(id="t2", name="table_2", schema_name="public", catalog="cat", status="production") | ||
| table3 = Table(id="t3", name="table_3", schema_name="public", catalog="cat", status="deprecated") | ||
|
|
||
| session_mock.exec.return_value.all.return_value = [table1, table2, table3] | ||
|
|
||
| # Mock execute for vector search | ||
| session_mock.execute.return_value.fetchall.return_value = [("t1",)] | ||
|
|
||
| # Mock session.get for final return | ||
| def mock_get(cls, id): | ||
| return {"t1": table1, "t2": table2, "t3": table3}.get(id) | ||
| session_mock.get.side_effect = mock_get | ||
|
|
||
| # Mock enrichment version for keyword search | ||
| session_mock.exec.return_value.first.return_value = None | ||
|
|
||
| # Test strict mode allows ONLY t1 | ||
| results = hybrid_search_tables( | ||
| query="table", | ||
| query_embedding=[0.0], | ||
| session=session_mock, | ||
| allowed_tables=["t1"], | ||
| allowed_statuses=["production"], | ||
| scoping_mode="strict" | ||
| ) | ||
|
|
||
| assert len(results) == 1 | ||
| assert results[0].id == "t1" | ||
|
|
||
| # Test hybrid mode allows t1 and t2 (because status="production") | ||
| session_mock.execute.return_value.fetchall.return_value = [("t1",), ("t2",)] | ||
| results_hybrid = hybrid_search_tables( | ||
| query="table", | ||
| query_embedding=[0.0], | ||
| session=session_mock, | ||
| allowed_tables=["t1"], | ||
| allowed_statuses=["production"], | ||
| scoping_mode="hybrid" | ||
| ) | ||
|
|
||
| assert len(results_hybrid) == 2 | ||
| ids = {r.id for r in results_hybrid} | ||
| assert "t1" in ids | ||
| assert "t2" in ids |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,39 @@ | ||
| """Add unique constraint to tables | ||
|
|
||
| Revision ID: 70e8a34ff877 | ||
| Revises: f9a3d1c8e205 | ||
| Create Date: 2026-07-07 11:17:43.459879 | ||
|
|
||
| """ | ||
| from typing import Sequence, Union | ||
|
|
||
| from alembic import op | ||
| import sqlalchemy as sa | ||
| import sqlmodel | ||
|
|
||
|
|
||
| # revision identifiers, used by Alembic. | ||
| revision: str = '70e8a34ff877' | ||
| down_revision: Union[str, None] = 'f9a3d1c8e205' | ||
| branch_labels: Union[str, Sequence[str], None] = None | ||
| depends_on: Union[str, Sequence[str], None] = None | ||
|
|
||
|
|
||
| def upgrade() -> None: | ||
| # ### commands auto generated by Alembic - please adjust! ### | ||
| op.drop_constraint('uq_table_profiles_table_id', 'table_profiles', type_='unique') | ||
| op.drop_index('ix_table_profiles_table_id', table_name='table_profiles') | ||
| op.create_index(op.f('ix_table_profiles_table_id'), 'table_profiles', ['table_id'], unique=True) | ||
| op.drop_column('table_profiles', 'is_partial') | ||
| op.create_unique_constraint('uq_table_fqn', 'tables', ['catalog', 'schema_name', 'name']) | ||
| # ### end Alembic commands ### | ||
|
|
||
|
|
||
| def downgrade() -> None: | ||
| # ### commands auto generated by Alembic - please adjust! ### | ||
| op.drop_constraint('uq_table_fqn', 'tables', type_='unique') | ||
| op.add_column('table_profiles', sa.Column('is_partial', sa.BOOLEAN(), server_default=sa.text('false'), autoincrement=False, nullable=False)) | ||
| op.drop_index(op.f('ix_table_profiles_table_id'), table_name='table_profiles') | ||
| op.create_index('ix_table_profiles_table_id', 'table_profiles', ['table_id'], unique=False) | ||
| op.create_unique_constraint('uq_table_profiles_table_id', 'table_profiles', ['table_id'], postgresql_nulls_not_distinct=False) | ||
| # ### end Alembic commands ### |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -18,6 +18,7 @@ | |||||||||||||||||||||||||||||||||
| UserScope, | ||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||
| from fastapi import APIRouter, Depends, Header, HTTPException, Query | ||||||||||||||||||||||||||||||||||
| from sqlalchemy.exc import IntegrityError | ||||||||||||||||||||||||||||||||||
| from sqlmodel import Session, col, select | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| from app.config import settings | ||||||||||||||||||||||||||||||||||
|
|
@@ -195,6 +196,20 @@ def create_table(payload: TableCreate, session: Session = Depends(get_session)): | |||||||||||||||||||||||||||||||||
| text_to_embed = f"Table name: {name}\nSchema: {schema_name}\nDescription: {description}\nColumns: {', '.join([c.get('name', '') for c in om_columns])}" | ||||||||||||||||||||||||||||||||||
| embedding = get_embedding(text_to_embed) | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| # Check for duplicate table | ||||||||||||||||||||||||||||||||||
| existing = session.exec( | ||||||||||||||||||||||||||||||||||
| select(Table).where( | ||||||||||||||||||||||||||||||||||
| Table.catalog == catalog_name, | ||||||||||||||||||||||||||||||||||
| Table.schema_name == schema_name, | ||||||||||||||||||||||||||||||||||
| Table.name == name | ||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||
| ).first() | ||||||||||||||||||||||||||||||||||
| if existing: | ||||||||||||||||||||||||||||||||||
| raise HTTPException( | ||||||||||||||||||||||||||||||||||
| status_code=409, | ||||||||||||||||||||||||||||||||||
| detail=f"Table '{catalog_name}.{schema_name}.{name}' already exists." | ||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| # Create the table | ||||||||||||||||||||||||||||||||||
| table = Table( | ||||||||||||||||||||||||||||||||||
| name=name, | ||||||||||||||||||||||||||||||||||
|
|
@@ -207,7 +222,14 @@ def create_table(payload: TableCreate, session: Session = Depends(get_session)): | |||||||||||||||||||||||||||||||||
| embedding=embedding, | ||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||
| session.add(table) | ||||||||||||||||||||||||||||||||||
| session.commit() | ||||||||||||||||||||||||||||||||||
| try: | ||||||||||||||||||||||||||||||||||
| session.commit() | ||||||||||||||||||||||||||||||||||
| except IntegrityError: | ||||||||||||||||||||||||||||||||||
| session.rollback() | ||||||||||||||||||||||||||||||||||
| raise HTTPException( | ||||||||||||||||||||||||||||||||||
| status_code=409, | ||||||||||||||||||||||||||||||||||
| detail=f"Table '{catalog_name}.{schema_name}.{name}' already exists." | ||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||
|
Comment on lines
+225
to
+232
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick win Ensure atomic creation of the table and its enrichment data. Using Using ♻️ Proposed fix to ensure atomicity try:
- session.commit()
+ session.flush()
except IntegrityError:
session.rollback()
raise HTTPException(
status_code=409,
detail=f"Table '{catalog_name}.{schema_name}.{name}' already exists."
)📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| # Extract columns in the format expected by the frontend | ||||||||||||||||||||||||||||||||||
| def parse_columns(cols): | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,4 @@ | ||
| import json | ||
| from app.main import app | ||
| with open("../frontend/openapi.json", "w") as f: | ||
| json.dump(app.openapi(), f, indent=2) | ||
|
Comment on lines
+3
to
+4
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win 🧩 Analysis chain🏁 Script executed: #!/bin/bash
set -euo pipefail
printf '--- backend/dump_schema.py ---\n'
cat -n backend/dump_schema.py
printf '\n--- frontend/package.json ---\n'
cat -n frontend/package.json
printf '\n--- path resolution probe ---\n'
python3 - <<'PY'
from pathlib import Path
cwd = Path.cwd()
script_dir = Path('backend').resolve()
print('cwd:', cwd)
print('script_dir:', script_dir)
print('open("../frontend/openapi.json") from repo root ->', (cwd / '../frontend/openapi.json').resolve())
print('expected repo frontend path ->', (cwd / 'frontend/openapi.json').resolve())
print('from backend dir ->', (script_dir / '../frontend/openapi.json').resolve())
PYRepository: StavPonte11/text2sql-onboarding Length of output: 260 🏁 Script executed: #!/bin/bash
set -euo pipefail
echo '--- backend/dump_schema.py ---'
cat -n backend/dump_schema.py
echo
echo '--- frontend/package.json ---'
cat -n frontend/package.json
echo
echo '--- repo path probe ---'
python3 - <<'PY'
from pathlib import Path
cwd = Path.cwd()
print('cwd:', cwd)
print('repo frontend path:', (cwd / 'frontend/openapi.json').resolve())
print('relative "../frontend/openapi.json" from repo root:', (cwd / '../frontend/openapi.json').resolve())
PYRepository: StavPonte11/text2sql-onboarding Length of output: 3440 🏁 Script executed: #!/bin/bash
set -euo pipefail
echo '--- references to dump_schema.py ---'
rg -n "dump_schema\.py|openapi\.json" -g '!**/node_modules/**' -g '!**/.git/**' .
echo
echo '--- backend tree ---'
git ls-files backend frontend | sed -n '1,200p'Repository: StavPonte11/text2sql-onboarding Length of output: 6192 🏁 Script executed: #!/bin/bash
set -euo pipefail
echo '--- references to backend/dump_schema.py in docs and scripts ---'
rg -n "dump_schema\.py|openapi\.json|generate-types|schema\.json|schema\.d.ts" README.md backend/README.md frontend/README.md backend frontend -g '!**/node_modules/**' -g '!**/.git/**'
echo
echo '--- backend README (if present) ---'
if [ -f backend/README.md ]; then
cat -n backend/README.md
fi
echo
echo '--- top-level README (if present) ---'
if [ -f README.md ]; then
cat -n README.md
fiRepository: StavPonte11/text2sql-onboarding Length of output: 570 🏁 Script executed: #!/bin/bash
set -euo pipefail
echo '--- backend/Dockerfile ---'
cat -n backend/Dockerfile
echo
echo '--- backend/pyproject.toml ---'
cat -n backend/pyproject.toml
echo
echo '--- backend/e2e_test.py (if it mentions schema generation) ---'
rg -n "dump_schema|openapi.json|frontend/openapi.json|schema.d.ts" backend -g '!**/.git/**'Repository: StavPonte11/text2sql-onboarding Length of output: 4975 Anchor 🤖 Prompt for AI Agents |
||
Uh oh!
There was an error while loading. Please reload this page.