From 2d58b9980e65540eb84f0aa8ae9a56a161f9cfac Mon Sep 17 00:00:00 2001 From: "fern-api[bot]" <115122769+fern-api[bot]@users.noreply.github.com> Date: Wed, 23 Sep 2026 18:32:18 +0000 Subject: [PATCH 1/3] [fern-generated] Update SDK Generated by Fern CLI Version: unknown Generators: - fernapi/fern-python-sdk: 4.25.5 --- reference.md | 350 +++++++++++++++++- src/zep_cloud/__init__.py | 2 + src/zep_cloud/batch/client.py | 18 +- src/zep_cloud/batch/raw_client.py | 10 + src/zep_cloud/core/client_wrapper.py | 4 +- src/zep_cloud/graph/__init__.py | 4 +- src/zep_cloud/graph/client.py | 138 ++++++- .../graph/document_summary/__init__.py | 4 + .../graph/document_summary/client.py | 196 ++++++++++ .../graph/document_summary/raw_client.py | 271 ++++++++++++++ src/zep_cloud/graph/episode/client.py | 41 +- src/zep_cloud/graph/episode/raw_client.py | 50 ++- src/zep_cloud/graph/node/client.py | 8 +- src/zep_cloud/graph/node/raw_client.py | 8 +- src/zep_cloud/graph/raw_client.py | 211 ++++++++++- src/zep_cloud/thread/client.py | 75 ++++ src/zep_cloud/thread/message/client.py | 2 + src/zep_cloud/thread/message/raw_client.py | 2 + src/zep_cloud/thread/raw_client.py | 153 ++++++++ src/zep_cloud/types/__init__.py | 2 + src/zep_cloud/types/batch_add_item.py | 6 + src/zep_cloud/types/batch_item_detail.py | 1 + src/zep_cloud/types/batch_summary.py | 1 + src/zep_cloud/types/document_summary.py | 51 +++ src/zep_cloud/types/entity_edge.py | 14 +- src/zep_cloud/types/entity_node.py | 15 + src/zep_cloud/types/episode.py | 5 + src/zep_cloud/types/graph.py | 7 + .../types/graph_episode_list_request.py | 19 +- src/zep_cloud/types/message.py | 2 +- src/zep_cloud/types/thread.py | 5 + .../types/thread_context_response.py | 2 +- src/zep_cloud/types/thread_summary.py | 2 +- src/zep_cloud/types/user.py | 6 + 34 files changed, 1639 insertions(+), 46 deletions(-) create mode 100644 src/zep_cloud/graph/document_summary/__init__.py create mode 100644 src/zep_cloud/graph/document_summary/client.py create mode 100644 src/zep_cloud/graph/document_summary/raw_client.py create mode 100644 src/zep_cloud/types/document_summary.py diff --git a/reference.md b/reference.md index d9dba041..18eb3175 100644 --- a/reference.md +++ b/reference.md @@ -1218,6 +1218,14 @@ Has no effect on graph_episode items.
+**strict_ontology:** `typing.Optional[bool]` — When true, prevents extraction of generic Entity nodes that do not match the configured ontology. + +
+
+ +
+
+ **request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration.
@@ -2480,6 +2488,17 @@ client.graph.add(
+**document_id:** `typing.Optional[str]` + +Optional document ID that groups episodes as chunks of the same document +on a graph. Parallel to thread_id for message threads. + +
+
+ +
+
+ **graph_id:** `typing.Optional[str]` — graph_id is the ID of the graph to which the data will be added. If adding to the user graph, please use user_id field instead.
@@ -2597,6 +2616,14 @@ client.graph.add_batch(
+**document_id:** `typing.Optional[str]` — Optional document ID applied to every episode in this batch request. + +
+
+ +
+
+ **graph_id:** `typing.Optional[str]` — graph_id is the ID of the graph to which the data will be added. If adding to the user graph, please use user_id field instead.
@@ -3062,6 +3089,85 @@ client.graph.create(
+ +
+ + +
client.graph.get_episodes_for_document(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +Returns episodes associated with a document on a graph. Documents group episodes as chunks, parallel to how threads group messages. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from zep_cloud import Zep + +client = Zep( + api_key="YOUR_API_KEY", +) +client.graph.get_episodes_for_document( + document_id="document_id", + graph_id="graph_id", +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**document_id:** `str` — Document ID + +
+
+ +
+
+ +**graph_id:** `str` — Graph ID + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ +
@@ -3078,7 +3184,14 @@ client.graph.create(
-Returns all graphs. In order to list users, use user.list_ordered instead +Returns a paginated directory of live standalone graphs in the +authenticated project. Optional `search` matches `graph_id`, `name`, and +`description` (metadata only; not graph contents). + +Default `pageSize` is 50 (range 1–100). To list users, use +`user.list_ordered` instead. See the +[graph directory guide](/graph-directory) for pagination, relevance +ordering, and Memory MCP exposure.
@@ -3274,6 +3387,7 @@ client.graph.add_nodes(
+Deprecated. Pattern detection is not part of Public API v4. Detects structural patterns in a knowledge graph including relationship frequencies, multi-hop paths, co-occurrences, hubs, and clusters. When a query is provided, uses hybrid search to discover seed nodes, @@ -3710,7 +3824,7 @@ Maximum number of nodes in the response, including admitted seeds. Filters constraining traversed edges and included nodes. Reuses the graph.search filter type. search_filters.episode_metadata_filters is -rejected: it cannot be enforced during graph traversal (spec-2 §9.4). +rejected: it cannot be enforced during graph traversal.
@@ -4741,6 +4855,76 @@ client.thread.get_user_context(
+ + + + +
client.thread.get_episodes(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +Returns graph episodes associated with a thread. Parallel to get_episodes_for_document for documents. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from zep_cloud import Zep + +client = Zep( + api_key="YOUR_API_KEY", +) +client.thread.get_episodes( + thread_id="threadId", +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**thread_id:** `str` — The ID of the thread + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ +
@@ -6056,6 +6240,132 @@ client.user.warm( + + + + +## Graph DocumentSummary +
client.graph.document_summary.get_by_graph_id(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +Returns incremental document summaries associated with the graph. Document summaries are derived similarly to thread summaries. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from zep_cloud import Zep + +client = Zep( + api_key="YOUR_API_KEY", +) +client.graph.document_summary.get_by_graph_id( + graph_id="graph_id", +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**graph_id:** `str` — Graph ID + +
+
+ +
+
+ +**cursor:** `typing.Optional[str]` + +Opaque cursor for pagination, obtained from the Zep-Next-Cursor response header +of the previous page. Encodes the sort field, direction, and continuation position. + +
+
+ +
+
+ +**direction:** `typing.Optional[str]` — Sort direction. One of "asc" or "desc" (default "desc"). + +
+
+ +
+
+ +**filters:** `typing.Optional[SearchFilters]` — Optional filters applied to the listed artifacts. Reuses the graph.search filter type. + +
+
+ +
+
+ +**limit:** `typing.Optional[int]` — Maximum number of items to return + +
+
+ +
+
+ +**order_by:** `typing.Optional[str]` — Field to sort by. One of "created_at", "valid_at", or "uuid" (default "uuid"). + +
+
+ +
+
+ +**uuid_cursor:** `typing.Optional[str]` + +UUID based cursor, used for pagination. Should be the UUID of the last item in the previous page. + +Deprecated: prefer Cursor, the opaque cursor returned via the Zep-Next-Cursor response header. + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ +
@@ -6726,6 +7036,17 @@ response header of the previous page.
+**episode_metadata_filters:** `typing.Optional[MetadataFilterGroup]` + +Restricts results to episodes whose stored metadata matches this +predicate. Same type and limits as graph.search episode_metadata_filters. + +
+
+ +
+
+ **limit:** `typing.Optional[int]` Maximum number of episodes to return. An explicit value is clamped to @@ -6740,7 +7061,9 @@ Maximum number of episodes to return. An explicit value is clamped to **mentioned_node_uuids:** `typing.Optional[typing.Sequence[str]]` Restricts results to episodes that mention any of the listed node -UUIDs. At most 256 entries; each must be a syntactically valid UUID. +UUIDs. The list can also contain episode UUIDs: an episode UUID +matches that episode, so one request can return a known set of +episodes. At most 256 entries; each must be a syntactically valid UUID.
@@ -6924,6 +7247,17 @@ response header of the previous page.
+**episode_metadata_filters:** `typing.Optional[MetadataFilterGroup]` + +Restricts results to episodes whose stored metadata matches this +predicate. Same type and limits as graph.search episode_metadata_filters. + +
+
+ +
+
+ **limit:** `typing.Optional[int]` Maximum number of episodes to return. An explicit value is clamped to @@ -6938,7 +7272,9 @@ Maximum number of episodes to return. An explicit value is clamped to **mentioned_node_uuids:** `typing.Optional[typing.Sequence[str]]` Restricts results to episodes that mention any of the listed node -UUIDs. At most 256 entries; each must be a syntactically valid UUID. +UUIDs. The list can also contain episode UUIDs: an episode UUID +matches that episode, so one request can return a known set of +episodes. At most 256 entries; each must be a syntactically valid UUID.
@@ -7588,7 +7924,7 @@ client.graph.node.get_edges(
-Deprecated. Use episode listing with `mentioned_node_uuids` (`POST /graph/episodes/graph/{graph_id}` or `POST /graph/episodes/user/{user_id}`) instead. Returns episodes that mentioned a given node, subject to an internal cap; responses reduced by that cap set the Zep-Truncated header. +Deprecated. Read the `episodes` field on the node; when `episodes_truncated` is true, use the episode list SDK methods `graph.episode.list_by_graph_id` or `graph.episode.list_by_user_id` with the `mentioned_node_uuids` filter. Returns episodes that mentioned a given node, subject to an internal cap; responses reduced by that cap set the Zep-Truncated header.
@@ -7742,7 +8078,7 @@ Direction field above. **filters:** `typing.Optional[SearchFilters]` Filters constraining the connecting edges (edge types, dates, and the -section-3 node-/episode-anchored fields) and the neighbor nodes +node- and episode-anchored UUID fields) and the neighbor nodes (node_labels/exclude_node_labels). Reuses the graph.search filter type. @@ -8660,7 +8996,7 @@ client.thread.message.update(
-**metadata:** `typing.Dict[str, typing.Optional[typing.Any]]` +**metadata:** `typing.Dict[str, typing.Optional[typing.Any]]` — Metadata to store on the message. Max 10 keys. Values must be strings, numbers, booleans, or arrays of scalars.
diff --git a/src/zep_cloud/__init__.py b/src/zep_cloud/__init__.py index 9b7b03c9..98bdfb33 100644 --- a/src/zep_cloud/__init__.py +++ b/src/zep_cloud/__init__.py @@ -32,6 +32,7 @@ DerivedNode, DetectConfig, DetectPatternsResponse, + DocumentSummary, EdgeType, EntityEdge, EntityEdgeSourceTarget, @@ -143,6 +144,7 @@ "DerivedNode", "DetectConfig", "DetectPatternsResponse", + "DocumentSummary", "EdgeType", "EntityEdge", "EntityEdgeSourceTarget", diff --git a/src/zep_cloud/batch/client.py b/src/zep_cloud/batch/client.py index 4b56e20a..66b1c9e2 100644 --- a/src/zep_cloud/batch/client.py +++ b/src/zep_cloud/batch/client.py @@ -83,6 +83,7 @@ def create( *, ignore_roles: typing.Optional[typing.Sequence[RoleType]] = OMIT, metadata: typing.Optional[typing.Dict[str, typing.Optional[typing.Any]]] = OMIT, + strict_ontology: typing.Optional[bool] = OMIT, request_options: typing.Optional[RequestOptions] = None, ) -> BatchSummary: """ @@ -98,6 +99,9 @@ def create( metadata : typing.Optional[typing.Dict[str, typing.Optional[typing.Any]]] + strict_ontology : typing.Optional[bool] + When true, prevents extraction of generic Entity nodes that do not match the configured ontology. + request_options : typing.Optional[RequestOptions] Request-specific configuration. @@ -116,7 +120,10 @@ def create( client.batch.create() """ _response = self._raw_client.create( - ignore_roles=ignore_roles, metadata=metadata, request_options=request_options + ignore_roles=ignore_roles, + metadata=metadata, + strict_ontology=strict_ontology, + request_options=request_options, ) return _response.data @@ -387,6 +394,7 @@ async def create( *, ignore_roles: typing.Optional[typing.Sequence[RoleType]] = OMIT, metadata: typing.Optional[typing.Dict[str, typing.Optional[typing.Any]]] = OMIT, + strict_ontology: typing.Optional[bool] = OMIT, request_options: typing.Optional[RequestOptions] = None, ) -> BatchSummary: """ @@ -402,6 +410,9 @@ async def create( metadata : typing.Optional[typing.Dict[str, typing.Optional[typing.Any]]] + strict_ontology : typing.Optional[bool] + When true, prevents extraction of generic Entity nodes that do not match the configured ontology. + request_options : typing.Optional[RequestOptions] Request-specific configuration. @@ -428,7 +439,10 @@ async def main() -> None: asyncio.run(main()) """ _response = await self._raw_client.create( - ignore_roles=ignore_roles, metadata=metadata, request_options=request_options + ignore_roles=ignore_roles, + metadata=metadata, + strict_ontology=strict_ontology, + request_options=request_options, ) return _response.data diff --git a/src/zep_cloud/batch/raw_client.py b/src/zep_cloud/batch/raw_client.py index d530e651..7765bac3 100644 --- a/src/zep_cloud/batch/raw_client.py +++ b/src/zep_cloud/batch/raw_client.py @@ -129,6 +129,7 @@ def create( *, ignore_roles: typing.Optional[typing.Sequence[RoleType]] = OMIT, metadata: typing.Optional[typing.Dict[str, typing.Optional[typing.Any]]] = OMIT, + strict_ontology: typing.Optional[bool] = OMIT, request_options: typing.Optional[RequestOptions] = None, ) -> HttpResponse[BatchSummary]: """ @@ -144,6 +145,9 @@ def create( metadata : typing.Optional[typing.Dict[str, typing.Optional[typing.Any]]] + strict_ontology : typing.Optional[bool] + When true, prevents extraction of generic Entity nodes that do not match the configured ontology. + request_options : typing.Optional[RequestOptions] Request-specific configuration. @@ -158,6 +162,7 @@ def create( json={ "ignore_roles": ignore_roles, "metadata": metadata, + "strict_ontology": strict_ontology, }, headers={ "content-type": "application/json", @@ -822,6 +827,7 @@ async def create( *, ignore_roles: typing.Optional[typing.Sequence[RoleType]] = OMIT, metadata: typing.Optional[typing.Dict[str, typing.Optional[typing.Any]]] = OMIT, + strict_ontology: typing.Optional[bool] = OMIT, request_options: typing.Optional[RequestOptions] = None, ) -> AsyncHttpResponse[BatchSummary]: """ @@ -837,6 +843,9 @@ async def create( metadata : typing.Optional[typing.Dict[str, typing.Optional[typing.Any]]] + strict_ontology : typing.Optional[bool] + When true, prevents extraction of generic Entity nodes that do not match the configured ontology. + request_options : typing.Optional[RequestOptions] Request-specific configuration. @@ -851,6 +860,7 @@ async def create( json={ "ignore_roles": ignore_roles, "metadata": metadata, + "strict_ontology": strict_ontology, }, headers={ "content-type": "application/json", diff --git a/src/zep_cloud/core/client_wrapper.py b/src/zep_cloud/core/client_wrapper.py index dde7c3ad..e4684068 100644 --- a/src/zep_cloud/core/client_wrapper.py +++ b/src/zep_cloud/core/client_wrapper.py @@ -22,10 +22,10 @@ def __init__( def get_headers(self) -> typing.Dict[str, str]: headers: typing.Dict[str, str] = { - "User-Agent": "zep-cloud/3.28.0", + "User-Agent": "zep-cloud/3.29.0", "X-Fern-Language": "Python", "X-Fern-SDK-Name": "zep-cloud", - "X-Fern-SDK-Version": "3.28.0", + "X-Fern-SDK-Version": "3.29.0", **(self.get_custom_headers() or {}), } headers["Authorization"] = f"Api-Key {self.api_key}" diff --git a/src/zep_cloud/graph/__init__.py b/src/zep_cloud/graph/__init__.py index 49f0683f..ff7e8d17 100644 --- a/src/zep_cloud/graph/__init__.py +++ b/src/zep_cloud/graph/__init__.py @@ -2,6 +2,6 @@ # isort: skip_file -from . import edge, episode, node, observation, thread_summary +from . import document_summary, edge, episode, node, observation, thread_summary -__all__ = ["edge", "episode", "node", "observation", "thread_summary"] +__all__ = ["document_summary", "edge", "episode", "node", "observation", "thread_summary"] diff --git a/src/zep_cloud/graph/client.py b/src/zep_cloud/graph/client.py index 0e12d30d..2d518b2b 100644 --- a/src/zep_cloud/graph/client.py +++ b/src/zep_cloud/graph/client.py @@ -16,6 +16,7 @@ from ..types.entity_type_response import EntityTypeResponse from ..types.episode import Episode from ..types.episode_data import EpisodeData +from ..types.episode_response import EpisodeResponse from ..types.graph import Graph from ..types.graph_data_type import GraphDataType from ..types.graph_list_response import GraphListResponse @@ -28,6 +29,7 @@ from ..types.reranker import Reranker from ..types.search_filters import SearchFilters from ..types.success_response import SuccessResponse +from .document_summary.client import AsyncDocumentSummaryClient, DocumentSummaryClient from .edge.client import AsyncEdgeClient, EdgeClient from .episode.client import AsyncEpisodeClient, EpisodeClient from .node.client import AsyncNodeClient, NodeClient @@ -42,6 +44,8 @@ class GraphClient: def __init__(self, *, client_wrapper: SyncClientWrapper): self._raw_client = RawGraphClient(client_wrapper=client_wrapper) + self.document_summary = DocumentSummaryClient(client_wrapper=client_wrapper) + self.edge = EdgeClient(client_wrapper=client_wrapper) self.episode = EpisodeClient(client_wrapper=client_wrapper) @@ -298,6 +302,7 @@ def add( data: str, type: GraphDataType, created_at: typing.Optional[str] = OMIT, + document_id: typing.Optional[str] = OMIT, graph_id: typing.Optional[str] = OMIT, metadata: typing.Optional[typing.Dict[str, typing.Optional[typing.Any]]] = OMIT, source_description: typing.Optional[str] = OMIT, @@ -316,6 +321,10 @@ def add( created_at : typing.Optional[str] + document_id : typing.Optional[str] + Optional document ID that groups episodes as chunks of the same document + on a graph. Parallel to thread_id for message threads. + graph_id : typing.Optional[str] graph_id is the ID of the graph to which the data will be added. If adding to the user graph, please use user_id field instead. @@ -354,6 +363,7 @@ def add( data=data, type=type, created_at=created_at, + document_id=document_id, graph_id=graph_id, metadata=metadata, source_description=source_description, @@ -367,6 +377,7 @@ def add_batch( self, *, episodes: typing.Sequence[EpisodeData], + document_id: typing.Optional[str] = OMIT, graph_id: typing.Optional[str] = OMIT, strict_ontology: typing.Optional[bool] = OMIT, user_id: typing.Optional[str] = OMIT, @@ -381,6 +392,9 @@ def add_batch( ---------- episodes : typing.Sequence[EpisodeData] + document_id : typing.Optional[str] + Optional document ID applied to every episode in this batch request. + graph_id : typing.Optional[str] graph_id is the ID of the graph to which the data will be added. If adding to the user graph, please use user_id field instead. @@ -416,6 +430,7 @@ def add_batch( """ _response = self._raw_client.add_batch( episodes=episodes, + document_id=document_id, graph_id=graph_id, strict_ontology=strict_ontology, user_id=user_id, @@ -667,6 +682,45 @@ def create( ) return _response.data + def get_episodes_for_document( + self, document_id: str, *, graph_id: str, request_options: typing.Optional[RequestOptions] = None + ) -> EpisodeResponse: + """ + Returns episodes associated with a document on a graph. Documents group episodes as chunks, parallel to how threads group messages. + + Parameters + ---------- + document_id : str + Document ID + + graph_id : str + Graph ID + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + EpisodeResponse + Episodes + + Examples + -------- + from zep_cloud import Zep + + client = Zep( + api_key="YOUR_API_KEY", + ) + client.graph.get_episodes_for_document( + document_id="document_id", + graph_id="graph_id", + ) + """ + _response = self._raw_client.get_episodes_for_document( + document_id, graph_id=graph_id, request_options=request_options + ) + return _response.data + def list_all( self, *, @@ -678,7 +732,14 @@ def list_all( request_options: typing.Optional[RequestOptions] = None, ) -> GraphListResponse: """ - Returns all graphs. In order to list users, use user.list_ordered instead + Returns a paginated directory of live standalone graphs in the + authenticated project. Optional `search` matches `graph_id`, `name`, and + `description` (metadata only; not graph contents). + + Default `pageSize` is 50 (range 1–100). To list users, use + `user.list_ordered` instead. See the + [graph directory guide](/graph-directory) for pagination, relevance + ordering, and Memory MCP exposure. Parameters ---------- @@ -795,6 +856,7 @@ def detect_patterns( request_options: typing.Optional[RequestOptions] = None, ) -> DetectPatternsResponse: """ + Deprecated. Pattern detection is not part of Public API v4. Detects structural patterns in a knowledge graph including relationship frequencies, multi-hop paths, co-occurrences, hubs, and clusters. When a query is provided, uses hybrid search to discover seed nodes, @@ -1018,7 +1080,7 @@ def get_subgraph( search_filters : typing.Optional[SearchFilters] Filters constraining traversed edges and included nodes. Reuses the graph.search filter type. search_filters.episode_metadata_filters is - rejected: it cannot be enforced during graph traversal (spec-2 §9.4). + rejected: it cannot be enforced during graph traversal. user_id : typing.Optional[str] user_id identifies the target user graph. Exactly one of user_id or @@ -1201,6 +1263,8 @@ def warm(self, graph_id: str, *, request_options: typing.Optional[RequestOptions class AsyncGraphClient: def __init__(self, *, client_wrapper: AsyncClientWrapper): self._raw_client = AsyncRawGraphClient(client_wrapper=client_wrapper) + self.document_summary = AsyncDocumentSummaryClient(client_wrapper=client_wrapper) + self.edge = AsyncEdgeClient(client_wrapper=client_wrapper) self.episode = AsyncEpisodeClient(client_wrapper=client_wrapper) @@ -1497,6 +1561,7 @@ async def add( data: str, type: GraphDataType, created_at: typing.Optional[str] = OMIT, + document_id: typing.Optional[str] = OMIT, graph_id: typing.Optional[str] = OMIT, metadata: typing.Optional[typing.Dict[str, typing.Optional[typing.Any]]] = OMIT, source_description: typing.Optional[str] = OMIT, @@ -1515,6 +1580,10 @@ async def add( created_at : typing.Optional[str] + document_id : typing.Optional[str] + Optional document ID that groups episodes as chunks of the same document + on a graph. Parallel to thread_id for message threads. + graph_id : typing.Optional[str] graph_id is the ID of the graph to which the data will be added. If adding to the user graph, please use user_id field instead. @@ -1561,6 +1630,7 @@ async def main() -> None: data=data, type=type, created_at=created_at, + document_id=document_id, graph_id=graph_id, metadata=metadata, source_description=source_description, @@ -1574,6 +1644,7 @@ async def add_batch( self, *, episodes: typing.Sequence[EpisodeData], + document_id: typing.Optional[str] = OMIT, graph_id: typing.Optional[str] = OMIT, strict_ontology: typing.Optional[bool] = OMIT, user_id: typing.Optional[str] = OMIT, @@ -1588,6 +1659,9 @@ async def add_batch( ---------- episodes : typing.Sequence[EpisodeData] + document_id : typing.Optional[str] + Optional document ID applied to every episode in this batch request. + graph_id : typing.Optional[str] graph_id is the ID of the graph to which the data will be added. If adding to the user graph, please use user_id field instead. @@ -1631,6 +1705,7 @@ async def main() -> None: """ _response = await self._raw_client.add_batch( episodes=episodes, + document_id=document_id, graph_id=graph_id, strict_ontology=strict_ontology, user_id=user_id, @@ -1906,6 +1981,53 @@ async def main() -> None: ) return _response.data + async def get_episodes_for_document( + self, document_id: str, *, graph_id: str, request_options: typing.Optional[RequestOptions] = None + ) -> EpisodeResponse: + """ + Returns episodes associated with a document on a graph. Documents group episodes as chunks, parallel to how threads group messages. + + Parameters + ---------- + document_id : str + Document ID + + graph_id : str + Graph ID + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + EpisodeResponse + Episodes + + Examples + -------- + import asyncio + + from zep_cloud import AsyncZep + + client = AsyncZep( + api_key="YOUR_API_KEY", + ) + + + async def main() -> None: + await client.graph.get_episodes_for_document( + document_id="document_id", + graph_id="graph_id", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.get_episodes_for_document( + document_id, graph_id=graph_id, request_options=request_options + ) + return _response.data + async def list_all( self, *, @@ -1917,7 +2039,14 @@ async def list_all( request_options: typing.Optional[RequestOptions] = None, ) -> GraphListResponse: """ - Returns all graphs. In order to list users, use user.list_ordered instead + Returns a paginated directory of live standalone graphs in the + authenticated project. Optional `search` matches `graph_id`, `name`, and + `description` (metadata only; not graph contents). + + Default `pageSize` is 50 (range 1–100). To list users, use + `user.list_ordered` instead. See the + [graph directory guide](/graph-directory) for pagination, relevance + ordering, and Memory MCP exposure. Parameters ---------- @@ -2050,6 +2179,7 @@ async def detect_patterns( request_options: typing.Optional[RequestOptions] = None, ) -> DetectPatternsResponse: """ + Deprecated. Pattern detection is not part of Public API v4. Detects structural patterns in a knowledge graph including relationship frequencies, multi-hop paths, co-occurrences, hubs, and clusters. When a query is provided, uses hybrid search to discover seed nodes, @@ -2289,7 +2419,7 @@ async def get_subgraph( search_filters : typing.Optional[SearchFilters] Filters constraining traversed edges and included nodes. Reuses the graph.search filter type. search_filters.episode_metadata_filters is - rejected: it cannot be enforced during graph traversal (spec-2 §9.4). + rejected: it cannot be enforced during graph traversal. user_id : typing.Optional[str] user_id identifies the target user graph. Exactly one of user_id or diff --git a/src/zep_cloud/graph/document_summary/__init__.py b/src/zep_cloud/graph/document_summary/__init__.py new file mode 100644 index 00000000..5cde0202 --- /dev/null +++ b/src/zep_cloud/graph/document_summary/__init__.py @@ -0,0 +1,4 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + diff --git a/src/zep_cloud/graph/document_summary/client.py b/src/zep_cloud/graph/document_summary/client.py new file mode 100644 index 00000000..8a771fcb --- /dev/null +++ b/src/zep_cloud/graph/document_summary/client.py @@ -0,0 +1,196 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from ...core.client_wrapper import AsyncClientWrapper, SyncClientWrapper +from ...core.request_options import RequestOptions +from ...types.document_summary import DocumentSummary +from ...types.search_filters import SearchFilters +from .raw_client import AsyncRawDocumentSummaryClient, RawDocumentSummaryClient + +# this is used as the default value for optional parameters +OMIT = typing.cast(typing.Any, ...) + + +class DocumentSummaryClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._raw_client = RawDocumentSummaryClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> RawDocumentSummaryClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + RawDocumentSummaryClient + """ + return self._raw_client + + def get_by_graph_id( + self, + graph_id: str, + *, + cursor: typing.Optional[str] = OMIT, + direction: typing.Optional[str] = OMIT, + filters: typing.Optional[SearchFilters] = OMIT, + limit: typing.Optional[int] = OMIT, + order_by: typing.Optional[str] = OMIT, + uuid_cursor: typing.Optional[str] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> typing.List[DocumentSummary]: + """ + Returns incremental document summaries associated with the graph. Document summaries are derived similarly to thread summaries. + + Parameters + ---------- + graph_id : str + Graph ID + + cursor : typing.Optional[str] + Opaque cursor for pagination, obtained from the Zep-Next-Cursor response header + of the previous page. Encodes the sort field, direction, and continuation position. + + direction : typing.Optional[str] + Sort direction. One of "asc" or "desc" (default "desc"). + + filters : typing.Optional[SearchFilters] + Optional filters applied to the listed artifacts. Reuses the graph.search filter type. + + limit : typing.Optional[int] + Maximum number of items to return + + order_by : typing.Optional[str] + Field to sort by. One of "created_at", "valid_at", or "uuid" (default "uuid"). + + uuid_cursor : typing.Optional[str] + UUID based cursor, used for pagination. Should be the UUID of the last item in the previous page. + + Deprecated: prefer Cursor, the opaque cursor returned via the Zep-Next-Cursor response header. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + typing.List[DocumentSummary] + Document summaries + + Examples + -------- + from zep_cloud import Zep + + client = Zep( + api_key="YOUR_API_KEY", + ) + client.graph.document_summary.get_by_graph_id( + graph_id="graph_id", + ) + """ + _response = self._raw_client.get_by_graph_id( + graph_id, + cursor=cursor, + direction=direction, + filters=filters, + limit=limit, + order_by=order_by, + uuid_cursor=uuid_cursor, + request_options=request_options, + ) + return _response.data + + +class AsyncDocumentSummaryClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._raw_client = AsyncRawDocumentSummaryClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> AsyncRawDocumentSummaryClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + AsyncRawDocumentSummaryClient + """ + return self._raw_client + + async def get_by_graph_id( + self, + graph_id: str, + *, + cursor: typing.Optional[str] = OMIT, + direction: typing.Optional[str] = OMIT, + filters: typing.Optional[SearchFilters] = OMIT, + limit: typing.Optional[int] = OMIT, + order_by: typing.Optional[str] = OMIT, + uuid_cursor: typing.Optional[str] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> typing.List[DocumentSummary]: + """ + Returns incremental document summaries associated with the graph. Document summaries are derived similarly to thread summaries. + + Parameters + ---------- + graph_id : str + Graph ID + + cursor : typing.Optional[str] + Opaque cursor for pagination, obtained from the Zep-Next-Cursor response header + of the previous page. Encodes the sort field, direction, and continuation position. + + direction : typing.Optional[str] + Sort direction. One of "asc" or "desc" (default "desc"). + + filters : typing.Optional[SearchFilters] + Optional filters applied to the listed artifacts. Reuses the graph.search filter type. + + limit : typing.Optional[int] + Maximum number of items to return + + order_by : typing.Optional[str] + Field to sort by. One of "created_at", "valid_at", or "uuid" (default "uuid"). + + uuid_cursor : typing.Optional[str] + UUID based cursor, used for pagination. Should be the UUID of the last item in the previous page. + + Deprecated: prefer Cursor, the opaque cursor returned via the Zep-Next-Cursor response header. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + typing.List[DocumentSummary] + Document summaries + + Examples + -------- + import asyncio + + from zep_cloud import AsyncZep + + client = AsyncZep( + api_key="YOUR_API_KEY", + ) + + + async def main() -> None: + await client.graph.document_summary.get_by_graph_id( + graph_id="graph_id", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.get_by_graph_id( + graph_id, + cursor=cursor, + direction=direction, + filters=filters, + limit=limit, + order_by=order_by, + uuid_cursor=uuid_cursor, + request_options=request_options, + ) + return _response.data diff --git a/src/zep_cloud/graph/document_summary/raw_client.py b/src/zep_cloud/graph/document_summary/raw_client.py new file mode 100644 index 00000000..c6ffd1f0 --- /dev/null +++ b/src/zep_cloud/graph/document_summary/raw_client.py @@ -0,0 +1,271 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing +from json.decoder import JSONDecodeError + +from ...core.api_error import ApiError as core_api_error_ApiError +from ...core.client_wrapper import AsyncClientWrapper, SyncClientWrapper +from ...core.http_response import AsyncHttpResponse, HttpResponse +from ...core.jsonable_encoder import jsonable_encoder +from ...core.pydantic_utilities import parse_obj_as +from ...core.request_options import RequestOptions +from ...core.serialization import convert_and_respect_annotation_metadata +from ...errors.bad_request_error import BadRequestError +from ...errors.internal_server_error import InternalServerError +from ...errors.not_found_error import NotFoundError +from ...types.api_error import ApiError as types_api_error_ApiError +from ...types.document_summary import DocumentSummary +from ...types.search_filters import SearchFilters + +# this is used as the default value for optional parameters +OMIT = typing.cast(typing.Any, ...) + + +class RawDocumentSummaryClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._client_wrapper = client_wrapper + + def get_by_graph_id( + self, + graph_id: str, + *, + cursor: typing.Optional[str] = OMIT, + direction: typing.Optional[str] = OMIT, + filters: typing.Optional[SearchFilters] = OMIT, + limit: typing.Optional[int] = OMIT, + order_by: typing.Optional[str] = OMIT, + uuid_cursor: typing.Optional[str] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[typing.List[DocumentSummary]]: + """ + Returns incremental document summaries associated with the graph. Document summaries are derived similarly to thread summaries. + + Parameters + ---------- + graph_id : str + Graph ID + + cursor : typing.Optional[str] + Opaque cursor for pagination, obtained from the Zep-Next-Cursor response header + of the previous page. Encodes the sort field, direction, and continuation position. + + direction : typing.Optional[str] + Sort direction. One of "asc" or "desc" (default "desc"). + + filters : typing.Optional[SearchFilters] + Optional filters applied to the listed artifacts. Reuses the graph.search filter type. + + limit : typing.Optional[int] + Maximum number of items to return + + order_by : typing.Optional[str] + Field to sort by. One of "created_at", "valid_at", or "uuid" (default "uuid"). + + uuid_cursor : typing.Optional[str] + UUID based cursor, used for pagination. Should be the UUID of the last item in the previous page. + + Deprecated: prefer Cursor, the opaque cursor returned via the Zep-Next-Cursor response header. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[typing.List[DocumentSummary]] + Document summaries + """ + _response = self._client_wrapper.httpx_client.request( + f"graph/document-summary/graph/{jsonable_encoder(graph_id)}", + method="POST", + json={ + "cursor": cursor, + "direction": direction, + "filters": convert_and_respect_annotation_metadata( + object_=filters, annotation=SearchFilters, direction="write" + ), + "limit": limit, + "order_by": order_by, + "uuid_cursor": uuid_cursor, + }, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + typing.List[DocumentSummary], + parse_obj_as( + type_=typing.List[DocumentSummary], # type: ignore + object_=_response.json(), + ), + ) + return HttpResponse(response=_response, data=_data) + if _response.status_code == 400: + raise BadRequestError( + headers=dict(_response.headers), + body=typing.cast( + typing.Optional[typing.Any], + parse_obj_as( + type_=typing.Optional[typing.Any], # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 404: + raise NotFoundError( + headers=dict(_response.headers), + body=typing.cast( + types_api_error_ApiError, + parse_obj_as( + type_=types_api_error_ApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 500: + raise InternalServerError( + headers=dict(_response.headers), + body=typing.cast( + types_api_error_ApiError, + parse_obj_as( + type_=types_api_error_ApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + _response_json = _response.json() + except JSONDecodeError: + raise core_api_error_ApiError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.text + ) + raise core_api_error_ApiError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response_json + ) + + +class AsyncRawDocumentSummaryClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._client_wrapper = client_wrapper + + async def get_by_graph_id( + self, + graph_id: str, + *, + cursor: typing.Optional[str] = OMIT, + direction: typing.Optional[str] = OMIT, + filters: typing.Optional[SearchFilters] = OMIT, + limit: typing.Optional[int] = OMIT, + order_by: typing.Optional[str] = OMIT, + uuid_cursor: typing.Optional[str] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[typing.List[DocumentSummary]]: + """ + Returns incremental document summaries associated with the graph. Document summaries are derived similarly to thread summaries. + + Parameters + ---------- + graph_id : str + Graph ID + + cursor : typing.Optional[str] + Opaque cursor for pagination, obtained from the Zep-Next-Cursor response header + of the previous page. Encodes the sort field, direction, and continuation position. + + direction : typing.Optional[str] + Sort direction. One of "asc" or "desc" (default "desc"). + + filters : typing.Optional[SearchFilters] + Optional filters applied to the listed artifacts. Reuses the graph.search filter type. + + limit : typing.Optional[int] + Maximum number of items to return + + order_by : typing.Optional[str] + Field to sort by. One of "created_at", "valid_at", or "uuid" (default "uuid"). + + uuid_cursor : typing.Optional[str] + UUID based cursor, used for pagination. Should be the UUID of the last item in the previous page. + + Deprecated: prefer Cursor, the opaque cursor returned via the Zep-Next-Cursor response header. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[typing.List[DocumentSummary]] + Document summaries + """ + _response = await self._client_wrapper.httpx_client.request( + f"graph/document-summary/graph/{jsonable_encoder(graph_id)}", + method="POST", + json={ + "cursor": cursor, + "direction": direction, + "filters": convert_and_respect_annotation_metadata( + object_=filters, annotation=SearchFilters, direction="write" + ), + "limit": limit, + "order_by": order_by, + "uuid_cursor": uuid_cursor, + }, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + typing.List[DocumentSummary], + parse_obj_as( + type_=typing.List[DocumentSummary], # type: ignore + object_=_response.json(), + ), + ) + return AsyncHttpResponse(response=_response, data=_data) + if _response.status_code == 400: + raise BadRequestError( + headers=dict(_response.headers), + body=typing.cast( + typing.Optional[typing.Any], + parse_obj_as( + type_=typing.Optional[typing.Any], # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 404: + raise NotFoundError( + headers=dict(_response.headers), + body=typing.cast( + types_api_error_ApiError, + parse_obj_as( + type_=types_api_error_ApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 500: + raise InternalServerError( + headers=dict(_response.headers), + body=typing.cast( + types_api_error_ApiError, + parse_obj_as( + type_=types_api_error_ApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + _response_json = _response.json() + except JSONDecodeError: + raise core_api_error_ApiError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.text + ) + raise core_api_error_ApiError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response_json + ) diff --git a/src/zep_cloud/graph/episode/client.py b/src/zep_cloud/graph/episode/client.py index 06419888..cee42952 100644 --- a/src/zep_cloud/graph/episode/client.py +++ b/src/zep_cloud/graph/episode/client.py @@ -7,6 +7,7 @@ from ...types.episode import Episode from ...types.episode_mentions import EpisodeMentions from ...types.episode_response import EpisodeResponse +from ...types.metadata_filter_group import MetadataFilterGroup from ...types.success_response import SuccessResponse from .raw_client import AsyncRawEpisodeClient, RawEpisodeClient @@ -76,6 +77,7 @@ def list_by_graph_id( *, cursor: typing.Optional[str] = OMIT, direction: typing.Optional[str] = OMIT, + episode_metadata_filters: typing.Optional[MetadataFilterGroup] = OMIT, limit: typing.Optional[int] = OMIT, mentioned_node_uuids: typing.Optional[typing.Sequence[str]] = OMIT, order_by: typing.Optional[str] = OMIT, @@ -96,13 +98,19 @@ def list_by_graph_id( direction : typing.Optional[str] Sort direction. One of "asc" or "desc". Defaults to "desc". + episode_metadata_filters : typing.Optional[MetadataFilterGroup] + Restricts results to episodes whose stored metadata matches this + predicate. Same type and limits as graph.search episode_metadata_filters. + limit : typing.Optional[int] Maximum number of episodes to return. An explicit value is clamped to 50; when omitted, the default page size (100) applies. mentioned_node_uuids : typing.Optional[typing.Sequence[str]] Restricts results to episodes that mention any of the listed node - UUIDs. At most 256 entries; each must be a syntactically valid UUID. + UUIDs. The list can also contain episode UUIDs: an episode UUID + matches that episode, so one request can return a known set of + episodes. At most 256 entries; each must be a syntactically valid UUID. order_by : typing.Optional[str] Field to sort by. One of "uuid" or "created_at". Defaults to "uuid". @@ -130,6 +138,7 @@ def list_by_graph_id( graph_id, cursor=cursor, direction=direction, + episode_metadata_filters=episode_metadata_filters, limit=limit, mentioned_node_uuids=mentioned_node_uuids, order_by=order_by, @@ -184,6 +193,7 @@ def list_by_user_id( *, cursor: typing.Optional[str] = OMIT, direction: typing.Optional[str] = OMIT, + episode_metadata_filters: typing.Optional[MetadataFilterGroup] = OMIT, limit: typing.Optional[int] = OMIT, mentioned_node_uuids: typing.Optional[typing.Sequence[str]] = OMIT, order_by: typing.Optional[str] = OMIT, @@ -204,13 +214,19 @@ def list_by_user_id( direction : typing.Optional[str] Sort direction. One of "asc" or "desc". Defaults to "desc". + episode_metadata_filters : typing.Optional[MetadataFilterGroup] + Restricts results to episodes whose stored metadata matches this + predicate. Same type and limits as graph.search episode_metadata_filters. + limit : typing.Optional[int] Maximum number of episodes to return. An explicit value is clamped to 50; when omitted, the default page size (100) applies. mentioned_node_uuids : typing.Optional[typing.Sequence[str]] Restricts results to episodes that mention any of the listed node - UUIDs. At most 256 entries; each must be a syntactically valid UUID. + UUIDs. The list can also contain episode UUIDs: an episode UUID + matches that episode, so one request can return a known set of + episodes. At most 256 entries; each must be a syntactically valid UUID. order_by : typing.Optional[str] Field to sort by. One of "uuid" or "created_at". Defaults to "uuid". @@ -238,6 +254,7 @@ def list_by_user_id( user_id, cursor=cursor, direction=direction, + episode_metadata_filters=episode_metadata_filters, limit=limit, mentioned_node_uuids=mentioned_node_uuids, order_by=order_by, @@ -452,6 +469,7 @@ async def list_by_graph_id( *, cursor: typing.Optional[str] = OMIT, direction: typing.Optional[str] = OMIT, + episode_metadata_filters: typing.Optional[MetadataFilterGroup] = OMIT, limit: typing.Optional[int] = OMIT, mentioned_node_uuids: typing.Optional[typing.Sequence[str]] = OMIT, order_by: typing.Optional[str] = OMIT, @@ -472,13 +490,19 @@ async def list_by_graph_id( direction : typing.Optional[str] Sort direction. One of "asc" or "desc". Defaults to "desc". + episode_metadata_filters : typing.Optional[MetadataFilterGroup] + Restricts results to episodes whose stored metadata matches this + predicate. Same type and limits as graph.search episode_metadata_filters. + limit : typing.Optional[int] Maximum number of episodes to return. An explicit value is clamped to 50; when omitted, the default page size (100) applies. mentioned_node_uuids : typing.Optional[typing.Sequence[str]] Restricts results to episodes that mention any of the listed node - UUIDs. At most 256 entries; each must be a syntactically valid UUID. + UUIDs. The list can also contain episode UUIDs: an episode UUID + matches that episode, so one request can return a known set of + episodes. At most 256 entries; each must be a syntactically valid UUID. order_by : typing.Optional[str] Field to sort by. One of "uuid" or "created_at". Defaults to "uuid". @@ -514,6 +538,7 @@ async def main() -> None: graph_id, cursor=cursor, direction=direction, + episode_metadata_filters=episode_metadata_filters, limit=limit, mentioned_node_uuids=mentioned_node_uuids, order_by=order_by, @@ -576,6 +601,7 @@ async def list_by_user_id( *, cursor: typing.Optional[str] = OMIT, direction: typing.Optional[str] = OMIT, + episode_metadata_filters: typing.Optional[MetadataFilterGroup] = OMIT, limit: typing.Optional[int] = OMIT, mentioned_node_uuids: typing.Optional[typing.Sequence[str]] = OMIT, order_by: typing.Optional[str] = OMIT, @@ -596,13 +622,19 @@ async def list_by_user_id( direction : typing.Optional[str] Sort direction. One of "asc" or "desc". Defaults to "desc". + episode_metadata_filters : typing.Optional[MetadataFilterGroup] + Restricts results to episodes whose stored metadata matches this + predicate. Same type and limits as graph.search episode_metadata_filters. + limit : typing.Optional[int] Maximum number of episodes to return. An explicit value is clamped to 50; when omitted, the default page size (100) applies. mentioned_node_uuids : typing.Optional[typing.Sequence[str]] Restricts results to episodes that mention any of the listed node - UUIDs. At most 256 entries; each must be a syntactically valid UUID. + UUIDs. The list can also contain episode UUIDs: an episode UUID + matches that episode, so one request can return a known set of + episodes. At most 256 entries; each must be a syntactically valid UUID. order_by : typing.Optional[str] Field to sort by. One of "uuid" or "created_at". Defaults to "uuid". @@ -638,6 +670,7 @@ async def main() -> None: user_id, cursor=cursor, direction=direction, + episode_metadata_filters=episode_metadata_filters, limit=limit, mentioned_node_uuids=mentioned_node_uuids, order_by=order_by, diff --git a/src/zep_cloud/graph/episode/raw_client.py b/src/zep_cloud/graph/episode/raw_client.py index 0c449529..1ae6fd2c 100644 --- a/src/zep_cloud/graph/episode/raw_client.py +++ b/src/zep_cloud/graph/episode/raw_client.py @@ -9,6 +9,7 @@ from ...core.jsonable_encoder import jsonable_encoder from ...core.pydantic_utilities import parse_obj_as from ...core.request_options import RequestOptions +from ...core.serialization import convert_and_respect_annotation_metadata from ...errors.bad_request_error import BadRequestError from ...errors.forbidden_error import ForbiddenError from ...errors.internal_server_error import InternalServerError @@ -17,6 +18,7 @@ from ...types.episode import Episode from ...types.episode_mentions import EpisodeMentions from ...types.episode_response import EpisodeResponse +from ...types.metadata_filter_group import MetadataFilterGroup from ...types.success_response import SuccessResponse # this is used as the default value for optional parameters @@ -108,6 +110,7 @@ def list_by_graph_id( *, cursor: typing.Optional[str] = OMIT, direction: typing.Optional[str] = OMIT, + episode_metadata_filters: typing.Optional[MetadataFilterGroup] = OMIT, limit: typing.Optional[int] = OMIT, mentioned_node_uuids: typing.Optional[typing.Sequence[str]] = OMIT, order_by: typing.Optional[str] = OMIT, @@ -128,13 +131,19 @@ def list_by_graph_id( direction : typing.Optional[str] Sort direction. One of "asc" or "desc". Defaults to "desc". + episode_metadata_filters : typing.Optional[MetadataFilterGroup] + Restricts results to episodes whose stored metadata matches this + predicate. Same type and limits as graph.search episode_metadata_filters. + limit : typing.Optional[int] Maximum number of episodes to return. An explicit value is clamped to 50; when omitted, the default page size (100) applies. mentioned_node_uuids : typing.Optional[typing.Sequence[str]] Restricts results to episodes that mention any of the listed node - UUIDs. At most 256 entries; each must be a syntactically valid UUID. + UUIDs. The list can also contain episode UUIDs: an episode UUID + matches that episode, so one request can return a known set of + episodes. At most 256 entries; each must be a syntactically valid UUID. order_by : typing.Optional[str] Field to sort by. One of "uuid" or "created_at". Defaults to "uuid". @@ -153,6 +162,9 @@ def list_by_graph_id( json={ "cursor": cursor, "direction": direction, + "episode_metadata_filters": convert_and_respect_annotation_metadata( + object_=episode_metadata_filters, annotation=MetadataFilterGroup, direction="write" + ), "limit": limit, "mentioned_node_uuids": mentioned_node_uuids, "order_by": order_by, @@ -282,6 +294,7 @@ def list_by_user_id( *, cursor: typing.Optional[str] = OMIT, direction: typing.Optional[str] = OMIT, + episode_metadata_filters: typing.Optional[MetadataFilterGroup] = OMIT, limit: typing.Optional[int] = OMIT, mentioned_node_uuids: typing.Optional[typing.Sequence[str]] = OMIT, order_by: typing.Optional[str] = OMIT, @@ -302,13 +315,19 @@ def list_by_user_id( direction : typing.Optional[str] Sort direction. One of "asc" or "desc". Defaults to "desc". + episode_metadata_filters : typing.Optional[MetadataFilterGroup] + Restricts results to episodes whose stored metadata matches this + predicate. Same type and limits as graph.search episode_metadata_filters. + limit : typing.Optional[int] Maximum number of episodes to return. An explicit value is clamped to 50; when omitted, the default page size (100) applies. mentioned_node_uuids : typing.Optional[typing.Sequence[str]] Restricts results to episodes that mention any of the listed node - UUIDs. At most 256 entries; each must be a syntactically valid UUID. + UUIDs. The list can also contain episode UUIDs: an episode UUID + matches that episode, so one request can return a known set of + episodes. At most 256 entries; each must be a syntactically valid UUID. order_by : typing.Optional[str] Field to sort by. One of "uuid" or "created_at". Defaults to "uuid". @@ -327,6 +346,9 @@ def list_by_user_id( json={ "cursor": cursor, "direction": direction, + "episode_metadata_filters": convert_and_respect_annotation_metadata( + object_=episode_metadata_filters, annotation=MetadataFilterGroup, direction="write" + ), "limit": limit, "mentioned_node_uuids": mentioned_node_uuids, "order_by": order_by, @@ -766,6 +788,7 @@ async def list_by_graph_id( *, cursor: typing.Optional[str] = OMIT, direction: typing.Optional[str] = OMIT, + episode_metadata_filters: typing.Optional[MetadataFilterGroup] = OMIT, limit: typing.Optional[int] = OMIT, mentioned_node_uuids: typing.Optional[typing.Sequence[str]] = OMIT, order_by: typing.Optional[str] = OMIT, @@ -786,13 +809,19 @@ async def list_by_graph_id( direction : typing.Optional[str] Sort direction. One of "asc" or "desc". Defaults to "desc". + episode_metadata_filters : typing.Optional[MetadataFilterGroup] + Restricts results to episodes whose stored metadata matches this + predicate. Same type and limits as graph.search episode_metadata_filters. + limit : typing.Optional[int] Maximum number of episodes to return. An explicit value is clamped to 50; when omitted, the default page size (100) applies. mentioned_node_uuids : typing.Optional[typing.Sequence[str]] Restricts results to episodes that mention any of the listed node - UUIDs. At most 256 entries; each must be a syntactically valid UUID. + UUIDs. The list can also contain episode UUIDs: an episode UUID + matches that episode, so one request can return a known set of + episodes. At most 256 entries; each must be a syntactically valid UUID. order_by : typing.Optional[str] Field to sort by. One of "uuid" or "created_at". Defaults to "uuid". @@ -811,6 +840,9 @@ async def list_by_graph_id( json={ "cursor": cursor, "direction": direction, + "episode_metadata_filters": convert_and_respect_annotation_metadata( + object_=episode_metadata_filters, annotation=MetadataFilterGroup, direction="write" + ), "limit": limit, "mentioned_node_uuids": mentioned_node_uuids, "order_by": order_by, @@ -940,6 +972,7 @@ async def list_by_user_id( *, cursor: typing.Optional[str] = OMIT, direction: typing.Optional[str] = OMIT, + episode_metadata_filters: typing.Optional[MetadataFilterGroup] = OMIT, limit: typing.Optional[int] = OMIT, mentioned_node_uuids: typing.Optional[typing.Sequence[str]] = OMIT, order_by: typing.Optional[str] = OMIT, @@ -960,13 +993,19 @@ async def list_by_user_id( direction : typing.Optional[str] Sort direction. One of "asc" or "desc". Defaults to "desc". + episode_metadata_filters : typing.Optional[MetadataFilterGroup] + Restricts results to episodes whose stored metadata matches this + predicate. Same type and limits as graph.search episode_metadata_filters. + limit : typing.Optional[int] Maximum number of episodes to return. An explicit value is clamped to 50; when omitted, the default page size (100) applies. mentioned_node_uuids : typing.Optional[typing.Sequence[str]] Restricts results to episodes that mention any of the listed node - UUIDs. At most 256 entries; each must be a syntactically valid UUID. + UUIDs. The list can also contain episode UUIDs: an episode UUID + matches that episode, so one request can return a known set of + episodes. At most 256 entries; each must be a syntactically valid UUID. order_by : typing.Optional[str] Field to sort by. One of "uuid" or "created_at". Defaults to "uuid". @@ -985,6 +1024,9 @@ async def list_by_user_id( json={ "cursor": cursor, "direction": direction, + "episode_metadata_filters": convert_and_respect_annotation_metadata( + object_=episode_metadata_filters, annotation=MetadataFilterGroup, direction="write" + ), "limit": limit, "mentioned_node_uuids": mentioned_node_uuids, "order_by": order_by, diff --git a/src/zep_cloud/graph/node/client.py b/src/zep_cloud/graph/node/client.py index 8fafd397..3cd22131 100644 --- a/src/zep_cloud/graph/node/client.py +++ b/src/zep_cloud/graph/node/client.py @@ -212,7 +212,7 @@ def get_episodes( self, node_uuid: str, *, request_options: typing.Optional[RequestOptions] = None ) -> EpisodeResponse: """ - Deprecated. Use episode listing with `mentioned_node_uuids` (`POST /graph/episodes/graph/{graph_id}` or `POST /graph/episodes/user/{user_id}`) instead. Returns episodes that mentioned a given node, subject to an internal cap; responses reduced by that cap set the Zep-Truncated header. + Deprecated. Read the `episodes` field on the node; when `episodes_truncated` is true, use the episode list SDK methods `graph.episode.list_by_graph_id` or `graph.episode.list_by_user_id` with the `mentioned_node_uuids` filter. Returns episodes that mentioned a given node, subject to an internal cap; responses reduced by that cap set the Zep-Truncated header. Parameters ---------- @@ -277,7 +277,7 @@ def get_neighbors( filters : typing.Optional[SearchFilters] Filters constraining the connecting edges (edge types, dates, and the - section-3 node-/episode-anchored fields) and the neighbor nodes + node- and episode-anchored UUID fields) and the neighbor nodes (node_labels/exclude_node_labels). Reuses the graph.search filter type. @@ -657,7 +657,7 @@ async def get_episodes( self, node_uuid: str, *, request_options: typing.Optional[RequestOptions] = None ) -> EpisodeResponse: """ - Deprecated. Use episode listing with `mentioned_node_uuids` (`POST /graph/episodes/graph/{graph_id}` or `POST /graph/episodes/user/{user_id}`) instead. Returns episodes that mentioned a given node, subject to an internal cap; responses reduced by that cap set the Zep-Truncated header. + Deprecated. Read the `episodes` field on the node; when `episodes_truncated` is true, use the episode list SDK methods `graph.episode.list_by_graph_id` or `graph.episode.list_by_user_id` with the `mentioned_node_uuids` filter. Returns episodes that mentioned a given node, subject to an internal cap; responses reduced by that cap set the Zep-Truncated header. Parameters ---------- @@ -730,7 +730,7 @@ async def get_neighbors( filters : typing.Optional[SearchFilters] Filters constraining the connecting edges (edge types, dates, and the - section-3 node-/episode-anchored fields) and the neighbor nodes + node- and episode-anchored UUID fields) and the neighbor nodes (node_labels/exclude_node_labels). Reuses the graph.search filter type. diff --git a/src/zep_cloud/graph/node/raw_client.py b/src/zep_cloud/graph/node/raw_client.py index 879bf2f6..20e6ce39 100644 --- a/src/zep_cloud/graph/node/raw_client.py +++ b/src/zep_cloud/graph/node/raw_client.py @@ -310,7 +310,7 @@ def get_episodes( self, node_uuid: str, *, request_options: typing.Optional[RequestOptions] = None ) -> HttpResponse[EpisodeResponse]: """ - Deprecated. Use episode listing with `mentioned_node_uuids` (`POST /graph/episodes/graph/{graph_id}` or `POST /graph/episodes/user/{user_id}`) instead. Returns episodes that mentioned a given node, subject to an internal cap; responses reduced by that cap set the Zep-Truncated header. + Deprecated. Read the `episodes` field on the node; when `episodes_truncated` is true, use the episode list SDK methods `graph.episode.list_by_graph_id` or `graph.episode.list_by_user_id` with the `mentioned_node_uuids` filter. Returns episodes that mentioned a given node, subject to an internal cap; responses reduced by that cap set the Zep-Truncated header. Parameters ---------- @@ -407,7 +407,7 @@ def get_neighbors( filters : typing.Optional[SearchFilters] Filters constraining the connecting edges (edge types, dates, and the - section-3 node-/episode-anchored fields) and the neighbor nodes + node- and episode-anchored UUID fields) and the neighbor nodes (node_labels/exclude_node_labels). Reuses the graph.search filter type. @@ -1039,7 +1039,7 @@ async def get_episodes( self, node_uuid: str, *, request_options: typing.Optional[RequestOptions] = None ) -> AsyncHttpResponse[EpisodeResponse]: """ - Deprecated. Use episode listing with `mentioned_node_uuids` (`POST /graph/episodes/graph/{graph_id}` or `POST /graph/episodes/user/{user_id}`) instead. Returns episodes that mentioned a given node, subject to an internal cap; responses reduced by that cap set the Zep-Truncated header. + Deprecated. Read the `episodes` field on the node; when `episodes_truncated` is true, use the episode list SDK methods `graph.episode.list_by_graph_id` or `graph.episode.list_by_user_id` with the `mentioned_node_uuids` filter. Returns episodes that mentioned a given node, subject to an internal cap; responses reduced by that cap set the Zep-Truncated header. Parameters ---------- @@ -1136,7 +1136,7 @@ async def get_neighbors( filters : typing.Optional[SearchFilters] Filters constraining the connecting edges (edge types, dates, and the - section-3 node-/episode-anchored fields) and the neighbor nodes + node- and episode-anchored UUID fields) and the neighbor nodes (node_labels/exclude_node_labels). Reuses the graph.search filter type. diff --git a/src/zep_cloud/graph/raw_client.py b/src/zep_cloud/graph/raw_client.py index 4527e28d..221611e7 100644 --- a/src/zep_cloud/graph/raw_client.py +++ b/src/zep_cloud/graph/raw_client.py @@ -27,6 +27,7 @@ from ..types.entity_type_response import EntityTypeResponse from ..types.episode import Episode from ..types.episode_data import EpisodeData +from ..types.episode_response import EpisodeResponse from ..types.graph import Graph from ..types.graph_data_type import GraphDataType from ..types.graph_list_response import GraphListResponse @@ -523,6 +524,7 @@ def add( data: str, type: GraphDataType, created_at: typing.Optional[str] = OMIT, + document_id: typing.Optional[str] = OMIT, graph_id: typing.Optional[str] = OMIT, metadata: typing.Optional[typing.Dict[str, typing.Optional[typing.Any]]] = OMIT, source_description: typing.Optional[str] = OMIT, @@ -541,6 +543,10 @@ def add( created_at : typing.Optional[str] + document_id : typing.Optional[str] + Optional document ID that groups episodes as chunks of the same document + on a graph. Parallel to thread_id for message threads. + graph_id : typing.Optional[str] graph_id is the ID of the graph to which the data will be added. If adding to the user graph, please use user_id field instead. @@ -569,6 +575,7 @@ def add( json={ "created_at": created_at, "data": data, + "document_id": document_id, "graph_id": graph_id, "metadata": metadata, "source_description": source_description, @@ -627,6 +634,7 @@ def add_batch( self, *, episodes: typing.Sequence[EpisodeData], + document_id: typing.Optional[str] = OMIT, graph_id: typing.Optional[str] = OMIT, strict_ontology: typing.Optional[bool] = OMIT, user_id: typing.Optional[str] = OMIT, @@ -641,6 +649,9 @@ def add_batch( ---------- episodes : typing.Sequence[EpisodeData] + document_id : typing.Optional[str] + Optional document ID applied to every episode in this batch request. + graph_id : typing.Optional[str] graph_id is the ID of the graph to which the data will be added. If adding to the user graph, please use user_id field instead. @@ -662,6 +673,7 @@ def add_batch( "graph-batch", method="POST", json={ + "document_id": document_id, "episodes": convert_and_respect_annotation_metadata( object_=episodes, annotation=typing.Sequence[EpisodeData], direction="write" ), @@ -1073,6 +1085,88 @@ def create( status_code=_response.status_code, headers=dict(_response.headers), body=_response_json ) + def get_episodes_for_document( + self, document_id: str, *, graph_id: str, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[EpisodeResponse]: + """ + Returns episodes associated with a document on a graph. Documents group episodes as chunks, parallel to how threads group messages. + + Parameters + ---------- + document_id : str + Document ID + + graph_id : str + Graph ID + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[EpisodeResponse] + Episodes + """ + _response = self._client_wrapper.httpx_client.request( + f"graph/documents/{jsonable_encoder(document_id)}/episodes", + method="GET", + params={ + "graph_id": graph_id, + }, + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + EpisodeResponse, + parse_obj_as( + type_=EpisodeResponse, # type: ignore + object_=_response.json(), + ), + ) + return HttpResponse(response=_response, data=_data) + if _response.status_code == 400: + raise BadRequestError( + headers=dict(_response.headers), + body=typing.cast( + typing.Optional[typing.Any], + parse_obj_as( + type_=typing.Optional[typing.Any], # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 404: + raise NotFoundError( + headers=dict(_response.headers), + body=typing.cast( + types_api_error_ApiError, + parse_obj_as( + type_=types_api_error_ApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 500: + raise InternalServerError( + headers=dict(_response.headers), + body=typing.cast( + types_api_error_ApiError, + parse_obj_as( + type_=types_api_error_ApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + _response_json = _response.json() + except JSONDecodeError: + raise core_api_error_ApiError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.text + ) + raise core_api_error_ApiError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response_json + ) + def list_all( self, *, @@ -1084,7 +1178,14 @@ def list_all( request_options: typing.Optional[RequestOptions] = None, ) -> HttpResponse[GraphListResponse]: """ - Returns all graphs. In order to list users, use user.list_ordered instead + Returns a paginated directory of live standalone graphs in the + authenticated project. Optional `search` matches `graph_id`, `name`, and + `description` (metadata only; not graph contents). + + Default `pageSize` is 50 (range 1–100). To list users, use + `user.list_ordered` instead. See the + [graph directory guide](/graph-directory) for pagination, relevance + ordering, and Memory MCP exposure. Parameters ---------- @@ -1266,6 +1367,7 @@ def detect_patterns( request_options: typing.Optional[RequestOptions] = None, ) -> HttpResponse[DetectPatternsResponse]: """ + Deprecated. Pattern detection is not part of Public API v4. Detects structural patterns in a knowledge graph including relationship frequencies, multi-hop paths, co-occurrences, hubs, and clusters. When a query is provided, uses hybrid search to discover seed nodes, @@ -1593,7 +1695,7 @@ def get_subgraph( search_filters : typing.Optional[SearchFilters] Filters constraining traversed edges and included nodes. Reuses the graph.search filter type. search_filters.episode_metadata_filters is - rejected: it cannot be enforced during graph traversal (spec-2 §9.4). + rejected: it cannot be enforced during graph traversal. user_id : typing.Optional[str] user_id identifies the target user graph. Exactly one of user_id or @@ -2473,6 +2575,7 @@ async def add( data: str, type: GraphDataType, created_at: typing.Optional[str] = OMIT, + document_id: typing.Optional[str] = OMIT, graph_id: typing.Optional[str] = OMIT, metadata: typing.Optional[typing.Dict[str, typing.Optional[typing.Any]]] = OMIT, source_description: typing.Optional[str] = OMIT, @@ -2491,6 +2594,10 @@ async def add( created_at : typing.Optional[str] + document_id : typing.Optional[str] + Optional document ID that groups episodes as chunks of the same document + on a graph. Parallel to thread_id for message threads. + graph_id : typing.Optional[str] graph_id is the ID of the graph to which the data will be added. If adding to the user graph, please use user_id field instead. @@ -2519,6 +2626,7 @@ async def add( json={ "created_at": created_at, "data": data, + "document_id": document_id, "graph_id": graph_id, "metadata": metadata, "source_description": source_description, @@ -2577,6 +2685,7 @@ async def add_batch( self, *, episodes: typing.Sequence[EpisodeData], + document_id: typing.Optional[str] = OMIT, graph_id: typing.Optional[str] = OMIT, strict_ontology: typing.Optional[bool] = OMIT, user_id: typing.Optional[str] = OMIT, @@ -2591,6 +2700,9 @@ async def add_batch( ---------- episodes : typing.Sequence[EpisodeData] + document_id : typing.Optional[str] + Optional document ID applied to every episode in this batch request. + graph_id : typing.Optional[str] graph_id is the ID of the graph to which the data will be added. If adding to the user graph, please use user_id field instead. @@ -2612,6 +2724,7 @@ async def add_batch( "graph-batch", method="POST", json={ + "document_id": document_id, "episodes": convert_and_respect_annotation_metadata( object_=episodes, annotation=typing.Sequence[EpisodeData], direction="write" ), @@ -3023,6 +3136,88 @@ async def create( status_code=_response.status_code, headers=dict(_response.headers), body=_response_json ) + async def get_episodes_for_document( + self, document_id: str, *, graph_id: str, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[EpisodeResponse]: + """ + Returns episodes associated with a document on a graph. Documents group episodes as chunks, parallel to how threads group messages. + + Parameters + ---------- + document_id : str + Document ID + + graph_id : str + Graph ID + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[EpisodeResponse] + Episodes + """ + _response = await self._client_wrapper.httpx_client.request( + f"graph/documents/{jsonable_encoder(document_id)}/episodes", + method="GET", + params={ + "graph_id": graph_id, + }, + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + EpisodeResponse, + parse_obj_as( + type_=EpisodeResponse, # type: ignore + object_=_response.json(), + ), + ) + return AsyncHttpResponse(response=_response, data=_data) + if _response.status_code == 400: + raise BadRequestError( + headers=dict(_response.headers), + body=typing.cast( + typing.Optional[typing.Any], + parse_obj_as( + type_=typing.Optional[typing.Any], # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 404: + raise NotFoundError( + headers=dict(_response.headers), + body=typing.cast( + types_api_error_ApiError, + parse_obj_as( + type_=types_api_error_ApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 500: + raise InternalServerError( + headers=dict(_response.headers), + body=typing.cast( + types_api_error_ApiError, + parse_obj_as( + type_=types_api_error_ApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + _response_json = _response.json() + except JSONDecodeError: + raise core_api_error_ApiError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.text + ) + raise core_api_error_ApiError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response_json + ) + async def list_all( self, *, @@ -3034,7 +3229,14 @@ async def list_all( request_options: typing.Optional[RequestOptions] = None, ) -> AsyncHttpResponse[GraphListResponse]: """ - Returns all graphs. In order to list users, use user.list_ordered instead + Returns a paginated directory of live standalone graphs in the + authenticated project. Optional `search` matches `graph_id`, `name`, and + `description` (metadata only; not graph contents). + + Default `pageSize` is 50 (range 1–100). To list users, use + `user.list_ordered` instead. See the + [graph directory guide](/graph-directory) for pagination, relevance + ordering, and Memory MCP exposure. Parameters ---------- @@ -3216,6 +3418,7 @@ async def detect_patterns( request_options: typing.Optional[RequestOptions] = None, ) -> AsyncHttpResponse[DetectPatternsResponse]: """ + Deprecated. Pattern detection is not part of Public API v4. Detects structural patterns in a knowledge graph including relationship frequencies, multi-hop paths, co-occurrences, hubs, and clusters. When a query is provided, uses hybrid search to discover seed nodes, @@ -3543,7 +3746,7 @@ async def get_subgraph( search_filters : typing.Optional[SearchFilters] Filters constraining traversed edges and included nodes. Reuses the graph.search filter type. search_filters.episode_metadata_filters is - rejected: it cannot be enforced during graph traversal (spec-2 §9.4). + rejected: it cannot be enforced during graph traversal. user_id : typing.Optional[str] user_id identifies the target user graph. Exactly one of user_id or diff --git a/src/zep_cloud/thread/client.py b/src/zep_cloud/thread/client.py index e4a8add5..66e5ab88 100644 --- a/src/zep_cloud/thread/client.py +++ b/src/zep_cloud/thread/client.py @@ -5,6 +5,7 @@ from ..core.client_wrapper import AsyncClientWrapper, SyncClientWrapper from ..core.request_options import RequestOptions from ..types.add_thread_messages_response import AddThreadMessagesResponse +from ..types.episode_response import EpisodeResponse from ..types.message import Message from ..types.message_list_response import MessageListResponse from ..types.role_type import RoleType @@ -200,6 +201,39 @@ def get_user_context( ) return _response.data + def get_episodes( + self, thread_id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> EpisodeResponse: + """ + Returns graph episodes associated with a thread. Parallel to get_episodes_for_document for documents. + + Parameters + ---------- + thread_id : str + The ID of the thread + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + EpisodeResponse + Episodes + + Examples + -------- + from zep_cloud import Zep + + client = Zep( + api_key="YOUR_API_KEY", + ) + client.thread.get_episodes( + thread_id="threadId", + ) + """ + _response = self._raw_client.get_episodes(thread_id, request_options=request_options) + return _response.data + def get( self, thread_id: str, @@ -635,6 +669,47 @@ async def main() -> None: ) return _response.data + async def get_episodes( + self, thread_id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> EpisodeResponse: + """ + Returns graph episodes associated with a thread. Parallel to get_episodes_for_document for documents. + + Parameters + ---------- + thread_id : str + The ID of the thread + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + EpisodeResponse + Episodes + + Examples + -------- + import asyncio + + from zep_cloud import AsyncZep + + client = AsyncZep( + api_key="YOUR_API_KEY", + ) + + + async def main() -> None: + await client.thread.get_episodes( + thread_id="threadId", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.get_episodes(thread_id, request_options=request_options) + return _response.data + async def get( self, thread_id: str, diff --git a/src/zep_cloud/thread/message/client.py b/src/zep_cloud/thread/message/client.py index 0c8adcba..17e3fb9d 100644 --- a/src/zep_cloud/thread/message/client.py +++ b/src/zep_cloud/thread/message/client.py @@ -42,6 +42,7 @@ def update( The UUID of the message. metadata : typing.Dict[str, typing.Optional[typing.Any]] + Metadata to store on the message. Max 10 keys. Values must be strings, numbers, booleans, or arrays of scalars. request_options : typing.Optional[RequestOptions] Request-specific configuration. @@ -98,6 +99,7 @@ async def update( The UUID of the message. metadata : typing.Dict[str, typing.Optional[typing.Any]] + Metadata to store on the message. Max 10 keys. Values must be strings, numbers, booleans, or arrays of scalars. request_options : typing.Optional[RequestOptions] Request-specific configuration. diff --git a/src/zep_cloud/thread/message/raw_client.py b/src/zep_cloud/thread/message/raw_client.py index 20029489..aa21ec8a 100644 --- a/src/zep_cloud/thread/message/raw_client.py +++ b/src/zep_cloud/thread/message/raw_client.py @@ -38,6 +38,7 @@ def update( The UUID of the message. metadata : typing.Dict[str, typing.Optional[typing.Any]] + Metadata to store on the message. Max 10 keys. Values must be strings, numbers, booleans, or arrays of scalars. request_options : typing.Optional[RequestOptions] Request-specific configuration. @@ -121,6 +122,7 @@ async def update( The UUID of the message. metadata : typing.Dict[str, typing.Optional[typing.Any]] + Metadata to store on the message. Max 10 keys. Values must be strings, numbers, booleans, or arrays of scalars. request_options : typing.Optional[RequestOptions] Request-specific configuration. diff --git a/src/zep_cloud/thread/raw_client.py b/src/zep_cloud/thread/raw_client.py index 94237e62..6ee7901a 100644 --- a/src/zep_cloud/thread/raw_client.py +++ b/src/zep_cloud/thread/raw_client.py @@ -16,6 +16,7 @@ from ..errors.not_found_error import NotFoundError from ..types.add_thread_messages_response import AddThreadMessagesResponse from ..types.api_error import ApiError as types_api_error_ApiError +from ..types.episode_response import EpisodeResponse from ..types.message import Message from ..types.message_list_response import MessageListResponse from ..types.role_type import RoleType @@ -335,6 +336,82 @@ def get_user_context( status_code=_response.status_code, headers=dict(_response.headers), body=_response_json ) + def get_episodes( + self, thread_id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[EpisodeResponse]: + """ + Returns graph episodes associated with a thread. Parallel to get_episodes_for_document for documents. + + Parameters + ---------- + thread_id : str + The ID of the thread + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[EpisodeResponse] + Episodes + """ + _response = self._client_wrapper.httpx_client.request( + f"threads/{jsonable_encoder(thread_id)}/episodes", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + EpisodeResponse, + parse_obj_as( + type_=EpisodeResponse, # type: ignore + object_=_response.json(), + ), + ) + return HttpResponse(response=_response, data=_data) + if _response.status_code == 403: + raise ForbiddenError( + headers=dict(_response.headers), + body=typing.cast( + types_api_error_ApiError, + parse_obj_as( + type_=types_api_error_ApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 404: + raise NotFoundError( + headers=dict(_response.headers), + body=typing.cast( + types_api_error_ApiError, + parse_obj_as( + type_=types_api_error_ApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 500: + raise InternalServerError( + headers=dict(_response.headers), + body=typing.cast( + types_api_error_ApiError, + parse_obj_as( + type_=types_api_error_ApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + _response_json = _response.json() + except JSONDecodeError: + raise core_api_error_ApiError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.text + ) + raise core_api_error_ApiError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response_json + ) + def get( self, thread_id: str, @@ -973,6 +1050,82 @@ async def get_user_context( status_code=_response.status_code, headers=dict(_response.headers), body=_response_json ) + async def get_episodes( + self, thread_id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[EpisodeResponse]: + """ + Returns graph episodes associated with a thread. Parallel to get_episodes_for_document for documents. + + Parameters + ---------- + thread_id : str + The ID of the thread + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[EpisodeResponse] + Episodes + """ + _response = await self._client_wrapper.httpx_client.request( + f"threads/{jsonable_encoder(thread_id)}/episodes", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + EpisodeResponse, + parse_obj_as( + type_=EpisodeResponse, # type: ignore + object_=_response.json(), + ), + ) + return AsyncHttpResponse(response=_response, data=_data) + if _response.status_code == 403: + raise ForbiddenError( + headers=dict(_response.headers), + body=typing.cast( + types_api_error_ApiError, + parse_obj_as( + type_=types_api_error_ApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 404: + raise NotFoundError( + headers=dict(_response.headers), + body=typing.cast( + types_api_error_ApiError, + parse_obj_as( + type_=types_api_error_ApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 500: + raise InternalServerError( + headers=dict(_response.headers), + body=typing.cast( + types_api_error_ApiError, + parse_obj_as( + type_=types_api_error_ApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + _response_json = _response.json() + except JSONDecodeError: + raise core_api_error_ApiError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.text + ) + raise core_api_error_ApiError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response_json + ) + async def get( self, thread_id: str, diff --git a/src/zep_cloud/types/__init__.py b/src/zep_cloud/types/__init__.py index 535b0a0d..9a8e0913 100644 --- a/src/zep_cloud/types/__init__.py +++ b/src/zep_cloud/types/__init__.py @@ -31,6 +31,7 @@ from .derived_node import DerivedNode from .detect_config import DetectConfig from .detect_patterns_response import DetectPatternsResponse +from .document_summary import DocumentSummary from .edge_type import EdgeType from .entity_edge import EntityEdge from .entity_edge_source_target import EntityEdgeSourceTarget @@ -133,6 +134,7 @@ "DerivedNode", "DetectConfig", "DetectPatternsResponse", + "DocumentSummary", "EdgeType", "EntityEdge", "EntityEdgeSourceTarget", diff --git a/src/zep_cloud/types/batch_add_item.py b/src/zep_cloud/types/batch_add_item.py index a95cb4ab..687a33fc 100644 --- a/src/zep_cloud/types/batch_add_item.py +++ b/src/zep_cloud/types/batch_add_item.py @@ -14,6 +14,12 @@ class BatchAddItem(UniversalBaseModel): created_at: typing.Optional[str] = None data: typing.Optional[str] = None data_type: typing.Optional[GraphDataType] = None + document_id: typing.Optional[str] = pydantic.Field(default=None) + """ + Optional document ID for graph_episode items. Groups episodes as document + chunks. Ignored for thread_message items. + """ + graph_id: typing.Optional[str] = None metadata: typing.Optional[typing.Dict[str, typing.Optional[typing.Any]]] = None name: typing.Optional[str] = None diff --git a/src/zep_cloud/types/batch_item_detail.py b/src/zep_cloud/types/batch_item_detail.py index 439cd2fb..5d646aa9 100644 --- a/src/zep_cloud/types/batch_item_detail.py +++ b/src/zep_cloud/types/batch_item_detail.py @@ -10,6 +10,7 @@ class BatchItemDetail(UniversalBaseModel): created_at: typing.Optional[str] = None + document_id: typing.Optional[str] = None episode_uuid: typing.Optional[str] = pydantic.Field(default=None) """ EpisodeUUID is the UUID of the episode that will be (or has been) created diff --git a/src/zep_cloud/types/batch_summary.py b/src/zep_cloud/types/batch_summary.py index aaf0d40e..0ad20440 100644 --- a/src/zep_cloud/types/batch_summary.py +++ b/src/zep_cloud/types/batch_summary.py @@ -19,6 +19,7 @@ class BatchSummary(UniversalBaseModel): processed_at: typing.Optional[str] = None progress: typing.Optional[BatchProgress] = None status: typing.Optional[BatchStatus] = None + strict_ontology: typing.Optional[bool] = None updated_at: typing.Optional[str] = None if IS_PYDANTIC_V2: diff --git a/src/zep_cloud/types/document_summary.py b/src/zep_cloud/types/document_summary.py new file mode 100644 index 00000000..a9dd72cf --- /dev/null +++ b/src/zep_cloud/types/document_summary.py @@ -0,0 +1,51 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +import typing_extensions +from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel +from ..core.serialization import FieldMetadata + + +class DocumentSummary(UniversalBaseModel): + created_at: typing.Optional[str] = pydantic.Field(default=None) + """ + CreatedAt is when the summary node was first created. + """ + + document_id: typing.Optional[str] = pydantic.Field(default=None) + """ + DocumentID is the customer-facing document identifier. + """ + + last_summarized_at: typing.Optional[str] = pydantic.Field(default=None) + """ + LastSummarizedAt is the wall-clock timestamp of the most recent + summary update. + """ + + last_summarized_episode_valid_at: typing.Optional[str] = pydantic.Field(default=None) + """ + LastSummarizedEpisodeValidAt is the maximum episode reference time + (valid_at) covered by the most recent summary. + """ + + summary: typing.Optional[str] = pydantic.Field(default=None) + """ + Summary is the incremental summary content. + """ + + uuid_: typing_extensions.Annotated[typing.Optional[str], FieldMetadata(alias="uuid")] = pydantic.Field(default=None) + """ + UUID of the document summary (derived) node. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/zep_cloud/types/entity_edge.py b/src/zep_cloud/types/entity_edge.py index 91a79a29..df80e16f 100644 --- a/src/zep_cloud/types/entity_edge.py +++ b/src/zep_cloud/types/entity_edge.py @@ -34,6 +34,12 @@ class EntityEdge(UniversalBaseModel): Fact representing the edge and nodes that it connects """ + hyperedge_uuid: typing.Optional[str] = pydantic.Field(default=None) + """ + HyperedgeUUID groups the pairwise edges projected from the same atomic + multi-entity fact. Omitted when the edge is not part of a hyperedge. + """ + invalid_at: typing.Optional[str] = pydantic.Field(default=None) """ Datetime of when the fact stopped being true @@ -68,7 +74,7 @@ class EntityEdge(UniversalBaseModel): source_node_labels: typing.Optional[typing.List[str]] = pydantic.Field(default=None) """ SourceNodeLabels are the labels of the source node at read time. Same - read-time-projection semantics as SourceNodeName (spec-2 §4). + read-time-projection semantics as SourceNodeName. """ source_node_name: typing.Optional[str] = pydantic.Field(default=None) @@ -77,7 +83,7 @@ class EntityEdge(UniversalBaseModel): read-time projection of current node state, not a stored edge attribute: a subsequent node rename is reflected on the next read. Omitted (the edge is still returned) if the source node cannot be - resolved, for example if it was deleted concurrently (spec-2 §4). + resolved, for example if it was deleted concurrently. """ source_node_uuid: str = pydantic.Field() @@ -88,13 +94,13 @@ class EntityEdge(UniversalBaseModel): target_node_labels: typing.Optional[typing.List[str]] = pydantic.Field(default=None) """ TargetNodeLabels are the labels of the target node at read time. Same - read-time-projection semantics as SourceNodeName (spec-2 §4). + read-time-projection semantics as SourceNodeName. """ target_node_name: typing.Optional[str] = pydantic.Field(default=None) """ TargetNodeName is the name of the target node at read time. Same - read-time-projection semantics as SourceNodeName (spec-2 §4). + read-time-projection semantics as SourceNodeName. """ target_node_uuid: str = pydantic.Field() diff --git a/src/zep_cloud/types/entity_node.py b/src/zep_cloud/types/entity_node.py index abed7274..9161cc17 100644 --- a/src/zep_cloud/types/entity_node.py +++ b/src/zep_cloud/types/entity_node.py @@ -19,6 +19,21 @@ class EntityNode(UniversalBaseModel): Creation time of the node """ + episodes: typing.Optional[typing.List[str]] = pydantic.Field(default=None) + """ + The UUIDs of the live episodes that mention this node, newest first. The + list is complete when `episodes_truncated` is false. The list is empty + when the node has more than 100 source episodes; list episodes with the + `mentioned_node_uuids` filter to read them. + """ + + episodes_truncated: typing.Optional[bool] = pydantic.Field(default=None) + """ + True when the node has more than 100 source episodes, so `episodes` is + empty, or when provenance is unavailable. False means `episodes` is the + complete set. + """ + labels: typing.Optional[typing.List[str]] = pydantic.Field(default=None) """ Labels associated with the node diff --git a/src/zep_cloud/types/episode.py b/src/zep_cloud/types/episode.py index fa179eb7..9584ce87 100644 --- a/src/zep_cloud/types/episode.py +++ b/src/zep_cloud/types/episode.py @@ -13,6 +13,11 @@ class Episode(UniversalBaseModel): content: str created_at: str + document_id: typing.Optional[str] = pydantic.Field(default=None) + """ + Optional document ID, will be present if the episode is part of a document + """ + metadata: typing.Optional[typing.Dict[str, typing.Optional[typing.Any]]] = None processed: typing.Optional[bool] = None relevance: typing.Optional[float] = pydantic.Field(default=None) diff --git a/src/zep_cloud/types/graph.py b/src/zep_cloud/types/graph.py index dd05ec34..38dc6dca 100644 --- a/src/zep_cloud/types/graph.py +++ b/src/zep_cloud/types/graph.py @@ -9,6 +9,13 @@ class Graph(UniversalBaseModel): + canonical_graph_uuid: typing.Optional[str] = pydantic.Field(default=None) + """ + CanonicalGraphUUID is graphs.uuid, the v4 graph address (spec-3 §13.5). + Omitted when the graphs row does not yet exist. Distinct from UUID, + which is the group-row identifier. + """ + created_at: typing.Optional[str] = None description: typing.Optional[str] = None graph_id: typing.Optional[str] = None diff --git a/src/zep_cloud/types/graph_episode_list_request.py b/src/zep_cloud/types/graph_episode_list_request.py index e7bd251d..3a85f828 100644 --- a/src/zep_cloud/types/graph_episode_list_request.py +++ b/src/zep_cloud/types/graph_episode_list_request.py @@ -1,9 +1,11 @@ # This file was auto-generated by Fern from our API Definition. +from __future__ import annotations + import typing import pydantic -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel +from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel, update_forward_refs class GraphEpisodeListRequest(UniversalBaseModel): @@ -18,6 +20,12 @@ class GraphEpisodeListRequest(UniversalBaseModel): Sort direction. One of "asc" or "desc". Defaults to "desc". """ + episode_metadata_filters: typing.Optional["MetadataFilterGroup"] = pydantic.Field(default=None) + """ + Restricts results to episodes whose stored metadata matches this + predicate. Same type and limits as graph.search episode_metadata_filters. + """ + limit: typing.Optional[int] = pydantic.Field(default=None) """ Maximum number of episodes to return. An explicit value is clamped to @@ -27,7 +35,9 @@ class GraphEpisodeListRequest(UniversalBaseModel): mentioned_node_uuids: typing.Optional[typing.List[str]] = pydantic.Field(default=None) """ Restricts results to episodes that mention any of the listed node - UUIDs. At most 256 entries; each must be a syntactically valid UUID. + UUIDs. The list can also contain episode UUIDs: an episode UUID + matches that episode, so one request can return a known set of + episodes. At most 256 entries; each must be a syntactically valid UUID. """ order_by: typing.Optional[str] = pydantic.Field(default=None) @@ -43,3 +53,8 @@ class Config: frozen = True smart_union = True extra = pydantic.Extra.allow + + +from .metadata_filter_group import MetadataFilterGroup # noqa: E402, F401, I001 + +update_forward_refs(GraphEpisodeListRequest) diff --git a/src/zep_cloud/types/message.py b/src/zep_cloud/types/message.py index 6fa51810..5edc1414 100644 --- a/src/zep_cloud/types/message.py +++ b/src/zep_cloud/types/message.py @@ -22,7 +22,7 @@ class Message(UniversalBaseModel): metadata: typing.Optional[typing.Dict[str, typing.Optional[typing.Any]]] = pydantic.Field(default=None) """ - The metadata associated with the message. + The metadata associated with the message. Max 10 keys. Values must be strings, numbers, booleans, or arrays of scalars. """ name: typing.Optional[str] = pydantic.Field(default=None) diff --git a/src/zep_cloud/types/thread.py b/src/zep_cloud/types/thread.py index 964b51c9..ef3e5602 100644 --- a/src/zep_cloud/types/thread.py +++ b/src/zep_cloud/types/thread.py @@ -10,6 +10,11 @@ class Thread(UniversalBaseModel): created_at: typing.Optional[str] = None + graph_uuid: typing.Optional[str] = pydantic.Field(default=None) + """ + GraphUUID is the graphs.uuid of the owning user's graph (spec-3 section 13.5). + """ + project_uuid: typing.Optional[str] = None thread_id: typing.Optional[str] = None user_id: typing.Optional[str] = None diff --git a/src/zep_cloud/types/thread_context_response.py b/src/zep_cloud/types/thread_context_response.py index f6cc4d9b..dbd84361 100644 --- a/src/zep_cloud/types/thread_context_response.py +++ b/src/zep_cloud/types/thread_context_response.py @@ -9,7 +9,7 @@ class ThreadContextResponse(UniversalBaseModel): context: typing.Optional[str] = pydantic.Field(default=None) """ - Context block containing relevant facts, entities, and messages/episodes from the user graph. Meant to be replaced in the system prompt on every chat turn. + Context block containing relevant facts, entities, and messages/episodes from the user graph. Pass it through the model provider's untrusted-data channel. """ if IS_PYDANTIC_V2: diff --git a/src/zep_cloud/types/thread_summary.py b/src/zep_cloud/types/thread_summary.py index db33c75d..88385d63 100644 --- a/src/zep_cloud/types/thread_summary.py +++ b/src/zep_cloud/types/thread_summary.py @@ -44,7 +44,7 @@ class ThreadSummary(UniversalBaseModel): uuid_: typing_extensions.Annotated[typing.Optional[str], FieldMetadata(alias="uuid")] = pydantic.Field(default=None) """ - UUID of the thread summary node. + UUID of the derived thread summary node. """ if IS_PYDANTIC_V2: diff --git a/src/zep_cloud/types/user.py b/src/zep_cloud/types/user.py index 50b8df9a..5ccefd3f 100644 --- a/src/zep_cloud/types/user.py +++ b/src/zep_cloud/types/user.py @@ -14,6 +14,12 @@ class User(UniversalBaseModel): disable_default_ontology: typing.Optional[bool] = None email: typing.Optional[str] = None first_name: typing.Optional[str] = None + graph_uuid: typing.Optional[str] = pydantic.Field(default=None) + """ + GraphUUID is the graphs.uuid of the user's graph (spec-3 section 13.5). + Omitted when the graph row does not exist. Read-only; never accepted as input. + """ + id: typing.Optional[int] = None last_name: typing.Optional[str] = None metadata: typing.Optional[typing.Dict[str, typing.Optional[typing.Any]]] = pydantic.Field(default=None) From cc5adb04a77f70b6a4fe0a96cd5ce973ad0bbb1c Mon Sep 17 00:00:00 2001 From: "fern-api[bot]" <115122769+fern-api[bot]@users.noreply.github.com> Date: Wed, 23 Sep 2026 18:32:19 +0000 Subject: [PATCH 2/3] [fern-replay] advance lockfile --- .fern/replay.lock | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/.fern/replay.lock b/.fern/replay.lock index afed4a45..a21a790c 100644 --- a/.fern/replay.lock +++ b/.fern/replay.lock @@ -24,5 +24,11 @@ generations: cli_version: unknown generator_versions: fernapi/fern-python-sdk: 4.25.5 -current_generation: 8a3fb853912eadd78157bed5f9811ae936e834a8 + - commit_sha: c78e60a610fc85f34bc472c3338458a2b92d5936 + tree_hash: 562cd14134ac13986361bb7ea200734775c03b2b + timestamp: 2026-09-23T18:32:15.305Z + cli_version: unknown + generator_versions: + fernapi/fern-python-sdk: 4.25.5 +current_generation: c78e60a610fc85f34bc472c3338458a2b92d5936 patches: [] From 7a52092b15facfa8aeb43e0bd0b663a91126e676 Mon Sep 17 00:00:00 2001 From: "zep-sdk-release-bot[bot]" Date: Wed, 23 Sep 2026 18:32:25 +0000 Subject: [PATCH 3/3] chore: bump SDK version to 3.29.0 --- pyproject.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 1e936748..70bd5399 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,10 +1,10 @@ [project] name = "zep-cloud" -version = "3.28.0" +version = "3.29.0" [tool.poetry] name = "zep-cloud" -version = "3.28.0" +version = "3.29.0" description = "" readme = "README.md" authors = []