zep-cloud 3.2.0__tar.gz → 3.4.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (96) hide show
  1. {zep_cloud-3.2.0 → zep_cloud-3.4.0}/PKG-INFO +1 -1
  2. {zep_cloud-3.2.0 → zep_cloud-3.4.0}/pyproject.toml +1 -1
  3. {zep_cloud-3.2.0 → zep_cloud-3.4.0}/src/zep_cloud/__init__.py +2 -0
  4. {zep_cloud-3.2.0 → zep_cloud-3.4.0}/src/zep_cloud/core/client_wrapper.py +2 -2
  5. {zep_cloud-3.2.0 → zep_cloud-3.4.0}/src/zep_cloud/graph/client.py +4 -4
  6. {zep_cloud-3.2.0 → zep_cloud-3.4.0}/src/zep_cloud/graph/raw_client.py +4 -4
  7. zep_cloud-3.4.0/src/zep_cloud/graph/utils.py +135 -0
  8. {zep_cloud-3.2.0 → zep_cloud-3.4.0}/src/zep_cloud/thread/client.py +132 -0
  9. {zep_cloud-3.2.0 → zep_cloud-3.4.0}/src/zep_cloud/thread/raw_client.py +164 -0
  10. {zep_cloud-3.2.0 → zep_cloud-3.4.0}/src/zep_cloud/types/__init__.py +2 -0
  11. zep_cloud-3.4.0/src/zep_cloud/types/add_thread_messages_request.py +36 -0
  12. {zep_cloud-3.2.0 → zep_cloud-3.4.0}/src/zep_cloud/types/add_thread_messages_response.py +1 -0
  13. zep_cloud-3.2.0/src/zep_cloud/graph/utils.py +0 -70
  14. {zep_cloud-3.2.0 → zep_cloud-3.4.0}/LICENSE +0 -0
  15. {zep_cloud-3.2.0 → zep_cloud-3.4.0}/README.md +0 -0
  16. {zep_cloud-3.2.0 → zep_cloud-3.4.0}/src/zep_cloud/base_client.py +0 -0
  17. {zep_cloud-3.2.0 → zep_cloud-3.4.0}/src/zep_cloud/client.py +0 -0
  18. {zep_cloud-3.2.0 → zep_cloud-3.4.0}/src/zep_cloud/core/__init__.py +0 -0
  19. {zep_cloud-3.2.0 → zep_cloud-3.4.0}/src/zep_cloud/core/api_error.py +0 -0
  20. {zep_cloud-3.2.0 → zep_cloud-3.4.0}/src/zep_cloud/core/datetime_utils.py +0 -0
  21. {zep_cloud-3.2.0 → zep_cloud-3.4.0}/src/zep_cloud/core/file.py +0 -0
  22. {zep_cloud-3.2.0 → zep_cloud-3.4.0}/src/zep_cloud/core/force_multipart.py +0 -0
  23. {zep_cloud-3.2.0 → zep_cloud-3.4.0}/src/zep_cloud/core/http_client.py +0 -0
  24. {zep_cloud-3.2.0 → zep_cloud-3.4.0}/src/zep_cloud/core/http_response.py +0 -0
  25. {zep_cloud-3.2.0 → zep_cloud-3.4.0}/src/zep_cloud/core/jsonable_encoder.py +0 -0
  26. {zep_cloud-3.2.0 → zep_cloud-3.4.0}/src/zep_cloud/core/pydantic_utilities.py +0 -0
  27. {zep_cloud-3.2.0 → zep_cloud-3.4.0}/src/zep_cloud/core/query_encoder.py +0 -0
  28. {zep_cloud-3.2.0 → zep_cloud-3.4.0}/src/zep_cloud/core/remove_none_from_dict.py +0 -0
  29. {zep_cloud-3.2.0 → zep_cloud-3.4.0}/src/zep_cloud/core/request_options.py +0 -0
  30. {zep_cloud-3.2.0 → zep_cloud-3.4.0}/src/zep_cloud/core/serialization.py +0 -0
  31. {zep_cloud-3.2.0 → zep_cloud-3.4.0}/src/zep_cloud/environment.py +0 -0
  32. {zep_cloud-3.2.0 → zep_cloud-3.4.0}/src/zep_cloud/errors/__init__.py +0 -0
  33. {zep_cloud-3.2.0 → zep_cloud-3.4.0}/src/zep_cloud/errors/bad_request_error.py +0 -0
  34. {zep_cloud-3.2.0 → zep_cloud-3.4.0}/src/zep_cloud/errors/internal_server_error.py +0 -0
  35. {zep_cloud-3.2.0 → zep_cloud-3.4.0}/src/zep_cloud/errors/not_found_error.py +0 -0
  36. {zep_cloud-3.2.0 → zep_cloud-3.4.0}/src/zep_cloud/external_clients/graph.py +0 -0
  37. {zep_cloud-3.2.0 → zep_cloud-3.4.0}/src/zep_cloud/external_clients/ontology.py +0 -0
  38. {zep_cloud-3.2.0 → zep_cloud-3.4.0}/src/zep_cloud/external_clients/user.py +0 -0
  39. {zep_cloud-3.2.0 → zep_cloud-3.4.0}/src/zep_cloud/graph/__init__.py +0 -0
  40. {zep_cloud-3.2.0 → zep_cloud-3.4.0}/src/zep_cloud/graph/edge/__init__.py +0 -0
  41. {zep_cloud-3.2.0 → zep_cloud-3.4.0}/src/zep_cloud/graph/edge/client.py +0 -0
  42. {zep_cloud-3.2.0 → zep_cloud-3.4.0}/src/zep_cloud/graph/edge/raw_client.py +0 -0
  43. {zep_cloud-3.2.0 → zep_cloud-3.4.0}/src/zep_cloud/graph/episode/__init__.py +0 -0
  44. {zep_cloud-3.2.0 → zep_cloud-3.4.0}/src/zep_cloud/graph/episode/client.py +0 -0
  45. {zep_cloud-3.2.0 → zep_cloud-3.4.0}/src/zep_cloud/graph/episode/raw_client.py +0 -0
  46. {zep_cloud-3.2.0 → zep_cloud-3.4.0}/src/zep_cloud/graph/node/__init__.py +0 -0
  47. {zep_cloud-3.2.0 → zep_cloud-3.4.0}/src/zep_cloud/graph/node/client.py +0 -0
  48. {zep_cloud-3.2.0 → zep_cloud-3.4.0}/src/zep_cloud/graph/node/raw_client.py +0 -0
  49. {zep_cloud-3.2.0 → zep_cloud-3.4.0}/src/zep_cloud/py.typed +0 -0
  50. {zep_cloud-3.2.0 → zep_cloud-3.4.0}/src/zep_cloud/thread/__init__.py +0 -0
  51. {zep_cloud-3.2.0 → zep_cloud-3.4.0}/src/zep_cloud/thread/types/__init__.py +0 -0
  52. {zep_cloud-3.2.0 → zep_cloud-3.4.0}/src/zep_cloud/thread/types/thread_get_user_context_request_mode.py +0 -0
  53. {zep_cloud-3.2.0 → zep_cloud-3.4.0}/src/zep_cloud/types/add_triple_response.py +0 -0
  54. {zep_cloud-3.2.0 → zep_cloud-3.4.0}/src/zep_cloud/types/api_error.py +0 -0
  55. {zep_cloud-3.2.0 → zep_cloud-3.4.0}/src/zep_cloud/types/clone_graph_response.py +0 -0
  56. {zep_cloud-3.2.0 → zep_cloud-3.4.0}/src/zep_cloud/types/comparison_operator.py +0 -0
  57. {zep_cloud-3.2.0 → zep_cloud-3.4.0}/src/zep_cloud/types/date_filter.py +0 -0
  58. {zep_cloud-3.2.0 → zep_cloud-3.4.0}/src/zep_cloud/types/edge_type.py +0 -0
  59. {zep_cloud-3.2.0 → zep_cloud-3.4.0}/src/zep_cloud/types/entity_edge.py +0 -0
  60. {zep_cloud-3.2.0 → zep_cloud-3.4.0}/src/zep_cloud/types/entity_edge_source_target.py +0 -0
  61. {zep_cloud-3.2.0 → zep_cloud-3.4.0}/src/zep_cloud/types/entity_node.py +0 -0
  62. {zep_cloud-3.2.0 → zep_cloud-3.4.0}/src/zep_cloud/types/entity_property.py +0 -0
  63. {zep_cloud-3.2.0 → zep_cloud-3.4.0}/src/zep_cloud/types/entity_property_type.py +0 -0
  64. {zep_cloud-3.2.0 → zep_cloud-3.4.0}/src/zep_cloud/types/entity_type.py +0 -0
  65. {zep_cloud-3.2.0 → zep_cloud-3.4.0}/src/zep_cloud/types/entity_type_response.py +0 -0
  66. {zep_cloud-3.2.0 → zep_cloud-3.4.0}/src/zep_cloud/types/episode.py +0 -0
  67. {zep_cloud-3.2.0 → zep_cloud-3.4.0}/src/zep_cloud/types/episode_data.py +0 -0
  68. {zep_cloud-3.2.0 → zep_cloud-3.4.0}/src/zep_cloud/types/episode_mentions.py +0 -0
  69. {zep_cloud-3.2.0 → zep_cloud-3.4.0}/src/zep_cloud/types/episode_response.py +0 -0
  70. {zep_cloud-3.2.0 → zep_cloud-3.4.0}/src/zep_cloud/types/fact_rating_examples.py +0 -0
  71. {zep_cloud-3.2.0 → zep_cloud-3.4.0}/src/zep_cloud/types/fact_rating_instruction.py +0 -0
  72. {zep_cloud-3.2.0 → zep_cloud-3.4.0}/src/zep_cloud/types/graph.py +0 -0
  73. {zep_cloud-3.2.0 → zep_cloud-3.4.0}/src/zep_cloud/types/graph_data_type.py +0 -0
  74. {zep_cloud-3.2.0 → zep_cloud-3.4.0}/src/zep_cloud/types/graph_edges_request.py +0 -0
  75. {zep_cloud-3.2.0 → zep_cloud-3.4.0}/src/zep_cloud/types/graph_list_response.py +0 -0
  76. {zep_cloud-3.2.0 → zep_cloud-3.4.0}/src/zep_cloud/types/graph_nodes_request.py +0 -0
  77. {zep_cloud-3.2.0 → zep_cloud-3.4.0}/src/zep_cloud/types/graph_search_results.py +0 -0
  78. {zep_cloud-3.2.0 → zep_cloud-3.4.0}/src/zep_cloud/types/graph_search_scope.py +0 -0
  79. {zep_cloud-3.2.0 → zep_cloud-3.4.0}/src/zep_cloud/types/message.py +0 -0
  80. {zep_cloud-3.2.0 → zep_cloud-3.4.0}/src/zep_cloud/types/message_list_response.py +0 -0
  81. {zep_cloud-3.2.0 → zep_cloud-3.4.0}/src/zep_cloud/types/models_fact_rating_examples.py +0 -0
  82. {zep_cloud-3.2.0 → zep_cloud-3.4.0}/src/zep_cloud/types/models_fact_rating_instruction.py +0 -0
  83. {zep_cloud-3.2.0 → zep_cloud-3.4.0}/src/zep_cloud/types/reranker.py +0 -0
  84. {zep_cloud-3.2.0 → zep_cloud-3.4.0}/src/zep_cloud/types/role_type.py +0 -0
  85. {zep_cloud-3.2.0 → zep_cloud-3.4.0}/src/zep_cloud/types/search_filters.py +0 -0
  86. {zep_cloud-3.2.0 → zep_cloud-3.4.0}/src/zep_cloud/types/success_response.py +0 -0
  87. {zep_cloud-3.2.0 → zep_cloud-3.4.0}/src/zep_cloud/types/thread.py +0 -0
  88. {zep_cloud-3.2.0 → zep_cloud-3.4.0}/src/zep_cloud/types/thread_context_response.py +0 -0
  89. {zep_cloud-3.2.0 → zep_cloud-3.4.0}/src/zep_cloud/types/thread_list_response.py +0 -0
  90. {zep_cloud-3.2.0 → zep_cloud-3.4.0}/src/zep_cloud/types/user.py +0 -0
  91. {zep_cloud-3.2.0 → zep_cloud-3.4.0}/src/zep_cloud/types/user_list_response.py +0 -0
  92. {zep_cloud-3.2.0 → zep_cloud-3.4.0}/src/zep_cloud/types/user_node_response.py +0 -0
  93. {zep_cloud-3.2.0 → zep_cloud-3.4.0}/src/zep_cloud/user/__init__.py +0 -0
  94. {zep_cloud-3.2.0 → zep_cloud-3.4.0}/src/zep_cloud/user/client.py +0 -0
  95. {zep_cloud-3.2.0 → zep_cloud-3.4.0}/src/zep_cloud/user/raw_client.py +0 -0
  96. {zep_cloud-3.2.0 → zep_cloud-3.4.0}/src/zep_cloud/version.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: zep-cloud
