Skip to content

Commit 825b8fe

Browse files
authored
docs(views): add docs for freeform views (#45)
1 parent 4284344 commit 825b8fe

16 files changed

+191
-41
lines changed

docs/concepts/freeform_views.md

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,41 @@
22

33
Freeform views are a type of [view](views.md) that provides a way for developers using db-ally to define what they need from the LLM without requiring a fixed response structure. This flexibility is beneficial when the data structure is unknown beforehand or when potential queries are too diverse to be covered by a structured view. Though freeform views offer more flexibility than structured views, they are less predictable, efficient, and secure, and may be more challenging to integrate with other systems. For these reasons, we recommend using [structured views](./structured_views.md) when possible.
44

5-
Unlike structured views, which define a response format and a set of operations the LLM may use in response to natural language queries, freeform views only have one task - to respond directly to natural language queries with data from the datasource. They accomplish this by implementing the [`ask`][dbally.views.base.BaseView] method. This method takes a natural language query as input and returns a response. The method also has access to the LLM model (via the `llm` attribute), which is typically used to retrieve the correct data from the source (for example, by generating a source-specific query string). To learn more about implementing freeform views, refer to the [How to: Custom Freeform Views](../how-to/custom_freeform_views.md) guide.
5+
Unlike structured views, which define a response format and a set of operations the LLM may use in response to natural language queries, freeform views only have one task - to respond directly to natural language queries with data from the datasource. They accomplish this by implementing the [`ask`][dbally.views.base.BaseView.ask] method. This method takes a natural language query as input and returns a response. The method also has access to the LLM model (via the `llm` attribute), which is typically used to retrieve the correct data from the source (for example, by generating a source-specific query string).
66

7-
## Security
7+
!!! example
8+
For instance, this is a simple view that uses SQLAlchemy to query candidate table in a SQL database. It contains a single table definition, which LLM will use to generate the appropriate SQL query to answer the question:
9+
10+
```python
11+
class CandidateView(BaseText2SQLView):
12+
"""
13+
A view for retrieving candidates from the database.
14+
"""
15+
16+
def get_tables(self) -> List[TableConfig]:
17+
"""
18+
Defines the tables available in the database.
19+
"""
20+
return [
21+
TableConfig(
22+
name="candidate",
23+
columns=[
24+
ColumnConfig(
25+
name="name",
26+
data_type="VARCHAR",
27+
),
28+
ColumnConfig(
29+
name="country",
30+
data_type="VARCHAR",
31+
),
32+
...
33+
],
34+
),
35+
...
36+
]
37+
```
38+
39+
To learn more about implementing freeform views, refer to the [How to: Create text-to-SQL Freeform View](../how-to/custom_freeform_views.md) guide.
840

941
!!! warning
10-
When using freeform views, the LLM typically gets raw access to the data source and can execute arbitrary operations on it using the query language of the data source (e.g., SQL). This can be powerful but also necessitates that the developer be extremely cautious about securing the data source outside of db-ally. For instance, in the case of Relational Databases, the developer should ensure that the database user used by db-ally has read-only access to the database, and that the database does not contain any sensitive data that shouldn't be exposed to the LLM.
42+
When using freeform views, the LLM typically gets raw access to the data source and can execute arbitrary operations on it using the query language of the data source (e.g., SQL). This can be powerful but also necessitates that the developer be extremely cautious about securing the data source outside of db-ally. For instance, in the case of Relational Databases, the developer should ensure that the database user used by db-ally has read-only access to the database, and that the database does not contain any sensitive data that shouldn't be exposed to the LLM.

docs/concepts/structured_views.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ Given different natural language queries, a db-ally view will produce different
1010
Each structured view can contain one or more “filters”, which the LLM may decide to choose and apply to the extracted data so that it meets the criteria specified in the natural language query. Given such a query, LLM chooses which filters to use, provides arguments to the filters, and connects the filters with Boolean operators. The LLM expresses these filter combinations using a special language called [IQL](iql.md), in which the defined view filters provide a layer of abstraction between the LLM and the raw syntax used to query the data source (e.g., SQL).
1111

1212
!!! example
13-
For instance, this is a simple [view that uses SQLAlchemy](../how-to/sql_views.md) to select data from specific columns in a SQL database. It contains a single filter, that the LLM may optionally use to control which table rows to fetch:
13+
For instance, this is a simple [view that uses SQLAlchemy](../how-to/views/sql.md) to select data from specific columns in a SQL database. It contains a single filter, that the LLM may optionally use to control which table rows to fetch:
1414

1515
```python
1616
class CandidateView(SqlAlchemyBaseView):

docs/how-to/custom_freeform_views.md

Lines changed: 0 additions & 3 deletions
This file was deleted.

docs/how-to/custom_views.md renamed to docs/how-to/views/custom.md

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
# How-To: Use custom data sources with db-ally
22

33
!!! note
4-
This is an advanced topic. If you're looking to create a view that retrieves data from an SQL database, please refer to the [SQL Views](sql_views.md) guide instead.
4+
This is an advanced topic. If you're looking to create a view that retrieves data from an SQL database, please refer to the [SQL Views](sql.md) guide instead.
55

6-
In this guide, we'll show you how to create [structured views](../concepts/structured_views.md) that connect to custom data sources. This could be useful if you need to retrieve data from a REST API, a NoSQL database, or any other data source not supported by the built-in base views.
6+
In this guide, we'll show you how to create [structured views](../../concepts/structured_views.md) that connect to custom data sources. This could be useful if you need to retrieve data from a REST API, a NoSQL database, or any other data source not supported by the built-in base views.
77

8-
# Summary
8+
## Intro
99
Firstly, we will create a custom base view called `FilteredIterableBaseView` that retrieves data from a Python iterable and allows it to be filtered. It forms the base that implements data source-specific logic and lets other views inherit from it in order to define filters for specific use cases (similar to how `SqlAlchemyBaseView` is a base view provided by db-ally).
1010

1111
Then, we will create a view called `CandidatesView` that inherits from `FilteredIterableBaseView` and represents a use case of retrieving a list of job candidates. We will define filters for this view.
@@ -53,7 +53,7 @@ class CandidateView(FilteredIterableBaseView):
5353

5454
Lastly, we will illustrate how to use the `CandidatesView` like any other view in db-ally. We will create an instance of the view, add it to a collection, and start querying it.
5555

56-
## Types of Custom Views
56+
## Types of custom views
5757
There are two main ways to create custom structured views:
5858

5959
* By subclassing the `MethodsBaseView`: This is the most common method. These views expect filters to be defined as class methods and help manage them. All the built-in db-ally views use this method.
@@ -62,12 +62,12 @@ There are two main ways to create custom structured views:
6262
If you're not sure which method to choose, we recommend starting with the `MethodsBaseView`. It's simpler and easier to use, and you can switch to the `BaseStructuredView` later if you find you need more control over filter management. For this guide, we'll focus on the `MethodsBaseView`.
6363

6464
!!! note
65-
Both are methods of creating [structured views](../concepts/structured_views.md). If you're looking to create a [freeform view](../concepts/freeform_views.md), refer to the [Freeform Views](custom_freeform_views.md) guide instead.
65+
Both are methods of creating [structured views](../../concepts/structured_views.md). If you're looking to create a [freeform view](../../concepts/freeform_views.md), refer to the [Freeform Views](custom_freeform_views.md) guide instead.
6666

67-
## The Example
67+
## Example
6868
Throughout the guide, we'll use an example of creating a custom base view called `FilteredIterableBaseView`. To keep things simple, the "data source" it uses is a list defined in Python. The goal is to demonstrate how to create a custom view and define filters for it. In most real-world scenarios, data would usually come from an external source, like a REST API or a database.
6969

70-
Next, we are going to create a view that inherits from `FilteredIterableBaseView` and implements a use case of retrieving a list of job candidates. This is the same use case from the [Quickstart](../quickstart/index.md) guide - but this time we'll use a custom view instead of the built-in `SqlAlchemyBaseView`. For comparison, you can refer to the Quickstart guide.
70+
Next, we are going to create a view that inherits from `FilteredIterableBaseView` and implements a use case of retrieving a list of job candidates. This is the same use case from the [Quickstart](../../quickstart/index.md) guide - but this time we'll use a custom view instead of the built-in `SqlAlchemyBaseView`. For comparison, you can refer to the Quickstart guide.
7171

7272
Before we start, let's define a simple data class to represent a candidate:
7373

@@ -83,8 +83,8 @@ class Candidate:
8383
country: str
8484
```
8585

86-
## Creating a Custom View
87-
In db-ally, the typical approach is to have a base view that inherits from `MethodsBaseView` and implements elements specific to a type of data source (for example, [`SqlAlchemyBaseView`](./sql_views.md) is a base view provided by db-ally). Subsequently, you can create views that inherit from this base view, reflecting business logic specific to given use cases, including defining the filter methods.
86+
## Creating a custom view
87+
In db-ally, the typical approach is to have a base view that inherits from `MethodsBaseView` and implements elements specific to a type of data source (for example, [`SqlAlchemyBaseView`](sql.md) is a base view provided by db-ally). Subsequently, you can create views that inherit from this base view, reflecting business logic specific to given use cases, including defining the filter methods.
8888

8989
For our example, let's create a base class called `FilteredIterableBaseView`:
9090

@@ -100,7 +100,7 @@ class FilteredIterableBaseView(MethodsBaseView):
100100
For now, this class is empty, but we'll build upon it in the following sections.
101101

102102
## Specifying how filters should be applied
103-
Classes that inherit from `FilteredIterableBaseView` can define filters as methods. The LLM will choose which of these filters to use, feed them with arguments, and determine how to combine them using boolean operators. To achieve this, the LLM uses its own language, [IQL](../concepts/iql.md). db-ally translates the IQL expressions and passes the parsed IQL tree to your view via the `apply_filters` method. This method is responsible for identifying the selected filter methods and applying them to the data (which could vary based on the data source).
103+
Classes that inherit from `FilteredIterableBaseView` can define filters as methods. The LLM will choose which of these filters to use, feed them with arguments, and determine how to combine them using boolean operators. To achieve this, the LLM uses its own language, [IQL](../../concepts/iql.md). db-ally translates the IQL expressions and passes the parsed IQL tree to your view via the `apply_filters` method. This method is responsible for identifying the selected filter methods and applying them to the data (which could vary based on the data source).
104104

105105
Let's implement the required `apply_filters` method in our `FilteredIterableBaseView`. Additionally, we'll create a helper method, `build_filter_node`, responsible for handling a single node of the IQL tree (either a filter or a logical operator). We'll use recursion to handle these nodes since they can have children (a logical operator can have two children that are filters, for instance).
106106

@@ -170,7 +170,7 @@ def execute(self, dry_run: bool = False) -> ViewExecutionResult:
170170

171171
The `execute` function gets the data (by calling the `get_data` method) and applies the combined filters to it. We're using the [`filter`](https://docs.python.org/3/library/functions.html#filter) function from Python's standard library to accomplish this. The filtered data is then returned as a list.
172172

173-
## Creating a View
173+
## Creating a view
174174
Now that `FilteredIterableBaseView` is complete, we can create a view that inherits from it and represents the use case of retrieving a list of job candidates. We'll name this view `CandidatesView`:
175175

176176
```python
@@ -244,5 +244,5 @@ Retrieved 1 candidates:
244244
Candidate(id=2, name='Jane Doe', position='Data Engineer', years_of_experience=3, country='France')
245245
```
246246

247-
## Full Example
247+
## Full example
248248
You can access the complete example here: [custom_views_code.py](custom_views_code.py)
File renamed without changes.

docs/how-to/pandas_views.md renamed to docs/how-to/views/pandas.md

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
# How-To: Use Pandas DataFrames with db-ally
22

3-
In this guide, you will learn how to write [structured views](../concepts/structured_views.md) that use [Pandas](https://pandas.pydata.org/) DataFrames as their data source. You will understand how to define such a view, create filters that operate on the DataFrame, and register it while providing it with the source DataFrame.
3+
In this guide, you will learn how to write [structured views](../../concepts/structured_views.md) that use [Pandas](https://pandas.pydata.org/) DataFrames as their data source. You will understand how to define such a view, create filters that operate on the DataFrame, and register it while providing it with the source DataFrame.
44

5-
The example used in this guide is a DataFrame containing information about candidates. The DataFrame includes columns such as `id`, `name`, `country`, `years_of_experience`. This is the same use case as the one in the [Quickstart](../quickstart/index.md) and [Custom Views](./custom_views.md) guides. Please feel free to compare the different approaches.
5+
The example used in this guide is a DataFrame containing information about candidates. The DataFrame includes columns such as `id`, `name`, `country`, `years_of_experience`. This is the same use case as the one in the [Quickstart](../../quickstart/index.md) and [Custom Views](./custom.md) guides. Please feel free to compare the different approaches.
66

7-
## The DataFrame
7+
## Data
88
Here is an example of a DataFrame containing information about candidates:
99

1010
```python
@@ -19,7 +19,7 @@ CANDIDATE_DATA = pd.DataFrame.from_records([
1919
])
2020
```
2121

22-
## View Definition
22+
## View definition
2323
Views operating on Pandas DataFrames are defined by subclassing the `DataFrameBaseView` class:
2424

2525
```python
@@ -69,8 +69,8 @@ def senior_data_scientist_position(self) -> pd.Series:
6969
& (self.df.years_of_experience >= 3)
7070
```
7171

72-
# Registering the View
73-
To use the view, you need to create a [Collection](../concepts/collections.md) and register the view with it. This is done in the same manner as registering other types of views, but you need to provide the view with the DataFrame on which it should operate:
72+
## Registering the view
73+
To use the view, you need to create a [Collection](../../concepts/collections.md) and register the view with it. This is done in the same manner as registering other types of views, but you need to provide the view with the DataFrame on which it should operate:
7474

7575
```python
7676
import dbally
@@ -94,5 +94,5 @@ Retrieved 1 candidates:
9494
{'id': 2, 'name': 'Jane Doe', 'position': 'Data Engineer', 'years_of_experience': 3, 'country': 'France'}
9595
```
9696

97-
## Full Example
97+
## Full example
9898
You can access the complete example here: [pandas_views_code.py](pandas_views_code.py)
File renamed without changes.

0 commit comments

Comments
 (0)