Skip to content

Commit 6a6e4d3

Browse files
committed
feat(nl-search): add async support for nl search model operations
- add AsyncNLSearchModel class for async individual model operations - add AsyncNLSearchModels class for async mod
1 parent 9956fd9 commit 6a6e4d3

File tree

5 files changed

+440
-0
lines changed

5 files changed

+440
-0
lines changed
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
"""
2+
This module provides async functionality for managing individual NL search models in Typesense.
3+
4+
It contains the AsyncNLSearchModel class, which allows for retrieving, updating, and deleting
5+
NL search models asynchronously.
6+
7+
Classes:
8+
AsyncNLSearchModel: Manages async operations on a single NL search model in the Typesense API.
9+
10+
Dependencies:
11+
- typesense.async_api_call: Provides the AsyncApiCall class for making async API requests.
12+
- typesense.types.nl_search_model: Provides NLSearchModelDeleteSchema, NLSearchModelSchema, and NLSearchModelUpdateSchema types.
13+
14+
Note: This module uses conditional imports to support both Python 3.11+ and earlier versions.
15+
"""
16+
17+
from typesense.async_api_call import AsyncApiCall
18+
from typesense.types.nl_search_model import (
19+
NLSearchModelDeleteSchema,
20+
NLSearchModelSchema,
21+
NLSearchModelUpdateSchema,
22+
)
23+
24+
25+
class AsyncNLSearchModel:
26+
"""
27+
Manages async operations on a single NL search model in the Typesense API.
28+
29+
This class provides async methods to retrieve, update, and delete an NL search model.
30+
31+
Attributes:
32+
model_id (str): The ID of the NL search model.
33+
api_call (AsyncApiCall): The AsyncApiCall instance for making async API requests.
34+
"""
35+
36+
def __init__(self, api_call: AsyncApiCall, model_id: str) -> None:
37+
"""
38+
Initialize the AsyncNLSearchModel instance.
39+
40+
Args:
41+
api_call (AsyncApiCall): The AsyncApiCall instance for making async API requests.
42+
model_id (str): The ID of the NL search model.
43+
"""
44+
self.model_id = model_id
45+
self.api_call = api_call
46+
47+
async def retrieve(self) -> NLSearchModelSchema:
48+
"""
49+
Retrieve this specific NL search model.
50+
51+
Returns:
52+
NLSearchModelSchema: The schema containing the NL search model details.
53+
"""
54+
response: NLSearchModelSchema = await self.api_call.get(
55+
self._endpoint_path,
56+
as_json=True,
57+
entity_type=NLSearchModelSchema,
58+
)
59+
return response
60+
61+
async def update(self, model: NLSearchModelUpdateSchema) -> NLSearchModelSchema:
62+
"""
63+
Update this specific NL search model.
64+
65+
Args:
66+
model (NLSearchModelUpdateSchema):
67+
The schema containing the updated model details.
68+
69+
Returns:
70+
NLSearchModelSchema: The schema containing the updated NL search model.
71+
"""
72+
response: NLSearchModelSchema = await self.api_call.put(
73+
self._endpoint_path,
74+
body=model,
75+
entity_type=NLSearchModelSchema,
76+
)
77+
return response
78+
79+
async def delete(self) -> NLSearchModelDeleteSchema:
80+
"""
81+
Delete this specific NL search model.
82+
83+
Returns:
84+
NLSearchModelDeleteSchema: The schema containing the deletion response.
85+
"""
86+
response: NLSearchModelDeleteSchema = await self.api_call.delete(
87+
self._endpoint_path,
88+
entity_type=NLSearchModelDeleteSchema,
89+
)
90+
return response
91+
92+
@property
93+
def _endpoint_path(self) -> str:
94+
"""
95+
Construct the API endpoint path for this specific NL search model.
96+
97+
Returns:
98+
str: The constructed endpoint path.
99+
"""
100+
from typesense.async_nl_search_models import AsyncNLSearchModels
101+
102+
return "/".join([AsyncNLSearchModels.resource_path, self.model_id])
Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
"""
2+
This module provides async functionality for managing NL search models in Typesense.
3+
4+
It contains the AsyncNLSearchModels class, which allows for creating, retrieving, and
5+
accessing individual NL search models asynchronously.
6+
7+
Classes:
8+
AsyncNLSearchModels: Manages NL search models in the Typesense API (async).
9+
10+
Dependencies:
11+
- typesense.async_api_call: Provides the AsyncApiCall class for making async API requests.
12+
- typesense.async_nl_search_model: Provides the AsyncNLSearchModel class for individual NL search model operations.
13+
- typesense.types.nl_search_model: Provides NLSearchModelCreateSchema, NLSearchModelSchema, and NLSearchModelsRetrieveSchema types.
14+
15+
Note: This module uses conditional imports to support both Python 3.11+ and earlier versions.
16+
"""
17+
18+
import sys
19+
20+
from typesense.async_api_call import AsyncApiCall
21+
from typesense.async_nl_search_model import AsyncNLSearchModel
22+
from typesense.types.nl_search_model import (
23+
NLSearchModelCreateSchema,
24+
NLSearchModelSchema,
25+
NLSearchModelsRetrieveSchema,
26+
)
27+
28+
if sys.version_info >= (3, 11):
29+
import typing
30+
else:
31+
import typing_extensions as typing
32+
33+
34+
class AsyncNLSearchModels:
35+
"""
36+
Manages NL search models in the Typesense API (async).
37+
38+
This class provides async methods to create, retrieve, and access individual NL search models.
39+
40+
Attributes:
41+
resource_path (str): The API endpoint path for NL search models operations.
42+
api_call (AsyncApiCall): The AsyncApiCall instance for making async API requests.
43+
nl_search_models (Dict[str, AsyncNLSearchModel]):
44+
A dictionary of AsyncNLSearchModel instances, keyed by model ID.
45+
"""
46+
47+
resource_path: typing.Final[str] = "/nl_search_models"
48+
49+
def __init__(self, api_call: AsyncApiCall) -> None:
50+
"""
51+
Initialize the AsyncNLSearchModels instance.
52+
53+
Args:
54+
api_call (AsyncApiCall): The AsyncApiCall instance for making async API requests.
55+
"""
56+
self.api_call = api_call
57+
self.nl_search_models: typing.Dict[str, AsyncNLSearchModel] = {}
58+
59+
def __getitem__(self, model_id: str) -> AsyncNLSearchModel:
60+
"""
61+
Get or create an AsyncNLSearchModel instance for a given model ID.
62+
63+
This method allows accessing NL search models using dictionary-like syntax.
64+
If the AsyncNLSearchModel instance doesn't exist, it creates a new one.
65+
66+
Args:
67+
model_id (str): The ID of the NL search model.
68+
69+
Returns:
70+
AsyncNLSearchModel: The AsyncNLSearchModel instance for the specified model ID.
71+
72+
Example:
73+
>>> nl_search_models = AsyncNLSearchModels(async_api_call)
74+
>>> model = nl_search_models["model_id"]
75+
"""
76+
if model_id not in self.nl_search_models:
77+
self.nl_search_models[model_id] = AsyncNLSearchModel(
78+
self.api_call,
79+
model_id,
80+
)
81+
return self.nl_search_models[model_id]
82+
83+
async def create(self, model: NLSearchModelCreateSchema) -> NLSearchModelSchema:
84+
"""
85+
Create a new NL search model.
86+
87+
Args:
88+
model (NLSearchModelCreateSchema):
89+
The schema for creating the NL search model.
90+
91+
Returns:
92+
NLSearchModelSchema: The created NL search model.
93+
94+
Example:
95+
>>> nl_search_models = AsyncNLSearchModels(async_api_call)
96+
>>> model = await nl_search_models.create(
97+
... {
98+
... "api_key": "key",
99+
... "model_name": "openai/gpt-3.5-turbo",
100+
... "system_prompt": "System prompt",
101+
... }
102+
... )
103+
"""
104+
response: NLSearchModelSchema = await self.api_call.post(
105+
endpoint=AsyncNLSearchModels.resource_path,
106+
entity_type=NLSearchModelSchema,
107+
as_json=True,
108+
body=model,
109+
)
110+
return response
111+
112+
async def retrieve(self) -> NLSearchModelsRetrieveSchema:
113+
"""
114+
Retrieve all NL search models.
115+
116+
Returns:
117+
NLSearchModelsRetrieveSchema: A list of all NL search models.
118+
119+
Example:
120+
>>> nl_search_models = AsyncNLSearchModels(async_api_call)
121+
>>> all_models = await nl_search_models.retrieve()
122+
>>> for model in all_models:
123+
... print(model["id"])
124+
"""
125+
response: NLSearchModelsRetrieveSchema = await self.api_call.get(
126+
endpoint=AsyncNLSearchModels.resource_path,
127+
entity_type=NLSearchModelsRetrieveSchema,
128+
as_json=True,
129+
)
130+
return response

