Skip to content
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

feat: Gradio Adater implementation #39

Merged
merged 26 commits into from
Jun 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ class CandidateView(SqlAlchemyBaseView):
"""
return Candidate.country == country

engine = create_engine('sqlite:///candidates.db')
engine = create_engine('sqlite:///examples/recruiting/data/candidates.db')
llm = LiteLLM(model_name="gpt-3.5-turbo")
my_collection = create_collection("collection_name", llm)
my_collection.add(CandidateView, lambda: CandidateView(engine))
Expand Down
2 changes: 1 addition & 1 deletion docs/how-to/sql_views.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ You need to connect to the database using SQLAlchemy before you can use your vie
```python
from sqlalchemy import create_engine

engine = create_engine('sqlite:///candidates.db')
engine = create_engine('sqlite:///examples/recruiting/data/candidates.db')
```

## Registering the view
Expand Down
2 changes: 1 addition & 1 deletion docs/how-to/use_elastic_vector_store_code.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
from dbally.similarity.elastic_vector_search import ElasticVectorStore

load_dotenv()
engine = create_engine("sqlite:///candidates.db")
engine = create_engine("sqlite:///examples/recruiting/data/candidates.db")


Base = automap_base()
Expand Down
2 changes: 1 addition & 1 deletion docs/how-to/use_elasticsearch_store_code.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
from dbally.similarity.elasticsearch_store import ElasticsearchStore

load_dotenv()
engine = create_engine("sqlite:///candidates.db")
engine = create_engine("sqlite:///examples/recruiting/data/candidates.db")


Base = automap_base()
Expand Down
44 changes: 44 additions & 0 deletions docs/how-to/visualize_views.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# How-To: Visualize Views

To create simple UI interface use [create_gradio_interface function](https://github.com/deepsense-ai/db-ally/tree/main/src/dbally/gradio/gradio_interface.py) It allows to display Data Preview related to Views
and execute user queries.

## Installation
```bash
pip install dbally["gradio"]
```
When You plan to use some other feature like faiss similarity store install them as well.

```bash
pip install dbally["faiss"]
```

## Create own gradio interface
Define collection with implemented views

```python
llm = LiteLLM(model_name="gpt-3.5-turbo")
await country_similarity.update()
collection = dbally.create_collection("recruitment", llm, event_handlers=[CLIEventHandler()])
collection.add(CandidateView, lambda: CandidateView(engine))
collection.add(SampleText2SQLViewCyphers, lambda: SampleText2SQLViewCyphers(create_freeform_memory_engine()))
```

>_**NOTE**_: The following code requires environment variables to proceed with LLM queries. For the example below, set the
> ```OPENAI_API_KEY``` environment variable.

Create gradio interface
```python
gradio_interface = await create_gradio_interface(user_collection=collection)
```

Launch the gradio interface. To publish public interface pass argument `share=True`
```python
gradio_interface.launch()
```

The endpoint is set by triggering python module with Gradio Adapter launch command.
Private endpoint is set to http://127.0.0.1:7860/ by default.

## Links
* [Example Gradio Interface](https://github.com/deepsense-ai/db-ally/tree/main/examples/visualize_views_code.py)
4 changes: 2 additions & 2 deletions docs/quickstart/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,14 +30,14 @@ pip install dbally[litellm]

## Database Configuration

In this guide, we will use an example SQLAlchemy database containing a single table named `candidates`. This table includes columns such as `id`, `name`, `country`, `years_of_experience`, `position`, `university`, `skills`, and `tags`. You can download the example database from [candidates.db](candidates.db). Alternatively, you can use your own database and models.
In this guide, we will use an example SQLAlchemy database containing a single table named `candidates`. This table includes columns such as `id`, `name`, `country`, `years_of_experience`, `position`, `university`, `skills`, and `tags`. You can download the example database from [candidates.db](https://github.com/deepsense-ai/db-ally/tree/main/examples/recruiting/candidates.db). Alternatively, you can use your own database and models.

To connect to the database using SQLAlchemy, you need an engine and your database models. Start by creating an engine:

```python
from sqlalchemy import create_engine

engine = create_engine('sqlite:///candidates.db')
engine = create_engine('sqlite:///examples/recruiting/data/candidates.db')
```

Next, define an SQLAlchemy model for the `candidates` table. You can either declare the `Candidate` model using [declarative mapping](https://docs.sqlalchemy.org/en/20/orm/mapping_styles.html#declarative-mapping) or generate it using [automap](https://docs.sqlalchemy.org/en/20/orm/extensions/automap.html). For simplicity, we'll use automap:
Expand Down
2 changes: 1 addition & 1 deletion docs/quickstart/quickstart2_code.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
from dbally.llms.litellm import LiteLLM

load_dotenv()
engine = create_engine("sqlite:///candidates.db")
engine = create_engine("sqlite:///examples/recruiting/data/candidates.db")

Base = automap_base()
Base.prepare(autoload_with=engine)
Expand Down
2 changes: 1 addition & 1 deletion docs/quickstart/quickstart3_code.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
from dbally.embeddings.litellm import LiteLLMEmbeddingClient
from dbally.llms.litellm import LiteLLM

engine = create_engine("sqlite:///candidates.db")
engine = create_engine("sqlite:///examples/recruiting/data/candidates.db")

Base = automap_base()
Base.prepare(autoload_with=engine)
Expand Down
5 changes: 4 additions & 1 deletion docs/quickstart/quickstart_code.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,19 @@
from dbally.llms.litellm import LiteLLM


engine = create_engine('sqlite:///candidates.db')
engine = create_engine("sqlite:///examples/recruiting/data/candidates.db")

Base = automap_base()
Base.prepare(autoload_with=engine)

Candidate = Base.classes.candidates


class CandidateView(SqlAlchemyBaseView):
"""
A view for retrieving candidates from the database.
"""

def get_select(self) -> sqlalchemy.Select:
"""
Creates the initial SqlAlchemy select object, which will be used to build the query.
Expand Down Expand Up @@ -52,6 +54,7 @@ def from_country(self, country: str) -> sqlalchemy.ColumnElement:
"""
return Candidate.country == country


async def main():
llm = LiteLLM(model_name="gpt-3.5-turbo")

Expand Down
Binary file not shown.
Binary file not shown.
4 changes: 2 additions & 2 deletions examples/recruiting.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
from dataclasses import dataclass
from typing import List

from recruting.db import ENGINE, fill_candidate_table, get_recruitment_db_description
from recruting.views import RecruitmentView
from recruiting.db import ENGINE, fill_candidate_table, get_recruitment_db_description
from recruiting.views import RecruitmentView

import dbally
from dbally.audit.event_handlers.cli_event_handler import CLIEventHandler
Expand Down
File renamed without changes.
68 changes: 68 additions & 0 deletions examples/recruiting/candidate_view_with_similarity_store.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
# pylint: disable=missing-return-doc, missing-param-doc, missing-function-docstring

import sqlalchemy
from sqlalchemy import create_engine
from sqlalchemy.ext.automap import automap_base
from typing_extensions import Annotated

from dbally import SqlAlchemyBaseView, decorators
from dbally.embeddings.litellm import LiteLLMEmbeddingClient
from dbally.similarity import FaissStore, SimilarityIndex, SimpleSqlAlchemyFetcher

engine = create_engine("sqlite:///examples/recruiting/data/candidates.db")

Base = automap_base()
Base.prepare(autoload_with=engine)

Candidate = Base.classes.candidates

country_similarity = SimilarityIndex(
fetcher=SimpleSqlAlchemyFetcher(
engine,
table=Candidate,
column=Candidate.country,
),
store=FaissStore(
index_dir="./similarity_indexes",
index_name="country_similarity",
embedding_client=LiteLLMEmbeddingClient(
model="text-embedding-3-small", # to use openai embedding model
),
),
)


class CandidateView(SqlAlchemyBaseView):
"""
A view for retrieving candidates from the database.
"""

def get_select(self) -> sqlalchemy.Select:
"""
Creates the initial SqlAlchemy select object, which will be used to build the query.
"""
return sqlalchemy.select(Candidate)

@decorators.view_filter()
def at_least_experience(self, years: int) -> sqlalchemy.ColumnElement:
"""
Filters candidates with at least `years` of experience.
"""
return Candidate.years_of_experience >= years

@decorators.view_filter()
def senior_data_scientist_position(self) -> sqlalchemy.ColumnElement:
"""
Filters candidates that can be considered for a senior data scientist position.
"""
return sqlalchemy.and_(
Candidate.position.in_(["Data Scientist", "Machine Learning Engineer", "Data Engineer"]),
Candidate.years_of_experience >= 3,
)

@decorators.view_filter()
def from_country(self, country: Annotated[str, country_similarity]) -> sqlalchemy.ColumnElement:
"""
Filters candidates from a specific country.
"""
return Candidate.country == country
42 changes: 42 additions & 0 deletions examples/recruiting/cypher_text2sql_view.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# pylint: disable=missing-return-doc, missing-function-docstring, missing-class-docstring, missing-return-type-doc
from typing import List

import sqlalchemy
from sqlalchemy import text

from dbally.views.freeform.text2sql import BaseText2SQLView, ColumnConfig, TableConfig


class SampleText2SQLViewCyphers(BaseText2SQLView):
def get_tables(self) -> List[TableConfig]:
return [
TableConfig(
name="security_specialists",
columns=[
ColumnConfig("id", "SERIAL PRIMARY KEY"),
ColumnConfig("name", "VARCHAR(255)"),
ColumnConfig("cypher", "VARCHAR(255)"),
],
description="Knowledge base",
)
]


def create_freeform_memory_engine() -> sqlalchemy.Engine:
freeform_engine = sqlalchemy.create_engine("sqlite:///:memory:")

statements = [
"CREATE TABLE security_specialists (id INTEGER PRIMARY KEY, name TEXT, cypher TEXT)",
"INSERT INTO security_specialists (name, cypher) VALUES ('Alice', 'HAMAC')",
"INSERT INTO security_specialists (name, cypher) VALUES ('Bob', 'AES')",
"INSERT INTO security_specialists (name, cypher) VALUES ('Charlie', 'RSA')",
"INSERT INTO security_specialists (name, cypher) VALUES ('David', 'SHA2')",
]

with freeform_engine.connect() as conn:
for statement in statements:
conn.execute(text(statement))

conn.commit()

return freeform_engine
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
24 changes: 24 additions & 0 deletions examples/visualize_views_code.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# pylint: disable=missing-function-docstring
import asyncio

from recruiting.candidate_view_with_similarity_store import CandidateView, country_similarity, engine
from recruiting.cypher_text2sql_view import SampleText2SQLViewCyphers, create_freeform_memory_engine

import dbally
from dbally.audit import CLIEventHandler
from dbally.gradio import create_gradio_interface
from dbally.llms.litellm import LiteLLM


async def main():
await country_similarity.update()
llm = LiteLLM(model_name="gpt-3.5-turbo")
collection = dbally.create_collection("candidates", llm, event_handlers=[CLIEventHandler()])
collection.add(CandidateView, lambda: CandidateView(engine))
collection.add(SampleText2SQLViewCyphers, lambda: SampleText2SQLViewCyphers(create_freeform_memory_engine()))
gradio_interface = await create_gradio_interface(user_collection=collection)
gradio_interface.launch()


if __name__ == "__main__":
asyncio.run(main())
1 change: 1 addition & 0 deletions mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ nav:
- how-to/use_elastic_store.md
- how-to/use_custom_similarity_store.md
- how-to/update_similarity_indexes.md
- how-to/visualize_views.md
- how-to/log_runs_to_langsmith.md
- how-to/create_custom_event_handler.md
- how-to/openai_assistants_integration.md
Expand Down
6 changes: 5 additions & 1 deletion setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,11 @@ benchmark =
pydantic-settings~=2.0.3
psycopg2-binary~=2.9.9
elasticsearch =
elasticsearch==8.13.1
elasticsearch~=8.13.1
gradio =
gradio~=4.31.5
gradio_client~=0.16.4


[options.packages.find]
where = src
Expand Down
Loading
Loading