3
- Version: 3.2.0
3
+ Version: 3.4.0
4
4
  Summary:
5
5
  Requires-Python: >=3.9.0,<4.0
6
6
  Classifier: Intended Audience :: Developers
@@ -3,7 +3,7 @@ name = "zep-cloud"
3
3
 
4
4
  [tool.poetry]
5
5
  name = "zep-cloud"
6
- version = "3.2.0"
6
+ version = "3.4.0"
7
7
  description = ""
8
8
  readme = "README.md"
9
9
  authors = []
@@ -3,6 +3,7 @@
3
3
  # isort: skip_file
4
4
 
5
5
  from .types import (
6
+ AddThreadMessagesRequest,
6
7
  AddThreadMessagesResponse,
7
8
  AddTripleResponse,
8
9
  ApiError,
@@ -53,6 +54,7 @@ from .thread import ThreadGetUserContextRequestMode
53
54
  from .version import __version__
54
55
 
55
56
  __all__ = [
57
+ "AddThreadMessagesRequest",
56
58
  "AddThreadMessagesResponse",
57
59
  "AddTripleResponse",
58
60
  "ApiError",
@@ -22,10 +22,10 @@ class BaseClientWrapper:
22
22
 
23
23
  def get_headers(self) -> typing.Dict[str, str]:
24
24
  headers: typing.Dict[str, str] = {
25
- "User-Agent": "zep-cloud/3.2.0",
25
+ "User-Agent": "zep-cloud/3.4.0",
26
26
  "X-Fern-Language": "Python",
27
27
  "X-Fern-SDK-Name": "zep-cloud",
28
- "X-Fern-SDK-Version": "3.2.0",
28
+ "X-Fern-SDK-Version": "3.4.0",
29
29
  **(self.get_custom_headers() or {}),
30
30
  }
31
31
  headers["Authorization"] = f"Api-Key {self.api_key}"
@@ -389,7 +389,7 @@ class GraphClient:
389
389
  Returns
390
390
  -------
391
391
  CloneGraphResponse
392
- Response object containing group_id or user_id pointing to the new graph
392
+ Response object containing graph_id or user_id pointing to the new graph
393
393
 
394
394
  Examples
395
395
  --------
@@ -467,7 +467,7 @@ class GraphClient:
467
467
  request_options: typing.Optional[RequestOptions] = None,
468
468
  ) -> GraphListResponse:
469
469
  """
470
- List all graphs. In order to list users, use user.list_ordered instead
470
+ Returns all graphs. In order to list users, use user.list_ordered instead
471
471
 
472
472
  Parameters
473
473
  ----------
@@ -1107,7 +1107,7 @@ class AsyncGraphClient:
1107
1107
  Returns
1108
1108
  -------
1109
1109
  CloneGraphResponse
1110
- Response object containing group_id or user_id pointing to the new graph
1110
+ Response object containing graph_id or user_id pointing to the new graph
1111
1111
 
1112
1112
  Examples
1113
1113
  --------
@@ -1201,7 +1201,7 @@ class AsyncGraphClient:
1201
1201
  request_options: typing.Optional[RequestOptions] = None,
1202
1202
  ) -> GraphListResponse:
1203
1203
  """
1204
- List all graphs. In order to list users, use user.list_ordered instead
1204
+ Returns all graphs. In order to list users, use user.list_ordered instead
1205
1205
 
1206
1206
  Parameters
1207
1207
  ----------
@@ -573,7 +573,7 @@ class RawGraphClient:
573
573
  Returns
574
574
  -------
575
575
  HttpResponse[CloneGraphResponse]
576
- Response object containing group_id or user_id pointing to the new graph
576
+ Response object containing graph_id or user_id pointing to the new graph
577
577
  """
578
578
  _response = self._client_wrapper.httpx_client.request(
579
579
  "graph/clone",
@@ -727,7 +727,7 @@ class RawGraphClient:
727
727
  request_options: typing.Optional[RequestOptions] = None,
728
728
  ) -> HttpResponse[GraphListResponse]:
729
729
  """
730
- List all graphs. In order to list users, use user.list_ordered instead
730
+ Returns all graphs. In order to list users, use user.list_ordered instead
731
731
 
732
732
  Parameters
733
733
  ----------
@@ -1704,7 +1704,7 @@ class AsyncRawGraphClient:
1704
1704
  Returns
1705
1705
  -------
1706
1706
  AsyncHttpResponse[CloneGraphResponse]
1707
- Response object containing group_id or user_id pointing to the new graph
1707
+ Response object containing graph_id or user_id pointing to the new graph
1708
1708
  """
1709
1709
  _response = await self._client_wrapper.httpx_client.request(
1710
1710
  "graph/clone",
@@ -1858,7 +1858,7 @@ class AsyncRawGraphClient:
1858
1858
  request_options: typing.Optional[RequestOptions] = None,
1859
1859
  ) -> AsyncHttpResponse[GraphListResponse]:
1860
1860
  """
1861
- List all graphs. In order to list users, use user.list_ordered instead
1861
+ Returns all graphs. In order to list users, use user.list_ordered instead
1862
1862
 
1863
1863
  Parameters
1864
1864
  ----------
@@ -0,0 +1,135 @@
1
+ from datetime import datetime
2
+ from typing import List
3
+
4
+ from zep_cloud import EntityEdge, EntityNode, Episode
5
+
6
+ DATE_FORMAT = "%Y-%m-%d %H:%M:%S"
7
+
8
+
9
+ def parse_iso_datetime(iso_string: str) -> datetime:
10
+ """Parse ISO datetime string, handling Z suffix for UTC."""
11
+ try:
12
+ return datetime.fromisoformat(iso_string)
13
+ except ValueError:
14
+ # Handle Z suffix for Python 3.9 compatibility
15
+ if iso_string.endswith('Z'):
16
+ return datetime.fromisoformat(iso_string[:-1] + '+00:00')
17
+ raise
18
+
19
+ TEMPLATE_STRING = """
20
+ FACTS and ENTITIES{episodes_header} represent relevant context to the current conversation.
21
+
22
+ # These are the most relevant facts and their valid date ranges
23
+ # format: FACT (Date range: from - to)
24
+ <FACTS>
25
+ {facts}
26
+ </FACTS>
27
+
28
+ # These are the most relevant entities
29
+ # Name: ENTITY_NAME
30
+ # Label: entity_label (if present)
31
+ # Attributes: (if present)
32
+ # attr_name: attr_value
33
+ # Summary: entity summary
34
+ <ENTITIES>
35
+ {entities}
36
+ </ENTITIES>
37
+ {episodes_section}
38
+ """
39
+
40
+
41
+ def format_edge_date_range(edge: EntityEdge) -> str:
42
+ """
43
+ Format the date range of an entity edge.
44
+
45
+ Args:
46
+ edge: The entity edge to format.
47
+
48
+ Returns:
49
+ A string representation of the date range.
50
+ """
51
+ valid_at = "date unknown"
52
+ invalid_at = "present"
53
+
54
+ if edge.valid_at is not None:
55
+ valid_at = parse_iso_datetime(edge.valid_at).strftime(DATE_FORMAT)
56
+ if edge.invalid_at is not None:
57
+ invalid_at = parse_iso_datetime(edge.invalid_at).strftime(DATE_FORMAT)
58
+
59
+ return f"{valid_at} - {invalid_at}"
60
+
61
+
62
+ def compose_context_string(edges: List[EntityEdge], nodes: List[EntityNode], episodes: List[Episode]) -> str:
63
+ """
64
+ Compose a search context from entity edges, nodes, and episodes.
65
+
66
+ Args:
67
+ edges: List of entity edges.
68
+ nodes: List of entity nodes.
69
+ episodes: List of episodes.
70
+
71
+ Returns:
72
+ A formatted string containing facts, entities, and episodes.
73
+ """
74
+ facts = []
75
+ for edge in edges:
76
+ fact = f" - {edge.fact} (Date range: {format_edge_date_range(edge)})"
77
+ facts.append(fact)
78
+
79
+ entities = []
80
+ for node in nodes:
81
+ entity_parts = [f"Name: {node.name}"]
82
+
83
+ if hasattr(node, 'labels') and node.labels:
84
+ labels = list(node.labels) # Create a copy to avoid modifying original
85
+ if 'Entity' in labels:
86
+ labels.remove('Entity')
87
+ if labels: # Only add label if there are remaining labels after removing 'Entity'
88
+ entity_parts.append(f"Label: {labels[0]}")
89
+
90
+ if hasattr(node, 'attributes') and node.attributes:
91
+ # Filter out 'labels' from attributes as it's redundant with the Label field
92
+ filtered_attributes = {k: v for k, v in node.attributes.items() if k != 'labels'}
93
+ if filtered_attributes: # Only add attributes section if there are non-label attributes
94
+ entity_parts.append("Attributes:")
95
+ for attr_name, attr_value in filtered_attributes.items():
96
+ entity_parts.append(f" {attr_name}: {attr_value}")
97
+
98
+ if node.summary:
99
+ entity_parts.append(f"Summary: {node.summary}")
100
+
101
+ entity = "\n".join(entity_parts)
102
+ entities.append(entity)
103
+
104
+ # Format episodes
105
+ episodes_list = []
106
+ if episodes:
107
+ for episode in episodes:
108
+ role_prefix = ""
109
+ if hasattr(episode, 'role') and episode.role:
110
+ if hasattr(episode, 'role_type') and episode.role_type:
111
+ role_prefix = f"{episode.role} ({episode.role_type}): "
112
+ else:
113
+ role_prefix = f"{episode.role}: "
114
+ elif hasattr(episode, 'role_type') and episode.role_type:
115
+ role_prefix = f"({episode.role_type}): "
116
+
117
+ timestamp = parse_iso_datetime(episode.created_at).strftime(DATE_FORMAT)
118
+
119
+ episode_str = f" - {role_prefix}{episode.content} ({timestamp})"
120
+ episodes_list.append(episode_str)
121
+
122
+ facts_str = "\n".join(facts) if facts else ""
123
+ entities_str = "\n".join(entities) if entities else ""
124
+ episodes_str = "\n".join(episodes_list) if episodes_list else ""
125
+
126
+ # Determine if episodes section should be included
127
+ episodes_header = ", and EPISODES" if episodes else ""
128
+ episodes_section = f"\n# These are the most relevant episodes\n<EPISODES>\n{episodes_str}\n</EPISODES>" if episodes else ""
129
+
130
+ return TEMPLATE_STRING.format(
131
+ episodes_header=episodes_header,
132
+ facts=facts_str,
133
+ entities=entities_str,
134
+ episodes_section=episodes_section
135
+ )
@@ -308,6 +308,68 @@ class ThreadClient:
308
308
  )
