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