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

docs: Add a readme for the pgai docs. #307

Open
wants to merge 27 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
9a7835b
feat: Add a readme for the pgai docs.
billy-the-fish Dec 16, 2024
8b033e8
Apply suggestions from code review
billy-the-fish Dec 16, 2024
c779dbc
feat: update on review.
billy-the-fish Dec 16, 2024
c374cdc
chore: fix broken pgai build by pinning hatchling (#308)
JamesGuthrie Dec 16, 2024
3ced1f6
chore: support uv in extension install, use for dev (#309)
JamesGuthrie Dec 16, 2024
f6af7f0
feat: add a semantic catalog for db objs and example sql
jgpruitt Dec 2, 2024
606f4bd
feat: add ai.find_relevant_sql semantic catalog function
jgpruitt Dec 11, 2024
fb8d1c2
feat: add ai.find_relevant_obj() functions
jgpruitt Dec 11, 2024
fb73597
ci: pgspot chokes on valid code. disabling for now
jgpruitt Dec 11, 2024
29f52b8
fix: ignore ollama.base_url in test
jgpruitt Dec 11, 2024
28f5009
feat: only find relevant db objs that user has privs to
jgpruitt Dec 12, 2024
7464d74
feat: allow find_relevant_obj to be restricted to a given obj type
jgpruitt Dec 12, 2024
ae3d529
feat: return dist and add max_dist filter to semantic_catalog funcs
jgpruitt Dec 13, 2024
e8f4276
chore: clean up event triggers to only update columns strictly required
jgpruitt Dec 13, 2024
3eca351
chore: add foreign key constraints to semantic catalog on vectorizer
jgpruitt Dec 13, 2024
1f34e48
chore: reorder arguments for semantic catalog functions
jgpruitt Dec 13, 2024
937710a
chore: support multiple objtype filters in find_relevant_obj()
jgpruitt Dec 13, 2024
3ffdee5
feat: add vectorizer_embed convenience function
jgpruitt Dec 13, 2024
65321e4
chore: make an immutable version of vectorizer_embed
jgpruitt Dec 16, 2024
0b699f8
chore: rename semantic_catalog.name to semantic_catalog.catalog_name
jgpruitt Dec 16, 2024
6906c51
fix: exclude python system packages for versioned extension (#310)
JamesGuthrie Dec 17, 2024
586cdc7
fix: deprecation warning on re.split
MasterOdin Dec 17, 2024
4cd8a61
chore: remove pip caching
JamesGuthrie Dec 17, 2024
b62fb49
docs: remove openai mention from quickstart, fix opclasses in hnsw in…
Askir Dec 17, 2024
438a3fb
feat: construct a prompt for text-to-sql using relevant desc
jgpruitt Dec 17, 2024
1479ac5
feat: add a text_to_sql function
jgpruitt Dec 17, 2024
5d92f19
chore: split embedders in individual files (#315)
smoya Dec 18, 2024
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
1 change: 0 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ jobs:
uses: actions/setup-python@v5
with:
python-version: "3.10"
cache: "pip" # caching pip dependencies

- name: Verify Docker installation
run: |
Expand Down
73 changes: 73 additions & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@

<p align="center">
<img height="200" src="/docs/images/pgai_logo.png" alt="pgai"/>
</p>

<div align=center>

<h3>pgai documentation</h3>

[![Discord](https://img.shields.io/badge/Join_us_on_Discord-black?style=for-the-badge&logo=discord&logoColor=white)](https://discord.gg/KRdHVXAmkp)
[![Try Timescale for free](https://img.shields.io/badge/Try_Timescale_for_free-black?style=for-the-badge&logo=timescale&logoColor=white)](https://tsdb.co/gh-pgai-signup)
</div>

pgai is a PostgreSQL extension that simplifies data storage and retrieval for [Retrieval Augmented Generation](https://en.wikipedia.org/wiki/Prompt_engineering#Retrieval-augmented_generation) (RAG), and other AI applications.
In particular, it automates the creation and sync of embeddings for your data stored in PostgreSQL, simplifies
[semantic search](https://en.wikipedia.org/wiki/Semantic_search), and allows you to call LLM models from SQL.

The pgai documentation helps you setup, use and develop the projects that make up pgai.


## pgai Vectorizer

Vectorizer automates the embedding process within your database management by treating embeddings as a declarative,
DDL-like feature — like an index.

- **Get started**:
* [Vectorizer quickstart for Ollama](/docs/vectorizer-quick-start.md): setup your developer environment, create and run a vectorizer.
* [Vectorizer quickstart for OpenAI](/docs/vectorizer-quick-start-openai.md): setup your developer environment, create and run a vectorizer using OpenAI.
* [Vectorizer quickstart for Voyage](/docs/vectorizer-quick-start-voyage.md): setup your developer environment, create and run a vectorizer using Voyage.
- **Use**:
* [Automate AI embedding with pgai Vectorizer](/docs/vectorizer.md): a comprehensive overview of Vectorizer features,
demonstrating how it streamlines the process of working with vector embeddings in your database.
* [Run vectorizers using pgai vectorizer worker](/docs/vectorizer-worker.md): run vectorizers on a self-hosted TimescaleDB instance.
- **Develop**:
* [Add a Vectorizer embedding integration](/docs/vectorizer-add-a-embedding-integration.md):
- **Reference**:
* [pgai Vectorizer API reference](/docs/vectorizer-api-reference.md): API reference for Vectorizer functions

## pgai install

* [Install pgai with Docker](/docs/install_docker.md): run pgai in a container environment.
* [Install pgai from source](/docs/install_from_source.md): create a developer environment for pgai.

## pgai model calling

Simplifies data storage and retrieval for AI apps.

- **Choose your model**:

| **Model** | **Tokenize** | **Embed** | **Chat Complete** | **Generate** | **Moderate** | **Classify** | **Rerank** |
|------------------|:------------:|:---------:|:-----------------:|:------------:|:------------:|:------------:|:----------:|
| **[Ollama](/docs/ollama.md)** | | ✔️ | ✔️ | ✔️ | | | |
| **[OpenAI](/docs/openai.md)** | ✔️️ | ✔️ | ✔️ | | ✔️ | | |
| **[Anthropic](/docs/anthropic.md)** | | | | ✔️ | | | |
| **[Cohere](/docs/cohere.md)** | ✔️ | ✔️ | ✔️ | | | ✔️ | ✔️ |
| **[Voyage AI](/docs/voyageai.md)** | | ✔️ | | | | | |


- **Use**:
* [Delayed embed](/docs/delayed_embed.md): run pgai using pgai or TimescaleDB background actions.
* [Load dataset from Hugging Face](/docs/load_dataset_from_huggingface.md): load datasets from Hugging Face's datasets library directly into your PostgreSQL database.
* [Moderate comments using OpenAI](/docs/moderate.md): use triggers or actions to moderate comments using OpenAI.
* [Secure pgai with user privilages](/docs/privileges.md): grant the necessary permissions for a specific user or role to use pgai functionality.










2 changes: 1 addition & 1 deletion docs/moderate.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Moderate
# Moderate comments using OpenAI

Let's say you want to moderate comments using OpenAI. You can do it in two ways:

Expand Down
1 change: 1 addition & 0 deletions docs/privileges.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
# Secure pgai with user privilages

The ai.grant_ai_usage function is an important security and access control tool
in the pgai extension. Its primary purpose is to grant the necessary permissions
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Adding a Vectorizer embedding integration
# Add a Vectorizer embedding integration

We welcome contributions to add new vectorizer embedding integrations.

Expand Down Expand Up @@ -31,13 +31,17 @@ integration. Update the tests to account for the new function.
The vectorizer worker reads the database's vectorizer configuration at runtime
and turns it into a `pgai.vectorizer.Config`.

To add a new integration, add a new embedding class with fields corresponding
to the database's jsonb configuration to `pgai/vectorizer/embeddings.py`. See
To add a new integration, add a new file containing the embedding class
with fields corresponding to the database's jsonb configuration into the
[embedders directory] directory. See
the existing implementations for examples of how to do this. Implement the
`Embedder` class' abstract methods. Use first-party python libraries for the
integration, if available. If no first-party python libraries are available,
use direct HTTP requests.

Remember to include the import line of your recently created class into the
[embedders \_\_init\_\_.py].

Add tests which perform end-to-end testing of the new integration. There are
two options for handling API calls to the integration API:

Expand All @@ -49,6 +53,8 @@ used conservatively. We will determine on a case-by-case basis what level of
testing we would like.

[vcr.py]:https://vcrpy.readthedocs.io/en/latest/
[embedders directory]:/projects/pgai/pgai/vectorizer/embedders
[embedders \_\_init\_\_.py]:/projects/pgai/pgai/vectorizer/embedders/__init__.py

## Documentation

Expand Down
8 changes: 4 additions & 4 deletions docs/vectorizer-api-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -602,7 +602,7 @@ HNSW is suitable for in-memory datasets and scenarios where query speed is cruci
```sql
SELECT ai.create_vectorizer(
'blog_posts'::regclass,
indexing => ai.indexing_hnsw(min_rows => 50000, opclass => 'vector_l2_ops'),
indexing => ai.indexing_hnsw(min_rows => 50000, opclass => 'vector_l1_ops'),
-- other parameters...
);
```
Expand All @@ -614,10 +614,10 @@ HNSW is suitable for in-memory datasets and scenarios where query speed is cruci
| Name | Type | Default | Required | Description |
|------|------|---------------------|-|----------------------------------------------------------------------------------------------------------------|
|min_rows| int | 100000 |✖| The minimum number of rows before creating the index |
|opclass| text | `vector_cosine_ops` |✖| The operator class for the index. Possible values are:`vector_cosine_ops`, `vector_l2_ops`, or `vector_ip_ops` |
|opclass| text | `vector_cosine_ops` |✖| The operator class for the index. Possible values are:`vector_cosine_ops`, `vector_l1_ops`, or `vector_ip_ops` |
|m| int | - |✖| Advanced [HNSW parameters](https://en.wikipedia.org/wiki/Hierarchical_navigable_small_world) |
|ef_construction| int | - |✖| Advanced [HNSW parameters](https://en.wikipedia.org/wiki/Hierarchical_navigable_small_world) |
| create_when_queue_empty| boolean | true |✖| Create the index only after all of the embeddings have been generated. |
|ef_construction| int | - |✖| Advanced [HNSW parameters](https://en.wikipedia.org/wiki/Hierarchical_navigable_small_world) |
| create_when_queue_empty| boolean | true |✖| Create the index only after all of the embeddings have been generated. |


#### Returns
Expand Down
2 changes: 1 addition & 1 deletion docs/vectorizer-quick-start.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ If you prefer working with the OpenAI API instead of self-hosting models, you ca

## Setup a local development environment

To set up a development environment for OpenAI, use a docker compose file that includes a:
To set up a development environment, use a docker compose file that includes a:
- Postgres deployment image with the TimescaleDB and pgai extensions installed
- pgai vectorizer worker image
- ollama image to host embedding and large language models
Expand Down
9 changes: 5 additions & 4 deletions projects/extension/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ RUN set -e; \
FROM base AS pgai-test-db
ENV PG_MAJOR=${PG_MAJOR}
ENV PIP_BREAK_SYSTEM_PACKAGES=1
RUN pip install uv==0.5.9
WORKDIR /pgai
COPY . .
RUN just build install
Expand All @@ -58,17 +59,17 @@ FROM base
ENV WHERE_AM_I=docker
USER root

RUN pip install --break-system-packages uv==0.5.9

# install pgspot
ENV PIP_BREAK_SYSTEM_PACKAGES=1
RUN set -eux; \
git clone https://github.com/timescale/pgspot.git /build/pgspot; \
pip install /build/pgspot; \
uv pip install --system --break-system-packages /build/pgspot; \
rm -rf /build/pgspot

# install our test python dependencies
ENV PIP_BREAK_SYSTEM_PACKAGES=1
COPY requirements-test.txt /build/requirements-test.txt
RUN pip install -r /build/requirements-test.txt
RUN uv pip install --system --break-system-packages -r /build/requirements-test.txt
RUN rm -r /build

WORKDIR /pgai
33 changes: 26 additions & 7 deletions projects/extension/build.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ def check_versions():


def parse_version(version: str) -> tuple[int, int, int, str | None]:
parts = re.split(r"[.-]", version, 4)
parts = re.split(r"[.-]", version, maxsplit=4)
return (
int(parts[0]),
int(parts[1]),
Expand Down Expand Up @@ -283,6 +283,13 @@ def build_idempotent_sql_file(input_file: Path) -> str:
r = plpy.execute("select coalesce(pg_catalog.current_setting('ai.python_lib_dir', true), '{python_install_dir()}') as python_lib_dir")
python_lib_dir = r[0]["python_lib_dir"]
from pathlib import Path
import sys
import sysconfig
# Note: the "old" (pre-0.4.0) packages are installed as system-level python packages
# and take precedence over our extension-version specific packages.
# By removing the whole thing from the path we won't run into package conflicts.
if "purelib" in sysconfig.get_path_names() and sysconfig.get_path("purelib") in sys.path:
sys.path.remove(sysconfig.get_path("purelib"))
python_lib_dir = Path(python_lib_dir).joinpath("{this_version()}")
import site
site.addsitedir(str(python_lib_dir))
Expand Down Expand Up @@ -451,9 +458,13 @@ def install_old_py_deps() -> None:
old_reqs_file = ext_dir().joinpath("old_requirements.txt").resolve()
if old_reqs_file.is_file():
env = {k: v for k, v in os.environ.items()}
env["PIP_BREAK_SYSTEM_PACKAGES"] = "1"
cmd = (
f"pip3 install -v --compile --break-system-packages -r {old_reqs_file}"
if shutil.which("uv") is None
else f"uv pip install -v --compile --system --break-system-packages -r {old_reqs_file}"
)
subprocess.run(
f"pip3 install -v --compile -r {old_reqs_file}",
cmd,
shell=True,
check=True,
env=env,
Expand Down Expand Up @@ -482,8 +493,10 @@ def install_prior_py() -> None:
env=os.environ,
)
tmp_src_dir = tmp_dir.joinpath("projects", "extension").resolve()
bin = "pip3" if shutil.which("uv") is None else "uv pip"
cmd = f'{bin} install -v --compile --target "{version_target_dir}" "{tmp_src_dir}"'
subprocess.run(
f'pip3 install -v --compile -t "{version_target_dir}" "{tmp_src_dir}"',
cmd,
check=True,
shell=True,
env=os.environ,
Expand Down Expand Up @@ -524,17 +537,23 @@ def install_py() -> None:
"pgai-*.dist-info"
): # delete package info if exists
shutil.rmtree(d)
bin = "pip3" if shutil.which("uv") is None else "uv pip"
cmd = f'{bin} install -v --no-deps --compile --target "{version_target_dir}" "{ext_dir()}"'
subprocess.run(
f'pip3 install -v --no-deps --compile -t "{version_target_dir}" "{ext_dir()}"',
cmd,
check=True,
shell=True,
env=os.environ,
cwd=str(ext_dir()),
)
else:
version_target_dir.mkdir(exist_ok=True)
bin = "pip3" if shutil.which("uv") is None else "uv pip"
cmd = (
f'{bin} install -v --compile --target "{version_target_dir}" "{ext_dir()}"'
)
subprocess.run(
f'pip3 install -v --compile -t "{version_target_dir}" "{ext_dir()}"',
cmd,
check=True,
shell=True,
env=os.environ,
Expand Down Expand Up @@ -633,7 +652,7 @@ def lint_py() -> None:

def lint() -> None:
lint_py()
lint_sql()
# lint_sql() # TODO: enable this when pgspot is fixed


def format_py() -> None:
Expand Down
1 change: 1 addition & 0 deletions projects/extension/requirements-test.txt
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@ python-dotenv==1.0.1
fastapi==0.112.0
fastapi-cli==0.0.5
psycopg[binary]==3.2.1
uv==0.5.9
65 changes: 65 additions & 0 deletions projects/extension/sql/idempotent/013-vectorizer-api.sql
Original file line number Diff line number Diff line change
Expand Up @@ -596,3 +596,68 @@ select
end as pending_items
from ai.vectorizer v
;

-------------------------------------------------------------------------------
-- vectorizer_embed
create or replace function ai.vectorizer_embed
( embedding_config pg_catalog.jsonb
, input_text pg_catalog.text
, input_type pg_catalog.text default null
) returns @extschema:[email protected]
as $func$
declare
_emb @extschema:[email protected];
begin
case embedding_config operator(pg_catalog.->>) 'implementation'
when 'openai' then
_emb = ai.openai_embed
( embedding_config operator(pg_catalog.->>) 'model'
, input_text
, api_key_name=>(embedding_config operator(pg_catalog.->>) 'api_key_name')
, dimensions=>(embedding_config operator(pg_catalog.->>) 'dimensions')::pg_catalog.int4
, openai_user=>(embedding_config operator(pg_catalog.->>) 'user')
);
when 'ollama' then
_emb = ai.ollama_embed
( embedding_config operator(pg_catalog.->>) 'model'
, input_text
, host=>(embedding_config operator(pg_catalog.->>) 'base_url')
, keep_alive=>(embedding_config operator(pg_catalog.->>) 'keep_alive')
, embedding_options=>(embedding_config operator(pg_catalog.->) 'options')
);
when 'voyageai' then
_emb = ai.voyageai_embed
( embedding_config operator(pg_catalog.->>) 'model'
, input_text
, input_type=>coalesce(input_type, 'query')
, api_key_name=>(embedding_config operator(pg_catalog.->>) 'api_key_name')
);
else
raise exception 'unsupported embedding implementation';
end case;

return _emb;
end
$func$ language plpgsql immutable security invoker
set search_path to pg_catalog, pg_temp
;

-------------------------------------------------------------------------------
-- vectorizer_embed
create or replace function ai.vectorizer_embed
( vectorizer_id pg_catalog.int4
, input_text pg_catalog.text
, input_type pg_catalog.text default null
) returns @extschema:[email protected]
as $func$
select ai.vectorizer_embed
( v.config operator(pg_catalog.->) 'embedding'
, input_text
, input_type
)
from ai.vectorizer v
where v.id operator(pg_catalog.=) vectorizer_id
;
$func$ language sql stable security invoker
set search_path to pg_catalog, pg_temp
;
Loading