309
309
  return _response.data
310
310
 
311
+ def add_messages_batch(
312
+ self,
313
+ thread_id: str,
314
+ *,
315
+ messages: typing.Sequence[Message],
316
+ ignore_roles: typing.Optional[typing.Sequence[RoleType]] = OMIT,
317
+ return_context: typing.Optional[bool] = OMIT,
318
+ request_options: typing.Optional[RequestOptions] = None,
319
+ ) -> AddThreadMessagesResponse:
320
+ """
321
+ Add messages to a thread in batch mode. This will process messages concurrently, which is useful for data migrations.
322
+
323
+ Parameters
324
+ ----------
325
+ thread_id : str
326
+ The ID of the thread to which messages should be added.
327
+
328
+ messages : typing.Sequence[Message]
329
+ A list of message objects, where each message contains a role and content.
330
+
331
+ ignore_roles : typing.Optional[typing.Sequence[RoleType]]
332
+ Optional list of role types to ignore when adding messages to graph memory.
333
+ The message itself will still be added, retained and used as context for messages
334
+ that are added to a user's graph.
335
+
336
+ return_context : typing.Optional[bool]
337
+ Optionally return memory context relevant to the most recent messages.
338
+
339
+ request_options : typing.Optional[RequestOptions]
340
+ Request-specific configuration.
341
+
342
+ Returns
343
+ -------
344
+ AddThreadMessagesResponse
345
+ An object, optionally containing user context retrieved for the last thread message
346
+
347
+ Examples
348
+ --------
349
+ from zep_cloud import Message, Zep
350
+
351
+ client = Zep(
352
+ api_key="YOUR_API_KEY",
353
+ )
354
+ client.thread.add_messages_batch(
355
+ thread_id="threadId",
356
+ messages=[
357
+ Message(
358
+ content="content",
359
+ role="norole",
360
+ )
361
+ ],
362
+ )
363
+ """
364
+ _response = self._raw_client.add_messages_batch(
365
+ thread_id,
366
+ messages=messages,
367
+ ignore_roles=ignore_roles,
368
+ return_context=return_context,
369
+ request_options=request_options,
370
+ )
371
+ return _response.data
372
+
311
373
 
