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: adding basic example for MVI #369

Closed
wants to merge 55 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
55 commits
Select commit Hold shift + click to select a range
7ecb504
chore: bump momento-wire-types
malandis Aug 15, 2023
65225fd
feat: add index control responses
malandis Aug 15, 2023
c9b675d
feat: add vector index client, control operations, and config
malandis Aug 15, 2023
955483b
test: read local credentials for testing (temporary)
malandis Aug 15, 2023
a216033
test: test vector index control operations
malandis Aug 15, 2023
8a11f19
refactor: add sorted set responses to top level package
malandis Aug 15, 2023
5b5fd73
feat: add data client and upsert item batch
malandis Aug 16, 2023
4ae1745
tests: test upsert and search
malandis Aug 16, 2023
b8b3d7a
refactor: move vector index responses under separate subpackage
malandis Aug 16, 2023
076a64e
refactor: shorten data response class names
malandis Aug 16, 2023
4a429f6
refactor: use "Preview" prefix to vector index client
malandis Aug 16, 2023
b0f48bd
docs: update docs to use "Preview" terminology
malandis Aug 16, 2023
53af9ed
docs: add more detail to the search API
malandis Aug 16, 2023
42ab139
refactor: clean up reading root certificates for vector index client
malandis Aug 16, 2023
bf1195e
chore: suppress running vector index client tests by default
malandis Aug 16, 2023
252126b
fix: flake8 errors
malandis Aug 16, 2023
fc57ab1
fix: mypy errors
malandis Aug 16, 2023
d978bbf
test: add test that exercises topk
malandis Aug 16, 2023
5085502
chore: remove unneded fields from list index response
malandis Aug 17, 2023
7c604f6
fix: flake8 error
malandis Aug 17, 2023
bbc63c2
refactor: rename `_VectorIndexClient` to `_VectorIndexDataClient`
malandis Aug 17, 2023
6bb890e
refactor: use dedicated vector index control client and grpc manager
malandis Aug 17, 2023
e82cd9f
refactor: make independent VectorIndexConfiguration
malandis Aug 17, 2023
d9829d6
chore: update to latest protos
malandis Aug 18, 2023
a718383
feat: replace upsert operation with add
malandis Aug 18, 2023
2b9321c
feat: add delete item api
malandis Aug 18, 2023
fe3791d
Merge branch 'main' into feat/vector-index-client
malandis Aug 18, 2023
3244da3
fix: flake8 errors
malandis Aug 18, 2023
6fdde39
fix: mypy errors
malandis Aug 18, 2023
682c0ea
docs: tweak documentation
malandis Aug 18, 2023
51c840c
chore: use default factory for metadata in dataclasses
malandis Aug 20, 2023
8bee250
refactor: remove code to connect to local instance
malandis Aug 23, 2023
b2d491e
tests: test the vector index directory as well
malandis Aug 24, 2023
60ba689
fix: remove vestiges of root certificates
malandis Aug 24, 2023
a620837
chore: remove unused import
malandis Aug 24, 2023
3cf48a6
chore: implement missing tests, assertions, and fix flake errors
pratik151192 Aug 24, 2023
e00bd2c
fix: fixing CI workflow env variable name
pratik151192 Aug 24, 2023
ba16de6
chore: adding service name to exceptions and extending assert messages
pratik151192 Aug 24, 2023
691028c
chore: set max parallelism to 2 for tests
pratik151192 Aug 25, 2023
2758ec2
Merge branch 'main' into feat/vector-index-client
pratik151192 Aug 25, 2023
f9affe4
chore: add max parallel under strategy level
pratik151192 Aug 25, 2023
d81a31e
fix: re-add imports accidentlly deleted
pratik151192 Aug 25, 2023
bb7bb69
chore: fixing isort erros
pratik151192 Aug 25, 2023
7548c0b
chore: change tests parallelism to 1
pratik151192 Aug 25, 2023
afceb8f
chore: run MVI tests on macOS
pratik151192 Aug 25, 2023
355d884
chore: readding MVI vector name to workflows
pratik151192 Aug 25, 2023
36f7ffd
chore: add poetry to macOs path as its not auto added
pratik151192 Aug 25, 2023
b056822
chore: provide explicit poetry path
pratik151192 Aug 25, 2023
2f437ad
chore: remove max-parallel to non MVI tests
pratik151192 Aug 25, 2023
b99977b
feat: add vanilla example for mvi
pratik151192 Aug 25, 2023
e59588d
chore: removed unneeded logging library
pratik151192 Aug 25, 2023
4728d9a
chore: add README and a sleep before search
pratik151192 Aug 25, 2023
b89d119
chore: adding delete examples and updating readme
pratik151192 Aug 25, 2023
6e174dd
chore: Update READMe
pratik151192 Aug 25, 2023
9d7467b
chore: adding sleep before searching after deleting items
pratik151192 Aug 25, 2023
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
67 changes: 67 additions & 0 deletions .github/workflows/on-pull-request-mvi.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
name: On Pull Request

