conversations.py 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308
  1. from builtins import list as _list
  2. from pathlib import Path
  3. from typing import Any, Optional
  4. from uuid import UUID
  5. from shared.api.models import (
  6. WrappedBooleanResponse,
  7. WrappedConversationMessagesResponse,
  8. WrappedConversationResponse,
  9. WrappedConversationsResponse,
  10. WrappedMessageResponse,
  11. )
  12. class ConversationsSDK:
  13. def __init__(self, client):
  14. self.client = client
  15. def create(
  16. self,
  17. name: Optional[str] = None,
  18. ) -> WrappedConversationResponse:
  19. """Create a new conversation.
  20. Returns:
  21. WrappedConversationResponse
  22. """
  23. data: dict[str, Any] = {}
  24. if name:
  25. data["name"] = name
  26. response_dict = self.client._make_request(
  27. "POST",
  28. "conversations",
  29. json=data,
  30. version="v3",
  31. )
  32. return WrappedConversationResponse(**response_dict)
  33. def list(
  34. self,
  35. ids: Optional[list[str | UUID]] = None,
  36. offset: Optional[int] = 0,
  37. limit: Optional[int] = 100,
  38. ) -> WrappedConversationsResponse:
  39. """List conversations with pagination and sorting options.
  40. Args:
  41. ids (Optional[list[str | UUID]]): List of conversation IDs to retrieve
  42. offset (int, optional): Specifies the number of objects to skip. Defaults to 0.
  43. limit (int, optional): Specifies a limit on the number of objects to return, ranging between 1 and 100. Defaults to 100.
  44. Returns:
  45. WrappedConversationsResponse
  46. """
  47. params: dict = {
  48. "offset": offset,
  49. "limit": limit,
  50. }
  51. if ids:
  52. params["ids"] = ids
  53. response_dict = self.client._make_request(
  54. "GET",
  55. "conversations",
  56. params=params,
  57. version="v3",
  58. )
  59. return WrappedConversationsResponse(**response_dict)
  60. def retrieve(
  61. self,
  62. id: str | UUID,
  63. ) -> WrappedConversationMessagesResponse:
  64. """Get detailed information about a specific conversation.
  65. Args:
  66. id (str | UUID): The ID of the conversation to retrieve
  67. Returns:
  68. WrappedConversationMessagesResponse
  69. """
  70. response_dict = self.client._make_request(
  71. "GET",
  72. f"conversations/{str(id)}",
  73. version="v3",
  74. )
  75. return WrappedConversationMessagesResponse(**response_dict)
  76. def update(
  77. self,
  78. id: str | UUID,
  79. name: str,
  80. ) -> WrappedConversationResponse:
  81. """Update an existing conversation.
  82. Args:
  83. id (str | UUID): The ID of the conversation to update
  84. name (str): The new name of the conversation
  85. Returns:
  86. WrappedConversationResponse
  87. """
  88. data: dict[str, Any] = {
  89. "name": name,
  90. }
  91. response_dict = self.client._make_request(
  92. "POST",
  93. f"conversations/{str(id)}",
  94. json=data,
  95. version="v3",
  96. )
  97. return WrappedConversationResponse(**response_dict)
  98. def delete(
  99. self,
  100. id: str | UUID,
  101. ) -> WrappedBooleanResponse:
  102. """Delete a conversation.
  103. Args:
  104. id (str | UUID): The ID of the conversation to delete
  105. Returns:
  106. WrappedBooleanResponse
  107. """
  108. response_dict = self.client._make_request(
  109. "DELETE",
  110. f"conversations/{str(id)}",
  111. version="v3",
  112. )
  113. return WrappedBooleanResponse(**response_dict)
  114. def add_message(
  115. self,
  116. id: str | UUID,
  117. content: str,
  118. role: str,
  119. metadata: Optional[dict] = None,
  120. parent_id: Optional[str] = None,
  121. ) -> WrappedMessageResponse:
  122. """Add a new message to a conversation.
  123. Args:
  124. id (str | UUID): The ID of the conversation to add the message to
  125. content (str): The content of the message
  126. role (str): The role of the message (e.g., "user" or "assistant")
  127. parent_id (Optional[str]): The ID of the parent message
  128. metadata (Optional[dict]): Additional metadata to attach to the message
  129. Returns:
  130. WrappedMessageResponse
  131. """
  132. data: dict[str, Any] = {
  133. "content": content,
  134. "role": role,
  135. }
  136. if parent_id:
  137. data["parent_id"] = parent_id
  138. if metadata:
  139. data["metadata"] = metadata
  140. response_dict = self.client._make_request(
  141. "POST",
  142. f"conversations/{str(id)}/messages",
  143. json=data,
  144. version="v3",
  145. )
  146. return WrappedMessageResponse(**response_dict)
  147. def update_message(
  148. self,
  149. id: str | UUID,
  150. message_id: str,
  151. content: Optional[str] = None,
  152. metadata: Optional[dict] = None,
  153. ) -> WrappedMessageResponse:
  154. """Update an existing message in a conversation.
  155. Args:
  156. id (str | UUID): The ID of the conversation containing the message
  157. message_id (str): The ID of the message to update
  158. content (str): The new content of the message
  159. metadata (dict): Additional metadata to attach to the message
  160. Returns:
  161. WrappedMessageResponse
  162. """
  163. data: dict[str, Any] = {"content": content}
  164. if metadata:
  165. data["metadata"] = metadata
  166. response_dict = self.client._make_request(
  167. "POST",
  168. f"conversations/{str(id)}/messages/{message_id}",
  169. json=data,
  170. version="v3",
  171. )
  172. return WrappedMessageResponse(**response_dict)
  173. def export(
  174. self,
  175. output_path: str | Path,
  176. columns: Optional[_list[str]] = None,
  177. filters: Optional[dict] = None,
  178. include_header: bool = True,
  179. ) -> None:
  180. """Export conversations to a CSV file, streaming the results directly
  181. to disk.
  182. Args:
  183. output_path (str | Path): Local path where the CSV file should be saved
  184. columns (Optional[list[str]]): Specific columns to export. If None, exports default columns
  185. filters (Optional[dict]): Optional filters to apply when selecting conversations
  186. include_header (bool): Whether to include column headers in the CSV (default: True)
  187. Returns:
  188. None
  189. """
  190. # Convert path to string if it's a Path object
  191. output_path = (
  192. str(output_path) if isinstance(output_path, Path) else output_path
  193. )
  194. # Prepare request data
  195. data: dict[str, Any] = {"include_header": include_header}
  196. if columns:
  197. data["columns"] = columns
  198. if filters:
  199. data["filters"] = filters
  200. # Stream response directly to file
  201. with open(output_path, "wb") as f:
  202. with self.client.client.post(
  203. f"{self.client.base_url}/v3/conversations/export",
  204. json=data,
  205. headers={
  206. "Accept": "text/csv",
  207. **self.client._get_auth_header(),
  208. },
  209. ) as response:
  210. if response.status != 200:
  211. raise ValueError(
  212. f"Export failed with status {response.status}",
  213. response,
  214. )
  215. for chunk in response.content.iter_chunks():
  216. if chunk:
  217. f.write(chunk[0])
  218. def export_messages(
  219. self,
  220. output_path: str | Path,
  221. columns: Optional[_list[str]] = None,
  222. filters: Optional[dict] = None,
  223. include_header: bool = True,
  224. ) -> None:
  225. """Export messages to a CSV file, streaming the results directly to
  226. disk.
  227. Args:
  228. output_path (str | Path): Local path where the CSV file should be saved
  229. columns (Optional[list[str]]): Specific columns to export. If None, exports default columns
  230. filters (Optional[dict]): Optional filters to apply when selecting messages
  231. include_header (bool): Whether to include column headers in the CSV (default: True)
  232. Returns:
  233. None
  234. """
  235. # Convert path to string if it's a Path object
  236. output_path = (
  237. str(output_path) if isinstance(output_path, Path) else output_path
  238. )
  239. # Prepare request data
  240. data: dict[str, Any] = {"include_header": include_header}
  241. if columns:
  242. data["columns"] = columns
  243. if filters:
  244. data["filters"] = filters
  245. # Stream response directly to file
  246. with open(output_path, "wb") as f:
  247. with self.client.session.post(
  248. f"{self.client.base_url}/v3/conversations/export_messages",
  249. json=data,
  250. headers={
  251. "Accept": "text/csv",
  252. **self.client._get_auth_header(),
  253. },
  254. ) as response:
  255. if response.status_code != 200:
  256. raise ValueError(
  257. f"Export failed with status {response.status_code}",
  258. response,
  259. )
  260. for chunk in response.iter_bytes():
  261. if chunk:
  262. f.write(chunk[0])