312
374
  class AsyncThreadClient:
313
375
  def __init__(self, *, client_wrapper: AsyncClientWrapper):
@@ -647,3 +709,73 @@ class AsyncThreadClient:
647
709
  request_options=request_options,
648
710
  )
649
711
  return _response.data
712
+
713
+ async def add_messages_batch(
714
+ self,
715
+ thread_id: str,
716
+ *,
717
+ messages: typing.Sequence[Message],
718
+ ignore_roles: typing.Optional[typing.Sequence[RoleType]] = OMIT,
719
+ return_context: typing.Optional[bool] = OMIT,
720
+ request_options: typing.Optional[RequestOptions] = None,
721
+ ) -> AddThreadMessagesResponse:
722
+ """
723
+ Add messages to a thread in batch mode. This will process messages concurrently, which is useful for data migrations.
724
+
725
+ Parameters
726
+ ----------
727
+ thread_id : str
728
+ The ID of the thread to which messages should be added.
729
+
730
+ messages : typing.Sequence[Message]
731
+ A list of message objects, where each message contains a role and content.
732
+
733
+ ignore_roles : typing.Optional[typing.Sequence[RoleType]]
734
+ Optional list of role types to ignore when adding messages to graph memory.
735
+ The message itself will still be added, retained and used as context for messages
736
+ that are added to a user's graph.
737
+
738
+ return_context : typing.Optional[bool]
739
+ Optionally return memory context relevant to the most recent messages.
740
+
741
+ request_options : typing.Optional[RequestOptions]
742
+ Request-specific configuration.
743
+
744
+ Returns
745
+ -------
746
+ AddThreadMessagesResponse
747
+ An object, optionally containing user context retrieved for the last thread message
748
+
749
+ Examples
750
+ --------
751
+ import asyncio
752
+
753
+ from zep_cloud import AsyncZep, Message
754
+
755
+ client = AsyncZep(
756
+ api_key="YOUR_API_KEY",
757
+ )
758
+
759
+
760
+ async def main() -> None:
761
+ await client.thread.add_messages_batch(
762
+ thread_id="threadId",
763
+ messages=[
764
+ Message(
765
+ content="content",
766
+ role="norole",
767
+ )
768
+ ],
769
+ )
770
+
771
+
772
+ asyncio.run(main())
773
+ """
774
+ _response = await self._raw_client.add_messages_batch(
775
+ thread_id,
776
+ messages=messages,
777
+ ignore_roles=ignore_roles,
778
+ return_context=return_context,
779
+ request_options=request_options,
780
+ )
781
+ return _response.data
@@ -506,6 +506,88 @@ class RawThreadClient:
506
506
  status_code=_response.status_code, headers=dict(_response.headers), body=_response_json
