|
12 | 12 | from ...core.serialization import convert_and_respect_annotation_metadata |
13 | 13 | from ..types.connect_session_settings import ConnectSessionSettings |
14 | 14 | from .raw_client import AsyncRawChatClient, RawChatClient |
15 | | -from .socket_client import AsyncChatSocketClient, ChatSocketClient |
| 15 | +from .socket_client import AsyncChatClientWithWebsocket |
16 | 16 |
|
17 | 17 | try: |
18 | 18 | from websockets.legacy.client import connect as websockets_client_connect # type: ignore |
|
22 | 22 |
|
23 | 23 | class ChatClient: |
24 | 24 | def __init__(self, *, client_wrapper: SyncClientWrapper): |
25 | | - self._raw_client = RawChatClient(client_wrapper=client_wrapper) |
26 | | - |
27 | | - @property |
28 | | - def with_raw_response(self) -> RawChatClient: |
29 | | - """ |
30 | | - Retrieves a raw implementation of this client that returns raw responses. |
31 | | -
|
32 | | - Returns |
33 | | - ------- |
34 | | - RawChatClient |
35 | | - """ |
36 | | - return self._raw_client |
37 | | - |
38 | | - @contextmanager |
39 | | - def connect( |
40 | | - self, |
41 | | - *, |
42 | | - access_token: typing.Optional[str] = None, |
43 | | - config_id: typing.Optional[str] = None, |
44 | | - config_version: typing.Optional[int] = None, |
45 | | - event_limit: typing.Optional[int] = None, |
46 | | - resumed_chat_group_id: typing.Optional[str] = None, |
47 | | - verbose_transcription: typing.Optional[bool] = None, |
48 | | - api_key: typing.Optional[str] = None, |
49 | | - session_settings: ConnectSessionSettings, |
50 | | - request_options: typing.Optional[RequestOptions] = None, |
51 | | - ) -> typing.Iterator[ChatSocketClient]: |
52 | | - """ |
53 | | - Chat with Empathic Voice Interface (EVI) |
54 | | -
|
55 | | - Parameters |
56 | | - ---------- |
57 | | - access_token : typing.Optional[str] |
58 | | - Access token used for authenticating the client. If not provided, an `api_key` must be provided to authenticate. |
59 | | -
|
60 | | - The access token is generated using both an API key and a Secret key, which provides an additional layer of security compared to using just an API key. |
61 | | -
|
62 | | - For more details, refer to the [Authentication Strategies Guide](/docs/introduction/api-key#authentication-strategies). |
63 | | -
|
64 | | - config_id : typing.Optional[str] |
65 | | - The unique identifier for an EVI configuration. |
66 | | -
|
67 | | - Include this ID in your connection request to equip EVI with the Prompt, Language Model, Voice, and Tools associated with the specified configuration. If omitted, EVI will apply [default configuration settings](/docs/speech-to-speech-evi/configuration/build-a-configuration#default-configuration). |
68 | | -
|
69 | | - For help obtaining this ID, see our [Configuration Guide](/docs/speech-to-speech-evi/configuration). |
70 | | -
|
71 | | - config_version : typing.Optional[int] |
72 | | - The version number of the EVI configuration specified by the `config_id`. |
73 | | -
|
74 | | - Configs, as well as Prompts and Tools, are versioned. This versioning system supports iterative development, allowing you to progressively refine configurations and revert to previous versions if needed. |
75 | | -
|
76 | | - Include this parameter to apply a specific version of an EVI configuration. If omitted, the latest version will be applied. |
77 | | -
|
78 | | - event_limit : typing.Optional[int] |
79 | | - The maximum number of chat events to return from chat history. By default, the system returns up to 300 events (100 events per page × 3 pages). Set this parameter to a smaller value to limit the number of events returned. |
80 | | -
|
81 | | - resumed_chat_group_id : typing.Optional[str] |
82 | | - The unique identifier for a Chat Group. Use this field to preserve context from a previous Chat session. |
83 | | -
|
84 | | - A Chat represents a single session from opening to closing a WebSocket connection. In contrast, a Chat Group is a series of resumed Chats that collectively represent a single conversation spanning multiple sessions. Each Chat includes a Chat Group ID, which is used to preserve the context of previous Chat sessions when starting a new one. |
85 | | -
|
86 | | - Including the Chat Group ID in the `resumed_chat_group_id` query parameter is useful for seamlessly resuming a Chat after unexpected network disconnections and for picking up conversations exactly where you left off at a later time. This ensures preserved context across multiple sessions. |
87 | | -
|
88 | | - There are three ways to obtain the Chat Group ID: |
89 | | -
|
90 | | - - [Chat Metadata](/reference/speech-to-speech-evi/chat#receive.ChatMetadata): Upon establishing a WebSocket connection with EVI, the user receives a Chat Metadata message. This message contains a `chat_group_id`, which can be used to resume conversations within this chat group in future sessions. |
91 | | -
|
92 | | - - [List Chats endpoint](/reference/speech-to-speech-evi/chats/list-chats): Use the GET `/v0/evi/chats` endpoint to obtain the Chat Group ID of individual Chat sessions. This endpoint lists all available Chat sessions and their associated Chat Group ID. |
93 | | -
|
94 | | - - [List Chat Groups endpoint](/reference/speech-to-speech-evi/chat-groups/list-chat-groups): Use the GET `/v0/evi/chat_groups` endpoint to obtain the Chat Group IDs of all Chat Groups associated with an API key. This endpoint returns a list of all available chat groups. |
95 | | -
|
96 | | - verbose_transcription : typing.Optional[bool] |
97 | | - A flag to enable verbose transcription. Set this query parameter to `true` to have unfinalized user transcripts be sent to the client as interim UserMessage messages. The [interim](/reference/speech-to-speech-evi/chat#receive.UserMessage.interim) field on a [UserMessage](/reference/speech-to-speech-evi/chat#receive.UserMessage) denotes whether the message is "interim" or "final." |
98 | | -
|
99 | | - api_key : typing.Optional[str] |
100 | | - API key used for authenticating the client. If not provided, an `access_token` must be provided to authenticate. |
101 | | -
|
102 | | - For more details, refer to the [Authentication Strategies Guide](/docs/introduction/api-key#authentication-strategies). |
103 | | -
|
104 | | - session_settings : ConnectSessionSettings |
105 | | -
|
106 | | - request_options : typing.Optional[RequestOptions] |
107 | | - Request-specific configuration. |
108 | | -
|
109 | | - Returns |
110 | | - ------- |
111 | | - ChatSocketClient |
112 | | - """ |
113 | | - ws_url = self._raw_client._client_wrapper.get_environment().evi + "/chat" |
114 | | - query_params = httpx.QueryParams() |
115 | | - if access_token is not None: |
116 | | - query_params = query_params.add("access_token", access_token) |
117 | | - if config_id is not None: |
118 | | - query_params = query_params.add("config_id", config_id) |
119 | | - if config_version is not None: |
120 | | - query_params = query_params.add("config_version", config_version) |
121 | | - if event_limit is not None: |
122 | | - query_params = query_params.add("event_limit", event_limit) |
123 | | - if resumed_chat_group_id is not None: |
124 | | - query_params = query_params.add("resumed_chat_group_id", resumed_chat_group_id) |
125 | | - if verbose_transcription is not None: |
126 | | - query_params = query_params.add("verbose_transcription", verbose_transcription) |
127 | | - if api_key is not None: |
128 | | - query_params = query_params.add("api_key", api_key) |
129 | | - if ( |
130 | | - convert_and_respect_annotation_metadata( |
131 | | - object_=session_settings, annotation=ConnectSessionSettings, direction="write" |
132 | | - ) |
133 | | - is not None |
134 | | - ): |
135 | | - query_params = query_params.add( |
136 | | - "session_settings", |
137 | | - convert_and_respect_annotation_metadata( |
138 | | - object_=session_settings, annotation=ConnectSessionSettings, direction="write" |
139 | | - ), |
140 | | - ) |
141 | | - ws_url = ws_url + f"?{query_params}" |
142 | | - headers = self._raw_client._client_wrapper.get_headers() |
143 | | - if request_options and "additional_headers" in request_options: |
144 | | - headers.update(request_options["additional_headers"]) |
145 | | - try: |
146 | | - with websockets_sync_client.connect(ws_url, additional_headers=headers) as protocol: |
147 | | - yield ChatSocketClient(websocket=protocol) |
148 | | - except websockets.exceptions.InvalidStatusCode as exc: |
149 | | - status_code: int = exc.status_code |
150 | | - if status_code == 401: |
151 | | - raise ApiError( |
152 | | - status_code=status_code, |
153 | | - headers=dict(headers), |
154 | | - body="Websocket initialized with invalid credentials.", |
155 | | - ) |
156 | | - raise ApiError( |
157 | | - status_code=status_code, |
158 | | - headers=dict(headers), |
159 | | - body="Unexpected error when initializing websocket connection.", |
160 | | - ) |
161 | | - |
| 25 | + raise NotImplementedError("Synchronous client is not supported yet.") |
162 | 26 |
|
163 | 27 | class AsyncChatClient: |
164 | 28 | def __init__(self, *, client_wrapper: AsyncClientWrapper): |
@@ -188,7 +52,7 @@ async def connect( |
188 | 52 | api_key: typing.Optional[str] = None, |
189 | 53 | session_settings: ConnectSessionSettings, |
190 | 54 | request_options: typing.Optional[RequestOptions] = None, |
191 | | - ) -> typing.AsyncIterator[AsyncChatSocketClient]: |
| 55 | + ) -> typing.AsyncIterator[AsyncChatClientWithWebsocket]: |
192 | 56 | """ |
193 | 57 | Chat with Empathic Voice Interface (EVI) |
194 | 58 |
|
@@ -284,7 +148,7 @@ async def connect( |
284 | 148 | headers.update(request_options["additional_headers"]) |
285 | 149 | try: |
286 | 150 | async with websockets_client_connect(ws_url, extra_headers=headers) as protocol: |
287 | | - yield AsyncChatSocketClient(websocket=protocol) |
| 151 | + yield AsyncChatClientWithWebsocket(client_wrapper=self._raw_client._client_wrapper) |
288 | 152 | except websockets.exceptions.InvalidStatusCode as exc: |
289 | 153 | status_code: int = exc.status_code |
290 | 154 | if status_code == 401: |
|
0 commit comments