Skip to content

Commit 392c72f

Browse files
committed
feat(convo): add async support for conversation model operations
- add AsyncConversationModel class for async individual model operations - add AsyncConversationsModels class for async models collection operations - add async tests for conversation model and models functionality - add async fixtures for testing async conversation model operations
1 parent 7a2f2e3 commit 392c72f

5 files changed

Lines changed: 473 additions & 0 deletions

File tree

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

tests/conversation_model_test.py

Lines changed: 103 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_conversation_model import AsyncConversationModel
16+
from typesense.async_conversations_models import AsyncConversationsModels
1417
from typesense.conversation_model import ConversationModel
1518
from typesense.conversations_models import ConversationsModels
1619
from typesense.types.conversations_model import (
@@ -44,6 +47,29 @@ def test_init(fake_api_call: ApiCall) -> None:
4447
)
4548

4649

50+
def test_init_async(fake_async_api_call: AsyncApiCall) -> None:
51+
"""Test that the AsyncConversationModel object is initialized correctly."""
52+
conversation_model = AsyncConversationModel(
53+
fake_async_api_call,
54+
"conversation_model_id",
55+
)
56+
57+
assert conversation_model.model_id == "conversation_model_id"
58+
assert_match_object(conversation_model.api_call, fake_async_api_call)
59+
assert_object_lists_match(
60+
conversation_model.api_call.node_manager.nodes,
61+
fake_async_api_call.node_manager.nodes,
62+
)
63+
assert_match_object(
64+
conversation_model.api_call.config.nearest_node,
65+
fake_async_api_call.config.nearest_node,
66+
)
67+
assert (
68+
conversation_model._endpoint_path # noqa: WPS437
69+
== "/conversations/models/conversation_model_id"
70+
)
71+
72+
4773
@pytest.mark.open_ai
4874
def test_actual_retrieve(
4975
actual_conversations_models: ConversationsModels,
@@ -113,3 +139,80 @@ def test_actual_delete(
113139
assert response.get("system_prompt") == "This is a system prompt"
114140
assert response.get("id") == create_conversations_model
115141
assert response.get("id") == create_conversations_model
142+
143+
144+
@pytest.mark.open_ai
145+
async def test_actual_retrieve_async(
146+
actual_async_conversations_models: AsyncConversationsModels,
147+
delete_all_conversations_models: None,
148+
create_conversations_model: str,
149+
) -> None:
150+
"""Test it can retrieve a conversation_model from Typesense Server."""
151+
response = await actual_async_conversations_models[
152+
create_conversations_model
153+
].retrieve()
154+
155+
assert_to_contain_keys(
156+
response,
157+
["id", "model_name", "system_prompt", "max_bytes", "api_key"],
158+
)
159+
assert response.get("id") == create_conversations_model
160+
161+
162+
@pytest.mark.open_ai
163+
async def test_actual_update_async(
164+
actual_async_conversations_models: AsyncConversationsModels,
165+
delete_all_conversations_models: None,
166+
create_conversations_model: str,
167+
) -> None:
168+
"""Test that it can update a conversation_model from Typesense Server."""
169+
response = await actual_async_conversations_models[
170+
create_conversations_model
171+
].update(
172+
{"system_prompt": "This is a new system prompt"},
173+
)
174+
175+
assert_to_contain_keys(
176+
response,
177+
[
178+
"id",
179+
"model_name",
180+
"system_prompt",
181+
"max_bytes",
182+
"api_key",
183+
"ttl",
184+
"history_collection",
185+
],
186+
)
187+
188+
assert response.get("system_prompt") == "This is a new system prompt"
189+
assert response.get("id") == create_conversations_model
190+
191+
192+
@pytest.mark.open_ai
193+
async def test_actual_delete_async(
194+
actual_async_conversations_models: AsyncConversationsModels,
195+
delete_all_conversations_models: None,
196+
create_conversations_model: str,
197+
) -> None:
198+
"""Test that it can delete an conversation_model from Typesense Server."""
199+
response = await actual_async_conversations_models[
200+
create_conversations_model
201+
].delete()
202+
203+
assert_to_contain_keys(
204+
response,
205+
[
206+
"id",
207+
"model_name",
208+
"system_prompt",
209+
"max_bytes",
210+
"api_key",
211+
"ttl",
212+
"history_collection",
213+
],
214+
)
215+
216+
assert response.get("system_prompt") == "This is a system prompt"
217+
assert response.get("id") == create_conversations_model
218+
assert response.get("id") == create_conversations_model

0 commit comments

Comments
 (0)