507
507
  )
508
508
 
509
+ def add_messages_batch(
510
+ self,
511
+ thread_id: str,
512
+ *,
513
+ messages: typing.Sequence[Message],
514
+ ignore_roles: typing.Optional[typing.Sequence[RoleType]] = OMIT,
515
+ return_context: typing.Optional[bool] = OMIT,
516
+ request_options: typing.Optional[RequestOptions] = None,
517
+ ) -> HttpResponse[AddThreadMessagesResponse]:
518
+ """
519
+ Add messages to a thread in batch mode. This will process messages concurrently, which is useful for data migrations.
520
+
521
+ Parameters
522
+ ----------
523
+ thread_id : str
524
+ The ID of the thread to which messages should be added.
525
+
526
+ messages : typing.Sequence[Message]
527
+ A list of message objects, where each message contains a role and content.
528
+
529
+ ignore_roles : typing.Optional[typing.Sequence[RoleType]]
530
+ Optional list of role types to ignore when adding messages to graph memory.
531
+ The message itself will still be added, retained and used as context for messages
532
+ that are added to a user's graph.
533
+
534
+ return_context : typing.Optional[bool]
535
+ Optionally return memory context relevant to the most recent messages.
536
+
537
+ request_options : typing.Optional[RequestOptions]
538
+ Request-specific configuration.
539
+
540
+ Returns
541
+ -------
542
+ HttpResponse[AddThreadMessagesResponse]
543
+ An object, optionally containing user context retrieved for the last thread message
544
+ """
545
+ _response = self._client_wrapper.httpx_client.request(
546
+ f"threads/{jsonable_encoder(thread_id)}/messages-batch",
547
+ method="POST",
548
+ json={
549
+ "ignore_roles": ignore_roles,
550
+ "messages": convert_and_respect_annotation_metadata(
551
+ object_=messages, annotation=typing.Sequence[Message], direction="write"
552
+ ),
553
+ "return_context": return_context,
554
+ },
555
+ headers={
556
+ "content-type": "application/json",
557
+ },
558
+ request_options=request_options,
559
+ omit=OMIT,
560
+ )
561
+ try:
562
+ if 200 <= _response.status_code < 300:
563
+ _data = typing.cast(
564
+ AddThreadMessagesResponse,
565
+ parse_obj_as(
566
+ type_=AddThreadMessagesResponse, # type: ignore
567
+ object_=_response.json(),
568
+ ),
569
+ )
570
+ return HttpResponse(response=_response, data=_data)
571
+ if _response.status_code == 500:
572
+ raise InternalServerError(
573
+ headers=dict(_response.headers),
574
+ body=typing.cast(
575
+ types_api_error_ApiError,
576
+ parse_obj_as(
577
+ type_=types_api_error_ApiError, # type: ignore
578
+ object_=_response.json(),
579
+ ),
580
+ ),
581
+ )
582
+ _response_json = _response.json()
583
+ except JSONDecodeError:
584
+ raise core_api_error_ApiError(
585
+ status_code=_response.status_code, headers=dict(_response.headers), body=_response.text
586
+ )
587
+ raise core_api_error_ApiError(
588
+ status_code=_response.status_code, headers=dict(_response.headers), body=_response_json
589
+ )
590
+
509
591
 