tests/fixtures/nl_search_model_fixtures.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@
77
from dotenv import load_dotenv
88

99
from typesense.api_call import ApiCall
10+
from typesense.async_api_call import AsyncApiCall
11+
from typesense.async_nl_search_model import AsyncNLSearchModel
12+
from typesense.async_nl_search_models import AsyncNLSearchModels
1013
from typesense.nl_search_model import NLSearchModel
1114
from typesense.nl_search_models import NLSearchModels
1215

@@ -76,3 +79,27 @@ def actual_nl_search_models_fixture(
7679
) -> NLSearchModels:
7780
"""Return an NLSearchModels object using a real API."""
7881
return NLSearchModels(actual_api_call)
82+
83+
84+
@pytest.fixture(scope="function", name="actual_async_nl_search_models")
85+
def actual_async_nl_search_models_fixture(
86+
actual_async_api_call: AsyncApiCall,
87+
) -> AsyncNLSearchModels:
88+
"""Return a AsyncNLSearchModels object using a real API."""
89+
return AsyncNLSearchModels(actual_async_api_call)
90+
91+
92+
@pytest.fixture(scope="function", name="fake_async_nl_search_models")
93+
def fake_async_nl_search_models_fixture(
94+
fake_async_api_call: AsyncApiCall,
95+
) -> AsyncNLSearchModels:
96+
"""Return a AsyncNLSearchModels object with test values."""
97+
return AsyncNLSearchModels(fake_async_api_call)
98+
99+
100+
@pytest.fixture(scope="function", name="fake_async_nl_search_model")
101+
def fake_async_nl_search_model_fixture(
102+
fake_async_api_call: AsyncApiCall,
103+
) -> AsyncNLSearchModel:
104+
"""Return a AsyncNLSearchModel object with test values."""
105+
return AsyncNLSearchModel(fake_async_api_call, "nl_search_model_id")

