Skip to content

Commit 20b6c63

Browse files
authored
Add AI Proxy client (deven96#71)
* seperate db client and tests from aiproxy * create ai base client and cleanup * default args cleanup * rename pool class * fleshout ai client commands * fix typespec with incomplete servertypes * update python client test workflow for aiproxy * add aiproxy tests for store commands * isolate db tests * update readme, update ai test and remove dependency on ci spinning up db and ai servers * fix typegen, trace AIStoreType * bump python client version * cleanup workflow
1 parent c3d8f32 commit 20b6c63

File tree

30 files changed

+1049
-136
lines changed

30 files changed

+1049
-136
lines changed

.github/workflows/test.yml

Lines changed: 2 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -83,30 +83,14 @@ jobs:
8383
run: |
8484
poetry run isort . -c --profile black; echo $?
8585
poetry run black . --check; echo $?
86-
87-
- name: Set up Rust cache
88-
uses: Swatinem/rust-cache@v2
89-
with:
90-
workspaces: ahnlich
91-
92-
- name: Run Ahnlich DB Server
93-
working-directory: ./ahnlich
94-
run: |
95-
cargo run --bin ahnlich-db run --port 1349 &
96-
echo $! > server_pid.txt
97-
98-
99-
- name: Wait for Ahnlich DB to be ready
100-
working-directory: ./ahnlich
101-
run: |
102-
chmod +x wait-for-it.sh
103-
./wait-for-it.sh localhost 1349 -t 30
10486
10587
- name: Run Python Client Tests
10688
working-directory: ./sdk/ahnlich-client-py
10789
env:
10890
AHNLICH_DB_HOST: "127.0.0.1"
10991
AHNLICH_DB_PORT: 1349
92+
AHNLICH_AI_HOST: "127.0.0.1"
93+
AHNLICH_AI_PORT: 8000
11094
run: |
11195
poetry run pytest --junitxml=./python.xml
11296
@@ -117,10 +101,6 @@ jobs:
117101
name: python
118102
path: sdk/ahnlich-client-py/python.xml
119103

120-
- name: Stop Ahnlich DB Server
121-
working-directory: ./ahnlich
122-
run: |
123-
kill $(cat server_pid.txt)
124104

125105

126106
upload-test-results:

ahnlich/typegen/src/tracers/query/ai.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,9 @@ pub fn trace_ai_query_enum() -> Registry {
134134
tracer
135135
.trace_simple_type::<Algorithm>()
136136
.expect("Error tracing Algorithm");
137+
tracer
138+
.trace_simple_type::<AIStoreType>()
139+
.expect("Error tracing AIStoretype");
137140

138141
tracer
139142
.trace_simple_type::<NonLinearAlgorithm>()

ahnlich/typegen/src/tracers/server_response/ai.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,13 @@ pub fn trace_ai_server_response_enum() -> Registry {
120120
.trace_type::<MetadataValue>(&samples)
121121
.inspect_err(|err| println!("Failed to parse type {}", err.explanation()))
122122
.unwrap();
123+
let _ = tracer
124+
.trace_type::<ServerType>(&samples)
125+
.inspect_err(|err| println!("Failed to parse type {}", err.explanation()))
126+
.unwrap();
127+
tracer
128+
.trace_simple_type::<AIStoreType>()
129+
.expect("Error tracing AIStoretype");
123130

124131
tracer
125132
.registry()

ahnlich/typegen/src/tracers/server_response/db.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,10 @@ pub fn trace_db_server_response_enum() -> Registry {
119119
.trace_type::<MetadataValue>(&samples)
120120
.inspect_err(|err| println!("Failed to parse type {}", err.explanation()))
121121
.unwrap();
122+
let _ = tracer
123+
.trace_type::<ServerType>(&samples)
124+
.inspect_err(|err| println!("Failed to parse type {}", err.explanation()))
125+
.unwrap();
122126

123127
tracer
124128
.registry()

sdk/ahnlich-client-py/MSG_TAG

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Add AI Client for AI Proxy

sdk/ahnlich-client-py/README.md

Lines changed: 201 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,20 +16,35 @@ The following topics are covered:
1616
* [Client](#client)
1717

1818
* [Connection Pooling](#connection-pooling)
19-
* [Requests](#requests)
19+
* [Requests - DB](#requests---db)
2020
* [Ping](#ping)
2121
* [Info Server](#info-server)
2222
* [List Connected Clients](#list-connected-clients)
2323
* [List Stores](#list-stores)
2424
* [Create Store](#create-store)
2525
* [Set](#set)
2626
* [Drop Store](#drop-store)
27+
* [Get Sim N](#get-sim-n)
2728
* [Get Key](#get-key)
2829
* [Get By Predicate](#get-by-predicate)
2930
* [Create Predicate Index](#create-predicate-index)
3031
* [Drop Predicate Index](#drop-predicate-index)
3132
* [Delete Key](#delete-key)
3233
* [Delete Predicate](#delete-predicate)
34+
35+
* [Requests - AI](#requests---ai)
36+
* [Ping](#ping-1)
37+
* [Info Server](#info-server-1)
38+
* [List Stores](#list-stores-1)
39+
* [Create Store](#create-store-1)
40+
* [Set](#set-1)
41+
* [Drop Store](#drop-store-1)
42+
* [Get Sim N](#get-sim-n-1)
43+
* [Get By Predicate](#get-by-predicate-1)
44+
* [Create Predicate Index](#create-predicate-index-1)
45+
* [Drop Predicate Index](#drop-predicate-index-1)
46+
* [Delete Key](#delete-key-1)
47+
3348
* [Bulk Requests](#bulk-requests)
3449
* [Client As Context Manager](#client-as-context-manager)
3550
* [How to Deploy to Artifactory](#deploy-to-artifactory)
@@ -109,7 +124,7 @@ Where:
109124

110125
- **dispose_batch_size**: maximum number of expired and idle connections to be disposed on connection release (if background collector is started the parameter is ignored).
111126

112-
## Requests
127+
## Requests - DB
113128

114129
### Ping
115130

@@ -315,9 +330,185 @@ response = client.delete_predicate(
315330
```
316331

317332

333+
## Requests - AI
334+
335+
336+
### Ping
337+
338+
```py
339+
from ahnlich_client_py import AhnlichAIClient
340+
client = AhnlichAIClient(address="127.0.0.1", port=port)
341+
342+
response = client.ping()
343+
```
344+
345+
### Info Server
346+
347+
```py
348+
from ahnlich_client_py import AhnlichAIClient
349+
client = AhnlichAIClient(address="127.0.0.1", port=port)
350+
351+
response = client.info_server()
352+
```
353+
354+
### List Stores
355+
356+
```py
357+
from ahnlich_client_py import AhnlichAIClient
358+
client = AhnlichAIClient(address="127.0.0.1", port=port)
359+
360+
response = client.list_stores()
361+
```
362+
363+
### Create Store
364+
365+
```py
366+
from ahnlich_client_py import AhnlichAIClient
367+
from ahnlich_client_py.internals import ai_query
368+
client = AhnlichAIClient(address="127.0.0.1", port=port)
369+
370+
response = client.create_store(
371+
store_name = "test store",
372+
model = ai_query.AIModel__Llama3(),
373+
store_type = ai_query.AIStoreType__RawString(),
374+
predicates = [
375+
"job"
376+
],
377+
non_linear_indices= []
378+
)
379+
380+
```
381+
382+
383+
### Set
384+
```py
385+
386+
from ahnlich_client_py import AhnlichAIClient
387+
from ahnlich_client_py.internals import ai_query
388+
client = AhnlichAIClient(address="127.0.0.1", port=port)
389+
390+
store_inputs = [
391+
(
392+
ai_query.StoreInput__RawString("Jordan One"),
393+
{"brand": ai_query.MetadataValue__RawString("Nike")},
394+
),
395+
(
396+
ai_query.StoreInput__RawString("Yeezey"),
397+
{"brand": ai_query.MetadataValue__RawString("Adidas")},
398+
),
399+
]
400+
401+
402+
response = client.set(
403+
store_name = "test store",
404+
inputs=store_inputs
405+
)
406+
```
407+
408+
409+
### Drop store
410+
```py
411+
from ahnlich_client_py import AhnlichAIClient
412+
client = AhnlichAIClient(address="127.0.0.1", port=port)
413+
414+
response = client.drop_store(
415+
store_name = "test store",
416+
error_if_not_exists=True
417+
)
418+
419+
420+
```
421+
422+
423+
### Get Sim N
424+
Returns an array of tuple of (store_key, store_value) of Maximum specified N
425+
426+
```py
427+
from ahnlich_client_py import AhnlichAIClient
428+
from ahnlich_client_py.internals import ai_query
429+
430+
client = AhnlichAIClient(address="127.0.0.1", port=port)
431+
432+
433+
search_input = ai_query.StoreInput__RawString("Jordan")
434+
435+
response = client.get_sim_n(
436+
store_name = "test store",
437+
search_input = search_input,
438+
closest_n = 3,
439+
algorithm = query.Algorithm__CosineSimilarity(),
440+
condition = None
441+
)
442+
```
443+
<u>*Closest_n is a Nonzero integer value*</u>
444+
445+
446+
### Get By Predicate
447+
Same as Get_key but returns results based defined conditions
448+
449+
```py
450+
from ahnlich_client_py import AhnlichAIClient
451+
client = AhnlichAIClient(address="127.0.0.1", port=port)
452+
453+
condition = query.PredicateCondition__Value(
454+
query.Predicate__Equals(
455+
key="brand",
456+
value=query.MetadataValue__RawString(value="Nike")
457+
)
458+
)
459+
response = client.get_by_predicate(
460+
store_name = "test store",
461+
condition=conditon
462+
)
463+
```
464+
465+
### Create Predicate Index
466+
```py
467+
from ahnlich_client_py import AhnlichAIClient
468+
client = AhnlichAIClient(address="127.0.0.1", port=port)
469+
470+
response = client.create_pred_index(
471+
store_name = "test store",
472+
predicates=["job", "rank"]
473+
)
474+
```
475+
476+
### Drop Predicate Index
477+
```py
478+
from ahnlich_client_py import AhnlichAIClient
479+
client = AhnlichAIClient(address="127.0.0.1", port=port)
480+
481+
response = client.drop_pred_index(
482+
store_name = "test store",
483+
predicates=["job"],
484+
error_if_not_exists=True
485+
)
486+
```
487+
488+
489+
490+
### Delete Key
491+
```py
492+
493+
from ahnlich_client_py import AhnlichAIClient
494+
client = AhnlichAIClient(address="127.0.0.1", port=port)
495+
496+
497+
498+
key = ai_query.StoreInput__RawString("Custom Made Jordan 4")
499+
500+
501+
response = client.delete_key(
502+
store_name = "test store",
503+
keys=[key]
504+
)
505+
```
506+
507+
508+
318509

319510
## Bulk Requests
320-
The client has the ability to send multiple requests at once, and these requests will be handled sequentially. The builder class takes care of this. The response is a list of all individual request responses.
511+
The clients has the ability to send multiple requests at once, and these requests will be handled sequentially. The builder class takes care of this. The response is a list of all individual request responses.
321512

322513

323514
```py
@@ -332,12 +523,12 @@ request_builder.list_stores()
332523

333524
response: server_response.ServerResult = client.exec()
334525
```
335-
526+
*Sample applies to the AIclient*
336527

337528

338529
## Client As Context Manager
339530

340-
The DB client class can be used as a context manager hereby closing the connection pool automatically upon context end.
531+
The DB and AI client class can be used as a context manager hereby closing the connection pool automatically upon context end.
341532

342533

343534
```py
@@ -358,7 +549,7 @@ Replace the contents of `MSG_TAG` file with your new tag message
358549

359550
From Feature branch, either use the makefile :
360551
```bash
361-
make bump-python-version [major, minor, patch]
552+
make bump-py-client BUMP_RULE=[major, minor, patch]
362553
```
363554
or
364555
```bash
@@ -410,14 +601,17 @@ condition = query.PredicateCondition__AND(
410601

411602
```
412603

604+
- Search Input: A string or binary file that can be stored by the aiproxy. Note, the binary file depends on the supported models used in a store or supported by Ahnlich AI
413605

606+
- AIModels: Supported AI models used by ahnlich ai
607+
- AIStoreType: A type of store to be created. Either a Binary or String
414608

415609
## Change Log
416610

417611
| Version| Description |
418612
| -------|:-------------:|
419613
| 0.1.0 | Base Python client to connect to ahnlich db. Bincode serialization and deserialization implemented |
420614
| 0.2.0 | Add Connection pooling mechanism for `AhnlichDBClient` |
421-
| | |
615+
| 0.3.0| Add AIProxy client: `AhnlichDBClient` |
422616

423617

sdk/ahnlich-client-py/VERSION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
CLIENT="0.2.0"
1+
CLIENT="0.3.0"
22
PROTOCOL="0.1.0"
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
from ahnlich_client_py import builders, libs
2-
from ahnlich_client_py.client import AhnlichDBClient
3-
from ahnlich_client_py.config import AhnlichDBPoolSettings
2+
from ahnlich_client_py.clients import AhnlichAIClient, AhnlichDBClient
3+
from ahnlich_client_py.config import AhnlichPoolSettings
44
from ahnlich_client_py.internals import ai_query, ai_response, db_query, db_response

0 commit comments

Comments
 (0)