510
592
  class AsyncRawThreadClient:
511
593
  def __init__(self, *, client_wrapper: AsyncClientWrapper):
@@ -984,3 +1066,85 @@ class AsyncRawThreadClient:
984
1066
  raise core_api_error_ApiError(
985
1067
  status_code=_response.status_code, headers=dict(_response.headers), body=_response_json
986
1068
  )
1069
+
1070
+ async def add_messages_batch(
1071
+ self,
1072
+ thread_id: str,
1073
+ *,
1074
+ messages: typing.Sequence[Message],
1075
+ ignore_roles: typing.Optional[typing.Sequence[RoleType]] = OMIT,
1076
+ return_context: typing.Optional[bool] = OMIT,
1077
+ request_options: typing.Optional[RequestOptions] = None,
1078
+ ) -> AsyncHttpResponse[AddThreadMessagesResponse]:
1079
+ """
1080
+ Add messages to a thread in batch mode. This will process messages concurrently, which is useful for data migrations.
1081
+
1082
+ Parameters
1083
+ ----------
1084
+ thread_id : str
1085
+ The ID of the thread to which messages should be added.
1086
+
1087
+ messages : typing.Sequence[Message]
1088
+ A list of message objects, where each message contains a role and content.
1089
+
1090
+ ignore_roles : typing.Optional[typing.Sequence[RoleType]]
1091
+ Optional list of role types to ignore when adding messages to graph memory.
1092
+ The message itself will still be added, retained and used as context for messages
1093
+ that are added to a user's graph.
1094
+
1095
+ return_context : typing.Optional[bool]
1096
+ Optionally return memory context relevant to the most recent messages.
1097
+
1098
+ request_options : typing.Optional[RequestOptions]
1099
+ Request-specific configuration.
1100
+
1101
+ Returns
1102
+ -------
1103
+ AsyncHttpResponse[AddThreadMessagesResponse]
1104
+ An object, optionally containing user context retrieved for the last thread message
1105
+ """
1106
+ _response = await self._client_wrapper.httpx_client.request(
1107
+ f"threads/{jsonable_encoder(thread_id)}/messages-batch",
1108
+ method="POST",
1109
+ json={
1110
+ "ignore_roles": ignore_roles,
1111
+ "messages": convert_and_respect_annotation_metadata(
1112
+ object_=messages, annotation=typing.Sequence[Message], direction="write"
1113
+ ),
1114
+ "return_context": return_context,
1115
+ },
1116
+ headers={
1117
+ "content-type": "application/json",
1118
+ },
1119
+ request_options=request_options,
1120
+ omit=OMIT,
1121
+ )
1122
+ try:
1123
+ if 200 <= _response.status_code < 300:
1124
+ _data = typing.cast(
1125
+ AddThreadMessagesResponse,
1126
+ parse_obj_as(
1127
+ type_=AddThreadMessagesResponse, # type: ignore
1128
+ object_=_response.json(),
1129
+ ),
1130
+ )
1131
+ return AsyncHttpResponse(response=_response, data=_data)
1132
+ if _response.status_code == 500:
1133
+ raise InternalServerError(
1134
+ headers=dict(_response.headers),
1135
+ body=typing.cast(
1136
+ types_api_error_ApiError,
1137
+ parse_obj_as(
1138
+ type_=types_api_error_ApiError, # type: ignore
1139
+ object_=_response.json(),
1140
+ ),
1141
+ ),
1142
+ )
1143
+ _response_json = _response.json()
1144
+ except JSONDecodeError:
1145
+ raise core_api_error_ApiError(
1146
+ status_code=_response.status_code, headers=dict(_response.headers), body=_response.text
1147
+ )
1148
+ raise core_api_error_ApiError(
1149
+ status_code=_response.status_code, headers=dict(_response.headers), body=_response_json
1150
+ )
@@ -2,6 +2,7 @@
2
2
 