tests/nl_search_model_test.py

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@
1111
assert_to_contain_keys,
1212
)
1313
from typesense.api_call import ApiCall
14+
from typesense.async_api_call import AsyncApiCall
15+
from typesense.async_nl_search_model import AsyncNLSearchModel
16+
from typesense.async_nl_search_models import AsyncNLSearchModels
1417
from typesense.nl_search_model import NLSearchModel
1518
from typesense.nl_search_models import NLSearchModels
1619

@@ -40,6 +43,29 @@ def test_init(fake_api_call: ApiCall) -> None:
4043
)
4144

4245

46+
def test_init_async(fake_async_api_call: AsyncApiCall) -> None:
47+
"""Test that the AsyncNLSearchModel object is initialized correctly."""
48+
nl_search_model = AsyncNLSearchModel(
49+
fake_async_api_call,
50+
"nl_search_model_id",
51+
)
52+
53+
assert nl_search_model.model_id == "nl_search_model_id"
54+
assert_match_object(nl_search_model.api_call, fake_async_api_call)
55+
assert_object_lists_match(
56+
nl_search_model.api_call.node_manager.nodes,
57+
fake_async_api_call.node_manager.nodes,
58+
)
59+
assert_match_object(
60+
nl_search_model.api_call.config.nearest_node,
61+
fake_async_api_call.config.nearest_node,
62+
)
63+
assert (
64+
nl_search_model._endpoint_path # noqa: WPS437
65+
== "/nl_search_models/nl_search_model_id"
66+
)
67+
68+
4369
@pytest.mark.open_ai
4470
def test_actual_retrieve(
4571
actual_nl_search_models: NLSearchModels,
@@ -97,3 +123,62 @@ def test_actual_delete(
97123
)
98124

99125
assert response.get("id") == create_nl_search_model
126+
127+
128+
@pytest.mark.open_ai
129+
async def test_actual_retrieve_async(
130+
actual_async_nl_search_models: AsyncNLSearchModels,
131+
delete_all_nl_search_models: None,
132+
create_nl_search_model: str,
133+
) -> None:
134+
"""Test it can retrieve an NL search model from Typesense Server."""
135+
response = await actual_async_nl_search_models[create_nl_search_model].retrieve()
136+
137+
assert_to_contain_keys(
138+
response,
139+
["id", "model_name", "system_prompt", "max_bytes", "api_key"],
140+
)
141+
assert response.get("id") == create_nl_search_model
142+
143+
144+
@pytest.mark.open_ai
145+
async def test_actual_update_async(
146+
actual_async_nl_search_models: AsyncNLSearchModels,
147+
delete_all_nl_search_models: None,
148+
create_nl_search_model: str,
149+
) -> None:
150+
"""Test that it can update an NL search model from Typesense Server."""
151+
response = await actual_async_nl_search_models[create_nl_search_model].update(
152+
{"system_prompt": "This is a new system prompt for NL search"},
153+
)
154+
155+
assert_to_contain_keys(
156+
response,
157+
[
158+
"id",
159+
"model_name",
160+
"system_prompt",
161+
"max_bytes",
162+
"api_key",
163+
],
164+
)
165+
166+
assert response.get("system_prompt") == "This is a new system prompt for NL search"
167+
assert response.get("id") == create_nl_search_model
168+
169+
170+
@pytest.mark.open_ai
171+
async def test_actual_delete_async(
172+
actual_async_nl_search_models: AsyncNLSearchModels,
173+
delete_all_nl_search_models: None,
174+
create_nl_search_model: str,
175+
) -> None:
176+
"""Test that it can delete an NL search model from Typesense Server."""
177+
response = await actual_async_nl_search_models[create_nl_search_model].delete()
178+
179+
assert_to_contain_keys(
180+
response,
181+
["id"],
182+
)
183+
184+
assert response.get("id") == create_nl_search_model

0 commit comments

Comments
 (0)