on:
pull_request:
branches: [main]

jobs:
test:
runs-on: macos-latest
strategy:
max-parallel: 2
matrix:
python-version: ["3.7", "3.8", "3.9", "3.10", "3.11"]
new-python-protobuf: ["true"]
include:
- python-version: "3.7"
new-python-protobuf: "false"

env:
TEST_AUTH_TOKEN: ${{ secrets.ALPHA_TEST_AUTH_TOKEN }}
TEST_CACHE_NAME: python-integration-test-${{ matrix.python-version }}-${{ matrix.new-python-protobuf }}-${{ github.sha }}
TEST_VECTOR_INDEX_NAME: python-integration-test-vector-${{ matrix.python-version }}-${{ matrix.new-python-protobuf }}-${{ github.sha }}

steps:
- uses: actions/checkout@v3

- name: Commitlint and Other Shared Build Steps
uses: momentohq/standards-and-practices/github-actions/shared-build@gh-actions-v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

- name: Setup Python ${{ matrix.python-version }}
uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python-version }}

- name: Bootstrap poetry
run: |
curl -sL https://install.python-poetry.org | python - -y --version 1.3.1

- name: Configure poetry
run: /Users/runner/.local/bin/poetry config virtualenvs.in-project true

- name: Install dependencies
run: /Users/runner/.local/bin/poetry install

- name: Install Old Protobuf
# Exercises the wire types generated against the old protobuf library
if: matrix.new-python-protobuf == 'false'
run: /Users/runner/.local/bin/poetry add "protobuf<3.20"

- name: Run mypy
# mypy has inconsistencies between 3.7 and the rest; default to lowest common denominator
if: matrix.python-version == '3.7'
run: /Users/runner/.local/bin/poetry run mypy src tests

- name: Run flake8
run: /Users/runner/.local/bin/poetry run flake8 src tests

- name: Run black
run: /Users/runner/.local/bin/poetry run black src tests --check --diff

- name: Run isort
run: /Users/runner/.local/bin/poetry run isort . --check --diff

- name: Run tests
run: /Users/runner/.local/bin/poetry run pytest tests/momento/vector_index_client -p no:sugar -q
4 changes: 3 additions & 1 deletion .github/workflows/on-pull-request.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,11 @@ jobs:
include:
- python-version: "3.7"
new-python-protobuf: "false"

env:
TEST_AUTH_TOKEN: ${{ secrets.ALPHA_TEST_AUTH_TOKEN }}
TEST_CACHE_NAME: python-integration-test-${{ matrix.python-version }}-${{ matrix.new-python-protobuf }}-${{ github.sha }}
TEST_VECTOR_INDEX_NAME: python-integration-test-vector-${{ matrix.python-version }}-${{ matrix.new-python-protobuf }}-${{ github.sha }}

steps:
- uses: actions/checkout@v3
Expand Down Expand Up @@ -61,7 +63,7 @@ jobs:
run: poetry run isort . --check --diff

- name: Run tests
run: poetry run pytest -p no:sugar -q
run: poetry run pytest -p no:sugar -q --ignore=tests/momento/vector_index_client