3
3
  # isort: skip_file
4
4
 
5
+ from .add_thread_messages_request import AddThreadMessagesRequest
5
6
  from .add_thread_messages_response import AddThreadMessagesResponse
6
7
  from .add_triple_response import AddTripleResponse
7
8
  from .api_error import ApiError
@@ -45,6 +46,7 @@ from .user_list_response import UserListResponse
45
46
  from .user_node_response import UserNodeResponse
46
47
 
47
48
  __all__ = [
49
+ "AddThreadMessagesRequest",
48
50
  "AddThreadMessagesResponse",
49
51
  "AddTripleResponse",
50
52
  "ApiError",
@@ -0,0 +1,36 @@
1
+ # This file was auto-generated by Fern from our API Definition.
2
+
3
+ import typing
4
+
5
+ import pydantic
6
+ from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel
7
+ from .message import Message
8
+ from .role_type import RoleType
9
+
10
+
11
+ class AddThreadMessagesRequest(UniversalBaseModel):
12
+ ignore_roles: typing.Optional[typing.List[RoleType]] = pydantic.Field(default=None)
13
+ """
14
+ Optional list of role types to ignore when adding messages to graph memory.
15
+ The message itself will still be added, retained and used as context for messages
16
+ that are added to a user's graph.
17
+ """
18
+
19
+ messages: typing.List[Message] = pydantic.Field()
20
+ """
21
+ A list of message objects, where each message contains a role and content.
22
+ """
23
+
24
+ return_context: typing.Optional[bool] = pydantic.Field(default=None)
25
+ """
26
+ Optionally return memory context relevant to the most recent messages.
27
+ """
28
+
29
+ if IS_PYDANTIC_V2:
30
+ model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
31
+ else:
32
+
33
+ class Config:
34
+ frozen = True
35
+ smart_union = True
36
+ extra = pydantic.Extra.allow
@@ -8,6 +8,7 @@ from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel
8
8
 
9
9
  class AddThreadMessagesResponse(UniversalBaseModel):
10
10
  context: typing.Optional[str] = None
11
+ message_uuids: typing.Optional[typing.List[str]] = None
11
12
 
12
13
  if IS_PYDANTIC_V2:
13
14
  model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
@@ -1,70 +0,0 @@
1
- from datetime import datetime
2
- from typing import List
3
-
4
- from zep_cloud import EntityEdge, EntityNode
5
-
6
- DATE_FORMAT = "%Y-%m-%d %H:%M:%S"
7
-
8
- TEMPLATE_STRING = """
9
- FACTS and ENTITIES represent relevant context to the current conversation.
10
-
11
- # These are the most relevant facts and their valid date ranges
12
- # format: FACT (Date range: from - to)
13
- <FACTS>
14
- %s
15
- </FACTS>
16
-
17
- # These are the most relevant entities
18
- # ENTITY_NAME: entity summary
19
- <ENTITIES>
20
- %s
21
- </ENTITIES>
22
- """
23
-
24
-
25
- def format_edge_date_range(edge: EntityEdge) -> str:
26
- """
27
- Format the date range of an entity edge.
28
-
29
- Args:
30
- edge: The entity edge to format.
31
-
32
- Returns:
33
- A string representation of the date range.
34
- """
35
- valid_at = "date unknown"
36
- invalid_at = "present"
37
-
38
- if edge.valid_at is not None:
39
- valid_at = datetime.fromisoformat(edge.valid_at).strftime(DATE_FORMAT)
40
- if edge.invalid_at is not None:
41
- invalid_at = datetime.fromisoformat(edge.invalid_at).strftime(DATE_FORMAT)
42
-
43
- return f"{valid_at} - {invalid_at}"
44
-
45
-
46
- def compose_context_string(edges: List[EntityEdge], nodes: List[EntityNode]) -> str:
47
- """
48
- Compose a search context from entity edges and nodes.
49
-
50
- Args:
51
- edges: List of entity edges.
52
- nodes: List of entity nodes.
53
-
54
- Returns:
55
- A formatted string containing facts and entities.
56
- """
57
- facts = []
58
- for edge in edges:
59
- fact = f" - {edge.fact} ({format_edge_date_range(edge)})"
60
- facts.append(fact)
61
-
62
- entities = []
63
- for node in nodes:
64
- entity = f" - {node.name}: {node.summary}"
65
- entities.append(entity)
66
-
67
- facts_str = "\n".join(facts)
68
- entities_str = "\n".join(entities)
69
-
70
- return TEMPLATE_STRING % (facts_str, entities_str)
File without changes
File without changes