test-examples:
runs-on: ubuntu-20.04
Expand Down
5 changes: 4 additions & 1 deletion .github/workflows/on-push-to-release-branch.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@ jobs:
env:
TEST_AUTH_TOKEN: ${{ secrets.ALPHA_TEST_AUTH_TOKEN }}
TEST_CACHE_NAME: python-integration-test-${{ matrix.python-version }}-${{ matrix.new-python-protobuf }}-${{ github.sha }}
TEST_VECTOR_INDEX_NAME: python-integration-test-vector-${{ matrix.python-version }}-${{ matrix.new-python-protobuf }}-${{ github.sha }}


steps:
- uses: actions/checkout@v3

Expand Down Expand Up @@ -79,7 +82,7 @@ jobs:
run: poetry run isort . --check --diff

- name: Run tests
run: poetry run pytest -p no:sugar -q
run: poetry run pytest -p no:sugar -q --ignore=tests/momento/vector_index_client

publish:
runs-on: ubuntu-20.04
Expand Down
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,5 @@ dist/
momento-examples-env/

*.egg-info
build
build
certs
5 changes: 5 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,10 @@ lint:
do-gen-sync:
@poetry run python src/momento/internal/codegen.py src/momento/internal/aio/_scs_control_client.py src/momento/internal/synchronous/_scs_control_client.py
@poetry run python src/momento/internal/codegen.py src/momento/internal/aio/_scs_data_client.py src/momento/internal/synchronous/_scs_data_client.py
@poetry run python src/momento/internal/codegen.py src/momento/internal/aio/_vector_index_control_client.py src/momento/internal/synchronous/_vector_index_control_client.py
@poetry run python src/momento/internal/codegen.py src/momento/internal/aio/_vector_index_data_client.py src/momento/internal/synchronous/_vector_index_data_client.py
@poetry run python src/momento/internal/codegen.py src/momento/cache_client_async.py src/momento/cache_client.py
@poetry run python src/momento/internal/codegen.py src/momento/vector_index_client_async.py src/momento/vector_index_client.py
@poetry run python src/momento/internal/codegen.py tests/momento/cache_client/shared_behaviors_async.py tests/momento/cache_client/shared_behaviors.py
@poetry run python src/momento/internal/codegen.py tests/momento/cache_client/test_init_async.py tests/momento/cache_client/test_init.py
@poetry run python src/momento/internal/codegen.py tests/momento/cache_client/test_control_async.py tests/momento/cache_client/test_control.py
Expand All @@ -38,6 +41,8 @@ do-gen-sync:
@poetry run python src/momento/internal/codegen.py tests/momento/cache_client/test_set_async.py tests/momento/cache_client/test_set.py
@poetry run python src/momento/internal/codegen.py tests/momento/cache_client/test_sorted_set_async.py tests/momento/cache_client/test_sorted_set.py
@poetry run python src/momento/internal/codegen.py tests/momento/cache_client/test_sorted_set_simple_async.py tests/momento/cache_client/test_sorted_set_simple.py
@poetry run python src/momento/internal/codegen.py tests/momento/vector_index_client/test_control_async.py tests/momento/vector_index_client/test_control.py
@poetry run python src/momento/internal/codegen.py tests/momento/vector_index_client/test_data_async.py tests/momento/vector_index_client/test_data.py

.PHONY: gen-sync
## Generate synchronous code and tests from asynchronous code.
Expand Down
25 changes: 25 additions & 0 deletions examples/vector-index/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<img src="https://docs.momentohq.com/img/logo.svg" alt="logo" width="400"/>

# Momento Python Vector Index Client Examples

## Prereqs

- [Python 3.7 or above is required](https://www.python.org/downloads/)
- A Momento Auth Token is required, you can generate one from the [Momento Console](https://console.gomomento.com).
- Developer libraries (gcc/python dev headers) installed on machine you intend to run on

## Running the Example Using Poetry (Recommended)

- We use [poetry](https://python-poetry.org/docs/) to manage dependencies and packaging. This allows us to cleanly separate release vs development dependencies, as well as streamline deployment. See the [poetry docs](https://python-poetry.org/docs/#installation) for installation instructions.

To set up the `poetry` environment for this project:

```bash
poetry install
```

To run the basic Momento Vector Index example

```bash
MOMENTO_AUTH_TOKEN=<YOUR_TOKEN> poetry run python -m example
```
Empty file.
99 changes: 99 additions & 0 deletions examples/vector-index/example.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
from time import sleep

from momento import VectorIndexConfigurations, PreviewVectorIndexClient, CredentialProvider
from momento.config import VectorIndexConfiguration
from momento.requests.vector_index import Item
from momento.responses.vector_index import CreateIndex, ListIndexes, AddItemBatch, DeleteItemBatch, Search, DeleteIndex

VECTOR_INDEX_CONFIGURATION: VectorIndexConfiguration = VectorIndexConfigurations.Default.latest()
VECTOR_AUTH_PROVIDER = CredentialProvider.from_environment_variable("MOMENTO_AUTH_TOKEN")

def _print_start_banner() -> None:
print("******************************************************************")
print("* Momento Example Start *")
print("******************************************************************\n")


def create_index(index_name: str) -> None:
print("Creating index with name " + index_name)
create_index_response = _client.create_index(index_name, num_dimensions=2)
if isinstance(create_index_response, CreateIndex.Success):
print("Index with name " + index_name + " successfully created!")
elif isinstance(create_index_response, CreateIndex.IndexAlreadyExists):
print("Index with name " + index_name + " already exists")
elif isinstance(create_index_response, CreateIndex.Error):
raise(Exception("Error while creating index " + create_index_response.message))
print("******************************************************************\n")


def list_indexes():
print("Listing indexes")
list_indexes_response = _client.list_indexes()
if isinstance(list_indexes_response, ListIndexes.Success):
for index in list_indexes_response.index_names:
print("Account has an index with name " + index)
elif isinstance(list_indexes_response, ListIndexes.Error):
print(Exception("Error while listing indexes " + list_indexes_response.message))
print("******************************************************************\n")

def add_items(index_name):
items = [
Item(id="test_item_1", vector=[1.0, 2.0], metadata={"key1": "value1"}),
Item(id="test_item_2", vector=[3.0, 4.0], metadata={"key2": "value2"}),
Item(id="test_item_3", vector=[5.0, 6.0], metadata={"key1": "value3", "key3": "value3"}),
]
print("Adding items " + str(items))
add_response = _client.add_item_batch(
index_name,
items=items,
)
if isinstance(add_response, AddItemBatch.Success):
print("Successfully added items")
elif isinstance(add_response, AddItemBatch.Error):
raise(Exception("Error while adding items to index " + index_name + " " + add_response.message))
print("******************************************************************\n")


def search(index_name):
query_vector = [1.0, 2.0]
top_k = 3
print("Searching index " + index_name + " with query_vector " + str(query_vector) + " and top " + str(top_k) + " elements")
search_response = _client.search(index_name, query_vector=query_vector, top_k=top_k)
if isinstance(search_response, Search.Success):
print("Search succeeded with " + str(len(search_response.hits)) + " matches")
elif isinstance(search_response, Search.Error):
raise(Exception("Error while searching on index " + index_name + " " + search_response.message))
print("******************************************************************\n")

def delete_items(index_name):
delete_response = _client.delete_item_batch(index_name, ids=["test_item_1", "test_item_3"])
if isinstance(delete_response, DeleteItemBatch.Success):
print("Successfully deleted items")
elif isinstance(delete_response, DeleteItemBatch.Error):
raise(Exception("Error while deleting items " + delete_response.message))

def delete_index(index_name):
print("Deleting index " + index_name)
del_response = _client.delete_index(index_name)

if isinstance(del_response, DeleteIndex.Success):
print("Index " + index_name + " deleted successfully!")
elif isinstance(del_response, DeleteIndex.Error):
raise(Exception("Failed to delete index " + index_name + " with error " + del_response.message))

if __name__ == "__main__":
_print_start_banner()
with PreviewVectorIndexClient(VECTOR_INDEX_CONFIGURATION, VECTOR_AUTH_PROVIDER) as _client:
index_name = "hello_momento_index"

create_index(index_name)
list_indexes()
add_items(index_name)
sleep(2)
search(index_name)
delete_items(index_name)
sleep(2)
print("\nDeleted two items; search will return 1 hit now")
search(index_name)
delete_index(index_name)

9 changes: 4 additions & 5 deletions poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 7 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ exclude = ["src/momento/internal/codegen.py"]
[tool.poetry.dependencies]
python = "^3.7"

momento-wire-types = "^0.67"
momento-wire-types = "^0.75.0"
grpcio = "^1.46.0"
# note if you bump this presigned url test need be updated
pyjwt = "^2.4.0"
Expand Down Expand Up @@ -108,6 +108,9 @@ module = [
"momento.internal.synchronous._scs_control_client",
"momento.internal.synchronous._scs_data_client",
"momento.internal.synchronous._scs_grpc_manager",
"momento.internal.synchronous._vector_index_control_client",
"momento.internal.synchronous._vector_index_data_client",
"momento.internal.synchronous._vector_index_grpc_manager",
"momento.internal.synchronous._add_header_client_interceptor",
"momento.internal.synchronous._retry_interceptor",
"momento.internal.common._data_client_ops",
Expand All @@ -117,6 +120,9 @@ module = [
"momento.internal.aio._scs_control_client",
"momento.internal.aio._scs_data_client",
"momento.internal.aio._scs_grpc_manager",
"momento.internal.aio._vector_index_control_client",
"momento.internal.aio._vector_index_data_client",
"momento.internal.aio._vector_index_grpc_manager",
"momento.internal.aio._utilities",
"momento.responses.control.signing_key.*",
]
Expand Down
7 changes: 6 additions & 1 deletion src/momento/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,11 @@
from .auth import CredentialProvider
from .cache_client import CacheClient
from .cache_client_async import CacheClientAsync
from .config import Configurations, TopicConfigurations
from .config import Configurations, TopicConfigurations, VectorIndexConfigurations
from .topic_client import TopicClient
from .topic_client_async import TopicClientAsync
from .vector_index_client import PreviewVectorIndexClient
from .vector_index_client_async import PreviewVectorIndexClientAsync

logging.getLogger("momentosdk").addHandler(logging.NullHandler())
logs.initialize_momento_logging()
Expand All @@ -23,8 +25,11 @@
"CredentialProvider",
"Configurations",
"TopicConfigurations",
"VectorIndexConfigurations",
"CacheClient",
"CacheClientAsync",
"TopicClient",
"TopicClientAsync",
"PreviewVectorIndexClient",
"PreviewVectorIndexClientAsync",
]
5 changes: 3 additions & 2 deletions src/momento/auth/momento_endpoint_resolver.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from jwt.exceptions import DecodeError

from momento.errors import InvalidArgumentException
from momento.internal.services import Service

_MOMENTO_CONTROL_ENDPOINT_PREFIX = "control."
_MOMENTO_CACHE_ENDPOINT_PREFIX = "cache."
Expand All @@ -29,7 +30,7 @@ class _Base64DecodedV1Token:

def resolve(auth_token: str) -> _TokenAndEndpoints:
if not auth_token:
raise InvalidArgumentException("malformed auth token")
raise InvalidArgumentException("malformed auth token", Service.AUTH)

if _is_base64(auth_token):
decoded_b64_token = base64.b64decode(auth_token).decode("utf-8")
Expand All @@ -52,7 +53,7 @@ def _get_endpoint_from_token(auth_token: str) -> _TokenAndEndpoints:
auth_token=auth_token,
)
except (DecodeError, KeyError) as e:
raise InvalidArgumentException("Invalid Auth token.") from e
raise InvalidArgumentException("Invalid Auth token.", Service.AUTH) from e


def _is_base64(value: Union[bytes, str]) -> bool:
Expand Down
Loading