diff --git a/.fern/metadata.json b/.fern/metadata.json new file mode 100644 index 00000000..57921656 --- /dev/null +++ b/.fern/metadata.json @@ -0,0 +1,16 @@ +{ + "cliVersion": "5.50.1", + "generatorName": "fernapi/fern-python-sdk", + "generatorVersion": "4.64.1", + "generatorConfig": { + "package_name": "zep_cloud", + "client": { + "filename": "base_client.py", + "class_name": "BaseClient", + "exported_filename": "client.py", + "exported_class_name": "Zep" + } + }, + "originGitCommit": "762fc6a35d65726598b9a2f54feec4c7840c960c", + "sdkVersion": "4.0.0a1" +} \ No newline at end of file diff --git a/.fern/replay.lock b/.fern/replay.lock new file mode 100644 index 00000000..467c1711 --- /dev/null +++ b/.fern/replay.lock @@ -0,0 +1,16 @@ +# DO NOT EDIT MANUALLY - Managed by Fern Replay +version: "1.0" +generations: + - commit_sha: 3878685b73f64065ae4c08c610276a2ad7b88778 + tree_hash: 7fbefd346835410c641ce62328a4df49e2cf5e44 + timestamp: 2026-08-25T00:34:33.745Z + cli_version: unknown + generator_versions: {} + - commit_sha: c42097c5befa65e42c8a20f01ef0c16e3fa94187 + tree_hash: 091c73124c013502de47b0a04a8ddedfd12fc82b + timestamp: 2026-08-25T00:34:34.343Z + cli_version: unknown + generator_versions: + fernapi/fern-python-sdk: 4.64.1 +current_generation: c42097c5befa65e42c8a20f01ef0c16e3fa94187 +patches: [] diff --git a/.fernignore b/.fernignore index 07850b98..d819dc03 100644 --- a/.fernignore +++ b/.fernignore @@ -9,3 +9,5 @@ .github LICENSE .gitattributes +.fern/replay.lock +.fern/replay.yml diff --git a/.gitignore b/.gitignore index 5b5da561..d2e4ca80 100644 --- a/.gitignore +++ b/.gitignore @@ -1,11 +1,5 @@ -.env -dist/ .mypy_cache/ +.ruff_cache/ __pycache__/ +dist/ poetry.toml -.idea/ -.vscode/ -*.env -.venv/ -venv/ -.cache/ \ No newline at end of file diff --git a/Makefile b/Makefile deleted file mode 100644 index e31d6c66..00000000 --- a/Makefile +++ /dev/null @@ -1,20 +0,0 @@ -.PHONY: all format lint test coverage - -all: help - -coverage: - poetry run pytest --cov \ - --cov-config=.coveragerc \ - --cov-report xml \ - --cov-report term-missing:skip-covered - -format: - poetry run ruff check --select I --fix . - poetry run ruff format . - -lint: - poetry run mypy --exclude tests src/ - poetry run ruff check src/ - -test: - poetry run pytest tests \ No newline at end of file diff --git a/README.md b/README.md index 03a20ed6..08ab94fa 100644 --- a/README.md +++ b/README.md @@ -1,27 +1,25 @@ +# Zep Python Library -[![Release to PyPI](https://github.com/getzep/zep-python/actions/workflows/release.yml/badge.svg)](https://github.com/getzep/zep-python/actions/workflows/release.yml) ![GitHub](https://img.shields.io/github/license/getzep/zep-python?color=blue) [![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-SDK%20generated%20by%20Fern-brightgreen)](https://github.com/fern-api/fern) +[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-Built%20with%20Fern-brightgreen)](https://buildwithfern.com?utm_source=github&utm_medium=github&utm_campaign=readme&utm_source=https%3A%2F%2Fgithub.com%2Fgetzep%2Fzep-python) +[![pypi](https://img.shields.io/pypi/v/zep-cloud)](https://pypi.python.org/pypi/zep-cloud) +The Zep Python library provides convenient access to the Zep APIs from Python. -

- - Zep Logo - -

+## Table of Contents -

-Zep: Long-Term Context for ‍AI Assistants. -

-

Recall, understand, and extract data from chat histories. Power personalized AI experiences.

-
- -

-Quick Start | -Documentation | -LangChain and -LlamaIndex Support | -Discord
-www.getzep.com -

+- [What Is Zep 💬](#what-is-zep-) +- [Installation](#installation) +- [Reference](#reference) +- [Usage](#usage) +- [Async Client](#async-client) +- [Exception Handling](#exception-handling) +- [Pagination](#pagination) +- [Advanced](#advanced) + - [Access Raw Response Data](#access-raw-response-data) + - [Retries](#retries) + - [Timeouts](#timeouts) + - [Custom Client](#custom-client) +- [Contributing](#contributing) ## What is Zep? 💬 Zep is a context engineering platform for AI Assistant apps. With Zep, you can provide AI assistants with the ability to recall past conversations, no matter how distant, while also reducing hallucinations, latency, and cost. @@ -105,3 +103,170 @@ LANGCHAIN_PROJECT=# If not specified, defaults to " ``` + +## Installation + +```sh +pip install zep-cloud +``` + +## Reference + +A full reference for this library is available [here](https://github.com/getzep/zep-python/blob/HEAD/./reference.md). + +## Usage + +Instantiate and use the client with the following: + +```python +from zep_cloud import Zep + +client = Zep( + api_key="", +) + +client.batch.create() +``` + +## Async Client + +The SDK also exports an `async` client so that you can make non-blocking calls to our API. Note that if you are constructing an Async httpx client class to pass into this client, use `httpx.AsyncClient()` instead of `httpx.Client()` (e.g. for the `httpx_client` parameter of this client). + +```python +import asyncio + +from zep_cloud import AsyncZep + +client = AsyncZep( + api_key="", +) + + +async def main() -> None: + await client.batch.create() + + +asyncio.run(main()) +``` + +## Exception Handling + +When the API returns a non-success status code (4xx or 5xx response), a subclass of the following error +will be thrown. + +```python +from zep_cloud.core.api_error import ApiError + +try: + client.batch.create(...) +except ApiError as e: + print(e.status_code) + print(e.body) +``` + +## Pagination + +Paginated requests will return a `SyncPager` or `AsyncPager`, which can be used as generators for the underlying object. + +```python +from zep_cloud import Zep + +client = Zep( + api_key="", +) + +client.batch.list( + limit=1, + cursor="cursor", + status="status", +) +``` + +```python +# You can also iterate through pages and access the typed response per page +pager = client.batch.list(...) +for page in pager.iter_pages(): + print(page.response) # access the typed response for each page + for item in page: + print(item) +``` + +## Advanced + +### Access Raw Response Data + +The SDK provides access to raw response data, including headers, through the `.with_raw_response` property. +The `.with_raw_response` property returns a "raw" client that can be used to access the `.headers` and `.data` attributes. + +```python +from zep_cloud import Zep + +client = Zep(...) +response = client.batch.with_raw_response.create(...) +print(response.headers) # access the response headers +print(response.status_code) # access the response status code +print(response.data) # access the underlying object +``` + +### Retries + +The SDK is instrumented with automatic retries with exponential backoff. A request will be retried as long +as the request is deemed retryable and the number of retry attempts has not grown larger than the configured +retry limit (default: 2). + +A request is deemed retryable when any of the following HTTP status codes is returned: + +- [408](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/408) (Timeout) +- [429](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/429) (Too Many Requests) +- [5XX](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/500) (Internal Server Errors) + +Use the `max_retries` request option to configure this behavior. + +```python +client.batch.create(..., request_options={ + "max_retries": 1 +}) +``` + +### Timeouts + +The SDK defaults to a 60 second timeout. You can configure this with a timeout option at the client or request level. + +```python +from zep_cloud import Zep + +client = Zep(..., timeout=20.0) + +# Override timeout for a specific method +client.batch.create(..., request_options={ + "timeout_in_seconds": 1 +}) +``` + +### Custom Client + +You can override the `httpx` client to customize it for your use-case. Some common use-cases include support for proxies +and transports. + +```python +import httpx +from zep_cloud import Zep + +client = Zep( + ..., + httpx_client=httpx.Client( + proxy="http://my.test.proxy.example.com", + transport=httpx.HTTPTransport(local_address="0.0.0.0"), + ), +) +``` + +## Contributing + +While we value open-source contributions to this SDK, this library is generated programmatically. +Additions made directly to this library would have to be moved over to our generation code, +otherwise they would be overwritten upon the next generated release. Feel free to open a PR as +a proof of concept, but know that we will not be able to merge it as-is. We suggest opening +an issue first to discuss with us! + +On the other hand, contributions to the README are always very welcome! diff --git a/poetry.lock b/poetry.lock index 07135604..9d0c35a8 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.8.4 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.8.5 and should not be changed by hand. [[package]] name = "annotated-types" @@ -11,34 +11,40 @@ files = [ {file = "annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89"}, ] +[package.dependencies] +typing-extensions = {version = ">=4.0.0", markers = "python_version < \"3.9\""} + [[package]] name = "anyio" -version = "4.12.1" -description = "High-level concurrency and networking framework on top of asyncio or Trio" +version = "4.5.2" +description = "High level compatibility layer for multiple asynchronous event loop implementations" optional = false -python-versions = ">=3.9" +python-versions = ">=3.8" files = [ - {file = "anyio-4.12.1-py3-none-any.whl", hash = "sha256:d405828884fc140aa80a3c667b8beed277f1dfedec42ba031bd6ac3db606ab6c"}, - {file = "anyio-4.12.1.tar.gz", hash = "sha256:41cfcc3a4c85d3f05c932da7c26d0201ac36f72abd4435ba90d0464a3ffed703"}, + {file = "anyio-4.5.2-py3-none-any.whl", hash = "sha256:c011ee36bc1e8ba40e5a81cb9df91925c218fe9b778554e0b56a21e1b5d4716f"}, + {file = "anyio-4.5.2.tar.gz", hash = "sha256:23009af4ed04ce05991845451e11ef02fc7c5ed29179ac9a420e5ad0ac7ddc5b"}, ] [package.dependencies] exceptiongroup = {version = ">=1.0.2", markers = "python_version < \"3.11\""} idna = ">=2.8" -typing_extensions = {version = ">=4.5", markers = "python_version < \"3.13\""} +sniffio = ">=1.1" +typing-extensions = {version = ">=4.1", markers = "python_version < \"3.11\""} [package.extras] -trio = ["trio (>=0.31.0)", "trio (>=0.32.0)"] +doc = ["Sphinx (>=7.4,<8.0)", "packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphinx-rtd-theme"] +test = ["anyio[trio]", "coverage[toml] (>=7)", "exceptiongroup (>=1.2.0)", "hypothesis (>=4.0)", "psutil (>=5.9)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "truststore (>=0.9.1)", "uvloop (>=0.21.0b1)"] +trio = ["trio (>=0.26.1)"] [[package]] name = "certifi" -version = "2026.2.25" +version = "2026.7.22" description = "Python package for providing Mozilla's CA Bundle." optional = false python-versions = ">=3.7" files = [ - {file = "certifi-2026.2.25-py3-none-any.whl", hash = "sha256:027692e4402ad994f1c42e52a4997a9763c646b73e4096e4d5d6db8af1d6f0fa"}, - {file = "certifi-2026.2.25.tar.gz", hash = "sha256:e887ab5cee78ea814d3472169153c2d12cd43b14bd03329a39a9c6e2e80bfba7"}, + {file = "certifi-2026.7.22-py3-none-any.whl", hash = "sha256:62f22742b58a1a33014a2b6b706588a8d7e2a88ae7bd1a6ebe8c992928483775"}, + {file = "certifi-2026.7.22.tar.gz", hash = "sha256:741e2c3b351ddf169a738da9f2c048608ff7f2c5cc02f1ebc6b118bb090d5d55"}, ] [[package]] @@ -69,6 +75,20 @@ typing-extensions = {version = ">=4.6.0", markers = "python_version < \"3.13\""} [package.extras] test = ["pytest (>=6)"] +[[package]] +name = "execnet" +version = "2.1.2" +description = "execnet: rapid multi-Python deployment" +optional = false +python-versions = ">=3.8" +files = [ + {file = "execnet-2.1.2-py3-none-any.whl", hash = "sha256:67fba928dd5a544b783f6056f449e5e3931a5c378b128bc18501f7ea79e296ec"}, + {file = "execnet-2.1.2.tar.gz", hash = "sha256:63d83bfdd9a23e35b9c6a3261412324f964c2ec8dcd8d3c6916ee9373e0befcd"}, +] + +[package.extras] +testing = ["hatch", "pre-commit", "pytest", "tox"] + [[package]] name = "h11" version = "0.16.0" @@ -127,17 +147,17 @@ zstd = ["zstandard (>=0.18.0)"] [[package]] name = "idna" -version = "3.11" +version = "3.15" description = "Internationalized Domain Names in Applications (IDNA)" optional = false python-versions = ">=3.8" files = [ - {file = "idna-3.11-py3-none-any.whl", hash = "sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea"}, - {file = "idna-3.11.tar.gz", hash = "sha256:795dafcc9c04ed0c1fb032c2aa73654d8e8c5023a7df64a53f39190ada629902"}, + {file = "idna-3.15-py3-none-any.whl", hash = "sha256:048adeaf8c2d788c40fee287673ccaa74c24ffd8dcf09ffa555a2fbb59f10ac8"}, + {file = "idna-3.15.tar.gz", hash = "sha256:ca962446ea538f7092a95e057da437618e886f4d349216d2b1e294abfdb65fdc"}, ] [package.extras] -all = ["flake8 (>=7.1.1)", "mypy (>=1.11.2)", "pytest (>=8.3.2)", "ruff (>=0.6.2)"] +all = ["mypy (>=1.11.2)", "pytest (>=8.3.2)", "ruff (>=0.6.2)"] [[package]] name = "iniconfig" @@ -216,46 +236,45 @@ files = [ [[package]] name = "packaging" -version = "26.0" +version = "26.2" description = "Core utilities for Python packages" optional = false python-versions = ">=3.8" files = [ - {file = "packaging-26.0-py3-none-any.whl", hash = "sha256:b36f1fef9334a5588b4166f8bcd26a14e521f2b55e6b9de3aaa80d3ff7a37529"}, - {file = "packaging-26.0.tar.gz", hash = "sha256:00243ae351a257117b6a241061796684b084ed1c516a08c48a3f7e147a9d80b4"}, + {file = "packaging-26.2-py3-none-any.whl", hash = "sha256:5fc45236b9446107ff2415ce77c807cee2862cb6fac22b8a73826d0693b0980e"}, + {file = "packaging-26.2.tar.gz", hash = "sha256:ff452ff5a3e828ce110190feff1178bb1f2ea2281fa2075aadb987c2fb221661"}, ] [[package]] name = "pluggy" -version = "1.6.0" +version = "1.5.0" description = "plugin and hook calling mechanisms for python" optional = false -python-versions = ">=3.9" +python-versions = ">=3.8" files = [ - {file = "pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746"}, - {file = "pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3"}, + {file = "pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669"}, + {file = "pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1"}, ] [package.extras] dev = ["pre-commit", "tox"] -testing = ["coverage", "pytest", "pytest-benchmark"] +testing = ["pytest", "pytest-benchmark"] [[package]] name = "pydantic" -version = "2.12.5" +version = "2.10.6" description = "Data validation using Python type hints" optional = false -python-versions = ">=3.9" +python-versions = ">=3.8" files = [ - {file = "pydantic-2.12.5-py3-none-any.whl", hash = "sha256:e561593fccf61e8a20fc46dfc2dfe075b8be7d0188df33f221ad1f0139180f9d"}, - {file = "pydantic-2.12.5.tar.gz", hash = "sha256:4d351024c75c0f085a9febbb665ce8c0c6ec5d30e903bdb6394b7ede26aebb49"}, + {file = "pydantic-2.10.6-py3-none-any.whl", hash = "sha256:427d664bf0b8a2b34ff5dd0f5a18df00591adcee7198fbd71981054cef37b584"}, + {file = "pydantic-2.10.6.tar.gz", hash = "sha256:ca5daa827cce33de7a42be142548b0096bf05a7e7b365aebfa5f8eeec7128236"}, ] [package.dependencies] annotated-types = ">=0.6.0" -pydantic-core = "2.41.5" -typing-extensions = ">=4.14.1" -typing-inspection = ">=0.4.2" +pydantic-core = "2.27.2" +typing-extensions = ">=4.12.2" [package.extras] email = ["email-validator (>=2.0.0)"] @@ -263,136 +282,115 @@ timezone = ["tzdata"] [[package]] name = "pydantic-core" -version = "2.41.5" +version = "2.27.2" description = "Core functionality for Pydantic validation and serialization" optional = false -python-versions = ">=3.9" +python-versions = ">=3.8" files = [ - {file = "pydantic_core-2.41.5-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:77b63866ca88d804225eaa4af3e664c5faf3568cea95360d21f4725ab6e07146"}, - {file = "pydantic_core-2.41.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:dfa8a0c812ac681395907e71e1274819dec685fec28273a28905df579ef137e2"}, - {file = "pydantic_core-2.41.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5921a4d3ca3aee735d9fd163808f5e8dd6c6972101e4adbda9a4667908849b97"}, - {file = "pydantic_core-2.41.5-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e25c479382d26a2a41b7ebea1043564a937db462816ea07afa8a44c0866d52f9"}, - {file = "pydantic_core-2.41.5-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f547144f2966e1e16ae626d8ce72b4cfa0caedc7fa28052001c94fb2fcaa1c52"}, - {file = "pydantic_core-2.41.5-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6f52298fbd394f9ed112d56f3d11aabd0d5bd27beb3084cc3d8ad069483b8941"}, - {file = "pydantic_core-2.41.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:100baa204bb412b74fe285fb0f3a385256dad1d1879f0a5cb1499ed2e83d132a"}, - {file = "pydantic_core-2.41.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:05a2c8852530ad2812cb7914dc61a1125dc4e06252ee98e5638a12da6cc6fb6c"}, - {file = "pydantic_core-2.41.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:29452c56df2ed968d18d7e21f4ab0ac55e71dc59524872f6fc57dcf4a3249ed2"}, - {file = "pydantic_core-2.41.5-cp310-cp310-musllinux_1_1_armv7l.whl", hash = "sha256:d5160812ea7a8a2ffbe233d8da666880cad0cbaf5d4de74ae15c313213d62556"}, - {file = "pydantic_core-2.41.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:df3959765b553b9440adfd3c795617c352154e497a4eaf3752555cfb5da8fc49"}, - {file = "pydantic_core-2.41.5-cp310-cp310-win32.whl", hash = "sha256:1f8d33a7f4d5a7889e60dc39856d76d09333d8a6ed0f5f1190635cbec70ec4ba"}, - {file = "pydantic_core-2.41.5-cp310-cp310-win_amd64.whl", hash = "sha256:62de39db01b8d593e45871af2af9e497295db8d73b085f6bfd0b18c83c70a8f9"}, - {file = "pydantic_core-2.41.5-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:a3a52f6156e73e7ccb0f8cced536adccb7042be67cb45f9562e12b319c119da6"}, - {file = "pydantic_core-2.41.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7f3bf998340c6d4b0c9a2f02d6a400e51f123b59565d74dc60d252ce888c260b"}, - {file = "pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:378bec5c66998815d224c9ca994f1e14c0c21cb95d2f52b6021cc0b2a58f2a5a"}, - {file = "pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e7b576130c69225432866fe2f4a469a85a54ade141d96fd396dffcf607b558f8"}, - {file = "pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6cb58b9c66f7e4179a2d5e0f849c48eff5c1fca560994d6eb6543abf955a149e"}, - {file = "pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:88942d3a3dff3afc8288c21e565e476fc278902ae4d6d134f1eeda118cc830b1"}, - {file = "pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f31d95a179f8d64d90f6831d71fa93290893a33148d890ba15de25642c5d075b"}, - {file = "pydantic_core-2.41.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c1df3d34aced70add6f867a8cf413e299177e0c22660cc767218373d0779487b"}, - {file = "pydantic_core-2.41.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:4009935984bd36bd2c774e13f9a09563ce8de4abaa7226f5108262fa3e637284"}, - {file = "pydantic_core-2.41.5-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:34a64bc3441dc1213096a20fe27e8e128bd3ff89921706e83c0b1ac971276594"}, - {file = "pydantic_core-2.41.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:c9e19dd6e28fdcaa5a1de679aec4141f691023916427ef9bae8584f9c2fb3b0e"}, - {file = "pydantic_core-2.41.5-cp311-cp311-win32.whl", hash = "sha256:2c010c6ded393148374c0f6f0bf89d206bf3217f201faa0635dcd56bd1520f6b"}, - {file = "pydantic_core-2.41.5-cp311-cp311-win_amd64.whl", hash = "sha256:76ee27c6e9c7f16f47db7a94157112a2f3a00e958bc626e2f4ee8bec5c328fbe"}, - {file = "pydantic_core-2.41.5-cp311-cp311-win_arm64.whl", hash = "sha256:4bc36bbc0b7584de96561184ad7f012478987882ebf9f9c389b23f432ea3d90f"}, - {file = "pydantic_core-2.41.5-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:f41a7489d32336dbf2199c8c0a215390a751c5b014c2c1c5366e817202e9cdf7"}, - {file = "pydantic_core-2.41.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:070259a8818988b9a84a449a2a7337c7f430a22acc0859c6b110aa7212a6d9c0"}, - {file = "pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e96cea19e34778f8d59fe40775a7a574d95816eb150850a85a7a4c8f4b94ac69"}, - {file = "pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ed2e99c456e3fadd05c991f8f437ef902e00eedf34320ba2b0842bd1c3ca3a75"}, - {file = "pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:65840751b72fbfd82c3c640cff9284545342a4f1eb1586ad0636955b261b0b05"}, - {file = "pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e536c98a7626a98feb2d3eaf75944ef6f3dbee447e1f841eae16f2f0a72d8ddc"}, - {file = "pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eceb81a8d74f9267ef4081e246ffd6d129da5d87e37a77c9bde550cb04870c1c"}, - {file = "pydantic_core-2.41.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d38548150c39b74aeeb0ce8ee1d8e82696f4a4e16ddc6de7b1d8823f7de4b9b5"}, - {file = "pydantic_core-2.41.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:c23e27686783f60290e36827f9c626e63154b82b116d7fe9adba1fda36da706c"}, - {file = "pydantic_core-2.41.5-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:482c982f814460eabe1d3bb0adfdc583387bd4691ef00b90575ca0d2b6fe2294"}, - {file = "pydantic_core-2.41.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:bfea2a5f0b4d8d43adf9d7b8bf019fb46fdd10a2e5cde477fbcb9d1fa08c68e1"}, - {file = "pydantic_core-2.41.5-cp312-cp312-win32.whl", hash = "sha256:b74557b16e390ec12dca509bce9264c3bbd128f8a2c376eaa68003d7f327276d"}, - {file = "pydantic_core-2.41.5-cp312-cp312-win_amd64.whl", hash = "sha256:1962293292865bca8e54702b08a4f26da73adc83dd1fcf26fbc875b35d81c815"}, - {file = "pydantic_core-2.41.5-cp312-cp312-win_arm64.whl", hash = "sha256:1746d4a3d9a794cacae06a5eaaccb4b8643a131d45fbc9af23e353dc0a5ba5c3"}, - {file = "pydantic_core-2.41.5-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:941103c9be18ac8daf7b7adca8228f8ed6bb7a1849020f643b3a14d15b1924d9"}, - {file = "pydantic_core-2.41.5-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:112e305c3314f40c93998e567879e887a3160bb8689ef3d2c04b6cc62c33ac34"}, - {file = "pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0cbaad15cb0c90aa221d43c00e77bb33c93e8d36e0bf74760cd00e732d10a6a0"}, - {file = "pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:03ca43e12fab6023fc79d28ca6b39b05f794ad08ec2feccc59a339b02f2b3d33"}, - {file = "pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dc799088c08fa04e43144b164feb0c13f9a0bc40503f8df3e9fde58a3c0c101e"}, - {file = "pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:97aeba56665b4c3235a0e52b2c2f5ae9cd071b8a8310ad27bddb3f7fb30e9aa2"}, - {file = "pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:406bf18d345822d6c21366031003612b9c77b3e29ffdb0f612367352aab7d586"}, - {file = "pydantic_core-2.41.5-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b93590ae81f7010dbe380cdeab6f515902ebcbefe0b9327cc4804d74e93ae69d"}, - {file = "pydantic_core-2.41.5-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:01a3d0ab748ee531f4ea6c3e48ad9dac84ddba4b0d82291f87248f2f9de8d740"}, - {file = "pydantic_core-2.41.5-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:6561e94ba9dacc9c61bce40e2d6bdc3bfaa0259d3ff36ace3b1e6901936d2e3e"}, - {file = "pydantic_core-2.41.5-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:915c3d10f81bec3a74fbd4faebe8391013ba61e5a1a8d48c4455b923bdda7858"}, - {file = "pydantic_core-2.41.5-cp313-cp313-win32.whl", hash = "sha256:650ae77860b45cfa6e2cdafc42618ceafab3a2d9a3811fcfbd3bbf8ac3c40d36"}, - {file = "pydantic_core-2.41.5-cp313-cp313-win_amd64.whl", hash = "sha256:79ec52ec461e99e13791ec6508c722742ad745571f234ea6255bed38c6480f11"}, - {file = "pydantic_core-2.41.5-cp313-cp313-win_arm64.whl", hash = "sha256:3f84d5c1b4ab906093bdc1ff10484838aca54ef08de4afa9de0f5f14d69639cd"}, - {file = "pydantic_core-2.41.5-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:3f37a19d7ebcdd20b96485056ba9e8b304e27d9904d233d7b1015db320e51f0a"}, - {file = "pydantic_core-2.41.5-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:1d1d9764366c73f996edd17abb6d9d7649a7eb690006ab6adbda117717099b14"}, - {file = "pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:25e1c2af0fce638d5f1988b686f3b3ea8cd7de5f244ca147c777769e798a9cd1"}, - {file = "pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:506d766a8727beef16b7adaeb8ee6217c64fc813646b424d0804d67c16eddb66"}, - {file = "pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4819fa52133c9aa3c387b3328f25c1facc356491e6135b459f1de698ff64d869"}, - {file = "pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2b761d210c9ea91feda40d25b4efe82a1707da2ef62901466a42492c028553a2"}, - {file = "pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:22f0fb8c1c583a3b6f24df2470833b40207e907b90c928cc8d3594b76f874375"}, - {file = "pydantic_core-2.41.5-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2782c870e99878c634505236d81e5443092fba820f0373997ff75f90f68cd553"}, - {file = "pydantic_core-2.41.5-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:0177272f88ab8312479336e1d777f6b124537d47f2123f89cb37e0accea97f90"}, - {file = "pydantic_core-2.41.5-cp314-cp314-musllinux_1_1_armv7l.whl", hash = "sha256:63510af5e38f8955b8ee5687740d6ebf7c2a0886d15a6d65c32814613681bc07"}, - {file = "pydantic_core-2.41.5-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:e56ba91f47764cc14f1daacd723e3e82d1a89d783f0f5afe9c364b8bb491ccdb"}, - {file = "pydantic_core-2.41.5-cp314-cp314-win32.whl", hash = "sha256:aec5cf2fd867b4ff45b9959f8b20ea3993fc93e63c7363fe6851424c8a7e7c23"}, - {file = "pydantic_core-2.41.5-cp314-cp314-win_amd64.whl", hash = "sha256:8e7c86f27c585ef37c35e56a96363ab8de4e549a95512445b85c96d3e2f7c1bf"}, - {file = "pydantic_core-2.41.5-cp314-cp314-win_arm64.whl", hash = "sha256:e672ba74fbc2dc8eea59fb6d4aed6845e6905fc2a8afe93175d94a83ba2a01a0"}, - {file = "pydantic_core-2.41.5-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:8566def80554c3faa0e65ac30ab0932b9e3a5cd7f8323764303d468e5c37595a"}, - {file = "pydantic_core-2.41.5-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:b80aa5095cd3109962a298ce14110ae16b8c1aece8b72f9dafe81cf597ad80b3"}, - {file = "pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3006c3dd9ba34b0c094c544c6006cc79e87d8612999f1a5d43b769b89181f23c"}, - {file = "pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:72f6c8b11857a856bcfa48c86f5368439f74453563f951e473514579d44aa612"}, - {file = "pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5cb1b2f9742240e4bb26b652a5aeb840aa4b417c7748b6f8387927bc6e45e40d"}, - {file = "pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bd3d54f38609ff308209bd43acea66061494157703364ae40c951f83ba99a1a9"}, - {file = "pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2ff4321e56e879ee8d2a879501c8e469414d948f4aba74a2d4593184eb326660"}, - {file = "pydantic_core-2.41.5-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d0d2568a8c11bf8225044aa94409e21da0cb09dcdafe9ecd10250b2baad531a9"}, - {file = "pydantic_core-2.41.5-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:a39455728aabd58ceabb03c90e12f71fd30fa69615760a075b9fec596456ccc3"}, - {file = "pydantic_core-2.41.5-cp314-cp314t-musllinux_1_1_armv7l.whl", hash = "sha256:239edca560d05757817c13dc17c50766136d21f7cd0fac50295499ae24f90fdf"}, - {file = "pydantic_core-2.41.5-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:2a5e06546e19f24c6a96a129142a75cee553cc018ffee48a460059b1185f4470"}, - {file = "pydantic_core-2.41.5-cp314-cp314t-win32.whl", hash = "sha256:b4ececa40ac28afa90871c2cc2b9ffd2ff0bf749380fbdf57d165fd23da353aa"}, - {file = "pydantic_core-2.41.5-cp314-cp314t-win_amd64.whl", hash = "sha256:80aa89cad80b32a912a65332f64a4450ed00966111b6615ca6816153d3585a8c"}, - {file = "pydantic_core-2.41.5-cp314-cp314t-win_arm64.whl", hash = "sha256:35b44f37a3199f771c3eaa53051bc8a70cd7b54f333531c59e29fd4db5d15008"}, - {file = "pydantic_core-2.41.5-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:8bfeaf8735be79f225f3fefab7f941c712aaca36f1128c9d7e2352ee1aa87bdf"}, - {file = "pydantic_core-2.41.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:346285d28e4c8017da95144c7f3acd42740d637ff41946af5ce6e5e420502dd5"}, - {file = "pydantic_core-2.41.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a75dafbf87d6276ddc5b2bf6fae5254e3d0876b626eb24969a574fff9149ee5d"}, - {file = "pydantic_core-2.41.5-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7b93a4d08587e2b7e7882de461e82b6ed76d9026ce91ca7915e740ecc7855f60"}, - {file = "pydantic_core-2.41.5-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e8465ab91a4bd96d36dde3263f06caa6a8a6019e4113f24dc753d79a8b3a3f82"}, - {file = "pydantic_core-2.41.5-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:299e0a22e7ae2b85c1a57f104538b2656e8ab1873511fd718a1c1c6f149b77b5"}, - {file = "pydantic_core-2.41.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:707625ef0983fcfb461acfaf14de2067c5942c6bb0f3b4c99158bed6fedd3cf3"}, - {file = "pydantic_core-2.41.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f41eb9797986d6ebac5e8edff36d5cef9de40def462311b3eb3eeded1431e425"}, - {file = "pydantic_core-2.41.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0384e2e1021894b1ff5a786dbf94771e2986ebe2869533874d7e43bc79c6f504"}, - {file = "pydantic_core-2.41.5-cp39-cp39-musllinux_1_1_armv7l.whl", hash = "sha256:f0cd744688278965817fd0839c4a4116add48d23890d468bc436f78beb28abf5"}, - {file = "pydantic_core-2.41.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:753e230374206729bf0a807954bcc6c150d3743928a73faffee51ac6557a03c3"}, - {file = "pydantic_core-2.41.5-cp39-cp39-win32.whl", hash = "sha256:873e0d5b4fb9b89ef7c2d2a963ea7d02879d9da0da8d9d4933dee8ee86a8b460"}, - {file = "pydantic_core-2.41.5-cp39-cp39-win_amd64.whl", hash = "sha256:e4f4a984405e91527a0d62649ee21138f8e3d0ef103be488c1dc11a80d7f184b"}, - {file = "pydantic_core-2.41.5-graalpy311-graalpy242_311_native-macosx_10_12_x86_64.whl", hash = "sha256:b96d5f26b05d03cc60f11a7761a5ded1741da411e7fe0909e27a5e6a0cb7b034"}, - {file = "pydantic_core-2.41.5-graalpy311-graalpy242_311_native-macosx_11_0_arm64.whl", hash = "sha256:634e8609e89ceecea15e2d61bc9ac3718caaaa71963717bf3c8f38bfde64242c"}, - {file = "pydantic_core-2.41.5-graalpy311-graalpy242_311_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:93e8740d7503eb008aa2df04d3b9735f845d43ae845e6dcd2be0b55a2da43cd2"}, - {file = "pydantic_core-2.41.5-graalpy311-graalpy242_311_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f15489ba13d61f670dcc96772e733aad1a6f9c429cc27574c6cdaed82d0146ad"}, - {file = "pydantic_core-2.41.5-graalpy312-graalpy250_312_native-macosx_10_12_x86_64.whl", hash = "sha256:7da7087d756b19037bc2c06edc6c170eeef3c3bafcb8f532ff17d64dc427adfd"}, - {file = "pydantic_core-2.41.5-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:aabf5777b5c8ca26f7824cb4a120a740c9588ed58df9b2d196ce92fba42ff8dc"}, - {file = "pydantic_core-2.41.5-graalpy312-graalpy250_312_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c007fe8a43d43b3969e8469004e9845944f1a80e6acd47c150856bb87f230c56"}, - {file = "pydantic_core-2.41.5-graalpy312-graalpy250_312_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:76d0819de158cd855d1cbb8fcafdf6f5cf1eb8e470abe056d5d161106e38062b"}, - {file = "pydantic_core-2.41.5-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:b5819cd790dbf0c5eb9f82c73c16b39a65dd6dd4d1439dcdea7816ec9adddab8"}, - {file = "pydantic_core-2.41.5-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:5a4e67afbc95fa5c34cf27d9089bca7fcab4e51e57278d710320a70b956d1b9a"}, - {file = "pydantic_core-2.41.5-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ece5c59f0ce7d001e017643d8d24da587ea1f74f6993467d85ae8a5ef9d4f42b"}, - {file = "pydantic_core-2.41.5-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:16f80f7abe3351f8ea6858914ddc8c77e02578544a0ebc15b4c2e1a0e813b0b2"}, - {file = "pydantic_core-2.41.5-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:33cb885e759a705b426baada1fe68cbb0a2e68e34c5d0d0289a364cf01709093"}, - {file = "pydantic_core-2.41.5-pp310-pypy310_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:c8d8b4eb992936023be7dee581270af5c6e0697a8559895f527f5b7105ecd36a"}, - {file = "pydantic_core-2.41.5-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:242a206cd0318f95cd21bdacff3fcc3aab23e79bba5cac3db5a841c9ef9c6963"}, - {file = "pydantic_core-2.41.5-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:d3a978c4f57a597908b7e697229d996d77a6d3c94901e9edee593adada95ce1a"}, - {file = "pydantic_core-2.41.5-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:b2379fa7ed44ddecb5bfe4e48577d752db9fc10be00a6b7446e9663ba143de26"}, - {file = "pydantic_core-2.41.5-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:266fb4cbf5e3cbd0b53669a6d1b039c45e3ce651fd5442eff4d07c2cc8d66808"}, - {file = "pydantic_core-2.41.5-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:58133647260ea01e4d0500089a8c4f07bd7aa6ce109682b1426394988d8aaacc"}, - {file = "pydantic_core-2.41.5-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:287dad91cfb551c363dc62899a80e9e14da1f0e2b6ebde82c806612ca2a13ef1"}, - {file = "pydantic_core-2.41.5-pp311-pypy311_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:03b77d184b9eb40240ae9fd676ca364ce1085f203e1b1256f8ab9984dca80a84"}, - {file = "pydantic_core-2.41.5-pp311-pypy311_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:a668ce24de96165bb239160b3d854943128f4334822900534f2fe947930e5770"}, - {file = "pydantic_core-2.41.5-pp311-pypy311_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:f14f8f046c14563f8eb3f45f499cc658ab8d10072961e07225e507adb700e93f"}, - {file = "pydantic_core-2.41.5-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:56121965f7a4dc965bff783d70b907ddf3d57f6eba29b6d2e5dabfaf07799c51"}, - {file = "pydantic_core-2.41.5.tar.gz", hash = "sha256:08daa51ea16ad373ffd5e7606252cc32f07bc72b28284b6bc9c6df804816476e"}, + {file = "pydantic_core-2.27.2-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:2d367ca20b2f14095a8f4fa1210f5a7b78b8a20009ecced6b12818f455b1e9fa"}, + {file = "pydantic_core-2.27.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:491a2b73db93fab69731eaee494f320faa4e093dbed776be1a829c2eb222c34c"}, + {file = "pydantic_core-2.27.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7969e133a6f183be60e9f6f56bfae753585680f3b7307a8e555a948d443cc05a"}, + {file = "pydantic_core-2.27.2-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3de9961f2a346257caf0aa508a4da705467f53778e9ef6fe744c038119737ef5"}, + {file = "pydantic_core-2.27.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e2bb4d3e5873c37bb3dd58714d4cd0b0e6238cebc4177ac8fe878f8b3aa8e74c"}, + {file = "pydantic_core-2.27.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:280d219beebb0752699480fe8f1dc61ab6615c2046d76b7ab7ee38858de0a4e7"}, + {file = "pydantic_core-2.27.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47956ae78b6422cbd46f772f1746799cbb862de838fd8d1fbd34a82e05b0983a"}, + {file = "pydantic_core-2.27.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:14d4a5c49d2f009d62a2a7140d3064f686d17a5d1a268bc641954ba181880236"}, + {file = "pydantic_core-2.27.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:337b443af21d488716f8d0b6164de833e788aa6bd7e3a39c005febc1284f4962"}, + {file = "pydantic_core-2.27.2-cp310-cp310-musllinux_1_1_armv7l.whl", hash = "sha256:03d0f86ea3184a12f41a2d23f7ccb79cdb5a18e06993f8a45baa8dfec746f0e9"}, + {file = "pydantic_core-2.27.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:7041c36f5680c6e0f08d922aed302e98b3745d97fe1589db0a3eebf6624523af"}, + {file = "pydantic_core-2.27.2-cp310-cp310-win32.whl", hash = "sha256:50a68f3e3819077be2c98110c1f9dcb3817e93f267ba80a2c05bb4f8799e2ff4"}, + {file = "pydantic_core-2.27.2-cp310-cp310-win_amd64.whl", hash = "sha256:e0fd26b16394ead34a424eecf8a31a1f5137094cabe84a1bcb10fa6ba39d3d31"}, + {file = "pydantic_core-2.27.2-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:8e10c99ef58cfdf2a66fc15d66b16c4a04f62bca39db589ae8cba08bc55331bc"}, + {file = "pydantic_core-2.27.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:26f32e0adf166a84d0cb63be85c562ca8a6fa8de28e5f0d92250c6b7e9e2aff7"}, + {file = "pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8c19d1ea0673cd13cc2f872f6c9ab42acc4e4f492a7ca9d3795ce2b112dd7e15"}, + {file = "pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5e68c4446fe0810e959cdff46ab0a41ce2f2c86d227d96dc3847af0ba7def306"}, + {file = "pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d9640b0059ff4f14d1f37321b94061c6db164fbe49b334b31643e0528d100d99"}, + {file = "pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:40d02e7d45c9f8af700f3452f329ead92da4c5f4317ca9b896de7ce7199ea459"}, + {file = "pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1c1fd185014191700554795c99b347d64f2bb637966c4cfc16998a0ca700d048"}, + {file = "pydantic_core-2.27.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d81d2068e1c1228a565af076598f9e7451712700b673de8f502f0334f281387d"}, + {file = "pydantic_core-2.27.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:1a4207639fb02ec2dbb76227d7c751a20b1a6b4bc52850568e52260cae64ca3b"}, + {file = "pydantic_core-2.27.2-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:3de3ce3c9ddc8bbd88f6e0e304dea0e66d843ec9de1b0042b0911c1663ffd474"}, + {file = "pydantic_core-2.27.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:30c5f68ded0c36466acede341551106821043e9afaad516adfb6e8fa80a4e6a6"}, + {file = "pydantic_core-2.27.2-cp311-cp311-win32.whl", hash = "sha256:c70c26d2c99f78b125a3459f8afe1aed4d9687c24fd677c6a4436bc042e50d6c"}, + {file = "pydantic_core-2.27.2-cp311-cp311-win_amd64.whl", hash = "sha256:08e125dbdc505fa69ca7d9c499639ab6407cfa909214d500897d02afb816e7cc"}, + {file = "pydantic_core-2.27.2-cp311-cp311-win_arm64.whl", hash = "sha256:26f0d68d4b235a2bae0c3fc585c585b4ecc51382db0e3ba402a22cbc440915e4"}, + {file = "pydantic_core-2.27.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:9e0c8cfefa0ef83b4da9588448b6d8d2a2bf1a53c3f1ae5fca39eb3061e2f0b0"}, + {file = "pydantic_core-2.27.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:83097677b8e3bd7eaa6775720ec8e0405f1575015a463285a92bfdfe254529ef"}, + {file = "pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:172fce187655fece0c90d90a678424b013f8fbb0ca8b036ac266749c09438cb7"}, + {file = "pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:519f29f5213271eeeeb3093f662ba2fd512b91c5f188f3bb7b27bc5973816934"}, + {file = "pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:05e3a55d124407fffba0dd6b0c0cd056d10e983ceb4e5dbd10dda135c31071d6"}, + {file = "pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9c3ed807c7b91de05e63930188f19e921d1fe90de6b4f5cd43ee7fcc3525cb8c"}, + {file = "pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6fb4aadc0b9a0c063206846d603b92030eb6f03069151a625667f982887153e2"}, + {file = "pydantic_core-2.27.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:28ccb213807e037460326424ceb8b5245acb88f32f3d2777427476e1b32c48c4"}, + {file = "pydantic_core-2.27.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:de3cd1899e2c279b140adde9357c4495ed9d47131b4a4eaff9052f23398076b3"}, + {file = "pydantic_core-2.27.2-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:220f892729375e2d736b97d0e51466252ad84c51857d4d15f5e9692f9ef12be4"}, + {file = "pydantic_core-2.27.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a0fcd29cd6b4e74fe8ddd2c90330fd8edf2e30cb52acda47f06dd615ae72da57"}, + {file = "pydantic_core-2.27.2-cp312-cp312-win32.whl", hash = "sha256:1e2cb691ed9834cd6a8be61228471d0a503731abfb42f82458ff27be7b2186fc"}, + {file = "pydantic_core-2.27.2-cp312-cp312-win_amd64.whl", hash = "sha256:cc3f1a99a4f4f9dd1de4fe0312c114e740b5ddead65bb4102884b384c15d8bc9"}, + {file = "pydantic_core-2.27.2-cp312-cp312-win_arm64.whl", hash = "sha256:3911ac9284cd8a1792d3cb26a2da18f3ca26c6908cc434a18f730dc0db7bfa3b"}, + {file = "pydantic_core-2.27.2-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:7d14bd329640e63852364c306f4d23eb744e0f8193148d4044dd3dacdaacbd8b"}, + {file = "pydantic_core-2.27.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:82f91663004eb8ed30ff478d77c4d1179b3563df6cdb15c0817cd1cdaf34d154"}, + {file = "pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:71b24c7d61131bb83df10cc7e687433609963a944ccf45190cfc21e0887b08c9"}, + {file = "pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fa8e459d4954f608fa26116118bb67f56b93b209c39b008277ace29937453dc9"}, + {file = "pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ce8918cbebc8da707ba805b7fd0b382816858728ae7fe19a942080c24e5b7cd1"}, + {file = "pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:eda3f5c2a021bbc5d976107bb302e0131351c2ba54343f8a496dc8783d3d3a6a"}, + {file = "pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bd8086fa684c4775c27f03f062cbb9eaa6e17f064307e86b21b9e0abc9c0f02e"}, + {file = "pydantic_core-2.27.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:8d9b3388db186ba0c099a6d20f0604a44eabdeef1777ddd94786cdae158729e4"}, + {file = "pydantic_core-2.27.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:7a66efda2387de898c8f38c0cf7f14fca0b51a8ef0b24bfea5849f1b3c95af27"}, + {file = "pydantic_core-2.27.2-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:18a101c168e4e092ab40dbc2503bdc0f62010e95d292b27827871dc85450d7ee"}, + {file = "pydantic_core-2.27.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:ba5dd002f88b78a4215ed2f8ddbdf85e8513382820ba15ad5ad8955ce0ca19a1"}, + {file = "pydantic_core-2.27.2-cp313-cp313-win32.whl", hash = "sha256:1ebaf1d0481914d004a573394f4be3a7616334be70261007e47c2a6fe7e50130"}, + {file = "pydantic_core-2.27.2-cp313-cp313-win_amd64.whl", hash = "sha256:953101387ecf2f5652883208769a79e48db18c6df442568a0b5ccd8c2723abee"}, + {file = "pydantic_core-2.27.2-cp313-cp313-win_arm64.whl", hash = "sha256:ac4dbfd1691affb8f48c2c13241a2e3b60ff23247cbcf981759c768b6633cf8b"}, + {file = "pydantic_core-2.27.2-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:d3e8d504bdd3f10835468f29008d72fc8359d95c9c415ce6e767203db6127506"}, + {file = "pydantic_core-2.27.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:521eb9b7f036c9b6187f0b47318ab0d7ca14bd87f776240b90b21c1f4f149320"}, + {file = "pydantic_core-2.27.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:85210c4d99a0114f5a9481b44560d7d1e35e32cc5634c656bc48e590b669b145"}, + {file = "pydantic_core-2.27.2-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d716e2e30c6f140d7560ef1538953a5cd1a87264c737643d481f2779fc247fe1"}, + {file = "pydantic_core-2.27.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f66d89ba397d92f840f8654756196d93804278457b5fbede59598a1f9f90b228"}, + {file = "pydantic_core-2.27.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:669e193c1c576a58f132e3158f9dfa9662969edb1a250c54d8fa52590045f046"}, + {file = "pydantic_core-2.27.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9fdbe7629b996647b99c01b37f11170a57ae675375b14b8c13b8518b8320ced5"}, + {file = "pydantic_core-2.27.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d262606bf386a5ba0b0af3b97f37c83d7011439e3dc1a9298f21efb292e42f1a"}, + {file = "pydantic_core-2.27.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:cabb9bcb7e0d97f74df8646f34fc76fbf793b7f6dc2438517d7a9e50eee4f14d"}, + {file = "pydantic_core-2.27.2-cp38-cp38-musllinux_1_1_armv7l.whl", hash = "sha256:d2d63f1215638d28221f664596b1ccb3944f6e25dd18cd3b86b0a4c408d5ebb9"}, + {file = "pydantic_core-2.27.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:bca101c00bff0adb45a833f8451b9105d9df18accb8743b08107d7ada14bd7da"}, + {file = "pydantic_core-2.27.2-cp38-cp38-win32.whl", hash = "sha256:f6f8e111843bbb0dee4cb6594cdc73e79b3329b526037ec242a3e49012495b3b"}, + {file = "pydantic_core-2.27.2-cp38-cp38-win_amd64.whl", hash = "sha256:fd1aea04935a508f62e0d0ef1f5ae968774a32afc306fb8545e06f5ff5cdf3ad"}, + {file = "pydantic_core-2.27.2-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:c10eb4f1659290b523af58fa7cffb452a61ad6ae5613404519aee4bfbf1df993"}, + {file = "pydantic_core-2.27.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ef592d4bad47296fb11f96cd7dc898b92e795032b4894dfb4076cfccd43a9308"}, + {file = "pydantic_core-2.27.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c61709a844acc6bf0b7dce7daae75195a10aac96a596ea1b776996414791ede4"}, + {file = "pydantic_core-2.27.2-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:42c5f762659e47fdb7b16956c71598292f60a03aa92f8b6351504359dbdba6cf"}, + {file = "pydantic_core-2.27.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4c9775e339e42e79ec99c441d9730fccf07414af63eac2f0e48e08fd38a64d76"}, + {file = "pydantic_core-2.27.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:57762139821c31847cfb2df63c12f725788bd9f04bc2fb392790959b8f70f118"}, + {file = "pydantic_core-2.27.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0d1e85068e818c73e048fe28cfc769040bb1f475524f4745a5dc621f75ac7630"}, + {file = "pydantic_core-2.27.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:097830ed52fd9e427942ff3b9bc17fab52913b2f50f2880dc4a5611446606a54"}, + {file = "pydantic_core-2.27.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:044a50963a614ecfae59bb1eaf7ea7efc4bc62f49ed594e18fa1e5d953c40e9f"}, + {file = "pydantic_core-2.27.2-cp39-cp39-musllinux_1_1_armv7l.whl", hash = "sha256:4e0b4220ba5b40d727c7f879eac379b822eee5d8fff418e9d3381ee45b3b0362"}, + {file = "pydantic_core-2.27.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5e4f4bb20d75e9325cc9696c6802657b58bc1dbbe3022f32cc2b2b632c3fbb96"}, + {file = "pydantic_core-2.27.2-cp39-cp39-win32.whl", hash = "sha256:cca63613e90d001b9f2f9a9ceb276c308bfa2a43fafb75c8031c4f66039e8c6e"}, + {file = "pydantic_core-2.27.2-cp39-cp39-win_amd64.whl", hash = "sha256:77d1bca19b0f7021b3a982e6f903dcd5b2b06076def36a652e3907f596e29f67"}, + {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:2bf14caea37e91198329b828eae1618c068dfb8ef17bb33287a7ad4b61ac314e"}, + {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:b0cb791f5b45307caae8810c2023a184c74605ec3bcbb67d13846c28ff731ff8"}, + {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:688d3fd9fcb71f41c4c015c023d12a79d1c4c0732ec9eb35d96e3388a120dcf3"}, + {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3d591580c34f4d731592f0e9fe40f9cc1b430d297eecc70b962e93c5c668f15f"}, + {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:82f986faf4e644ffc189a7f1aafc86e46ef70372bb153e7001e8afccc6e54133"}, + {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:bec317a27290e2537f922639cafd54990551725fc844249e64c523301d0822fc"}, + {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:0296abcb83a797db256b773f45773da397da75a08f5fcaef41f2044adec05f50"}, + {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:0d75070718e369e452075a6017fbf187f788e17ed67a3abd47fa934d001863d9"}, + {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:7e17b560be3c98a8e3aa66ce828bdebb9e9ac6ad5466fba92eb74c4c95cb1151"}, + {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:c33939a82924da9ed65dab5a65d427205a73181d8098e79b6b426bdf8ad4e656"}, + {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:00bad2484fa6bda1e216e7345a798bd37c68fb2d97558edd584942aa41b7d278"}, + {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c817e2b40aba42bac6f457498dacabc568c3b7a986fc9ba7c8d9d260b71485fb"}, + {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:251136cdad0cb722e93732cb45ca5299fb56e1344a833640bf93b2803f8d1bfd"}, + {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d2088237af596f0a524d3afc39ab3b036e8adb054ee57cbb1dcf8e09da5b29cc"}, + {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:d4041c0b966a84b4ae7a09832eb691a35aec90910cd2dbe7a208de59be77965b"}, + {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:8083d4e875ebe0b864ffef72a4304827015cff328a1be6e22cc850753bfb122b"}, + {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:f141ee28a0ad2123b6611b6ceff018039df17f32ada8b534e6aa039545a3efb2"}, + {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:7d0c8399fcc1848491f00e0314bd59fb34a9c008761bcb422a057670c3f65e35"}, + {file = "pydantic_core-2.27.2.tar.gz", hash = "sha256:eb026e5a4c1fee05726072337ff51d1efb6f59090b7da90d30ea58625b1ffb39"}, ] [package.dependencies] -typing-extensions = ">=4.14.1" +typing-extensions = ">=4.6.0,<4.7.0 || >4.7.0" [[package]] name = "pytest" @@ -434,6 +432,26 @@ pytest = ">=7.0.0,<9" docs = ["sphinx (>=5.3)", "sphinx-rtd-theme (>=1.0)"] testing = ["coverage (>=6.2)", "hypothesis (>=5.7.1)"] +[[package]] +name = "pytest-xdist" +version = "3.6.1" +description = "pytest xdist plugin for distributed testing, most importantly across multiple CPUs" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pytest_xdist-3.6.1-py3-none-any.whl", hash = "sha256:9ed4adfb68a016610848639bb7e02c9352d5d9f03d04809919e2dafc3be4cca7"}, + {file = "pytest_xdist-3.6.1.tar.gz", hash = "sha256:ead156a4db231eec769737f57668ef58a2084a34b2e55c4a8fa20d861107300d"}, +] + +[package.dependencies] +execnet = ">=2.1" +pytest = ">=7.0.0" + +[package.extras] +psutil = ["psutil (>=3.0)"] +setproctitle = ["setproctitle"] +testing = ["filelock"] + [[package]] name = "python-dateutil" version = "2.9.0.post0" @@ -450,29 +468,29 @@ six = ">=1.5" [[package]] name = "ruff" -version = "0.5.7" +version = "0.11.5" description = "An extremely fast Python linter and code formatter, written in Rust." optional = false python-versions = ">=3.7" files = [ - {file = "ruff-0.5.7-py3-none-linux_armv6l.whl", hash = "sha256:548992d342fc404ee2e15a242cdbea4f8e39a52f2e7752d0e4cbe88d2d2f416a"}, - {file = "ruff-0.5.7-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:00cc8872331055ee017c4f1071a8a31ca0809ccc0657da1d154a1d2abac5c0be"}, - {file = "ruff-0.5.7-py3-none-macosx_11_0_arm64.whl", hash = "sha256:eaf3d86a1fdac1aec8a3417a63587d93f906c678bb9ed0b796da7b59c1114a1e"}, - {file = "ruff-0.5.7-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a01c34400097b06cf8a6e61b35d6d456d5bd1ae6961542de18ec81eaf33b4cb8"}, - {file = "ruff-0.5.7-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fcc8054f1a717e2213500edaddcf1dbb0abad40d98e1bd9d0ad364f75c763eea"}, - {file = "ruff-0.5.7-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7f70284e73f36558ef51602254451e50dd6cc479f8b6f8413a95fcb5db4a55fc"}, - {file = "ruff-0.5.7-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:a78ad870ae3c460394fc95437d43deb5c04b5c29297815a2a1de028903f19692"}, - {file = "ruff-0.5.7-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9ccd078c66a8e419475174bfe60a69adb36ce04f8d4e91b006f1329d5cd44bcf"}, - {file = "ruff-0.5.7-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7e31c9bad4ebf8fdb77b59cae75814440731060a09a0e0077d559a556453acbb"}, - {file = "ruff-0.5.7-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8d796327eed8e168164346b769dd9a27a70e0298d667b4ecee6877ce8095ec8e"}, - {file = "ruff-0.5.7-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:4a09ea2c3f7778cc635e7f6edf57d566a8ee8f485f3c4454db7771efb692c499"}, - {file = "ruff-0.5.7-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:a36d8dcf55b3a3bc353270d544fb170d75d2dff41eba5df57b4e0b67a95bb64e"}, - {file = "ruff-0.5.7-py3-none-musllinux_1_2_i686.whl", hash = "sha256:9369c218f789eefbd1b8d82a8cf25017b523ac47d96b2f531eba73770971c9e5"}, - {file = "ruff-0.5.7-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:b88ca3db7eb377eb24fb7c82840546fb7acef75af4a74bd36e9ceb37a890257e"}, - {file = "ruff-0.5.7-py3-none-win32.whl", hash = "sha256:33d61fc0e902198a3e55719f4be6b375b28f860b09c281e4bdbf783c0566576a"}, - {file = "ruff-0.5.7-py3-none-win_amd64.whl", hash = "sha256:083bbcbe6fadb93cd86709037acc510f86eed5a314203079df174c40bbbca6b3"}, - {file = "ruff-0.5.7-py3-none-win_arm64.whl", hash = "sha256:2dca26154ff9571995107221d0aeaad0e75a77b5a682d6236cf89a58c70b76f4"}, - {file = "ruff-0.5.7.tar.gz", hash = "sha256:8dfc0a458797f5d9fb622dd0efc52d796f23f0a1493a9527f4e49a550ae9a7e5"}, + {file = "ruff-0.11.5-py3-none-linux_armv6l.whl", hash = "sha256:2561294e108eb648e50f210671cc56aee590fb6167b594144401532138c66c7b"}, + {file = "ruff-0.11.5-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:ac12884b9e005c12d0bd121f56ccf8033e1614f736f766c118ad60780882a077"}, + {file = "ruff-0.11.5-py3-none-macosx_11_0_arm64.whl", hash = "sha256:4bfd80a6ec559a5eeb96c33f832418bf0fb96752de0539905cf7b0cc1d31d779"}, + {file = "ruff-0.11.5-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0947c0a1afa75dcb5db4b34b070ec2bccee869d40e6cc8ab25aca11a7d527794"}, + {file = "ruff-0.11.5-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ad871ff74b5ec9caa66cb725b85d4ef89b53f8170f47c3406e32ef040400b038"}, + {file = "ruff-0.11.5-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e6cf918390cfe46d240732d4d72fa6e18e528ca1f60e318a10835cf2fa3dc19f"}, + {file = "ruff-0.11.5-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:56145ee1478582f61c08f21076dc59153310d606ad663acc00ea3ab5b2125f82"}, + {file = "ruff-0.11.5-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e5f66f8f1e8c9fc594cbd66fbc5f246a8d91f916cb9667e80208663ec3728304"}, + {file = "ruff-0.11.5-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:80b4df4d335a80315ab9afc81ed1cff62be112bd165e162b5eed8ac55bfc8470"}, + {file = "ruff-0.11.5-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3068befab73620b8a0cc2431bd46b3cd619bc17d6f7695a3e1bb166b652c382a"}, + {file = "ruff-0.11.5-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:f5da2e710a9641828e09aa98b92c9ebbc60518fdf3921241326ca3e8f8e55b8b"}, + {file = "ruff-0.11.5-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:ef39f19cb8ec98cbc762344921e216f3857a06c47412030374fffd413fb8fd3a"}, + {file = "ruff-0.11.5-py3-none-musllinux_1_2_i686.whl", hash = "sha256:b2a7cedf47244f431fd11aa5a7e2806dda2e0c365873bda7834e8f7d785ae159"}, + {file = "ruff-0.11.5-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:81be52e7519f3d1a0beadcf8e974715b2dfc808ae8ec729ecfc79bddf8dbb783"}, + {file = "ruff-0.11.5-py3-none-win32.whl", hash = "sha256:e268da7b40f56e3eca571508a7e567e794f9bfcc0f412c4b607931d3af9c4afe"}, + {file = "ruff-0.11.5-py3-none-win_amd64.whl", hash = "sha256:6c6dc38af3cfe2863213ea25b6dc616d679205732dc0fb673356c2d69608f800"}, + {file = "ruff-0.11.5-py3-none-win_arm64.whl", hash = "sha256:67e241b4314f4eacf14a601d586026a962f4002a475aa702c69980a38087aa4e"}, + {file = "ruff-0.11.5.tar.gz", hash = "sha256:cae2e2439cb88853e421901ec040a758960b576126dab520fa08e9de431d1bef"}, ] [[package]] @@ -486,6 +504,17 @@ files = [ {file = "six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81"}, ] +[[package]] +name = "sniffio" +version = "1.3.1" +description = "Sniff out which async library your code is running under" +optional = false +python-versions = ">=3.7" +files = [ + {file = "sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2"}, + {file = "sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc"}, +] + [[package]] name = "tomli" version = "2.4.1" @@ -544,41 +573,27 @@ files = [ [[package]] name = "types-python-dateutil" -version = "2.9.0.20260124" +version = "2.9.0.20241206" description = "Typing stubs for python-dateutil" optional = false -python-versions = ">=3.9" +python-versions = ">=3.8" files = [ - {file = "types_python_dateutil-2.9.0.20260124-py3-none-any.whl", hash = "sha256:f802977ae08bf2260142e7ca1ab9d4403772a254409f7bbdf652229997124951"}, - {file = "types_python_dateutil-2.9.0.20260124.tar.gz", hash = "sha256:7d2db9f860820c30e5b8152bfe78dbdf795f7d1c6176057424e8b3fdd1f581af"}, + {file = "types_python_dateutil-2.9.0.20241206-py3-none-any.whl", hash = "sha256:e248a4bc70a486d3e3ec84d0dc30eec3a5f979d6e7ee4123ae043eedbb987f53"}, + {file = "types_python_dateutil-2.9.0.20241206.tar.gz", hash = "sha256:18f493414c26ffba692a72369fea7a154c502646301ebfe3d56a04b3767284cb"}, ] [[package]] name = "typing-extensions" -version = "4.15.0" -description = "Backported and Experimental Type Hints for Python 3.9+" -optional = false -python-versions = ">=3.9" -files = [ - {file = "typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548"}, - {file = "typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466"}, -] - -[[package]] -name = "typing-inspection" -version = "0.4.2" -description = "Runtime typing introspection tools" +version = "4.13.2" +description = "Backported and Experimental Type Hints for Python 3.8+" optional = false -python-versions = ">=3.9" +python-versions = ">=3.8" files = [ - {file = "typing_inspection-0.4.2-py3-none-any.whl", hash = "sha256:4ed1cacbdc298c220f1bd249ed5287caa16f34d44ef4e9c3d0cbad5b521545e7"}, - {file = "typing_inspection-0.4.2.tar.gz", hash = "sha256:ba561c48a67c5958007083d386c3295464928b01faa735ab8547c5692e87f464"}, + {file = "typing_extensions-4.13.2-py3-none-any.whl", hash = "sha256:a439e7c04b49fec3e5d3e2beaa21755cadbbdc391694e28ccdd36ca4a1408f8c"}, + {file = "typing_extensions-4.13.2.tar.gz", hash = "sha256:e6c81219bd689f51865d9e372991c540bda33a0379d5573cddb9a3a23f7caaef"}, ] -[package.dependencies] -typing-extensions = ">=4.12.0" - [metadata] lock-version = "2.0" -python-versions = ">=3.9.0,<4.0" -content-hash = "09269a6171a85bfeccb630fb8c37e9ae40a895585963be7836be46cbf293f6e9" +python-versions = "^3.8" +content-hash = "bcf31a142c86d9e556553c8c260a93b563ac64a043076dbd48b26111d422c26e" diff --git a/pyproject.toml b/pyproject.toml index 6eba05c7..b29e67c7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,15 +1,15 @@ [project] name = "zep-cloud" -version = "3.28.0" +dynamic = ["version"] +version = "4.0.0a1" [tool.poetry] name = "zep-cloud" -version = "3.28.0" +version = "4.0.0a1" description = "" readme = "README.md" authors = [] keywords = [] -packages = [{ include = "zep_cloud", from = "src" }] classifiers = [ "Intended Audience :: Developers", @@ -20,6 +20,9 @@ classifiers = [ "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", + "Programming Language :: Python :: 3.14", + "Programming Language :: Python :: 3.15", "Operating System :: OS Independent", "Operating System :: POSIX", "Operating System :: MacOS", @@ -28,25 +31,28 @@ classifiers = [ "Topic :: Software Development :: Libraries :: Python Modules", "Typing :: Typed" ] +packages = [ + { include = "zep_cloud", from = "src"} +] -[project.urls] +[tool.poetry.urls] Repository = 'https://github.com/getzep/zep-python' [tool.poetry.dependencies] -python = ">=3.9.0,<4.0" +python = "^3.8" httpx = ">=0.21.2" pydantic = ">= 1.9.2" pydantic-core = ">=2.18.2" typing_extensions = ">= 4.0.0" -python-dateutil = "^2.9.0" [tool.poetry.group.dev.dependencies] mypy = "==1.13.0" pytest = "^7.4.0" pytest-asyncio = "^0.23.5" +pytest-xdist = "^3.6.1" python-dateutil = "^2.9.0" types-python-dateutil = "^2.9.0.20240316" -ruff = "^0.5.0" +ruff = "==0.11.5" [tool.pytest.ini_options] testpaths = [ "tests" ] diff --git a/reference.md b/reference.md index d9dba041..fa34d1fd 100644 --- a/reference.md +++ b/reference.md @@ -1,6 +1,6 @@ # Reference -## UserGroup -
client.user_group.list_policy_sets(...) +## Batch +
client.batch.list(...) -> BatchPage
@@ -14,13 +14,17 @@ ```python from zep_cloud import Zep +from zep_cloud.environment import ZepEnvironment client = Zep( - api_key="YOUR_API_KEY", + api_key="", + environment=ZepEnvironment.DEFAULT, ) -client.user_group.list_policy_sets( - group_uuid="groupUUID", - project_id="projectId", + +client.batch.list( + limit=1, + cursor="cursor", + status="status", ) ``` @@ -37,7 +41,15 @@ client.user_group.list_policy_sets(
-**group_uuid:** `str` — UserGroup UUID +**limit:** `typing.Optional[int]` — Page size + +
+
+ +
+
+ +**cursor:** `typing.Optional[str]` — Opaque page cursor
@@ -45,7 +57,7 @@ client.user_group.list_policy_sets(
-**project_id:** `str` — Project UUID +**status:** `typing.Optional[str]` — Batch status filter
@@ -65,7 +77,7 @@ client.user_group.list_policy_sets(
-
client.user_group.attach_policy_set(...) +
client.batch.create(...) -> Batch
@@ -79,16 +91,15 @@ client.user_group.list_policy_sets( ```python from zep_cloud import Zep +from zep_cloud.environment import ZepEnvironment client = Zep( - api_key="YOUR_API_KEY", -) -client.user_group.attach_policy_set( - group_uuid="groupUUID", - project_id="projectId", - policy_set_uuid="policy_set_uuid", + api_key="", + environment=ZepEnvironment.DEFAULT, ) +client.batch.create() + ```
@@ -103,7 +114,7 @@ client.user_group.attach_policy_set(
-**group_uuid:** `str` — UserGroup UUID +**ignore_roles:** `typing.Optional[typing.List[str]]`
@@ -111,7 +122,7 @@ client.user_group.attach_policy_set(
-**project_id:** `str` — Project UUID +**metadata:** `typing.Optional[typing.Dict[str, typing.Any]]`
@@ -119,7 +130,7 @@ client.user_group.attach_policy_set(
-**policy_set_uuid:** `str` +**strict_ontology:** `typing.Optional[bool]`
@@ -139,7 +150,7 @@ client.user_group.attach_policy_set(
-
client.user_group.detach_policy_set(...) +
client.batch.get(...) -> Batch
@@ -153,14 +164,15 @@ client.user_group.attach_policy_set( ```python from zep_cloud import Zep +from zep_cloud.environment import ZepEnvironment client = Zep( - api_key="YOUR_API_KEY", + api_key="", + environment=ZepEnvironment.DEFAULT, ) -client.user_group.detach_policy_set( - group_uuid="groupUUID", - policy_set_uuid="policySetUUID", - project_id="projectId", + +client.batch.get( + batch_uuid="batch_uuid", ) ``` @@ -177,23 +189,7 @@ client.user_group.detach_policy_set(
-**group_uuid:** `str` — UserGroup UUID - -
-
- -
-
- -**policy_set_uuid:** `str` — Policy set UUID - -
-
- -
-
- -**project_id:** `str` — Project UUID +**batch_uuid:** `str` — Batch UUID
@@ -213,7 +209,7 @@ client.user_group.detach_policy_set(
-
client.user_group.list(...) +
client.batch.delete(...)
@@ -227,15 +223,15 @@ client.user_group.detach_policy_set( ```python from zep_cloud import Zep +from zep_cloud.environment import ZepEnvironment client = Zep( - api_key="YOUR_API_KEY", + api_key="", + environment=ZepEnvironment.DEFAULT, ) -client.user_group.list( - project_id="projectId", - page_number=1, - page_size=1, - search="search", + +client.batch.delete( + batch_uuid="batch_uuid", ) ``` @@ -252,31 +248,7 @@ client.user_group.list(
-**project_id:** `str` — Project UUID - -
-
- -
-
- -**page_number:** `int` — Page number - -
-
- -
-
- -**page_size:** `int` — Page size - -
-
- -
-
- -**search:** `typing.Optional[str]` — Name search +**batch_uuid:** `str` — Batch UUID
@@ -296,7 +268,7 @@ client.user_group.list(
-
client.user_group.create(...) +
client.batch.list_items(...) -> BatchItemPage
@@ -310,13 +282,17 @@ client.user_group.list( ```python from zep_cloud import Zep +from zep_cloud.environment import ZepEnvironment client = Zep( - api_key="YOUR_API_KEY", + api_key="", + environment=ZepEnvironment.DEFAULT, ) -client.user_group.create( - project_id="projectId", - name="name", + +client.batch.list_items( + batch_uuid="batch_uuid", + limit=1, + cursor="cursor", ) ``` @@ -333,7 +309,7 @@ client.user_group.create(
-**project_id:** `str` — Project UUID +**batch_uuid:** `str` — Batch UUID
@@ -341,7 +317,7 @@ client.user_group.create(
-**name:** `str` +**limit:** `typing.Optional[int]` — Page size
@@ -349,7 +325,7 @@ client.user_group.create(
-**description:** `typing.Optional[str]` +**cursor:** `typing.Optional[str]` — Opaque page cursor
@@ -369,7 +345,7 @@ client.user_group.create(
-
client.user_group.list_for_user(...) +
client.batch.add_items(...) -> BatchItemsResponse
@@ -383,13 +359,15 @@ client.user_group.create( ```python from zep_cloud import Zep +from zep_cloud.environment import ZepEnvironment client = Zep( - api_key="YOUR_API_KEY", + api_key="", + environment=ZepEnvironment.DEFAULT, ) -client.user_group.list_for_user( - user_uuid="userUUID", - project_id="projectId", + +client.batch.add_items( + batch_uuid="batch_uuid", ) ``` @@ -406,7 +384,7 @@ client.user_group.list_for_user(
-**user_uuid:** `str` — User UUID +**batch_uuid:** `str` — Batch UUID
@@ -414,7 +392,7 @@ client.user_group.list_for_user(
-**project_id:** `str` — Project UUID +**items:** `typing.Optional[typing.List[typing.Dict[str, typing.Any]]]`
@@ -434,7 +412,7 @@ client.user_group.list_for_user(
-
client.user_group.get(...) +
client.batch.process(...) -> ProcessBatchResult
@@ -448,13 +426,15 @@ client.user_group.list_for_user( ```python from zep_cloud import Zep +from zep_cloud.environment import ZepEnvironment client = Zep( - api_key="YOUR_API_KEY", + api_key="", + environment=ZepEnvironment.DEFAULT, ) -client.user_group.get( - group_uuid="groupUUID", - project_id="projectId", + +client.batch.process( + batch_uuid="batch_uuid", ) ``` @@ -471,15 +451,7 @@ client.user_group.get(
-**group_uuid:** `str` — UserGroup UUID - -
-
- -
-
- -**project_id:** `str` — Project UUID +**batch_uuid:** `str` — Batch UUID
@@ -499,7 +471,8 @@ client.user_group.get(
-
client.user_group.delete(...) +## Context +
client.context.create_template(...) -> ContextTemplate
@@ -513,15 +486,15 @@ client.user_group.get( ```python from zep_cloud import Zep +from zep_cloud.environment import ZepEnvironment client = Zep( - api_key="YOUR_API_KEY", -) -client.user_group.delete( - group_uuid="groupUUID", - project_id="projectId", + api_key="", + environment=ZepEnvironment.DEFAULT, ) +client.context.create_template() + ```
@@ -536,15 +509,7 @@ client.user_group.delete(
-**group_uuid:** `str` — UserGroup UUID - -
-
- -
-
- -**project_id:** `str` — Project UUID +**request:** `CreateContextTemplateRequest`
@@ -564,7 +529,7 @@ client.user_group.delete(
-
client.user_group.update(...) +
client.context.list_templates(...) -> ContextTemplatePage
@@ -578,14 +543,16 @@ client.user_group.delete( ```python from zep_cloud import Zep +from zep_cloud.environment import ZepEnvironment client = Zep( - api_key="YOUR_API_KEY", + api_key="", + environment=ZepEnvironment.DEFAULT, ) -client.user_group.update( - group_uuid="groupUUID", - project_id="projectId", - expected_version=1, + +client.context.list_templates( + limit=1, + cursor="cursor", ) ``` @@ -602,23 +569,7 @@ client.user_group.update(
-**group_uuid:** `str` — UserGroup UUID - -
-
- -
-
- -**project_id:** `str` — Project UUID - -
-
- -
-
- -**expected_version:** `int` +**limit:** `typing.Optional[int]` — Page size
@@ -626,7 +577,7 @@ client.user_group.update(
-**description:** `typing.Optional[str]` +**cursor:** `typing.Optional[str]` — Opaque page cursor
@@ -654,7 +605,7 @@ client.user_group.update(
-
client.user_group.list_members(...) +
client.context.get_template(...) -> ContextTemplate
@@ -668,16 +619,15 @@ client.user_group.update( ```python from zep_cloud import Zep +from zep_cloud.environment import ZepEnvironment client = Zep( - api_key="YOUR_API_KEY", + api_key="", + environment=ZepEnvironment.DEFAULT, ) -client.user_group.list_members( - group_uuid="groupUUID", - project_id="projectId", - page_number=1, - page_size=1, - search="search", + +client.context.get_template( + template_uuid="template_uuid", ) ``` @@ -694,7 +644,7 @@ client.user_group.list_members(
-**group_uuid:** `str` — UserGroup UUID +**template_uuid:** `str` — Template UUID
@@ -702,23 +652,58 @@ client.user_group.list_members(
-**project_id:** `str` — Project UUID +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration.
+
+
+ + + + +
+
client.context.update_template(...) -> ContextTemplate
-**page_number:** `int` — Page number - +#### 🔌 Usage + +
+
+ +
+
+ +```python +from zep_cloud import Zep +from zep_cloud.environment import ZepEnvironment + +client = Zep( + api_key="", + environment=ZepEnvironment.DEFAULT, +) + +client.context.update_template( + template_uuid="template_uuid", +) + +```
+
+
+ +#### ⚙️ Parameters
-**page_size:** `int` — Page size +
+
+ +**template_uuid:** `str` — Template UUID
@@ -726,7 +711,7 @@ client.user_group.list_members(
-**search:** `typing.Optional[str]` — User search +**request:** `CreateContextTemplateRequest`
@@ -746,7 +731,7 @@ client.user_group.list_members(
-
client.user_group.add_members(...) +
client.context.delete_template(...)
@@ -760,14 +745,15 @@ client.user_group.list_members( ```python from zep_cloud import Zep +from zep_cloud.environment import ZepEnvironment client = Zep( - api_key="YOUR_API_KEY", + api_key="", + environment=ZepEnvironment.DEFAULT, ) -client.user_group.add_members( - group_uuid="groupUUID", - project_id="projectId", - user_uuids=["user_uuids"], + +client.context.delete_template( + template_uuid="template_uuid", ) ``` @@ -784,23 +770,7 @@ client.user_group.add_members(
-**group_uuid:** `str` — UserGroup UUID - -
-
- -
-
- -**project_id:** `str` — Project UUID - -
-
- -
-
- -**user_uuids:** `typing.Sequence[str]` +**template_uuid:** `str` — Template UUID
@@ -820,7 +790,8 @@ client.user_group.add_members(
-
client.user_group.remove_members(...) +## Graph +
client.graph.create(...) -> Graph
@@ -834,16 +805,15 @@ client.user_group.add_members( ```python from zep_cloud import Zep +from zep_cloud.environment import ZepEnvironment client = Zep( - api_key="YOUR_API_KEY", -) -client.user_group.remove_members( - group_uuid="groupUUID", - project_id="projectId", - user_uuids=["user_uuids"], + api_key="", + environment=ZepEnvironment.DEFAULT, ) +client.graph.create() + ```
@@ -858,7 +828,15 @@ client.user_group.remove_members(
-**group_uuid:** `str` — UserGroup UUID +**description:** `typing.Optional[str]` + +
+
+ +
+
+ +**graph_id:** `typing.Optional[str]`
@@ -866,7 +844,7 @@ client.user_group.remove_members(
-**project_id:** `str` — Project UUID +**name:** `typing.Optional[str]`
@@ -874,7 +852,7 @@ client.user_group.remove_members(
-**user_uuids:** `typing.Sequence[str]` +**time_zone:** `typing.Optional[str]`
@@ -894,7 +872,7 @@ client.user_group.remove_members(
-
client.user_group.list_member_candidates(...) +
client.graph.list(...) -> GraphPage
@@ -908,16 +886,18 @@ client.user_group.remove_members( ```python from zep_cloud import Zep +from zep_cloud.environment import ZepEnvironment client = Zep( - api_key="YOUR_API_KEY", + api_key="", + environment=ZepEnvironment.DEFAULT, ) -client.user_group.list_member_candidates( - group_uuid="groupUUID", - project_id="projectId", - page_number=1, - page_size=1, - search="search", + +client.graph.list( + limit=1, + cursor="cursor", + order_by="order_by", + order="order", ) ``` @@ -934,7 +914,7 @@ client.user_group.list_member_candidates(
-**group_uuid:** `str` — UserGroup UUID +**limit:** `typing.Optional[int]` — Page size
@@ -942,7 +922,7 @@ client.user_group.list_member_candidates(
-**project_id:** `str` — Project UUID +**cursor:** `typing.Optional[str]` — Opaque page cursor
@@ -950,7 +930,7 @@ client.user_group.list_member_candidates(
-**page_number:** `int` — Page number +**order_by:** `typing.Optional[str]` — Sort field
@@ -958,7 +938,7 @@ client.user_group.list_member_candidates(
-**page_size:** `int` — Page size +**order:** `typing.Optional[str]` — asc or desc
@@ -966,7 +946,7 @@ client.user_group.list_member_candidates(
-**search:** `typing.Optional[str]` — User search +**search:** `typing.Optional[str]`
@@ -986,7 +966,7 @@ client.user_group.list_member_candidates(
-
client.user_group.remove_member(...) +
client.graph.lookup(...) -> Graph
@@ -1000,16 +980,15 @@ client.user_group.list_member_candidates( ```python from zep_cloud import Zep +from zep_cloud.environment import ZepEnvironment client = Zep( - api_key="YOUR_API_KEY", -) -client.user_group.remove_member( - group_uuid="groupUUID", - user_uuid="userUUID", - project_id="projectId", + api_key="", + environment=ZepEnvironment.DEFAULT, ) +client.graph.lookup() + ```
@@ -1024,23 +1003,7 @@ client.user_group.remove_member(
-**group_uuid:** `str` — UserGroup UUID - -
-
- -
-
- -**user_uuid:** `str` — User UUID - -
-
- -
-
- -**project_id:** `str` — Project UUID +**request:** `LookupRequest`
@@ -1060,25 +1023,10 @@ client.user_group.remove_member(
-## Batch -
client.batch.list(...) -
-
- -#### 📝 Description - +
client.graph.get(...) -> Graph
-
-
- -List batches for the current project, optionally filtered by batch status. -
-
-
-
- #### 🔌 Usage
@@ -1089,14 +1037,15 @@ List batches for the current project, optionally filtered by batch status. ```python from zep_cloud import Zep +from zep_cloud.environment import ZepEnvironment client = Zep( - api_key="YOUR_API_KEY", + api_key="", + environment=ZepEnvironment.DEFAULT, ) -client.batch.list( - limit=1, - cursor=1, - status="status", + +client.graph.get( + graph_uuid="graph_uuid", ) ``` @@ -1113,23 +1062,7 @@ client.batch.list(
-**limit:** `typing.Optional[int]` — Maximum number of batches to return. - -
-
- -
-
- -**cursor:** `typing.Optional[int]` — Pagination cursor from a previous response. - -
-
- -
-
- -**status:** `typing.Optional[str]` — Batch status filter. +**graph_uuid:** `str` — Graph UUID
@@ -1149,24 +1082,10 @@ client.batch.list(
-
client.batch.create(...) -
-
- -#### 📝 Description - -
-
- +
client.graph.delete(...) -> GraphDeleteResult
-Create a draft batch that can be filled with graph episodes and thread messages. -
-
-
-
- #### 🔌 Usage
@@ -1177,11 +1096,16 @@ Create a draft batch that can be filled with graph episodes and thread messages. ```python from zep_cloud import Zep +from zep_cloud.environment import ZepEnvironment client = Zep( - api_key="YOUR_API_KEY", + api_key="", + environment=ZepEnvironment.DEFAULT, +) + +client.graph.delete( + graph_uuid="graph_uuid", ) -client.batch.create() ```
@@ -1197,20 +1121,7 @@ client.batch.create()
-**ignore_roles:** `typing.Optional[typing.Sequence[RoleType]]` - -Optional list of message role types to skip during graph ingestion for -thread_message items in this batch. The messages are still stored and -retained as context, but no graph extraction is performed for them. -Has no effect on graph_episode items. - -
-
- -
-
- -**metadata:** `typing.Optional[typing.Dict[str, typing.Optional[typing.Any]]]` +**graph_uuid:** `str` — Graph UUID
@@ -1230,24 +1141,10 @@ Has no effect on graph_episode items.
-
client.batch.get(...) -
-
- -#### 📝 Description - -
-
- +
client.graph.update(...) -> Graph
-Get a batch summary, including runtime progress when the batch has been processed. -
-
-
-
- #### 🔌 Usage
@@ -1258,12 +1155,15 @@ Get a batch summary, including runtime progress when the batch has been processe ```python from zep_cloud import Zep +from zep_cloud.environment import ZepEnvironment client = Zep( - api_key="YOUR_API_KEY", + api_key="", + environment=ZepEnvironment.DEFAULT, ) -client.batch.get( - batch_id="batchId", + +client.graph.update( + graph_uuid="graph_uuid", ) ``` @@ -1280,7 +1180,7 @@ client.batch.get(
-**batch_id:** `str` — The batch ID. +**graph_uuid:** `str` — Graph UUID
@@ -1288,35 +1188,45 @@ client.batch.get(
-**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. +**description:** `typing.Optional[str]` — Omit to leave unchanged, send JSON null to clear, or send a value to set.
-
-
+
+
+**name:** `typing.Optional[str]` — Omit to leave unchanged, send JSON null to clear, or send a value to set. +
-
-
client.batch.delete(...)
-#### 📝 Description - -
-
+**time_zone:** `typing.Optional[str]` — Omit to leave unchanged, send JSON null to clear, or send a value to set. + +
+
-Delete a draft or invalid unprocessed batch. Processed batches cannot be deleted. +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+ +
+
+ +
client.graph.clone(...) -> CloneGraphResult +
+
#### 🔌 Usage @@ -1328,12 +1238,15 @@ Delete a draft or invalid unprocessed batch. Processed batches cannot be deleted ```python from zep_cloud import Zep +from zep_cloud.environment import ZepEnvironment client = Zep( - api_key="YOUR_API_KEY", + api_key="", + environment=ZepEnvironment.DEFAULT, ) -client.batch.delete( - batch_id="batchId", + +client.graph.clone( + graph_uuid="graph_uuid", ) ``` @@ -1350,7 +1263,15 @@ client.batch.delete(
-**batch_id:** `str` — The batch ID. +**graph_uuid:** `str` — Graph UUID + +
+
+ +
+
+ +**target_graph_id:** `typing.Optional[str]`
@@ -1370,24 +1291,10 @@ client.batch.delete(
-
client.batch.list_items(...) -
-
- -#### 📝 Description - -
-
- +
client.graph.get_context(...) -> GraphContextResponse
-List items in a batch, including derived runtime status when the batch has been processed. -
-
-
-
- #### 🔌 Usage
@@ -1398,15 +1305,15 @@ List items in a batch, including derived runtime status when the batch has been ```python from zep_cloud import Zep +from zep_cloud.environment import ZepEnvironment client = Zep( - api_key="YOUR_API_KEY", + api_key="", + environment=ZepEnvironment.DEFAULT, ) -client.batch.list_items( - batch_id="batchId", - limit=1, - cursor=1, - status="status", + +client.graph.get_context( + graph_uuid="graph_uuid", ) ``` @@ -1423,7 +1330,7 @@ client.batch.list_items(
-**batch_id:** `str` — The batch ID. +**graph_uuid:** `str` — Graph UUID
@@ -1431,7 +1338,7 @@ client.batch.list_items(
-**limit:** `typing.Optional[int]` — Maximum number of batch items to return. +**filters:** `typing.Optional[typing.Dict[str, typing.Any]]`
@@ -1439,7 +1346,7 @@ client.batch.list_items(
-**cursor:** `typing.Optional[int]` — Pagination cursor from a previous response. +**include_results:** `typing.Optional[bool]`
@@ -1447,7 +1354,7 @@ client.batch.list_items(
-**status:** `typing.Optional[str]` — Batch item status filter. +**max_characters:** `typing.Optional[int]`
@@ -1455,35 +1362,45 @@ client.batch.list_items(
-**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. +**query:** `typing.Optional[str]`
-
-
+
+
+**recency_bias:** `typing.Optional[str]` +
-
-
client.batch.add(...)
-#### 📝 Description - -
-
+**template_uuid:** `typing.Optional[str]` + +
+
-Add graph episodes and thread messages to a draft batch. Items are appended in request order. +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+ + +
+ +
client.graph.get_instructions(...) -> Instructions +
+
#### 🔌 Usage @@ -1494,18 +1411,16 @@ Add graph episodes and thread messages to a draft batch. Items are appended in r
```python -from zep_cloud import BatchAddItem, Zep +from zep_cloud import Zep +from zep_cloud.environment import ZepEnvironment client = Zep( - api_key="YOUR_API_KEY", + api_key="", + environment=ZepEnvironment.DEFAULT, ) -client.batch.add( - batch_id="batchId", - items=[ - BatchAddItem( - type="graph_episode", - ) - ], + +client.graph.get_instructions( + graph_uuid="graph_uuid", ) ``` @@ -1522,15 +1437,7 @@ client.batch.add(
-**batch_id:** `str` — The batch ID. - -
-
- -
-
- -**items:** `typing.Sequence[BatchAddItem]` +**graph_uuid:** `str` — Graph UUID
@@ -1550,24 +1457,10 @@ client.batch.add(
-
client.batch.process(...) -
-
- -#### 📝 Description - +
client.graph.set_instructions(...) -> Instructions
-
-
- -Start processing a filled batch. Repeated calls return a conflict. -
-
-
-
- #### 🔌 Usage
@@ -1578,12 +1471,15 @@ Start processing a filled batch. Repeated calls return a conflict. ```python from zep_cloud import Zep +from zep_cloud.environment import ZepEnvironment client = Zep( - api_key="YOUR_API_KEY", + api_key="", + environment=ZepEnvironment.DEFAULT, ) -client.batch.process( - batch_id="batchId", + +client.graph.set_instructions( + graph_uuid="graph_uuid", ) ``` @@ -1600,7 +1496,15 @@ client.batch.process(
-**batch_id:** `str` — The batch ID. +**graph_uuid:** `str` — Graph UUID + +
+
+ +
+
+ +**request:** `Instructions`
@@ -1620,25 +1524,10 @@ client.batch.process(
-## Context -
client.context.list_context_templates() -
-
- -#### 📝 Description - -
-
- +
client.graph.get_observation_steering(...) -> ObservationSteering
-Lists all context templates. -
-
-
-
- #### 🔌 Usage
@@ -1649,11 +1538,16 @@ Lists all context templates. ```python from zep_cloud import Zep +from zep_cloud.environment import ZepEnvironment client = Zep( - api_key="YOUR_API_KEY", + api_key="", + environment=ZepEnvironment.DEFAULT, +) + +client.graph.get_observation_steering( + graph_uuid="graph_uuid", ) -client.context.list_context_templates() ```
@@ -1669,6 +1563,14 @@ client.context.list_context_templates()
+**graph_uuid:** `str` — Graph UUID + +
+
+ +
+
+ **request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration.
@@ -1681,24 +1583,10 @@ client.context.list_context_templates()
-
client.context.create_context_template(...) -
-
- -#### 📝 Description - -
-
- +
client.graph.set_observation_steering(...) -> ObservationSteering
-Creates a new context template. -
-
-
-
- #### 🔌 Usage
@@ -1709,13 +1597,15 @@ Creates a new context template. ```python from zep_cloud import Zep +from zep_cloud.environment import ZepEnvironment client = Zep( - api_key="YOUR_API_KEY", + api_key="", + environment=ZepEnvironment.DEFAULT, ) -client.context.create_context_template( - template="template", - template_id="template_id", + +client.graph.set_observation_steering( + graph_uuid="graph_uuid", ) ``` @@ -1732,7 +1622,7 @@ client.context.create_context_template(
-**template:** `str` — The template content (max 1200 characters). +**graph_uuid:** `str` — Graph UUID
@@ -1740,7 +1630,7 @@ client.context.create_context_template(
-**template_id:** `str` — Unique identifier for the template (max 100 characters). +**request:** `ObservationSteering`
@@ -1760,24 +1650,10 @@ client.context.create_context_template(
-
client.context.get_context_template(...) -
-
- -#### 📝 Description - +
client.graph.get_ontology(...) -> Ontology
-
-
- -Retrieves a context template by template_id. -
-
-
-
- #### 🔌 Usage
@@ -1788,12 +1664,15 @@ Retrieves a context template by template_id. ```python from zep_cloud import Zep +from zep_cloud.environment import ZepEnvironment client = Zep( - api_key="YOUR_API_KEY", + api_key="", + environment=ZepEnvironment.DEFAULT, ) -client.context.get_context_template( - template_id="template_id", + +client.graph.get_ontology( + graph_uuid="graph_uuid", ) ``` @@ -1810,7 +1689,7 @@ client.context.get_context_template(
-**template_id:** `str` — Template ID +**graph_uuid:** `str` — Graph UUID
@@ -1830,24 +1709,10 @@ client.context.get_context_template(
-
client.context.update_context_template(...) -
-
- -#### 📝 Description - -
-
- +
client.graph.set_ontology(...) -> Ontology
-Updates an existing context template by template_id. -
-
-
-
- #### 🔌 Usage
@@ -1858,13 +1723,15 @@ Updates an existing context template by template_id. ```python from zep_cloud import Zep +from zep_cloud.environment import ZepEnvironment client = Zep( - api_key="YOUR_API_KEY", + api_key="", + environment=ZepEnvironment.DEFAULT, ) -client.context.update_context_template( - template_id="template_id", - template="template", + +client.graph.set_ontology( + graph_uuid="graph_uuid", ) ``` @@ -1881,7 +1748,7 @@ client.context.update_context_template(
-**template_id:** `str` — Template ID +**graph_uuid:** `str` — Graph UUID
@@ -1889,7 +1756,7 @@ client.context.update_context_template(
-**template:** `str` — The template content (max 1200 characters). +**request:** `Ontology`
@@ -1909,24 +1776,10 @@ client.context.update_context_template(
-
client.context.delete_context_template(...) -
-
- -#### 📝 Description - -
-
- +
client.graph.search_edges(...) -> EdgePage
-Deletes a context template by template_id. -
-
-
-
- #### 🔌 Usage
@@ -1937,12 +1790,17 @@ Deletes a context template by template_id. ```python from zep_cloud import Zep +from zep_cloud.environment import ZepEnvironment client = Zep( - api_key="YOUR_API_KEY", + api_key="", + environment=ZepEnvironment.DEFAULT, ) -client.context.delete_context_template( - template_id="template_id", + +client.graph.search_edges( + graph_uuid="graph_uuid", + limit=1, + cursor="cursor", ) ``` @@ -1959,7 +1817,7 @@ client.context.delete_context_template(
-**template_id:** `str` — Template ID +**graph_uuid:** `str` — Graph UUID
@@ -1967,37 +1825,46 @@ client.context.delete_context_template(
-**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. +**request:** `SearchRequest`
-
-
+
+
+**limit:** `typing.Optional[int]` — Page size +
-
-## Graph -
client.graph.list_custom_instructions(...)
-#### 📝 Description - -
-
+**cursor:** `typing.Optional[str]` — Opaque page cursor + +
+
-Lists all custom instructions for a project, user, or graph. +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. +
+ +
+
+
+ +
client.graph.search_episodes(...) -> EpisodePage +
+
+ #### 🔌 Usage
@@ -2008,13 +1875,17 @@ Lists all custom instructions for a project, user, or graph. ```python from zep_cloud import Zep +from zep_cloud.environment import ZepEnvironment client = Zep( - api_key="YOUR_API_KEY", + api_key="", + environment=ZepEnvironment.DEFAULT, ) -client.graph.list_custom_instructions( - user_id="user_id", - graph_id="graph_id", + +client.graph.search_episodes( + graph_uuid="graph_uuid", + limit=1, + cursor="cursor", ) ``` @@ -2031,7 +1902,7 @@ client.graph.list_custom_instructions(
-**user_id:** `typing.Optional[str]` — User ID to get user-specific instructions +**graph_uuid:** `str` — Graph UUID
@@ -2039,7 +1910,7 @@ client.graph.list_custom_instructions(
-**graph_id:** `typing.Optional[str]` — Graph ID to get graph-specific instructions +**request:** `SearchRequest`
@@ -2047,35 +1918,37 @@ client.graph.list_custom_instructions(
-**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. +**limit:** `typing.Optional[int]` — Page size
-
-
- - -
-
-
-
client.graph.add_custom_instructions(...)
-#### 📝 Description - -
-
+**cursor:** `typing.Optional[str]` — Opaque page cursor + +
+
-Adds new custom instructions for graphs without removing existing ones. If user_ids or graph_ids is empty, adds to project-wide default instructions. +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+ + +
+ +
client.graph.search_nodes(...) -> NodePage +
+
#### 🔌 Usage @@ -2086,18 +1959,18 @@ Adds new custom instructions for graphs without removing existing ones. If user_
```python -from zep_cloud import CustomInstruction, Zep +from zep_cloud import Zep +from zep_cloud.environment import ZepEnvironment client = Zep( - api_key="YOUR_API_KEY", + api_key="", + environment=ZepEnvironment.DEFAULT, ) -client.graph.add_custom_instructions( - instructions=[ - CustomInstruction( - name="name", - text="text", - ) - ], + +client.graph.search_nodes( + graph_uuid="graph_uuid", + limit=1, + cursor="cursor", ) ``` @@ -2114,7 +1987,15 @@ client.graph.add_custom_instructions(
-**instructions:** `typing.Sequence[CustomInstruction]` — Instructions to add to the graph. +**graph_uuid:** `str` — Graph UUID + +
+
+ +
+
+ +**request:** `SearchRequest`
@@ -2122,7 +2003,7 @@ client.graph.add_custom_instructions(
-**graph_ids:** `typing.Optional[typing.Sequence[str]]` — Graph IDs to add the instructions to. If empty, the instructions are added to the project-wide default. +**limit:** `typing.Optional[int]` — Page size
@@ -2130,7 +2011,7 @@ client.graph.add_custom_instructions(
-**user_ids:** `typing.Optional[typing.Sequence[str]]` — User IDs to add the instructions to. If empty, the instructions are added to the project-wide default. +**cursor:** `typing.Optional[str]` — Opaque page cursor
@@ -2150,24 +2031,10 @@ client.graph.add_custom_instructions(
-
client.graph.delete_custom_instructions(...) +
client.graph.search_observations(...) -> ObservationPage
-#### 📝 Description - -
-
- -
-
- -Deletes custom instructions for graphs or project wide defaults. -
-
-
-
- #### 🔌 Usage
@@ -2178,11 +2045,18 @@ Deletes custom instructions for graphs or project wide defaults. ```python from zep_cloud import Zep +from zep_cloud.environment import ZepEnvironment client = Zep( - api_key="YOUR_API_KEY", + api_key="", + environment=ZepEnvironment.DEFAULT, +) + +client.graph.search_observations( + graph_uuid="graph_uuid", + limit=1, + cursor="cursor", ) -client.graph.delete_custom_instructions() ```
@@ -2198,7 +2072,15 @@ client.graph.delete_custom_instructions()
-**graph_ids:** `typing.Optional[typing.Sequence[str]]` — Determines which group graphs will have their custom instructions deleted. If no graphs are provided, the project-wide custom instructions will be affected. +**graph_uuid:** `str` — Graph UUID + +
+
+ +
+
+ +**request:** `SearchRequest`
@@ -2206,7 +2088,7 @@ client.graph.delete_custom_instructions()
-**instruction_names:** `typing.Optional[typing.Sequence[str]]` — Unique identifier for the instructions to be deleted. If empty deletes all instructions. +**limit:** `typing.Optional[int]` — Page size
@@ -2214,7 +2096,7 @@ client.graph.delete_custom_instructions()
-**user_ids:** `typing.Optional[typing.Sequence[str]]` — Determines which user graphs will have their custom instructions deleted. If no users are provided, the project-wide custom instructions will be affected. +**cursor:** `typing.Optional[str]` — Opaque page cursor
@@ -2234,1897 +2116,11 @@ client.graph.delete_custom_instructions()
-
client.graph.list_entity_types(...) +
client.graph.search_thread_summaries(...) -> ThreadSummaryPage
-#### 📝 Description - -
-
- -
-
- -Returns all entity types for a project, user, or graph. -
-
-
-
- -#### 🔌 Usage - -
-
- -
-
- -```python -from zep_cloud import Zep - -client = Zep( - api_key="YOUR_API_KEY", -) -client.graph.list_entity_types( - user_id="user_id", - graph_id="graph_id", -) - -``` -
-
-
-
- -#### ⚙️ Parameters - -
-
- -
-
- -**user_id:** `typing.Optional[str]` — User ID to get user-specific entity types - -
-
- -
-
- -**graph_id:** `typing.Optional[str]` — Graph ID to get graph-specific entity types - -
-
- -
-
- -**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. - -
-
-
-
- - -
-
-
- -
client.graph.set_entity_types_internal(...) -
-
- -#### 📝 Description - -
-
- -
-
- -Sets the entity types for multiple users and graphs, replacing any existing ones. -
-
-
-
- -#### 🔌 Usage - -
-
- -
-
- -```python -from zep_cloud import Zep - -client = Zep( - api_key="YOUR_API_KEY", -) -client.graph.set_entity_types_internal() - -``` -
-
-
-
- -#### ⚙️ Parameters - -
-
- -
-
- -**edge_types:** `typing.Optional[typing.Sequence[EdgeType]]` - -
-
- -
-
- -**entity_types:** `typing.Optional[typing.Sequence[EntityType]]` - -
-
- -
-
- -**graph_ids:** `typing.Optional[typing.Sequence[str]]` - -
-
- -
-
- -**user_ids:** `typing.Optional[typing.Sequence[str]]` - -
-
- -
-
- -**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. - -
-
-
-
- - -
-
-
- -
client.graph.add(...) -
-
- -#### 📝 Description - -
-
- -
-
- -Add data to the graph. -
-
-
-
- -#### 🔌 Usage - -
-
- -
-
- -```python -from zep_cloud import Zep - -client = Zep( - api_key="YOUR_API_KEY", -) -client.graph.add( - data="data", - type="text", -) - -``` -
-
-
-
- -#### ⚙️ Parameters - -
-
- -
-
- -**data:** `str` - -
-
- -
-
- -**type:** `GraphDataType` - -
-
- -
-
- -**created_at:** `typing.Optional[str]` - -
-
- -
-
- -**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. - -
-
- -
-
- -**metadata:** `typing.Optional[typing.Dict[str, typing.Optional[typing.Any]]]` — Optional metadata key-value pairs. Max 10 keys. Values must be strings, numbers, booleans, or arrays of scalars. - -
-
- -
-
- -**source_description:** `typing.Optional[str]` - -
-
- -
-
- -**strict_ontology:** `typing.Optional[bool]` — When true, prevents extraction of generic Entity nodes that do not match the configured ontology. - -
-
- -
-
- -**user_id:** `typing.Optional[str]` — User ID is the ID of the user to which the data will be added. If not adding to a user graph, please use graph_id field instead. - -
-
- -
-
- -**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. - -
-
-
-
- - -
-
-
- -
client.graph.add_batch(...) -
-
- -#### 📝 Description - -
-
- -
-
- -Deprecated. Use the [Batch API](/adding-batch-data) (`client.batch.*`) instead. - -Adds data to the graph in batch mode, processing episodes concurrently. -
-
-
-
- -#### 🔌 Usage - -
-
- -
-
- -```python -from zep_cloud import EpisodeData, Zep - -client = Zep( - api_key="YOUR_API_KEY", -) -client.graph.add_batch( - episodes=[ - EpisodeData( - data="data", - type="text", - ) - ], -) - -``` -
-
-
-
- -#### ⚙️ Parameters - -
-
- -
-
- -**episodes:** `typing.Sequence[EpisodeData]` - -
-
- -
-
- -**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. - -
-
- -
-
- -**strict_ontology:** `typing.Optional[bool]` — When true, prevents extraction of generic Entity nodes that do not match the configured ontology. - -
-
- -
-
- -**user_id:** `typing.Optional[str]` — User ID is the ID of the user to which the data will be added. If not adding to a user graph, please use graph_id field instead. - -
-
- -
-
- -**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. - -
-
-
-
- - -
-
-
- -
client.graph.add_fact_triple(...) -
-
- -#### 📝 Description - -
-
- -
-
- -Add a fact triple for a user or group -
-
-
-
- -#### 🔌 Usage - -
-
- -
-
- -```python -from zep_cloud import Zep - -client = Zep( - api_key="YOUR_API_KEY", -) -client.graph.add_fact_triple( - fact="fact", - fact_name="fact_name", -) - -``` -
-
-
-
- -#### ⚙️ Parameters - -
-
- -
-
- -**fact:** `str` — The fact relating the two nodes that this edge represents - -
-
- -
-
- -**fact_name:** `str` — The name of the edge to add. Should be all caps using snake case (eg RELATES_TO) - -
-
- -
-
- -**created_at:** `typing.Optional[str]` — The timestamp of the message - -
-
- -
-
- -**edge_attributes:** `typing.Optional[typing.Dict[str, typing.Optional[typing.Any]]]` - -Additional attributes of the edge. Values must be scalar types (string, number, boolean, or null). -Nested objects and arrays are not allowed. - -
-
- -
-
- -**expired_at:** `typing.Optional[str]` — The time (if any) at which the edge expires - -
-
- -
-
- -**graph_id:** `typing.Optional[str]` - -
-
- -
-
- -**invalid_at:** `typing.Optional[str]` — The time (if any) at which the fact stops being true - -
-
- -
-
- -**metadata:** `typing.Optional[typing.Dict[str, typing.Optional[typing.Any]]]` - -Optional metadata key-value pairs for the shadow episode created for this fact triple. -Max 10 keys. Values must be strings, numbers, or booleans. - -
-
- -
-
- -**source_node_attributes:** `typing.Optional[typing.Dict[str, typing.Optional[typing.Any]]]` - -Additional attributes of the source node. Values must be scalar types (string, number, boolean, or null). -Nested objects and arrays are not allowed. - -
-
- -
-
- -**source_node_labels:** `typing.Optional[typing.Sequence[str]]` - -The labels for the source node. At most one entity-type label may be -provided so that manually-added triples remain consistent with automatic -episode extraction, which assigns one best-match entity type per node. -The base "Entity" label is added implicitly by the graph layer on save -and does not need to be supplied here. - -
-
- -
-
- -**source_node_name:** `typing.Optional[str]` — The name of the source node to add - -
-
- -
-
- -**source_node_summary:** `typing.Optional[str]` — The summary of the source node to add - -
-
- -
-
- -**source_node_uuid:** `typing.Optional[str]` — The source node uuid - -
-
- -
-
- -**target_node_attributes:** `typing.Optional[typing.Dict[str, typing.Optional[typing.Any]]]` - -Additional attributes of the target node. Values must be scalar types (string, number, boolean, or null). -Nested objects and arrays are not allowed. - -
-
- -
-
- -**target_node_labels:** `typing.Optional[typing.Sequence[str]]` - -The labels for the target node. At most one entity-type label may be -provided so that manually-added triples remain consistent with automatic -episode extraction, which assigns one best-match entity type per node. -The base "Entity" label is added implicitly by the graph layer on save -and does not need to be supplied here. - -
-
- -
-
- -**target_node_name:** `typing.Optional[str]` — The name of the target node to add - -
-
- -
-
- -**target_node_summary:** `typing.Optional[str]` — The summary of the target node to add - -
-
- -
-
- -**target_node_uuid:** `typing.Optional[str]` — The target node uuid - -
-
- -
-
- -**user_id:** `typing.Optional[str]` - -
-
- -
-
- -**valid_at:** `typing.Optional[str]` — The time at which the fact becomes true - -
-
- -
-
- -**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. - -
-
-
-
- - -
-
-
- -
client.graph.clone(...) -
-
- -#### 📝 Description - -
-
- -
-
- -Clone a user or group graph. -
-
-
-
- -#### 🔌 Usage - -
-
- -
-
- -```python -from zep_cloud import Zep - -client = Zep( - api_key="YOUR_API_KEY", -) -client.graph.clone() - -``` -
-
-
-
- -#### ⚙️ Parameters - -
-
- -
-
- -**source_graph_id:** `typing.Optional[str]` — source_graph_id is the ID of the graph to be cloned. Required if source_user_id is not provided - -
-
- -
-
- -**source_user_id:** `typing.Optional[str]` — user_id of the user whose graph is being cloned. Required if source_graph_id is not provided - -
-
- -
-
- -**target_graph_id:** `typing.Optional[str]` — target_graph_id is the ID to be set on the cloned graph. Must not point to an existing graph. Required if target_user_id is not provided. - -
-
- -
-
- -**target_user_id:** `typing.Optional[str]` — user_id to be set on the cloned user. Must not point to an existing user. Required if target_graph_id is not provided. - -
-
- -
-
- -**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. - -
-
-
-
- - -
-
-
- -
client.graph.create(...) -
-
- -#### 📝 Description - -
-
- -
-
- -Creates a new graph. -
-
-
-
- -#### 🔌 Usage - -
-
- -
-
- -```python -from zep_cloud import Zep - -client = Zep( - api_key="YOUR_API_KEY", -) -client.graph.create( - graph_id="graph_id", -) - -``` -
-
-
-
- -#### ⚙️ Parameters - -
-
- -
-
- -**graph_id:** `str` - -
-
- -
-
- -**description:** `typing.Optional[str]` - -
-
- -
-
- -**name:** `typing.Optional[str]` - -
-
- -
-
- -**time_zone:** `typing.Optional[str]` — The graph's IANA time zone. Stored on its group-backed subject. - -
-
- -
-
- -**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. - -
-
-
-
- - -
-
-
- -
client.graph.list_all(...) -
-
- -#### 📝 Description - -
-
- -
-
- -Returns all graphs. In order to list users, use user.list_ordered instead -
-
-
-
- -#### 🔌 Usage - -
-
- -
-
- -```python -from zep_cloud import Zep - -client = Zep( - api_key="YOUR_API_KEY", -) -client.graph.list_all( - page_number=1, - page_size=1, - search="search", - order_by="order_by", - asc=True, -) - -``` -
-
-
-
- -#### ⚙️ Parameters - -
-
- -
-
- -**page_number:** `typing.Optional[int]` — Page number for pagination, starting from 1. - -
-
- -
-
- -**page_size:** `typing.Optional[int]` — Number of graphs to retrieve per page (default 50, range 1-100; explicit 0 is invalid). - -
-
- -
-
- -**search:** `typing.Optional[str]` — Search term for filtering graphs by graph_id, name, or description. Queries longer than 200 Unicode code points after whitespace normalization are invalid. - -
-
- -
-
- -**order_by:** `typing.Optional[str]` — Column to sort by (created_at, graph_id, name). - -
-
- -
-
- -**asc:** `typing.Optional[bool]` — Sort in ascending order. - -
-
- -
-
- -**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. - -
-
-
-
- - -
-
-
- -
client.graph.add_nodes(...) -
-
- -#### 📝 Description - -
-
- -
-
- -Add entity nodes to a user or graph directly, without episode ingestion. Up to 100 nodes per request. -
-
-
-
- -#### 🔌 Usage - -
-
- -
-
- -```python -from zep_cloud import AddNodeItem, Zep - -client = Zep( - api_key="YOUR_API_KEY", -) -client.graph.add_nodes( - nodes=[ - AddNodeItem( - name="name", - ) - ], -) - -``` -
-
-
-
- -#### ⚙️ Parameters - -
-
- -
-
- -**nodes:** `typing.Sequence[AddNodeItem]` — The nodes to add. 1 to 100 items. - -
-
- -
-
- -**graph_id:** `typing.Optional[str]` - -
-
- -
-
- -**user_id:** `typing.Optional[str]` - -
-
- -
-
- -**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. - -
-
-
-
- - -
-
-
- -
client.graph.detect_patterns(...) -
-
- -#### 📝 Description - -
-
- -
-
- -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, -detects triple-frequency patterns, and returns resolved edges ranked by relevance. -
-
-
-
- -#### 🔌 Usage - -
-
- -
-
- -```python -from zep_cloud import Zep - -client = Zep( - api_key="YOUR_API_KEY", -) -client.graph.detect_patterns() - -``` -
-
-
-
- -#### ⚙️ Parameters - -
-
- -
-
- -**detect:** `typing.Optional[DetectConfig]` - -Which pattern types to detect with type-specific configuration. -Omit to detect all types with defaults. Ignored when query is set. - -
-
- -
-
- -**edge_limit:** `typing.Optional[int]` — Max resolved edges per pattern. Default: 10, Max: 100. Only used with query. - -
-
- -
-
- -**graph_id:** `typing.Optional[str]` — Graph ID when detecting patterns on a named graph - -
-
- -
-
- -**limit:** `typing.Optional[int]` — Max patterns to return. Default: 50, Max: 200 - -
-
- -
-
- -**min_occurrences:** `typing.Optional[int]` — Minimum occurrence count to report a pattern. Default: 2 - -
-
- -
-
- -**query:** `typing.Optional[str]` - -Search query for discovering seed nodes via hybrid search. -When set, forces triple-frequency detection only and enables edge resolution -with cross-encoder reranking. Mutually exclusive with seeds. - -
-
- -
-
- -**query_limit:** `typing.Optional[int]` — Max seed nodes from search. Default: 10, Max: 50. Only used with query. - -
-
- -
-
- -**recency_weight:** `typing.Optional[RecencyWeight]` - -Exponential half-life decay applied to edge created_at timestamps. -Valid values: none, 7_days, 30_days, 90_days. Default: none - -
-
- -
-
- -**search_filters:** `typing.Optional[SearchFilters]` - -Filters which edges/nodes participate in pattern detection. -Reuses the same filter format as /graph/search. - -
-
- -
-
- -**seeds:** `typing.Optional[PatternSeeds]` — Seed selection. If omitted, analyzes the entire graph. Mutually exclusive with query. - -
-
- -
-
- -**user_id:** `typing.Optional[str]` — User ID when detecting patterns on a user graph - -
-
- -
-
- -**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. - -
-
-
-
- - -
-
-
- -
client.graph.search(...) -
-
- -#### 📝 Description - -
-
- -
-
- -Perform a graph search query. -
-
-
-
- -#### 🔌 Usage - -
-
- -
-
- -```python -from zep_cloud import Zep - -client = Zep( - api_key="YOUR_API_KEY", -) -client.graph.search( - query="query", -) - -``` -
-
-
-
- -#### ⚙️ Parameters - -
-
- -
-
- -**query:** `str` — The string to search for (required) - -
-
- -
-
- -**bfs_origin_node_uuids:** `typing.Optional[typing.Sequence[str]]` — Nodes that are the origins of the BFS searches - -
-
- -
-
- -**center_node_uuid:** `typing.Optional[str]` — Node to rerank around for node distance reranking - -
-
- -
-
- -**graph_id:** `typing.Optional[str]` — The graph_id to search in. When searching user graph, please use user_id instead. - -
-
- -
-
- -**limit:** `typing.Optional[int]` — The maximum number of facts to retrieve for non-auto scopes. Defaults to 10. Limited to 50. Ignored when scope=auto. - -
-
- -
-
- -**max_characters:** `typing.Optional[int]` — Maximum total characters across all selected results when scope=auto. Defaults to 2500. Limited to 50000. - -
-
- -
-
- -**mmr_lambda:** `typing.Optional[float]` — weighting for maximal marginal relevance - -
-
- -
-
- -**reranker:** `typing.Optional[Reranker]` - -Defaults to RRF. Ignored when scope=auto except node_distance and episode_mentions are rejected; -auto search always uses RRF retrieval and applies its own internal rerank after retrieval. -episode_mentions ranks edge candidates by how many of the episodes listed -in search_filters.episode_uuids mention them; without episode_uuids it has -no effect and results are ranked as if no reranker were specified. - -
-
- -
-
- -**return_raw_results:** `typing.Optional[bool]` - -When scope=auto, include the selected raw graph results alongside the materialized context block. -For graph-service-backed auto mode, selected raw results may include episodes, -edges, nodes, observations, and thread_summaries. - -
-
- -
-
- -**scope:** `typing.Optional[GraphSearchScope]` — Defaults to Edges. - -
-
- -
-
- -**search_filters:** `typing.Optional[SearchFilters]` — Search filters to apply to the search - -
-
- -
-
- -**user_id:** `typing.Optional[str]` — The user_id when searching user graph. If not searching user graph, please use graph_id instead. - -
-
- -
-
- -**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. - -
-
-
-
- - -
-
-
- -
client.graph.get_subgraph(...) -
-
- -#### 📝 Description - -
-
- -
-
- -Returns the bounded neighborhood of a set of seed nodes as a single {nodes, edges} payload: breadth-first expansion up to a caller-specified depth, subject to explicit budgets, with explicit truncation reporting. -
-
-
-
- -#### 🔌 Usage - -
-
- -
-
- -```python -from zep_cloud import Zep - -client = Zep( - api_key="YOUR_API_KEY", -) -client.graph.get_subgraph( - seed_node_uuids=["seed_node_uuids"], -) - -``` -
-
-
-
- -#### ⚙️ Parameters - -
-
- -
-
- -**seed_node_uuids:** `typing.Sequence[str]` - -Seed node UUIDs to expand from, in traversal-priority order: seeds are -admitted before any expansion, in this order, and count toward -max_nodes first. 1-20 entries, required. Seeds that do not exist in -the target graph are ignored, not an error. - -
-
- -
-
- -**depth:** `typing.Optional[int]` — Maximum traversal depth from the seeds. 1-3. Defaults to 1. - -
-
- -
-
- -**direction:** `typing.Optional[str]` - -Edge orientation followed during expansion, relative to each frontier -node: "in" | "out" | "both". Defaults to "both". - -
-
- -
-
- -**graph_id:** `typing.Optional[str]` - -graph_id identifies the target named graph. Exactly one of user_id or -graph_id is required. - -
-
- -
-
- -**max_edges:** `typing.Optional[int]` — Maximum number of edges in the response. 1-1000. Defaults to 200. - -
-
- -
-
- -**max_nodes:** `typing.Optional[int]` - -Maximum number of nodes in the response, including admitted seeds. -1-500. Defaults to 100. - -
-
- -
-
- -**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). - -
-
- -
-
- -**user_id:** `typing.Optional[str]` - -user_id identifies the target user graph. Exactly one of user_id or -graph_id is required. - -
-
- -
-
- -**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. - -
-
-
-
- - -
-
-
- -
client.graph.get(...) -
-
- -#### 📝 Description - -
-
- -
-
- -Returns a graph. -
-
-
-
- -#### 🔌 Usage - -
-
- -
-
- -```python -from zep_cloud import Zep - -client = Zep( - api_key="YOUR_API_KEY", -) -client.graph.get( - graph_id="graphId", -) - -``` -
-
-
-
- -#### ⚙️ Parameters - -
-
- -
-
- -**graph_id:** `str` — The graph_id of the graph to get. - -
-
- -
-
- -**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. - -
-
-
-
- - -
-
-
- -
client.graph.delete(...) -
-
- -#### 📝 Description - -
-
- -
-
- -Deletes a graph. If you would like to delete a user graph, make sure to use user.delete instead. -
-
-
-
- -#### 🔌 Usage - -
-
- -
-
- -```python -from zep_cloud import Zep - -client = Zep( - api_key="YOUR_API_KEY", -) -client.graph.delete( - graph_id="graphId", -) - -``` -
-
-
-
- -#### ⚙️ Parameters - -
-
- -
-
- -**graph_id:** `str` — Graph ID - -
-
- -
-
- -**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. - -
-
-
-
- - -
-
-
- -
client.graph.update(...) -
-
- -#### 📝 Description - -
-
- -
-
- -Updates information about a graph. -
-
-
-
- -#### 🔌 Usage - -
-
- -
-
- -```python -from zep_cloud import Zep - -client = Zep( - api_key="YOUR_API_KEY", -) -client.graph.update( - graph_id="graphId", -) - -``` -
-
-
-
- -#### ⚙️ Parameters - -
-
- -
-
- -**graph_id:** `str` — Graph ID - -
-
- -
-
- -**description:** `typing.Optional[str]` - -
-
- -
-
- -**name:** `typing.Optional[str]` - -
-
- -
-
- -**time_zone:** `typing.Optional[str]` — The graph's IANA time zone. Stored on its group-backed subject. - -
-
- -
-
- -**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. - -
-
-
-
- - -
-
-
- -
client.graph.warm(...) -
-
- -#### 📝 Description - -
-
- -
-
- -Hints Zep to warm a graph for low-latency search -
-
-
-
- -#### 🔌 Usage - -
-
- -
-
- -```python -from zep_cloud import Zep - -client = Zep( - api_key="YOUR_API_KEY", -) -client.graph.warm( - graph_id="graphId", -) - -``` -
-
-
-
- -#### ⚙️ Parameters - -
-
- -
-
- -**graph_id:** `str` — The graph_id of the graph to warm. - -
-
- -
-
- -**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. - -
-
-
-
- - -
-
-
- -## Project -
client.project.get() -
-
- -#### 📝 Description - -
-
- -
-
- -Retrieve project info based on the provided api key. -
-
-
-
- -#### 🔌 Usage - -
-
- -
-
- -```python -from zep_cloud import Zep - -client = Zep( - api_key="YOUR_API_KEY", -) -client.project.get() - -``` -
-
-
-
- -#### ⚙️ Parameters - -
-
- -
-
- -**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. - -
-
-
-
- - -
-
-
- -
client.project.update(...) -
-
- -#### 📝 Description - -
-
- -
-
- -Sets or clears the project-level fallback time zone for the API key's project. -
-
-
-
- -#### 🔌 Usage +#### 🔌 Usage
@@ -4134,11 +2130,18 @@ Sets or clears the project-level fallback time zone for the API key's project. ```python from zep_cloud import Zep +from zep_cloud.environment import ZepEnvironment client = Zep( - api_key="YOUR_API_KEY", + api_key="", + environment=ZepEnvironment.DEFAULT, +) + +client.graph.search_thread_summaries( + graph_uuid="graph_uuid", + limit=1, + cursor="cursor", ) -client.project.update() ```
@@ -4154,7 +2157,7 @@ client.project.update()
-**default_time_zone:** `typing.Optional[str]` — The project's IANA fallback time zone. Null clears the existing value. +**graph_uuid:** `str` — Graph UUID
@@ -4162,70 +2165,15 @@ client.project.update()
-**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. +**request:** `SearchRequest`
-
-
- - - - -
- -
client.project.get_observation_steering(...) -
-
- -#### 📝 Description - -
-
- -
-
- -Returns project steering or the effective user/graph steering with project fallback. This API is experimental and may change in future releases. -
-
-
-
- -#### 🔌 Usage - -
-
- -
-
- -```python -from zep_cloud import Zep - -client = Zep( - api_key="YOUR_API_KEY", -) -client.project.get_observation_steering( - user_id="user_id", - graph_id="graph_id", -) - -``` -
-
-
-
- -#### ⚙️ Parameters - -
-
-**user_id:** `typing.Optional[str]` — User ID for user-specific steering +**limit:** `typing.Optional[int]` — Page size
@@ -4233,7 +2181,7 @@ client.project.get_observation_steering(
-**graph_id:** `typing.Optional[str]` — Graph ID for graph-specific steering +**cursor:** `typing.Optional[str]` — Opaque page cursor
@@ -4253,24 +2201,10 @@ client.project.get_observation_steering(
-
client.project.set_observation_steering(...) -
-
- -#### 📝 Description - -
-
- +
client.graph.get_subgraph(...) -> SubgraphResponse
-Replaces project, user, or graph steering. An empty configuration clears the project default or removes the user/graph override. Changes affect later materializer runs only. This API is experimental and may change in future releases. -
-
-
-
- #### 🔌 Usage
@@ -4281,13 +2215,15 @@ Replaces project, user, or graph steering. An empty configuration clears the pro ```python from zep_cloud import Zep +from zep_cloud.environment import ZepEnvironment client = Zep( - api_key="YOUR_API_KEY", + api_key="", + environment=ZepEnvironment.DEFAULT, ) -client.project.set_observation_steering( - user_id="user_id", - graph_id="graph_id", + +client.graph.get_subgraph( + graph_uuid="graph_uuid", ) ``` @@ -4304,7 +2240,7 @@ client.project.set_observation_steering(
-**user_id:** `typing.Optional[str]` — User ID for user-specific steering +**graph_uuid:** `str` — Graph UUID
@@ -4312,7 +2248,7 @@ client.project.set_observation_steering(
-**graph_id:** `typing.Optional[str]` — Graph ID for graph-specific steering +**depth:** `typing.Optional[int]`
@@ -4320,7 +2256,7 @@ client.project.set_observation_steering(
-**instruction:** `typing.Optional[str]` +**direction:** `typing.Optional[str]`
@@ -4328,7 +2264,7 @@ client.project.set_observation_steering(
-**types:** `typing.Optional[typing.Sequence[ObservationType]]` +**filters:** `typing.Optional[typing.Dict[str, typing.Any]]`
@@ -4336,70 +2272,23 @@ client.project.set_observation_steering(
-**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. +**max_edges:** `typing.Optional[int]`
-
-
- - - - -
- -## Task -
client.task.get(...) -
-
- -#### 📝 Description - -
-
- -
-
- -Gets a task by its ID -
-
-
-
- -#### 🔌 Usage - -
-
-```python -from zep_cloud import Zep - -client = Zep( - api_key="YOUR_API_KEY", -) -client.task.get( - task_id="task_id", -) - -``` -
-
+**max_nodes:** `typing.Optional[int]` +
-#### ⚙️ Parameters - -
-
-
-**task_id:** `str` — Task ID +**seed_node_uuids:** `typing.Optional[typing.List[str]]`
@@ -4419,25 +2308,10 @@ client.task.get(
-## Thread -
client.thread.list_all(...) -
-
- -#### 📝 Description - -
-
- +
client.graph.warm(...) -> AsyncResult
-Returns all threads. -
-
-
-
- #### 🔌 Usage
@@ -4448,15 +2322,15 @@ Returns all threads. ```python from zep_cloud import Zep +from zep_cloud.environment import ZepEnvironment client = Zep( - api_key="YOUR_API_KEY", + api_key="", + environment=ZepEnvironment.DEFAULT, ) -client.thread.list_all( - page_number=1, - page_size=1, - order_by="order_by", - asc=True, + +client.graph.warm( + graph_uuid="graph_uuid", ) ``` @@ -4473,31 +2347,7 @@ client.thread.list_all(
-**page_number:** `typing.Optional[int]` — Page number for pagination, starting from 1 - -
-
- -
-
- -**page_size:** `typing.Optional[int]` — Number of threads to retrieve per page. - -
-
- -
-
- -**order_by:** `typing.Optional[str]` — Field to order the results by: created_at, updated_at, user_id, thread_id. - -
-
- -
-
- -**asc:** `typing.Optional[bool]` — Order direction: true for ascending, false for descending. +**graph_uuid:** `str` — Graph UUID
@@ -4517,24 +2367,11 @@ client.thread.list_all(
-
client.thread.create(...) -
-
- -#### 📝 Description - -
-
- +## Lookup +
client.lookup.batch(...) -> LookupBatchResponse
-Start a new thread. -
-
-
-
- #### 🔌 Usage
@@ -4545,15 +2382,15 @@ Start a new thread. ```python from zep_cloud import Zep +from zep_cloud.environment import ZepEnvironment client = Zep( - api_key="YOUR_API_KEY", -) -client.thread.create( - thread_id="thread_id", - user_id="user_id", + api_key="", + environment=ZepEnvironment.DEFAULT, ) +client.lookup.batch() + ```
@@ -4568,7 +2405,15 @@ client.thread.create(
-**thread_id:** `str` — The unique identifier of the thread. +**graphs:** `typing.Optional[typing.List[str]]` + +
+
+ +
+
+ +**threads:** `typing.Optional[typing.List[str]]`
@@ -4576,7 +2421,7 @@ client.thread.create(
-**user_id:** `str` — The unique identifier of the user associated with the thread +**users:** `typing.Optional[typing.List[str]]`
@@ -4596,24 +2441,11 @@ client.thread.create(
-
client.thread.delete(...) -
-
- -#### 📝 Description - -
-
- +## Project +
client.project.get() -> Project
-Deletes a thread. -
-
-
-
- #### 🔌 Usage
@@ -4624,14 +2456,15 @@ Deletes a thread. ```python from zep_cloud import Zep +from zep_cloud.environment import ZepEnvironment client = Zep( - api_key="YOUR_API_KEY", -) -client.thread.delete( - thread_id="threadId", + api_key="", + environment=ZepEnvironment.DEFAULT, ) +client.project.get() + ```
@@ -4646,14 +2479,6 @@ client.thread.delete(
-**thread_id:** `str` — The ID of the thread for which memory should be deleted. - -
-
- -
-
- **request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration.
@@ -4666,24 +2491,10 @@ client.thread.delete(
-
client.thread.get_user_context(...) -
-
- -#### 📝 Description - -
-
- +
client.project.update(...) -> Project
-Returns most relevant context from the user graph (including memory from any/all past threads) based on the content of the past few messages of the given thread. -
-
-
-
- #### 🔌 Usage
@@ -4694,15 +2505,15 @@ Returns most relevant context from the user graph (including memory from any/all ```python from zep_cloud import Zep +from zep_cloud.environment import ZepEnvironment client = Zep( - api_key="YOUR_API_KEY", -) -client.thread.get_user_context( - thread_id="threadId", - template_id="template_id", + api_key="", + environment=ZepEnvironment.DEFAULT, ) +client.project.update() + ```
@@ -4717,15 +2528,7 @@ client.thread.get_user_context(
-**thread_id:** `str` — The ID of the current thread (for which context is being retrieved). - -
-
- -
-
- -**template_id:** `typing.Optional[str]` — Optional template ID to use for custom context rendering. +**default_time_zone:** `typing.Optional[str]` — Omit to leave unchanged, send JSON null to clear, or send a value to set.
@@ -4743,25 +2546,11 @@ client.thread.get_user_context( -
- -
client.thread.get(...) -
-
- -#### 📝 Description - -
-
- -
-
- -Returns messages for a thread. -
-
-
-
+
+ +
client.project.get_instructions() -> Instructions +
+
#### 🔌 Usage @@ -4773,17 +2562,15 @@ Returns messages for a thread. ```python from zep_cloud import Zep +from zep_cloud.environment import ZepEnvironment client = Zep( - api_key="YOUR_API_KEY", -) -client.thread.get( - thread_id="threadId", - limit=1, - cursor=1, - lastn=1, + api_key="", + environment=ZepEnvironment.DEFAULT, ) +client.project.get_instructions() + ```
@@ -4798,31 +2585,56 @@ client.thread.get(
-**thread_id:** `str` — Thread ID +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration.
+ + -
-
-**limit:** `typing.Optional[int]` — Limit the number of results returned -
+
+
client.project.set_instructions(...) -> Instructions
-**cursor:** `typing.Optional[int]` — Cursor for pagination - +#### 🔌 Usage + +
+
+ +
+
+ +```python +from zep_cloud import Zep +from zep_cloud.environment import ZepEnvironment + +client = Zep( + api_key="", + environment=ZepEnvironment.DEFAULT, +) + +client.project.set_instructions() + +```
+
+
+ +#### ⚙️ Parameters
-**lastn:** `typing.Optional[int]` — Number of most recent messages to return (overrides limit and cursor) +
+
+ +**request:** `Instructions`
@@ -4842,24 +2654,10 @@ client.thread.get(
-
client.thread.add_messages(...) -
-
- -#### 📝 Description - -
-
- +
client.project.get_observation_steering() -> ObservationSteering
-Add messages to a thread. -
-
-
-
- #### 🔌 Usage
@@ -4869,21 +2667,16 @@ Add messages to a thread.
```python -from zep_cloud import Message, Zep +from zep_cloud import Zep +from zep_cloud.environment import ZepEnvironment client = Zep( - api_key="YOUR_API_KEY", -) -client.thread.add_messages( - thread_id="threadId", - messages=[ - Message( - content="content", - role="norole", - ) - ], + api_key="", + environment=ZepEnvironment.DEFAULT, ) +client.project.get_observation_steering() + ```
@@ -4898,43 +2691,56 @@ client.thread.add_messages(
-**thread_id:** `str` — The ID of the thread to which messages should be added. +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration.
+
+
-
-
-**messages:** `typing.Sequence[Message]` — A list of message objects, where each message contains a role and content. -
+
+
client.project.set_observation_steering(...) -> ObservationSteering
-**ignore_roles:** `typing.Optional[typing.Sequence[RoleType]]` +#### 🔌 Usage -Optional list of role types to ignore when adding messages to graph memory. -The message itself will still be added, retained and used as context for messages -that are added to a user's graph. - -
-
+
+
-**return_context:** `typing.Optional[bool]` — Optionally return context block relevant to the most recent messages. - +```python +from zep_cloud import Zep +from zep_cloud.environment import ZepEnvironment + +client = Zep( + api_key="", + environment=ZepEnvironment.DEFAULT, +) + +client.project.set_observation_steering() + +``` +
+
+#### ⚙️ Parameters + +
+
+
-**strict_ontology:** `typing.Optional[bool]` — When true, prevents extraction of generic Entity nodes that do not match the configured ontology. +**request:** `ObservationSteering`
@@ -4954,26 +2760,10 @@ that are added to a user's graph.
-
client.thread.add_messages_batch(...) -
-
- -#### 📝 Description - +
client.project.get_ontology() -> Ontology
-
-
- -Deprecated. Use the [Batch API](/adding-batch-data) (`client.batch.*` with `type: "thread_message"`) instead. - -Adds messages to a thread in batch mode, processing messages concurrently. -
-
-
-
- #### 🔌 Usage
@@ -4983,21 +2773,16 @@ Adds messages to a thread in batch mode, processing messages concurrently.
```python -from zep_cloud import Message, Zep +from zep_cloud import Zep +from zep_cloud.environment import ZepEnvironment client = Zep( - api_key="YOUR_API_KEY", -) -client.thread.add_messages_batch( - thread_id="threadId", - messages=[ - Message( - content="content", - role="norole", - ) - ], + api_key="", + environment=ZepEnvironment.DEFAULT, ) +client.project.get_ontology() + ```
@@ -5012,43 +2797,56 @@ client.thread.add_messages_batch(
-**thread_id:** `str` — The ID of the thread to which messages should be added. +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration.
+
+
-
-
-**messages:** `typing.Sequence[Message]` — A list of message objects, where each message contains a role and content. -
+
+
client.project.set_ontology(...) -> Ontology
-**ignore_roles:** `typing.Optional[typing.Sequence[RoleType]]` +#### 🔌 Usage -Optional list of role types to ignore when adding messages to graph memory. -The message itself will still be added, retained and used as context for messages -that are added to a user's graph. - -
-
+
+
-**return_context:** `typing.Optional[bool]` — Optionally return context block relevant to the most recent messages. - +```python +from zep_cloud import Zep +from zep_cloud.environment import ZepEnvironment + +client = Zep( + api_key="", + environment=ZepEnvironment.DEFAULT, +) + +client.project.set_ontology() + +``` +
+
+#### ⚙️ Parameters + +
+
+
-**strict_ontology:** `typing.Optional[bool]` — When true, prevents extraction of generic Entity nodes that do not match the configured ontology. +**request:** `Ontology`
@@ -5068,24 +2866,10 @@ that are added to a user's graph.
-
client.thread.get_summary(...) -
-
- -#### 📝 Description - -
-
- +
client.project.get_user_summary_instructions() -> UserSummaryInstructions
-Returns the incremental summary generated from messages in the thread. Returns 404 if no summary exists for the thread. -
-
-
-
- #### 🔌 Usage
@@ -5096,14 +2880,15 @@ Returns the incremental summary generated from messages in the thread. Returns 4 ```python from zep_cloud import Zep +from zep_cloud.environment import ZepEnvironment client = Zep( - api_key="YOUR_API_KEY", -) -client.thread.get_summary( - thread_id="threadId", + api_key="", + environment=ZepEnvironment.DEFAULT, ) +client.project.get_user_summary_instructions() + ```
@@ -5118,14 +2903,6 @@ client.thread.get_summary(
-**thread_id:** `str` — The thread ID. - -
-
- -
-
- **request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration.
@@ -5138,25 +2915,10 @@ client.thread.get_summary(
-## User -
client.user.list_user_summary_instructions(...) -
-
- -#### 📝 Description - +
client.project.set_user_summary_instructions(...) -> UserSummaryInstructions
-
-
- -Lists all user summary instructions for a project, user. -
-
-
-
- #### 🔌 Usage
@@ -5167,14 +2929,15 @@ Lists all user summary instructions for a project, user. ```python from zep_cloud import Zep +from zep_cloud.environment import ZepEnvironment client = Zep( - api_key="YOUR_API_KEY", -) -client.user.list_user_summary_instructions( - user_id="user_id", + api_key="", + environment=ZepEnvironment.DEFAULT, ) +client.project.set_user_summary_instructions() + ```
@@ -5189,7 +2952,7 @@ client.user.list_user_summary_instructions(
-**user_id:** `typing.Optional[str]` — User ID to get user-specific instructions +**request:** `UserSummaryInstructions`
@@ -5209,24 +2972,11 @@ client.user.list_user_summary_instructions(
-
client.user.add_user_summary_instructions(...) -
-
- -#### 📝 Description - -
-
- +## Task +
client.task.list(...) -> TaskPage
-Adds new summary instructions for users graphs without removing existing ones. If user_ids is empty, adds to project-wide default instructions. -
-
-
-
- #### 🔌 Usage
@@ -5236,18 +2986,17 @@ Adds new summary instructions for users graphs without removing existing ones. I
```python -from zep_cloud import UserInstruction, Zep +from zep_cloud import Zep +from zep_cloud.environment import ZepEnvironment client = Zep( - api_key="YOUR_API_KEY", + api_key="", + environment=ZepEnvironment.DEFAULT, ) -client.user.add_user_summary_instructions( - instructions=[ - UserInstruction( - name="name", - text="text", - ) - ], + +client.task.list( + limit=1, + cursor="cursor", ) ``` @@ -5264,7 +3013,7 @@ client.user.add_user_summary_instructions(
-**instructions:** `typing.Sequence[UserInstruction]` — Instructions to add to the user summary generation. +**limit:** `typing.Optional[int]` — Page size
@@ -5272,7 +3021,7 @@ client.user.add_user_summary_instructions(
-**user_ids:** `typing.Optional[typing.Sequence[str]]` — User IDs to add the instructions to. If empty, the instructions are added to the project-wide default. +**cursor:** `typing.Optional[str]` — Opaque page cursor
@@ -5292,24 +3041,10 @@ client.user.add_user_summary_instructions(
-
client.user.delete_user_summary_instructions(...) -
-
- -#### 📝 Description - -
-
- +
client.task.get(...) -> Task
-Deletes user summary/instructions for users or project wide defaults. -
-
-
-
- #### 🔌 Usage
@@ -5320,11 +3055,16 @@ Deletes user summary/instructions for users or project wide defaults. ```python from zep_cloud import Zep +from zep_cloud.environment import ZepEnvironment client = Zep( - api_key="YOUR_API_KEY", + api_key="", + environment=ZepEnvironment.DEFAULT, +) + +client.task.get( + task_uuid="task_uuid", ) -client.user.delete_user_summary_instructions() ```
@@ -5340,15 +3080,7 @@ client.user.delete_user_summary_instructions()
-**instruction_names:** `typing.Optional[typing.Sequence[str]]` — Unique identifier for the instructions to be deleted. If empty deletes all instructions. - -
-
- -
-
- -**user_ids:** `typing.Optional[typing.Sequence[str]]` — Determines which users will have their custom instructions deleted. If no users are provided, the project-wide custom instructions will be effected. +**task_uuid:** `str` — Task UUID
@@ -5368,24 +3100,11 @@ client.user.delete_user_summary_instructions()
-
client.user.add(...) -
-
- -#### 📝 Description - -
-
- +## Thread +
client.thread.list(...) -> ThreadPage
-Adds a user. -
-
-
-
- #### 🔌 Usage
@@ -5396,12 +3115,19 @@ Adds a user. ```python from zep_cloud import Zep +from zep_cloud.environment import ZepEnvironment client = Zep( - api_key="YOUR_API_KEY", + api_key="", + environment=ZepEnvironment.DEFAULT, ) -client.user.add( - user_id="user_id", + +client.thread.list( + limit=1, + cursor="cursor", + order_by="order_by", + order="order", + user_uuid="user_uuid", ) ``` @@ -5418,31 +3144,7 @@ client.user.add(
-**user_id:** `str` — The unique identifier of the user. - -
-
- -
-
- -**disable_default_ontology:** `typing.Optional[bool]` — When true, disables the use of default/fallback ontology for the user's graph. - -
-
- -
-
- -**email:** `typing.Optional[str]` — The email address of the user. - -
-
- -
-
- -**first_name:** `typing.Optional[str]` — The first name of the user. +**limit:** `typing.Optional[int]` — Page size
@@ -5450,7 +3152,7 @@ client.user.add(
-**last_name:** `typing.Optional[str]` — The last name of the user. +**cursor:** `typing.Optional[str]` — Opaque page cursor
@@ -5458,7 +3160,7 @@ client.user.add(
-**metadata:** `typing.Optional[typing.Dict[str, typing.Optional[typing.Any]]]` — The metadata associated with the user. +**order_by:** `typing.Optional[str]` — Sort field
@@ -5466,7 +3168,7 @@ client.user.add(
-**time_zone:** `typing.Optional[str]` — The user's IANA time zone. Null or omission leaves it unset at creation. +**order:** `typing.Optional[str]` — asc or desc
@@ -5474,35 +3176,29 @@ client.user.add(
-**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. +**user_uuid:** `typing.Optional[str]` — Filter by user UUID
-
-
- - - - -
- -
client.user.list_ordered(...) -
-
- -#### 📝 Description - -
-
-Returns all users. +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+ +
+
+ +
client.thread.create(...) -> Thread +
+
#### 🔌 Usage @@ -5514,18 +3210,15 @@ Returns all users. ```python from zep_cloud import Zep +from zep_cloud.environment import ZepEnvironment client = Zep( - api_key="YOUR_API_KEY", -) -client.user.list_ordered( - page_number=1, - page_size=1, - search="search", - order_by="order_by", - asc=True, + api_key="", + environment=ZepEnvironment.DEFAULT, ) +client.thread.create() + ```
@@ -5540,7 +3233,7 @@ client.user.list_ordered(
-**page_number:** `typing.Optional[int]` — Page number for pagination, starting from 1 +**thread_id:** `typing.Optional[str]`
@@ -5548,7 +3241,7 @@ client.user.list_ordered(
-**page_size:** `typing.Optional[int]` — Number of users to retrieve per page +**user_uuid:** `typing.Optional[str]`
@@ -5556,59 +3249,78 @@ client.user.list_ordered(
-**search:** `typing.Optional[str]` — Search term for filtering users by user_id, name, or email +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration.
+ + -
-
-**order_by:** `typing.Optional[str]` — Column to sort by (created_at, user_id, email) -
+
+
client.thread.lookup(...) -> Thread
-**asc:** `typing.Optional[bool]` — Sort in ascending order - -
-
+#### 🔌 Usage
-**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. - +
+
+ +```python +from zep_cloud import Zep +from zep_cloud.environment import ZepEnvironment + +client = Zep( + api_key="", + environment=ZepEnvironment.DEFAULT, +) + +client.thread.lookup() + +```
+#### ⚙️ Parameters - - -
- -
client.user.get(...)
-#### 📝 Description -
+**request:** `LookupRequest` + +
+
+
-Returns a user. +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+ + +
+ +
client.thread.get(...) -> Thread +
+
#### 🔌 Usage @@ -5620,12 +3332,15 @@ Returns a user. ```python from zep_cloud import Zep +from zep_cloud.environment import ZepEnvironment client = Zep( - api_key="YOUR_API_KEY", + api_key="", + environment=ZepEnvironment.DEFAULT, ) -client.user.get( - user_id="userId", + +client.thread.get( + thread_uuid="thread_uuid", ) ``` @@ -5642,7 +3357,7 @@ client.user.get(
-**user_id:** `str` — The user_id of the user to get. +**thread_uuid:** `str` — Thread UUID
@@ -5662,24 +3377,10 @@ client.user.get(
-
client.user.delete(...) -
-
- -#### 📝 Description - +
client.thread.delete(...) -> ThreadDeleteResult
-
-
- -Deletes a user. -
-
-
-
- #### 🔌 Usage
@@ -5690,12 +3391,15 @@ Deletes a user. ```python from zep_cloud import Zep +from zep_cloud.environment import ZepEnvironment client = Zep( - api_key="YOUR_API_KEY", + api_key="", + environment=ZepEnvironment.DEFAULT, ) -client.user.delete( - user_id="userId", + +client.thread.delete( + thread_uuid="thread_uuid", ) ``` @@ -5712,7 +3416,7 @@ client.user.delete(
-**user_id:** `str` — User ID +**thread_uuid:** `str` — Thread UUID
@@ -5732,24 +3436,10 @@ client.user.delete(
-
client.user.update(...) -
-
- -#### 📝 Description - -
-
- +
client.thread.get_context(...) -> ThreadContextResponse
-Updates a user. -
-
-
-
- #### 🔌 Usage
@@ -5760,12 +3450,16 @@ Updates a user. ```python from zep_cloud import Zep +from zep_cloud.environment import ZepEnvironment client = Zep( - api_key="YOUR_API_KEY", + api_key="", + environment=ZepEnvironment.DEFAULT, ) -client.user.update( - user_id="userId", + +client.thread.get_context( + thread_uuid="thread_uuid", + template_uuid="template_uuid", ) ``` @@ -5782,7 +3476,7 @@ client.user.update(
-**user_id:** `str` — User ID +**thread_uuid:** `str` — Thread UUID
@@ -5790,7 +3484,7 @@ client.user.update(
-**disable_default_ontology:** `typing.Optional[bool]` — When true, disables the use of default/fallback ontology for the user's graph. +**template_uuid:** `typing.Optional[str]` — Context template UUID
@@ -5798,23 +3492,60 @@ client.user.update(
-**email:** `typing.Optional[str]` — The email address of the user. +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration.
+
+
+ + +
+
+
+
client.thread.list_episodes(...) -> EpisodePage
-**first_name:** `typing.Optional[str]` — The first name of the user. - +#### 🔌 Usage + +
+
+ +
+
+ +```python +from zep_cloud import Zep +from zep_cloud.environment import ZepEnvironment + +client = Zep( + api_key="", + environment=ZepEnvironment.DEFAULT, +) + +client.thread.list_episodes( + thread_uuid="thread_uuid", + limit=1, + cursor="cursor", +) + +```
+
+
+ +#### ⚙️ Parameters
-**last_name:** `typing.Optional[str]` — The last name of the user. +
+
+ +**thread_uuid:** `str` — Thread UUID
@@ -5822,7 +3553,7 @@ client.user.update(
-**metadata:** `typing.Optional[typing.Dict[str, typing.Optional[typing.Any]]]` — The metadata to update +**limit:** `typing.Optional[int]` — Page size
@@ -5830,7 +3561,7 @@ client.user.update(
-**time_zone:** `typing.Optional[str]` — The user's IANA time zone. Null clears the existing value. +**cursor:** `typing.Optional[str]` — Opaque page cursor
@@ -5850,24 +3581,10 @@ client.user.update(
-
client.user.get_node(...) -
-
- -#### 📝 Description - -
-
- +
client.thread.list_messages(...) -> MessagePage
-Returns a user's node. -
-
-
-
- #### 🔌 Usage
@@ -5878,12 +3595,17 @@ Returns a user's node. ```python from zep_cloud import Zep +from zep_cloud.environment import ZepEnvironment client = Zep( - api_key="YOUR_API_KEY", + api_key="", + environment=ZepEnvironment.DEFAULT, ) -client.user.get_node( - user_id="userId", + +client.thread.list_messages( + thread_uuid="thread_uuid", + limit=1, + cursor="cursor", ) ``` @@ -5900,7 +3622,7 @@ client.user.get_node(
-**user_id:** `str` — The user_id of the user to get the node for. +**thread_uuid:** `str` — Thread UUID
@@ -5908,35 +3630,37 @@ client.user.get_node(
-**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. +**limit:** `typing.Optional[int]` — Page size
-
-
- - - - -
-
client.user.get_threads(...)
-#### 📝 Description - -
-
+**cursor:** `typing.Optional[str]` — Opaque page cursor + +
+
-Returns all threads for a user. +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+ + +
+ +
client.thread.add_messages(...) -> AddMessagesResult +
+
#### 🔌 Usage @@ -5948,12 +3672,15 @@ Returns all threads for a user. ```python from zep_cloud import Zep +from zep_cloud.environment import ZepEnvironment client = Zep( - api_key="YOUR_API_KEY", + api_key="", + environment=ZepEnvironment.DEFAULT, ) -client.user.get_threads( - user_id="userId", + +client.thread.add_messages( + thread_uuid="thread_uuid", ) ``` @@ -5970,7 +3697,7 @@ client.user.get_threads(
-**user_id:** `str` — User ID +**thread_uuid:** `str` — Thread UUID
@@ -5978,36 +3705,54 @@ client.user.get_threads(
-**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. +**ignore_roles:** `typing.Optional[typing.List[str]]`
-
-
+
+
+**messages:** `typing.Optional[typing.List[AddMessage]]` +
-
-
client.user.warm(...)
-#### 📝 Description +**return_context:** `typing.Optional[bool]` + +
+
+**strict_ontology:** `typing.Optional[bool]` + +
+
+
-Hints Zep to warm a user's graph for low-latency search +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. +
+ + + +
+ +
client.thread.get_summary(...) -> ThreadSummary +
+
+ #### 🔌 Usage
@@ -6018,12 +3763,15 @@ Hints Zep to warm a user's graph for low-latency search ```python from zep_cloud import Zep +from zep_cloud.environment import ZepEnvironment client = Zep( - api_key="YOUR_API_KEY", + api_key="", + environment=ZepEnvironment.DEFAULT, ) -client.user.warm( - user_id="userId", + +client.thread.get_summary( + thread_uuid="thread_uuid", ) ``` @@ -6040,7 +3788,7 @@ client.user.warm(
-**user_id:** `str` — User ID +**thread_uuid:** `str` — Thread UUID
@@ -6060,25 +3808,11 @@ client.user.warm(
-## Graph Edge -
client.graph.edge.get_by_graph_id(...) -
-
- -#### 📝 Description - -
-
- +## User +
client.user.create(...) -> User
-Returns all edges for a graph. -
-
-
-
- #### 🔌 Usage
@@ -6089,14 +3823,15 @@ Returns all edges for a graph. ```python from zep_cloud import Zep +from zep_cloud.environment import ZepEnvironment client = Zep( - api_key="YOUR_API_KEY", -) -client.graph.edge.get_by_graph_id( - graph_id="graph_id", + api_key="", + environment=ZepEnvironment.DEFAULT, ) +client.user.create() + ```
@@ -6111,7 +3846,7 @@ client.graph.edge.get_by_graph_id(
-**graph_id:** `str` — Graph ID +**disable_default_ontology:** `typing.Optional[bool]`
@@ -6119,10 +3854,7 @@ client.graph.edge.get_by_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. +**email:** `typing.Optional[str]`
@@ -6130,7 +3862,7 @@ of the previous page. Encodes the sort field, direction, and continuation positi
-**direction:** `typing.Optional[str]` — Sort direction. One of "asc" or "desc" (default "desc"). +**first_name:** `typing.Optional[str]`
@@ -6138,7 +3870,7 @@ of the previous page. Encodes the sort field, direction, and continuation positi
-**filters:** `typing.Optional[SearchFilters]` — Optional filters applied to the listed artifacts. Reuses the graph.search filter type. +**last_name:** `typing.Optional[str]`
@@ -6146,7 +3878,7 @@ of the previous page. Encodes the sort field, direction, and continuation positi
-**limit:** `typing.Optional[int]` — Maximum number of items to return +**metadata:** `typing.Optional[typing.Dict[str, typing.Any]]`
@@ -6154,7 +3886,7 @@ of the previous page. Encodes the sort field, direction, and continuation positi
-**order_by:** `typing.Optional[str]` — Field to sort by. One of "created_at", "valid_at", or "uuid" (default "uuid"). +**time_zone:** `typing.Optional[str]`
@@ -6162,11 +3894,7 @@ of the previous page. Encodes the sort field, direction, and continuation positi
-**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. +**user_id:** `typing.Optional[str]`
@@ -6186,24 +3914,10 @@ Deprecated: prefer Cursor, the opaque cursor returned via the Zep-Next-Cursor re
-
client.graph.edge.get_by_user_id(...) -
-
- -#### 📝 Description - -
-
- +
client.user.list(...) -> UserPage
-Returns all edges for a user. -
-
-
-
- #### 🔌 Usage
@@ -6214,12 +3928,18 @@ Returns all edges for a user. ```python from zep_cloud import Zep +from zep_cloud.environment import ZepEnvironment client = Zep( - api_key="YOUR_API_KEY", + api_key="", + environment=ZepEnvironment.DEFAULT, ) -client.graph.edge.get_by_user_id( - user_id="user_id", + +client.user.list( + limit=1, + cursor="cursor", + order_by="order_by", + order="order", ) ``` @@ -6236,7 +3956,7 @@ client.graph.edge.get_by_user_id(
-**user_id:** `str` — User ID +**limit:** `typing.Optional[int]` — Page size
@@ -6244,10 +3964,15 @@ client.graph.edge.get_by_user_id(
-**cursor:** `typing.Optional[str]` +**cursor:** `typing.Optional[str]` — Opaque page cursor + +
+
+ +
+
-Opaque cursor for pagination, obtained from the Zep-Next-Cursor response header -of the previous page. Encodes the sort field, direction, and continuation position. +**order_by:** `typing.Optional[str]` — Sort field
@@ -6255,7 +3980,7 @@ of the previous page. Encodes the sort field, direction, and continuation positi
-**direction:** `typing.Optional[str]` — Sort direction. One of "asc" or "desc" (default "desc"). +**order:** `typing.Optional[str]` — asc or desc
@@ -6263,7 +3988,7 @@ of the previous page. Encodes the sort field, direction, and continuation positi
-**filters:** `typing.Optional[SearchFilters]` — Optional filters applied to the listed artifacts. Reuses the graph.search filter type. +**search:** `typing.Optional[str]`
@@ -6271,27 +3996,56 @@ of the previous page. Encodes the sort field, direction, and continuation positi
-**limit:** `typing.Optional[int]` — Maximum number of items to return +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration.
+
+
+ + + + +
+ +
client.user.lookup(...) -> User +
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from zep_cloud import Zep +from zep_cloud.environment import ZepEnvironment + +client = Zep( + api_key="", + environment=ZepEnvironment.DEFAULT, +) -
-
+client.user.lookup() -**order_by:** `typing.Optional[str]` — Field to sort by. One of "created_at", "valid_at", or "uuid" (default "uuid"). - +```
+
+
+ +#### ⚙️ Parameters
-**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:** `LookupRequest`
@@ -6311,24 +4065,10 @@ Deprecated: prefer Cursor, the opaque cursor returned via the Zep-Next-Cursor re
-
client.graph.edge.get(...) -
-
- -#### 📝 Description - -
-
- +
client.user.get(...) -> User
-Returns a specific edge by its UUID. -
-
-
-
- #### 🔌 Usage
@@ -6339,12 +4079,15 @@ Returns a specific edge by its UUID. ```python from zep_cloud import Zep +from zep_cloud.environment import ZepEnvironment client = Zep( - api_key="YOUR_API_KEY", + api_key="", + environment=ZepEnvironment.DEFAULT, ) -client.graph.edge.get( - uuid_="uuid", + +client.user.get( + user_uuid="user_uuid", ) ``` @@ -6361,7 +4104,7 @@ client.graph.edge.get(
-**uuid_:** `str` — Edge UUID +**user_uuid:** `str` — User UUID
@@ -6381,24 +4124,10 @@ client.graph.edge.get(
-
client.graph.edge.delete(...) -
-
- -#### 📝 Description - -
-
- +
client.user.delete(...) -> UserDeleteResult
-Deletes an edge by UUID. -
-
-
-
- #### 🔌 Usage
@@ -6409,12 +4138,15 @@ Deletes an edge by UUID. ```python from zep_cloud import Zep +from zep_cloud.environment import ZepEnvironment client = Zep( - api_key="YOUR_API_KEY", + api_key="", + environment=ZepEnvironment.DEFAULT, ) -client.graph.edge.delete( - uuid_="uuid", + +client.user.delete( + user_uuid="user_uuid", ) ``` @@ -6431,7 +4163,7 @@ client.graph.edge.delete(
-**uuid_:** `str` — Edge UUID +**user_uuid:** `str` — User UUID
@@ -6451,24 +4183,10 @@ client.graph.edge.delete(
-
client.graph.edge.update(...) -
-
- -#### 📝 Description - -
-
- +
client.user.update(...) -> User
-Updates an entity edge by UUID. -
-
-
-
- #### 🔌 Usage
@@ -6479,12 +4197,15 @@ Updates an entity edge by UUID. ```python from zep_cloud import Zep +from zep_cloud.environment import ZepEnvironment client = Zep( - api_key="YOUR_API_KEY", + api_key="", + environment=ZepEnvironment.DEFAULT, ) -client.graph.edge.update( - uuid_="uuid", + +client.user.update( + user_uuid="user_uuid", ) ``` @@ -6501,7 +4222,7 @@ client.graph.edge.update(
-**uuid_:** `str` — Edge UUID +**user_uuid:** `str` — User UUID
@@ -6509,7 +4230,7 @@ client.graph.edge.update(
-**attributes:** `typing.Optional[typing.Dict[str, typing.Optional[typing.Any]]]` — Updated attributes. Merged with existing attributes. Set a key to null to delete it. +**disable_default_ontology:** `typing.Optional[bool]` — Omit to leave unchanged, send JSON null to clear, or send a value to set.
@@ -6517,7 +4238,7 @@ client.graph.edge.update(
-**expired_at:** `typing.Optional[str]` — Updated time at which the edge expires +**email:** `typing.Optional[str]` — Omit to leave unchanged, send JSON null to clear, or send a value to set.
@@ -6525,7 +4246,7 @@ client.graph.edge.update(
-**fact:** `typing.Optional[str]` — Updated fact for the edge +**first_name:** `typing.Optional[str]` — Omit to leave unchanged, send JSON null to clear, or send a value to set.
@@ -6533,7 +4254,7 @@ client.graph.edge.update(
-**invalid_at:** `typing.Optional[str]` — Updated time at which the fact stopped being true +**last_name:** `typing.Optional[str]` — Omit to leave unchanged, send JSON null to clear, or send a value to set.
@@ -6541,7 +4262,7 @@ client.graph.edge.update(
-**name:** `typing.Optional[str]` — Updated name (relationship type) for the edge +**metadata:** `typing.Optional[typing.Dict[str, typing.Any]]`
@@ -6549,7 +4270,7 @@ client.graph.edge.update(
-**valid_at:** `typing.Optional[str]` — Updated time at which the fact becomes true +**time_zone:** `typing.Optional[str]` — Omit to leave unchanged, send JSON null to clear, or send a value to set.
@@ -6569,25 +4290,10 @@ client.graph.edge.update(
-## Graph Episode -
client.graph.episode.get_by_graph_id(...) -
-
- -#### 📝 Description - +
client.user.get_node(...) -> Node
-
-
- -Returns episodes by graph id. -
-
-
-
- #### 🔌 Usage
@@ -6598,13 +4304,15 @@ Returns episodes by graph id. ```python from zep_cloud import Zep +from zep_cloud.environment import ZepEnvironment client = Zep( - api_key="YOUR_API_KEY", + api_key="", + environment=ZepEnvironment.DEFAULT, ) -client.graph.episode.get_by_graph_id( - graph_id="graph_id", - lastn=1, + +client.user.get_node( + user_uuid="user_uuid", ) ``` @@ -6621,15 +4329,7 @@ client.graph.episode.get_by_graph_id(
-**graph_id:** `str` — Graph ID - -
-
- -
-
- -**lastn:** `typing.Optional[int]` — The number of most recent episodes to retrieve. +**user_uuid:** `str` — User UUID
@@ -6649,23 +4349,68 @@ client.graph.episode.get_by_graph_id(
-
client.graph.episode.list_by_graph_id(...) +
client.user.get_summary_instructions(...) -> UserSummaryInstructions +
+
+ +#### 🔌 Usage + +
+
+
-#### 📝 Description +```python +from zep_cloud import Zep +from zep_cloud.environment import ZepEnvironment + +client = Zep( + api_key="", + environment=ZepEnvironment.DEFAULT, +) + +client.user.get_summary_instructions( + user_uuid="user_uuid", +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+**user_uuid:** `str` — User UUID + +
+
+
-Returns a paginated, filterable list of episodes for a graph. +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+ +
+
+ +
client.user.set_summary_instructions(...) -> UserSummaryInstructions +
+
#### 🔌 Usage @@ -6677,12 +4422,15 @@ Returns a paginated, filterable list of episodes for a graph. ```python from zep_cloud import Zep +from zep_cloud.environment import ZepEnvironment client = Zep( - api_key="YOUR_API_KEY", + api_key="", + environment=ZepEnvironment.DEFAULT, ) -client.graph.episode.list_by_graph_id( - graph_id="graph_id", + +client.user.set_summary_instructions( + user_uuid="user_uuid", ) ``` @@ -6699,7 +4447,7 @@ client.graph.episode.list_by_graph_id(
-**graph_id:** `str` — Graph ID +**user_uuid:** `str` — User UUID
@@ -6707,10 +4455,7 @@ client.graph.episode.list_by_graph_id(
-**cursor:** `typing.Optional[str]` - -Opaque cursor for pagination, obtained from the Zep-Next-Cursor -response header of the previous page. +**request:** `UserSummaryInstructions`
@@ -6718,37 +4463,61 @@ response header of the previous page.
-**direction:** `typing.Optional[str]` — Sort direction. One of "asc" or "desc". Defaults to "desc". +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration.
+
+
+ + +
+
+
+## Graph DocumentSummary +
client.graph.document_summary.list(...) -> DocumentSummaryPage
-**limit:** `typing.Optional[int]` +#### 🔌 Usage -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]]` +```python +from zep_cloud import Zep +from zep_cloud.environment import ZepEnvironment -Restricts results to episodes that mention any of the listed node -UUIDs. At most 256 entries; each must be a syntactically valid UUID. - +client = Zep( + api_key="", + environment=ZepEnvironment.DEFAULT, +) + +client.graph.document_summary.list( + graph_uuid="graph_uuid", + limit=1, + cursor="cursor", +) + +``` +
+
+#### ⚙️ Parameters + +
+
+
-**order_by:** `typing.Optional[str]` — Field to sort by. One of "uuid" or "created_at". Defaults to "uuid". +**graph_uuid:** `str` — Graph UUID
@@ -6756,36 +4525,47 @@ UUIDs. At most 256 entries; each must be a syntactically valid UUID.
-**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. +**request:** `ArtifactListRequest`
-
-
+
+
+**limit:** `typing.Optional[int]` — Page size +
-
-
client.graph.episode.get_by_user_id(...)
-#### 📝 Description - -
-
+**cursor:** `typing.Optional[str]` — Opaque page cursor + +
+
-Returns episodes by user id. +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. +
+ + + +
+ +## Graph Episode +
client.graph.episode.list_for_document(...) -> EpisodePage +
+
+ #### 🔌 Usage
@@ -6796,13 +4576,18 @@ Returns episodes by user id. ```python from zep_cloud import Zep +from zep_cloud.environment import ZepEnvironment client = Zep( - api_key="YOUR_API_KEY", + api_key="", + environment=ZepEnvironment.DEFAULT, ) -client.graph.episode.get_by_user_id( - user_id="user_id", - lastn=1, + +client.graph.episode.list_for_document( + graph_uuid="graph_uuid", + document_id="document_id", + limit=1, + cursor="cursor", ) ``` @@ -6819,7 +4604,7 @@ client.graph.episode.get_by_user_id(
-**user_id:** `str` — User ID +**graph_uuid:** `str` — Graph UUID
@@ -6827,7 +4612,7 @@ client.graph.episode.get_by_user_id(
-**lastn:** `typing.Optional[int]` — The number of most recent episodes entries to retrieve. +**document_id:** `str` — Document ID
@@ -6835,35 +4620,37 @@ client.graph.episode.get_by_user_id(
-**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. +**limit:** `typing.Optional[int]` — Page size
-
-
- - - - -
-
client.graph.episode.list_by_user_id(...)
-#### 📝 Description - -
-
+**cursor:** `typing.Optional[str]` — Opaque page cursor + +
+
-Returns a paginated, filterable list of episodes for a user's graph. +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+ + +
+ +
client.graph.episode.add(...) -> AddEpisodeResult +
+
#### 🔌 Usage @@ -6875,12 +4662,15 @@ Returns a paginated, filterable list of episodes for a user's graph. ```python from zep_cloud import Zep +from zep_cloud.environment import ZepEnvironment client = Zep( - api_key="YOUR_API_KEY", + api_key="", + environment=ZepEnvironment.DEFAULT, ) -client.graph.episode.list_by_user_id( - user_id="user_id", + +client.graph.episode.add( + graph_uuid="graph_uuid", ) ``` @@ -6897,7 +4687,7 @@ client.graph.episode.list_by_user_id(
-**user_id:** `str` — User ID +**graph_uuid:** `str` — Graph UUID
@@ -6905,10 +4695,7 @@ client.graph.episode.list_by_user_id(
-**cursor:** `typing.Optional[str]` - -Opaque cursor for pagination, obtained from the Zep-Next-Cursor -response header of the previous page. +**created_at:** `typing.Optional[str]`
@@ -6916,7 +4703,7 @@ response header of the previous page.
-**direction:** `typing.Optional[str]` — Sort direction. One of "asc" or "desc". Defaults to "desc". +**data:** `typing.Optional[str]`
@@ -6924,10 +4711,15 @@ response header of the previous page.
-**limit:** `typing.Optional[int]` +**document_id:** `typing.Optional[str]` + +
+
+ +
+
-Maximum number of episodes to return. An explicit value is clamped to -50; when omitted, the default page size (100) applies. +**metadata:** `typing.Optional[typing.Dict[str, typing.Any]]`
@@ -6935,10 +4727,15 @@ Maximum number of episodes to return. An explicit value is clamped to
-**mentioned_node_uuids:** `typing.Optional[typing.Sequence[str]]` +**source_description:** `typing.Optional[str]` + +
+
-Restricts results to episodes that mention any of the listed node -UUIDs. At most 256 entries; each must be a syntactically valid UUID. +
+
+ +**strict_ontology:** `typing.Optional[bool]`
@@ -6946,7 +4743,7 @@ UUIDs. 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". +**type:** `typing.Optional[str]`
@@ -6966,11 +4763,11 @@ UUIDs. At most 256 entries; each must be a syntactically valid UUID.
-
client.graph.episode.get(...) +
client.graph.episode.list(...) -> EpisodePage
-#### 📝 Description +#### 🔌 Usage
@@ -6978,13 +4775,28 @@ UUIDs. At most 256 entries; each must be a syntactically valid UUID.
-Returns episodes by UUID +```python +from zep_cloud import Zep +from zep_cloud.environment import ZepEnvironment + +client = Zep( + api_key="", + environment=ZepEnvironment.DEFAULT, +) + +client.graph.episode.list( + graph_uuid="graph_uuid", + limit=1, + cursor="cursor", +) + +```
-#### 🔌 Usage +#### ⚙️ Parameters
@@ -6992,31 +4804,31 @@ Returns episodes by UUID
-```python -from zep_cloud import Zep - -client = Zep( - api_key="YOUR_API_KEY", -) -client.graph.episode.get( - uuid_="uuid", -) - -``` +**graph_uuid:** `str` — Graph UUID +
+ +
+
+ +**request:** `ArtifactListRequest` +
-#### ⚙️ Parameters -
+**limit:** `typing.Optional[int]` — Page size + +
+
+
-**uuid_:** `str` — Episode UUID +**cursor:** `typing.Optional[str]` — Opaque page cursor
@@ -7036,24 +4848,10 @@ client.graph.episode.get(
-
client.graph.episode.delete(...) -
-
- -#### 📝 Description - -
-
- +
client.graph.episode.get(...) -> Episode
-Deletes an episode by its UUID. -
-
-
-
- #### 🔌 Usage
@@ -7064,12 +4862,16 @@ Deletes an episode by its UUID. ```python from zep_cloud import Zep +from zep_cloud.environment import ZepEnvironment client = Zep( - api_key="YOUR_API_KEY", + api_key="", + environment=ZepEnvironment.DEFAULT, ) -client.graph.episode.delete( - uuid_="uuid", + +client.graph.episode.get( + graph_uuid="graph_uuid", + episode_uuid="episode_uuid", ) ``` @@ -7086,7 +4888,15 @@ client.graph.episode.delete(
-**uuid_:** `str` — Episode UUID +**graph_uuid:** `str` — Graph UUID + +
+
+ +
+
+ +**episode_uuid:** `str` — Episode UUID
@@ -7106,24 +4916,10 @@ client.graph.episode.delete(
-
client.graph.episode.update(...) -
-
- -#### 📝 Description - -
-
- +
client.graph.episode.delete(...) -> AsyncResult
-Update episode metadata with merge semantics. Supplied keys overwrite or add to existing metadata; keys set to null are removed. -
-
-
-
- #### 🔌 Usage
@@ -7134,13 +4930,16 @@ Update episode metadata with merge semantics. Supplied keys overwrite or add to ```python from zep_cloud import Zep +from zep_cloud.environment import ZepEnvironment client = Zep( - api_key="YOUR_API_KEY", + api_key="", + environment=ZepEnvironment.DEFAULT, ) -client.graph.episode.update( - uuid_="uuid", - metadata={"key": "value"}, + +client.graph.episode.delete( + graph_uuid="graph_uuid", + episode_uuid="episode_uuid", ) ``` @@ -7157,7 +4956,7 @@ client.graph.episode.update(
-**uuid_:** `str` — Episode UUID +**graph_uuid:** `str` — Graph UUID
@@ -7165,7 +4964,7 @@ client.graph.episode.update(
-**metadata:** `typing.Dict[str, typing.Optional[typing.Any]]` — Updated metadata. Merged with existing metadata: supplied keys overwrite/add, keys set to null are removed. Maximum 10 keys. Values must be scalars (string, number, boolean, null) or arrays of scalars. +**episode_uuid:** `str` — Episode UUID
@@ -7185,24 +4984,10 @@ client.graph.episode.update(
-
client.graph.episode.get_nodes_and_edges(...) -
-
- -#### 📝 Description - -
-
- +
client.graph.episode.update(...) -> Episode
-Deprecated. Use edge and node listing with `filters.episode_uuids` instead. Returns nodes and edges mentioned in an episode, subject to an internal cap; responses reduced by that cap set the Zep-Truncated header. -
-
-
-
- #### 🔌 Usage
@@ -7213,12 +4998,16 @@ Deprecated. Use edge and node listing with `filters.episode_uuids` instead. Retu ```python from zep_cloud import Zep +from zep_cloud.environment import ZepEnvironment client = Zep( - api_key="YOUR_API_KEY", + api_key="", + environment=ZepEnvironment.DEFAULT, ) -client.graph.episode.get_nodes_and_edges( - uuid_="uuid", + +client.graph.episode.update( + graph_uuid="graph_uuid", + episode_uuid="episode_uuid", ) ``` @@ -7235,7 +5024,7 @@ client.graph.episode.get_nodes_and_edges(
-**uuid_:** `str` — Episode uuid +**graph_uuid:** `str` — Graph UUID
@@ -7243,36 +5032,38 @@ client.graph.episode.get_nodes_and_edges(
-**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. +**episode_uuid:** `str` — Episode UUID
-
-
- - - - -
-## Graph Node -
client.graph.node.get_by_graph_id(...)
-#### 📝 Description - -
-
+**metadata:** `typing.Optional[typing.Dict[str, typing.Any]]` + +
+
-Returns all nodes for a graph. +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+ + +
+ +## Graph Edge +
client.graph.edge.add(...) -> AddEdgeResult +
+
#### 🔌 Usage @@ -7284,12 +5075,15 @@ Returns all nodes for a graph. ```python from zep_cloud import Zep +from zep_cloud.environment import ZepEnvironment client = Zep( - api_key="YOUR_API_KEY", + api_key="", + environment=ZepEnvironment.DEFAULT, ) -client.graph.node.get_by_graph_id( - graph_id="graph_id", + +client.graph.edge.add( + graph_uuid="graph_uuid", ) ``` @@ -7306,7 +5100,7 @@ client.graph.node.get_by_graph_id(
-**graph_id:** `str` — Graph ID +**graph_uuid:** `str` — Graph UUID
@@ -7314,10 +5108,7 @@ client.graph.node.get_by_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. +**attributes:** `typing.Optional[typing.Dict[str, typing.Any]]`
@@ -7325,7 +5116,7 @@ of the previous page. Encodes the sort field, direction, and continuation positi
-**direction:** `typing.Optional[str]` — Sort direction. One of "asc" or "desc" (default "desc"). +**expired_at:** `typing.Optional[str]`
@@ -7333,7 +5124,7 @@ of the previous page. Encodes the sort field, direction, and continuation positi
-**filters:** `typing.Optional[SearchFilters]` — Optional filters applied to the listed artifacts. Reuses the graph.search filter type. +**fact:** `typing.Optional[str]`
@@ -7341,7 +5132,7 @@ of the previous page. Encodes the sort field, direction, and continuation positi
-**limit:** `typing.Optional[int]` — Maximum number of items to return +**fact_name:** `typing.Optional[str]`
@@ -7349,7 +5140,7 @@ of the previous page. Encodes the sort field, direction, and continuation positi
-**order_by:** `typing.Optional[str]` — Field to sort by. One of "created_at", "valid_at", or "uuid" (default "uuid"). +**invalid_at:** `typing.Optional[str]`
@@ -7357,11 +5148,7 @@ of the previous page. Encodes the sort field, direction, and continuation positi
-**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. +**metadata:** `typing.Optional[typing.Dict[str, typing.Any]]`
@@ -7369,35 +5156,45 @@ Deprecated: prefer Cursor, the opaque cursor returned via the Zep-Next-Cursor re
-**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. +**source_node:** `typing.Optional[typing.Dict[str, typing.Any]]`
-
-
+
+
+**target_node:** `typing.Optional[typing.Dict[str, typing.Any]]` +
-
-
client.graph.node.get_by_user_id(...)
-#### 📝 Description - -
-
+**valid_at:** `typing.Optional[str]` + +
+
-Returns all nodes for a user +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+ + +
+ +
client.graph.edge.list(...) -> EdgePage +
+
#### 🔌 Usage @@ -7409,12 +5206,17 @@ Returns all nodes for a user ```python from zep_cloud import Zep +from zep_cloud.environment import ZepEnvironment client = Zep( - api_key="YOUR_API_KEY", + api_key="", + environment=ZepEnvironment.DEFAULT, ) -client.graph.node.get_by_user_id( - user_id="user_id", + +client.graph.edge.list( + graph_uuid="graph_uuid", + limit=1, + cursor="cursor", ) ``` @@ -7431,7 +5233,7 @@ client.graph.node.get_by_user_id(
-**user_id:** `str` — User ID +**graph_uuid:** `str` — Graph UUID
@@ -7439,10 +5241,7 @@ client.graph.node.get_by_user_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. +**request:** `ArtifactListRequest`
@@ -7450,7 +5249,7 @@ of the previous page. Encodes the sort field, direction, and continuation positi
-**direction:** `typing.Optional[str]` — Sort direction. One of "asc" or "desc" (default "desc"). +**limit:** `typing.Optional[int]` — Page size
@@ -7458,7 +5257,7 @@ of the previous page. Encodes the sort field, direction, and continuation positi
-**filters:** `typing.Optional[SearchFilters]` — Optional filters applied to the listed artifacts. Reuses the graph.search filter type. +**cursor:** `typing.Optional[str]` — Opaque page cursor
@@ -7466,63 +5265,89 @@ of the previous page. Encodes the sort field, direction, and continuation positi
-**limit:** `typing.Optional[int]` — Maximum number of items to return +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration.
+
+
-
-
-**order_by:** `typing.Optional[str]` — Field to sort by. One of "created_at", "valid_at", or "uuid" (default "uuid"). -
+
+
client.graph.edge.get(...) -> Edge
-**uuid_cursor:** `typing.Optional[str]` - -UUID based cursor, used for pagination. Should be the UUID of the last item in the previous page. +#### 🔌 Usage -Deprecated: prefer Cursor, the opaque cursor returned via the Zep-Next-Cursor response header. - -
-
+
+
-**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. - +```python +from zep_cloud import Zep +from zep_cloud.environment import ZepEnvironment + +client = Zep( + api_key="", + environment=ZepEnvironment.DEFAULT, +) + +client.graph.edge.get( + graph_uuid="graph_uuid", + edge_uuid="edge_uuid", +) + +```
+#### ⚙️ Parameters - - -
+
+
-
client.graph.node.get_edges(...)
-#### 📝 Description +**graph_uuid:** `str` — Graph UUID + +
+
+**edge_uuid:** `str` — Edge UUID + +
+
+
-Deprecated. Use edge listing with `filters.connected_node_uuids`, or the neighbors endpoint (`POST /graph/node/{node_uuid}/neighbors`), instead. Returns all edges for a node, subject to an internal cap; responses reduced by that cap set the Zep-Truncated header. +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+ + +
+ +
client.graph.edge.delete(...) -> AsyncResult +
+
#### 🔌 Usage @@ -7534,12 +5359,16 @@ Deprecated. Use edge listing with `filters.connected_node_uuids`, or the neighbo ```python from zep_cloud import Zep +from zep_cloud.environment import ZepEnvironment client = Zep( - api_key="YOUR_API_KEY", + api_key="", + environment=ZepEnvironment.DEFAULT, ) -client.graph.node.get_edges( - node_uuid="node_uuid", + +client.graph.edge.delete( + graph_uuid="graph_uuid", + edge_uuid="edge_uuid", ) ``` @@ -7556,7 +5385,15 @@ client.graph.node.get_edges(
-**node_uuid:** `str` — Node UUID +**graph_uuid:** `str` — Graph UUID + +
+
+ +
+
+ +**edge_uuid:** `str` — Edge UUID
@@ -7576,24 +5413,10 @@ client.graph.node.get_edges(
-
client.graph.node.get_episodes(...) +
client.graph.edge.update(...) -> Edge
-#### 📝 Description - -
-
- -
-
- -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. -
-
-
-
- #### 🔌 Usage
@@ -7604,12 +5427,16 @@ Deprecated. Use episode listing with `mentioned_node_uuids` (`POST /graph/episod ```python from zep_cloud import Zep +from zep_cloud.environment import ZepEnvironment client = Zep( - api_key="YOUR_API_KEY", + api_key="", + environment=ZepEnvironment.DEFAULT, ) -client.graph.node.get_episodes( - node_uuid="node_uuid", + +client.graph.edge.update( + graph_uuid="graph_uuid", + edge_uuid="edge_uuid", ) ``` @@ -7626,7 +5453,7 @@ client.graph.node.get_episodes(
-**node_uuid:** `str` — Node UUID +**graph_uuid:** `str` — Graph UUID
@@ -7634,35 +5461,46 @@ client.graph.node.get_episodes(
-**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. +**edge_uuid:** `str` — Edge UUID
-
-
+
+
+**attributes:** `typing.Optional[typing.Dict[str, typing.Any]]` +
-
-
client.graph.node.get_neighbors(...)
-#### 📝 Description - -
-
+**fact:** `typing.Optional[str]` — Omit to leave unchanged, send JSON null to clear, or send a value to set. + +
+
-Enumerates the distinct entity nodes directly connected to a node, together with the edges connecting each to it. +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+ + +
+ +## Graph Node +
client.graph.node.add(...) -> AddNodesResult +
+
#### 🔌 Usage @@ -7674,12 +5512,15 @@ Enumerates the distinct entity nodes directly connected to a node, together with ```python from zep_cloud import Zep +from zep_cloud.environment import ZepEnvironment client = Zep( - api_key="YOUR_API_KEY", + api_key="", + environment=ZepEnvironment.DEFAULT, ) -client.graph.node.get_neighbors( - node_uuid="node_uuid", + +client.graph.node.add( + graph_uuid="graph_uuid", ) ``` @@ -7696,7 +5537,7 @@ client.graph.node.get_neighbors(
-**node_uuid:** `str` — Node UUID +**graph_uuid:** `str` — Graph UUID
@@ -7704,10 +5545,7 @@ client.graph.node.get_neighbors(
-**cursor:** `typing.Optional[str]` - -Opaque cursor for pagination, obtained from the Zep-Next-Cursor -response header of the previous page. +**nodes:** `typing.Optional[typing.List[typing.Dict[str, typing.Any]]]`
@@ -7715,58 +5553,60 @@ response header of the previous page.
-**direction:** `typing.Optional[str]` - -Orientation of the connecting edge relative to the anchor node: "out" -(anchor is the edge's source), "in" (anchor is the edge's target), or -"both" (either). Defaults to "both". +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration.
+
+
-
-
- -**direction_sort:** `typing.Optional[str]` -Sort direction for order_by. One of "asc" or "desc". Defaults to -"desc". Named direction_sort to avoid clashing with the traversal -Direction field above. -
+
+
client.graph.node.list(...) -> NodePage
-**filters:** `typing.Optional[SearchFilters]` +#### 🔌 Usage -Filters constraining the connecting edges (edge types, dates, and the -section-3 node-/episode-anchored fields) and the neighbor nodes -(node_labels/exclude_node_labels). Reuses the graph.search filter -type. - -
-
+
+
-**limit:** `typing.Optional[int]` +```python +from zep_cloud import Zep +from zep_cloud.environment import ZepEnvironment -Maximum number of neighbor nodes to return. An explicit value is -clamped to 50; when omitted, the default page size (100) applies. - +client = Zep( + api_key="", + environment=ZepEnvironment.DEFAULT, +) + +client.graph.node.list( + graph_uuid="graph_uuid", + limit=1, + cursor="cursor", +) + +``` +
+
+#### ⚙️ Parameters +
-**order_by:** `typing.Optional[str]` +
+
-Field to sort neighbor nodes by. One of "uuid" or "created_at". -Defaults to "uuid". +**graph_uuid:** `str` — Graph UUID
@@ -7774,35 +5614,45 @@ Defaults to "uuid".
-**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. +**request:** `ArtifactListRequest`
-
-
+
+
+**limit:** `typing.Optional[int]` — Page size +
-
-
client.graph.node.get(...)
-#### 📝 Description - -
-
+**cursor:** `typing.Optional[str]` — Opaque page cursor + +
+
-Returns a specific node by its UUID. +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+ + +
+ +
client.graph.node.get(...) -> Node +
+
#### 🔌 Usage @@ -7814,12 +5664,16 @@ Returns a specific node by its UUID. ```python from zep_cloud import Zep +from zep_cloud.environment import ZepEnvironment client = Zep( - api_key="YOUR_API_KEY", + api_key="", + environment=ZepEnvironment.DEFAULT, ) + client.graph.node.get( - uuid_="uuid", + graph_uuid="graph_uuid", + node_uuid="node_uuid", ) ``` @@ -7836,7 +5690,15 @@ client.graph.node.get(
-**uuid_:** `str` — Node UUID +**graph_uuid:** `str` — Graph UUID + +
+
+ +
+
+ +**node_uuid:** `str` — Node UUID
@@ -7856,24 +5718,10 @@ client.graph.node.get(
-
client.graph.node.delete(...) -
-
- -#### 📝 Description - -
-
- +
client.graph.node.delete(...) -> AsyncResult
-Deletes a node by UUID. -
-
-
-
- #### 🔌 Usage
@@ -7884,12 +5732,16 @@ Deletes a node by UUID. ```python from zep_cloud import Zep +from zep_cloud.environment import ZepEnvironment client = Zep( - api_key="YOUR_API_KEY", + api_key="", + environment=ZepEnvironment.DEFAULT, ) + client.graph.node.delete( - uuid_="uuid", + graph_uuid="graph_uuid", + node_uuid="node_uuid", ) ``` @@ -7906,7 +5758,15 @@ client.graph.node.delete(
-**uuid_:** `str` — Node UUID +**graph_uuid:** `str` — Graph UUID + +
+
+ +
+
+ +**node_uuid:** `str` — Node UUID
@@ -7926,24 +5786,10 @@ client.graph.node.delete(
-
client.graph.node.update(...) -
-
- -#### 📝 Description - -
-
- +
client.graph.node.update(...) -> Node
-Updates an entity node by UUID. -
-
-
-
- #### 🔌 Usage
@@ -7954,12 +5800,16 @@ Updates an entity node by UUID. ```python from zep_cloud import Zep +from zep_cloud.environment import ZepEnvironment client = Zep( - api_key="YOUR_API_KEY", + api_key="", + environment=ZepEnvironment.DEFAULT, ) + client.graph.node.update( - uuid_="uuid", + graph_uuid="graph_uuid", + node_uuid="node_uuid", ) ``` @@ -7976,7 +5826,7 @@ client.graph.node.update(
-**uuid_:** `str` — Node UUID +**graph_uuid:** `str` — Graph UUID
@@ -7984,7 +5834,7 @@ client.graph.node.update(
-**attributes:** `typing.Optional[typing.Dict[str, typing.Optional[typing.Any]]]` — Updated attributes. Merged with existing attributes. Set a key to null to delete it. +**node_uuid:** `str` — Node UUID
@@ -7992,7 +5842,7 @@ client.graph.node.update(
-**labels:** `typing.Optional[typing.Sequence[str]]` — Updated labels for the node +**attributes:** `typing.Optional[typing.Dict[str, typing.Any]]`
@@ -8000,7 +5850,7 @@ client.graph.node.update(
-**name:** `typing.Optional[str]` — Updated name for the node +**name:** `typing.Optional[str]` — Omit to leave unchanged, send JSON null to clear, or send a value to set.
@@ -8008,7 +5858,7 @@ client.graph.node.update(
-**summary:** `typing.Optional[str]` — Updated summary for the node +**summary:** `typing.Optional[str]` — Omit to leave unchanged, send JSON null to clear, or send a value to set.
@@ -8028,25 +5878,10 @@ client.graph.node.update(
-## Graph Observation -
client.graph.observation.get_by_graph_id(...) -
-
- -#### 📝 Description - -
-
- +
client.graph.node.list_neighbors(...) -> NeighborPage
-Returns read-only observation nodes for a graph. -
-
-
-
- #### 🔌 Usage
@@ -8057,12 +5892,18 @@ Returns read-only observation nodes for a graph. ```python from zep_cloud import Zep +from zep_cloud.environment import ZepEnvironment client = Zep( - api_key="YOUR_API_KEY", + api_key="", + environment=ZepEnvironment.DEFAULT, ) -client.graph.observation.get_by_graph_id( - graph_id="graph_id", + +client.graph.node.list_neighbors( + graph_uuid="graph_uuid", + node_uuid="node_uuid", + limit=1, + cursor="cursor", ) ``` @@ -8079,18 +5920,7 @@ client.graph.observation.get_by_graph_id(
-**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. +**graph_uuid:** `str` — Graph UUID
@@ -8098,7 +5928,7 @@ of the previous page. Encodes the sort field, direction, and continuation positi
-**direction:** `typing.Optional[str]` — Sort direction. One of "asc" or "desc" (default "desc"). +**node_uuid:** `str` — Node UUID
@@ -8106,7 +5936,7 @@ of the previous page. Encodes the sort field, direction, and continuation positi
-**filters:** `typing.Optional[SearchFilters]` — Optional filters applied to the listed artifacts. Reuses the graph.search filter type. +**limit:** `typing.Optional[int]` — Page size
@@ -8114,7 +5944,7 @@ of the previous page. Encodes the sort field, direction, and continuation positi
-**limit:** `typing.Optional[int]` — Maximum number of items to return +**cursor:** `typing.Optional[str]` — Opaque page cursor
@@ -8122,7 +5952,7 @@ of the previous page. Encodes the sort field, direction, and continuation positi
-**order_by:** `typing.Optional[str]` — Field to sort by. One of "created_at", "valid_at", or "uuid" (default "uuid"). +**direction:** `typing.Optional[str]`
@@ -8130,11 +5960,7 @@ of the previous page. Encodes the sort field, direction, and continuation positi
-**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. +**filters:** `typing.Optional[typing.Dict[str, typing.Any]]`
@@ -8154,24 +5980,11 @@ Deprecated: prefer Cursor, the opaque cursor returned via the Zep-Next-Cursor re
-
client.graph.observation.get_by_user_id(...) -
-
- -#### 📝 Description - -
-
- +## Graph Observation +
client.graph.observation.list(...) -> ObservationPage
-Returns read-only observation nodes for a user's graph. -
-
-
-
- #### 🔌 Usage
@@ -8182,12 +5995,17 @@ Returns read-only observation nodes for a user's graph. ```python from zep_cloud import Zep +from zep_cloud.environment import ZepEnvironment client = Zep( - api_key="YOUR_API_KEY", + api_key="", + environment=ZepEnvironment.DEFAULT, ) -client.graph.observation.get_by_user_id( - user_id="user_id", + +client.graph.observation.list( + graph_uuid="graph_uuid", + limit=1, + cursor="cursor", ) ``` @@ -8204,34 +6022,7 @@ client.graph.observation.get_by_user_id(
-**user_id:** `str` — User 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. +**graph_uuid:** `str` — Graph UUID
@@ -8239,7 +6030,7 @@ of the previous page. Encodes the sort field, direction, and continuation positi
-**limit:** `typing.Optional[int]` — Maximum number of items to return +**request:** `ArtifactListRequest`
@@ -8247,7 +6038,7 @@ of the previous page. Encodes the sort field, direction, and continuation positi
-**order_by:** `typing.Optional[str]` — Field to sort by. One of "created_at", "valid_at", or "uuid" (default "uuid"). +**limit:** `typing.Optional[int]` — Page size
@@ -8255,11 +6046,7 @@ of the previous page. Encodes the sort field, direction, and continuation positi
-**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. +**cursor:** `typing.Optional[str]` — Opaque page cursor
@@ -8279,24 +6066,10 @@ Deprecated: prefer Cursor, the opaque cursor returned via the Zep-Next-Cursor re
-
client.graph.observation.get(...) -
-
- -#### 📝 Description - -
-
- +
client.graph.observation.get(...) -> Observation
-Returns a specific observation node by UUID. Observation nodes are read-only. -
-
-
-
- #### 🔌 Usage
@@ -8307,12 +6080,16 @@ Returns a specific observation node by UUID. Observation nodes are read-only. ```python from zep_cloud import Zep +from zep_cloud.environment import ZepEnvironment client = Zep( - api_key="YOUR_API_KEY", + api_key="", + environment=ZepEnvironment.DEFAULT, ) + client.graph.observation.get( - uuid_="uuid", + graph_uuid="graph_uuid", + observation_uuid="observation_uuid", ) ``` @@ -8329,7 +6106,15 @@ client.graph.observation.get(
-**uuid_:** `str` — Observation UUID +**graph_uuid:** `str` — Graph UUID + +
+
+ +
+
+ +**observation_uuid:** `str` — Observation UUID
@@ -8350,24 +6135,10 @@ client.graph.observation.get(
## Graph ThreadSummary -
client.graph.thread_summary.get_by_graph_id(...) -
-
- -#### 📝 Description - -
-
- +
client.graph.thread_summary.list(...) -> ThreadSummaryPage
-Returns incremental thread summaries associated with the graph. -
-
-
-
- #### 🔌 Usage
@@ -8378,12 +6149,17 @@ Returns incremental thread summaries associated with the graph. ```python from zep_cloud import Zep +from zep_cloud.environment import ZepEnvironment client = Zep( - api_key="YOUR_API_KEY", + api_key="", + environment=ZepEnvironment.DEFAULT, ) -client.graph.thread_summary.get_by_graph_id( - graph_id="graph_id", + +client.graph.thread_summary.list( + graph_uuid="graph_uuid", + limit=1, + cursor="cursor", ) ``` @@ -8400,34 +6176,7 @@ client.graph.thread_summary.get_by_graph_id(
-**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. +**graph_uuid:** `str` — Graph UUID
@@ -8435,7 +6184,7 @@ of the previous page. Encodes the sort field, direction, and continuation positi
-**limit:** `typing.Optional[int]` — Maximum number of items to return +**request:** `ArtifactListRequest`
@@ -8443,7 +6192,7 @@ of the previous page. Encodes the sort field, direction, and continuation positi
-**order_by:** `typing.Optional[str]` — Field to sort by. One of "created_at", "valid_at", or "uuid" (default "uuid"). +**limit:** `typing.Optional[int]` — Page size
@@ -8451,11 +6200,7 @@ of the previous page. Encodes the sort field, direction, and continuation positi
-**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. +**cursor:** `typing.Optional[str]` — Opaque page cursor
@@ -8475,24 +6220,11 @@ Deprecated: prefer Cursor, the opaque cursor returned via the Zep-Next-Cursor re
-
client.graph.thread_summary.get_by_user_id(...) -
-
- -#### 📝 Description - -
-
- +## Thread Message +
client.thread.message.get(...) -> Message
-Returns incremental thread summaries generated from messages in each thread associated with the user's graph. -
-
-
-
- #### 🔌 Usage
@@ -8503,12 +6235,16 @@ Returns incremental thread summaries generated from messages in each thread asso ```python from zep_cloud import Zep +from zep_cloud.environment import ZepEnvironment client = Zep( - api_key="YOUR_API_KEY", + api_key="", + environment=ZepEnvironment.DEFAULT, ) -client.graph.thread_summary.get_by_user_id( - user_id="user_id", + +client.thread.message.get( + thread_uuid="thread_uuid", + message_uuid="message_uuid", ) ``` @@ -8525,50 +6261,7 @@ client.graph.thread_summary.get_by_user_id(
-**user_id:** `str` — User 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"). +**thread_uuid:** `str` — Thread UUID
@@ -8576,11 +6269,7 @@ of the previous page. Encodes the sort field, direction, and continuation positi
-**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. +**message_uuid:** `str` — Message UUID
@@ -8600,25 +6289,10 @@ Deprecated: prefer Cursor, the opaque cursor returned via the Zep-Next-Cursor re
-## Thread Message -
client.thread.message.update(...) -
-
- -#### 📝 Description - +
client.thread.message.update(...) -> Message
-
-
- -Updates a message. -
-
-
-
- #### 🔌 Usage
@@ -8629,13 +6303,16 @@ Updates a message. ```python from zep_cloud import Zep +from zep_cloud.environment import ZepEnvironment client = Zep( - api_key="YOUR_API_KEY", + api_key="", + environment=ZepEnvironment.DEFAULT, ) + client.thread.message.update( - message_uuid="messageUUID", - metadata={"key": "value"}, + thread_uuid="thread_uuid", + message_uuid="message_uuid", ) ``` @@ -8652,7 +6329,15 @@ client.thread.message.update(
-**message_uuid:** `str` — The UUID of the message. +**thread_uuid:** `str` — Thread UUID + +
+
+ +
+
+ +**message_uuid:** `str` — Message UUID
@@ -8660,7 +6345,7 @@ client.thread.message.update(
-**metadata:** `typing.Dict[str, typing.Optional[typing.Any]]` +**metadata:** `typing.Optional[typing.Dict[str, typing.Any]]`
diff --git a/src/zep_cloud/__init__.py b/src/zep_cloud/__init__.py index 9b7b03c9..58bcf78c 100644 --- a/src/zep_cloud/__init__.py +++ b/src/zep_cloud/__init__.py @@ -2,230 +2,287 @@ # isort: skip_file -from .types import ( - AddNodeItem, - AddNodesResponse, - AddThreadMessagesRequest, - AddThreadMessagesResponse, - AddTripleResponse, - AddedNode, - ApiError, - ApidataBatchAddItemRole, - ApidataBatchAddItemType, - AttachedPolicySetsResponse, - BatchAddItem, - BatchItemDetail, - BatchItemKind, - BatchItemListResponse, - BatchItemStatus, - BatchListResponse, - BatchProgress, - BatchStatus, - BatchSummary, - CloneGraphResponse, - ClusterDetectConfig, - CoOccurrenceDetectConfig, - ComparisonOperator, - ContextTemplateResponse, - CustomInstruction, - DateFilter, - DerivedNode, - DetectConfig, - DetectPatternsResponse, - EdgeType, - EntityEdge, - EntityEdgeSourceTarget, - EntityNode, - EntityProperty, - EntityPropertyType, - EntityType, - EntityTypeResponse, - Episode, - EpisodeData, - EpisodeMentions, - EpisodeMetadataFilter, - EpisodeResponse, - GetTaskResponse, - Graph, - GraphDataType, - GraphEdgesRequest, - GraphEpisodeListRequest, - GraphListResponse, - GraphNodeNeighbor, - GraphNodesRequest, - GraphObservationsRequest, - GraphSearchResponseMetadata, - GraphSearchResults, - GraphSearchScope, - GraphSubgraphResponse, - GraphThreadSummariesRequest, - GraphitiMetadataFilterGroupType, - GraphitiSagaNode, - HubDetectConfig, - ListContextTemplatesResponse, - ListCustomInstructionsResponse, - ListUserInstructionsResponse, - Message, - MessageListResponse, - MetadataFilterGroup, - MutateUserGroupMembersRequest, - ObservationSteeringConfig, - ObservationType, - PathDetectConfig, - PatternMetadata, - PatternResult, - PatternSeeds, - PolicySetSummaryResponse, - ProjectInfo, - ProjectInfoResponse, - PropertyFilter, - RecencyWeight, - RelationshipDetectConfig, - Reranker, - RoleType, - SearchFilters, - SuccessResponse, - TaskErrorResponse, - TaskProgress, - Thread, - ThreadContextResponse, - ThreadListResponse, - ThreadSummary, - User, - UserGroup, - UserGroupKind, - UserGroupListResponse, - UserGroupMembersResponse, - UserGroupMembershipResponse, - UserGroupQuota, - UserGroupResponse, - UserInstruction, - UserListResponse, - UserMembershipGroupsResponse, - UserNodeResponse, -) -from .errors import BadRequestError, ConflictError, ForbiddenError, InternalServerError, NotFoundError -from . import batch, context, graph, project, task, thread, user, user_group -from .client import AsyncZep, Zep -from .environment import ZepEnvironment -from .version import __version__ +import typing +from importlib import import_module + +if typing.TYPE_CHECKING: + from .types import ( + AddEdgeResult, + AddEpisodeResult, + AddMessage, + AddMessagesResult, + AddNodesResult, + AddedEdge, + AddedNode, + ApiError, + ArtifactListRequest, + AsyncResult, + Batch, + BatchItem, + BatchItemPage, + BatchItemsResponse, + BatchPage, + CloneGraphResult, + ContextEdge, + ContextEpisode, + ContextNode, + ContextObservation, + ContextResults, + ContextTemplate, + ContextTemplatePage, + ContextThreadSummary, + CreateContextTemplateRequest, + DocumentSummary, + DocumentSummaryPage, + Edge, + EdgePage, + EdgeSourceTarget, + EdgeType, + EntityProperty, + EntityPropertyType, + EntityType, + Episode, + EpisodePage, + ErrorBody, + Graph, + GraphContextResponse, + GraphDeleteResult, + GraphPage, + Instructions, + LookupBatchResponse, + LookupItem, + LookupRequest, + Message, + MessagePage, + NeighborEntry, + NeighborPage, + Node, + NodePage, + Observation, + ObservationPage, + ObservationSteering, + Ontology, + ProcessBatchResult, + Project, + SearchRequest, + SubgraphResponse, + Task, + TaskPage, + TaskProgress, + Thread, + ThreadContextResponse, + ThreadDeleteResult, + ThreadPage, + ThreadSummary, + ThreadSummaryPage, + User, + UserDeleteResult, + UserPage, + UserSummaryInstructions, + ) + from .errors import BadRequestError, NotFoundError, UnauthorizedError + from . import batch, context, graph, lookup, project, task, thread, user + from .client import AsyncZep, Zep + from .environment import ZepEnvironment + from .version import __version__ +_dynamic_imports: typing.Dict[str, str] = { + "AddEdgeResult": ".types", + "AddEpisodeResult": ".types", + "AddMessage": ".types", + "AddMessagesResult": ".types", + "AddNodesResult": ".types", + "AddedEdge": ".types", + "AddedNode": ".types", + "ApiError": ".types", + "ArtifactListRequest": ".types", + "AsyncResult": ".types", + "AsyncZep": ".client", + "BadRequestError": ".errors", + "Batch": ".types", + "BatchItem": ".types", + "BatchItemPage": ".types", + "BatchItemsResponse": ".types", + "BatchPage": ".types", + "CloneGraphResult": ".types", + "ContextEdge": ".types", + "ContextEpisode": ".types", + "ContextNode": ".types", + "ContextObservation": ".types", + "ContextResults": ".types", + "ContextTemplate": ".types", + "ContextTemplatePage": ".types", + "ContextThreadSummary": ".types", + "CreateContextTemplateRequest": ".types", + "DocumentSummary": ".types", + "DocumentSummaryPage": ".types", + "Edge": ".types", + "EdgePage": ".types", + "EdgeSourceTarget": ".types", + "EdgeType": ".types", + "EntityProperty": ".types", + "EntityPropertyType": ".types", + "EntityType": ".types", + "Episode": ".types", + "EpisodePage": ".types", + "ErrorBody": ".types", + "Graph": ".types", + "GraphContextResponse": ".types", + "GraphDeleteResult": ".types", + "GraphPage": ".types", + "Instructions": ".types", + "LookupBatchResponse": ".types", + "LookupItem": ".types", + "LookupRequest": ".types", + "Message": ".types", + "MessagePage": ".types", + "NeighborEntry": ".types", + "NeighborPage": ".types", + "Node": ".types", + "NodePage": ".types", + "NotFoundError": ".errors", + "Observation": ".types", + "ObservationPage": ".types", + "ObservationSteering": ".types", + "Ontology": ".types", + "ProcessBatchResult": ".types", + "Project": ".types", + "SearchRequest": ".types", + "SubgraphResponse": ".types", + "Task": ".types", + "TaskPage": ".types", + "TaskProgress": ".types", + "Thread": ".types", + "ThreadContextResponse": ".types", + "ThreadDeleteResult": ".types", + "ThreadPage": ".types", + "ThreadSummary": ".types", + "ThreadSummaryPage": ".types", + "UnauthorizedError": ".errors", + "User": ".types", + "UserDeleteResult": ".types", + "UserPage": ".types", + "UserSummaryInstructions": ".types", + "Zep": ".client", + "ZepEnvironment": ".environment", + "__version__": ".version", + "batch": ".batch", + "context": ".context", + "graph": ".graph", + "lookup": ".lookup", + "project": ".project", + "task": ".task", + "thread": ".thread", + "user": ".user", +} + + +def __getattr__(attr_name: str) -> typing.Any: + module_name = _dynamic_imports.get(attr_name) + if module_name is None: + raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") + try: + module = import_module(module_name, __package__) + if module_name == f".{attr_name}": + return module + else: + return getattr(module, attr_name) + except ImportError as e: + raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e + except AttributeError as e: + raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e + + +def __dir__(): + lazy_attrs = list(_dynamic_imports.keys()) + return sorted(lazy_attrs) + __all__ = [ - "AddNodeItem", - "AddNodesResponse", - "AddThreadMessagesRequest", - "AddThreadMessagesResponse", - "AddTripleResponse", + "AddEdgeResult", + "AddEpisodeResult", + "AddMessage", + "AddMessagesResult", + "AddNodesResult", + "AddedEdge", "AddedNode", "ApiError", - "ApidataBatchAddItemRole", - "ApidataBatchAddItemType", + "ArtifactListRequest", + "AsyncResult", "AsyncZep", - "AttachedPolicySetsResponse", "BadRequestError", - "BatchAddItem", - "BatchItemDetail", - "BatchItemKind", - "BatchItemListResponse", - "BatchItemStatus", - "BatchListResponse", - "BatchProgress", - "BatchStatus", - "BatchSummary", - "CloneGraphResponse", - "ClusterDetectConfig", - "CoOccurrenceDetectConfig", - "ComparisonOperator", - "ConflictError", - "ContextTemplateResponse", - "CustomInstruction", - "DateFilter", - "DerivedNode", - "DetectConfig", - "DetectPatternsResponse", + "Batch", + "BatchItem", + "BatchItemPage", + "BatchItemsResponse", + "BatchPage", + "CloneGraphResult", + "ContextEdge", + "ContextEpisode", + "ContextNode", + "ContextObservation", + "ContextResults", + "ContextTemplate", + "ContextTemplatePage", + "ContextThreadSummary", + "CreateContextTemplateRequest", + "DocumentSummary", + "DocumentSummaryPage", + "Edge", + "EdgePage", + "EdgeSourceTarget", "EdgeType", - "EntityEdge", - "EntityEdgeSourceTarget", - "EntityNode", "EntityProperty", "EntityPropertyType", "EntityType", - "EntityTypeResponse", "Episode", - "EpisodeData", - "EpisodeMentions", - "EpisodeMetadataFilter", - "EpisodeResponse", - "ForbiddenError", - "GetTaskResponse", + "EpisodePage", + "ErrorBody", "Graph", - "GraphDataType", - "GraphEdgesRequest", - "GraphEpisodeListRequest", - "GraphListResponse", - "GraphNodeNeighbor", - "GraphNodesRequest", - "GraphObservationsRequest", - "GraphSearchResponseMetadata", - "GraphSearchResults", - "GraphSearchScope", - "GraphSubgraphResponse", - "GraphThreadSummariesRequest", - "GraphitiMetadataFilterGroupType", - "GraphitiSagaNode", - "HubDetectConfig", - "InternalServerError", - "ListContextTemplatesResponse", - "ListCustomInstructionsResponse", - "ListUserInstructionsResponse", + "GraphContextResponse", + "GraphDeleteResult", + "GraphPage", + "Instructions", + "LookupBatchResponse", + "LookupItem", + "LookupRequest", "Message", - "MessageListResponse", - "MetadataFilterGroup", - "MutateUserGroupMembersRequest", + "MessagePage", + "NeighborEntry", + "NeighborPage", + "Node", + "NodePage", "NotFoundError", - "ObservationSteeringConfig", - "ObservationType", - "PathDetectConfig", - "PatternMetadata", - "PatternResult", - "PatternSeeds", - "PolicySetSummaryResponse", - "ProjectInfo", - "ProjectInfoResponse", - "PropertyFilter", - "RecencyWeight", - "RelationshipDetectConfig", - "Reranker", - "RoleType", - "SearchFilters", - "SuccessResponse", - "TaskErrorResponse", + "Observation", + "ObservationPage", + "ObservationSteering", + "Ontology", + "ProcessBatchResult", + "Project", + "SearchRequest", + "SubgraphResponse", + "Task", + "TaskPage", "TaskProgress", "Thread", "ThreadContextResponse", - "ThreadListResponse", + "ThreadDeleteResult", + "ThreadPage", "ThreadSummary", + "ThreadSummaryPage", + "UnauthorizedError", "User", - "UserGroup", - "UserGroupKind", - "UserGroupListResponse", - "UserGroupMembersResponse", - "UserGroupMembershipResponse", - "UserGroupQuota", - "UserGroupResponse", - "UserInstruction", - "UserListResponse", - "UserMembershipGroupsResponse", - "UserNodeResponse", + "UserDeleteResult", + "UserPage", + "UserSummaryInstructions", "Zep", "ZepEnvironment", "__version__", "batch", "context", "graph", + "lookup", "project", "task", "thread", "user", - "user_group", ] diff --git a/src/zep_cloud/base_client.py b/src/zep_cloud/base_client.py index e95c27b5..998188e7 100644 --- a/src/zep_cloud/base_client.py +++ b/src/zep_cloud/base_client.py @@ -1,20 +1,25 @@ # This file was auto-generated by Fern from our API Definition. +from __future__ import annotations + import os import typing import httpx -from .batch.client import AsyncBatchClient, BatchClient -from .context.client import AsyncContextClient, ContextClient from .core.api_error import ApiError from .core.client_wrapper import AsyncClientWrapper, SyncClientWrapper +from .core.logging import LogConfig, Logger from .environment import ZepEnvironment -from .graph.client import AsyncGraphClient, GraphClient -from .project.client import AsyncProjectClient, ProjectClient -from .task.client import AsyncTaskClient, TaskClient -from .thread.client import AsyncThreadClient, ThreadClient -from .user.client import AsyncUserClient, UserClient -from .user_group.client import AsyncUserGroupClient, UserGroupClient + +if typing.TYPE_CHECKING: + from .batch.client import AsyncBatchClient, BatchClient + from .context.client import AsyncContextClient, ContextClient + from .graph.client import AsyncGraphClient, GraphClient + from .lookup.client import AsyncLookupClient, LookupClient + from .project.client import AsyncProjectClient, ProjectClient + from .task.client import AsyncTaskClient, TaskClient + from .thread.client import AsyncThreadClient, ThreadClient + from .user.client import AsyncUserClient, UserClient class BaseClient: @@ -48,6 +53,9 @@ class BaseClient: httpx_client : typing.Optional[httpx.Client] The httpx client to use for making requests, a preconfigured client is used by default, however this is useful should you want to pass in any custom httpx configuration. + logging : typing.Optional[typing.Union[LogConfig, Logger]] + Configure logging for the SDK. Accepts a LogConfig dict with 'level' (debug/info/warn/error), 'logger' (custom logger implementation), and 'silent' (boolean, defaults to True) fields. You can also pass a pre-configured Logger instance. + Examples -------- from zep_cloud import Zep @@ -67,6 +75,7 @@ def __init__( timeout: typing.Optional[float] = None, follow_redirects: typing.Optional[bool] = True, httpx_client: typing.Optional[httpx.Client] = None, + logging: typing.Optional[typing.Union[LogConfig, Logger]] = None, ): _defaulted_timeout = ( timeout if timeout is not None else 60 if httpx_client is None else httpx_client.timeout.read @@ -83,15 +92,80 @@ def __init__( if follow_redirects is not None else httpx.Client(timeout=_defaulted_timeout), timeout=_defaulted_timeout, + logging=logging, ) - self.user_group = UserGroupClient(client_wrapper=self._client_wrapper) - self.batch = BatchClient(client_wrapper=self._client_wrapper) - self.context = ContextClient(client_wrapper=self._client_wrapper) - self.graph = GraphClient(client_wrapper=self._client_wrapper) - self.project = ProjectClient(client_wrapper=self._client_wrapper) - self.task = TaskClient(client_wrapper=self._client_wrapper) - self.thread = ThreadClient(client_wrapper=self._client_wrapper) - self.user = UserClient(client_wrapper=self._client_wrapper) + self._batch: typing.Optional[BatchClient] = None + self._context: typing.Optional[ContextClient] = None + self._graph: typing.Optional[GraphClient] = None + self._lookup: typing.Optional[LookupClient] = None + self._project: typing.Optional[ProjectClient] = None + self._task: typing.Optional[TaskClient] = None + self._thread: typing.Optional[ThreadClient] = None + self._user: typing.Optional[UserClient] = None + + @property + def batch(self): + if self._batch is None: + from .batch.client import BatchClient # noqa: E402 + + self._batch = BatchClient(client_wrapper=self._client_wrapper) + return self._batch + + @property + def context(self): + if self._context is None: + from .context.client import ContextClient # noqa: E402 + + self._context = ContextClient(client_wrapper=self._client_wrapper) + return self._context + + @property + def graph(self): + if self._graph is None: + from .graph.client import GraphClient # noqa: E402 + + self._graph = GraphClient(client_wrapper=self._client_wrapper) + return self._graph + + @property + def lookup(self): + if self._lookup is None: + from .lookup.client import LookupClient # noqa: E402 + + self._lookup = LookupClient(client_wrapper=self._client_wrapper) + return self._lookup + + @property + def project(self): + if self._project is None: + from .project.client import ProjectClient # noqa: E402 + + self._project = ProjectClient(client_wrapper=self._client_wrapper) + return self._project + + @property + def task(self): + if self._task is None: + from .task.client import TaskClient # noqa: E402 + + self._task = TaskClient(client_wrapper=self._client_wrapper) + return self._task + + @property + def thread(self): + if self._thread is None: + from .thread.client import ThreadClient # noqa: E402 + + self._thread = ThreadClient(client_wrapper=self._client_wrapper) + return self._thread + + @property + def user(self): + if self._user is None: + from .user.client import UserClient # noqa: E402 + + self._user = UserClient(client_wrapper=self._client_wrapper) + return self._user class AsyncBaseClient: @@ -125,6 +199,9 @@ class AsyncBaseClient: httpx_client : typing.Optional[httpx.AsyncClient] The httpx client to use for making requests, a preconfigured client is used by default, however this is useful should you want to pass in any custom httpx configuration. + logging : typing.Optional[typing.Union[LogConfig, Logger]] + Configure logging for the SDK. Accepts a LogConfig dict with 'level' (debug/info/warn/error), 'logger' (custom logger implementation), and 'silent' (boolean, defaults to True) fields. You can also pass a pre-configured Logger instance. + Examples -------- from zep_cloud import AsyncZep @@ -144,6 +221,7 @@ def __init__( timeout: typing.Optional[float] = None, follow_redirects: typing.Optional[bool] = True, httpx_client: typing.Optional[httpx.AsyncClient] = None, + logging: typing.Optional[typing.Union[LogConfig, Logger]] = None, ): _defaulted_timeout = ( timeout if timeout is not None else 60 if httpx_client is None else httpx_client.timeout.read @@ -160,15 +238,80 @@ def __init__( if follow_redirects is not None else httpx.AsyncClient(timeout=_defaulted_timeout), timeout=_defaulted_timeout, + logging=logging, ) - self.user_group = AsyncUserGroupClient(client_wrapper=self._client_wrapper) - self.batch = AsyncBatchClient(client_wrapper=self._client_wrapper) - self.context = AsyncContextClient(client_wrapper=self._client_wrapper) - self.graph = AsyncGraphClient(client_wrapper=self._client_wrapper) - self.project = AsyncProjectClient(client_wrapper=self._client_wrapper) - self.task = AsyncTaskClient(client_wrapper=self._client_wrapper) - self.thread = AsyncThreadClient(client_wrapper=self._client_wrapper) - self.user = AsyncUserClient(client_wrapper=self._client_wrapper) + self._batch: typing.Optional[AsyncBatchClient] = None + self._context: typing.Optional[AsyncContextClient] = None + self._graph: typing.Optional[AsyncGraphClient] = None + self._lookup: typing.Optional[AsyncLookupClient] = None + self._project: typing.Optional[AsyncProjectClient] = None + self._task: typing.Optional[AsyncTaskClient] = None + self._thread: typing.Optional[AsyncThreadClient] = None + self._user: typing.Optional[AsyncUserClient] = None + + @property + def batch(self): + if self._batch is None: + from .batch.client import AsyncBatchClient # noqa: E402 + + self._batch = AsyncBatchClient(client_wrapper=self._client_wrapper) + return self._batch + + @property + def context(self): + if self._context is None: + from .context.client import AsyncContextClient # noqa: E402 + + self._context = AsyncContextClient(client_wrapper=self._client_wrapper) + return self._context + + @property + def graph(self): + if self._graph is None: + from .graph.client import AsyncGraphClient # noqa: E402 + + self._graph = AsyncGraphClient(client_wrapper=self._client_wrapper) + return self._graph + + @property + def lookup(self): + if self._lookup is None: + from .lookup.client import AsyncLookupClient # noqa: E402 + + self._lookup = AsyncLookupClient(client_wrapper=self._client_wrapper) + return self._lookup + + @property + def project(self): + if self._project is None: + from .project.client import AsyncProjectClient # noqa: E402 + + self._project = AsyncProjectClient(client_wrapper=self._client_wrapper) + return self._project + + @property + def task(self): + if self._task is None: + from .task.client import AsyncTaskClient # noqa: E402 + + self._task = AsyncTaskClient(client_wrapper=self._client_wrapper) + return self._task + + @property + def thread(self): + if self._thread is None: + from .thread.client import AsyncThreadClient # noqa: E402 + + self._thread = AsyncThreadClient(client_wrapper=self._client_wrapper) + return self._thread + + @property + def user(self): + if self._user is None: + from .user.client import AsyncUserClient # noqa: E402 + + self._user = AsyncUserClient(client_wrapper=self._client_wrapper) + return self._user def _get_base_url(*, base_url: typing.Optional[str] = None, environment: ZepEnvironment) -> str: diff --git a/src/zep_cloud/batch/client.py b/src/zep_cloud/batch/client.py index 4b56e20a..52781b8d 100644 --- a/src/zep_cloud/batch/client.py +++ b/src/zep_cloud/batch/client.py @@ -3,14 +3,14 @@ import typing from ..core.client_wrapper import AsyncClientWrapper, SyncClientWrapper +from ..core.pagination import AsyncPager, SyncPager from ..core.request_options import RequestOptions -from ..types.batch_add_item import BatchAddItem -from ..types.batch_item_detail import BatchItemDetail -from ..types.batch_item_list_response import BatchItemListResponse -from ..types.batch_list_response import BatchListResponse -from ..types.batch_summary import BatchSummary -from ..types.role_type import RoleType -from ..types.success_response import SuccessResponse +from ..types.batch import Batch +from ..types.batch_item import BatchItem +from ..types.batch_item_page import BatchItemPage +from ..types.batch_items_response import BatchItemsResponse +from ..types.batch_page import BatchPage +from ..types.process_batch_result import ProcessBatchResult from .raw_client import AsyncRawBatchClient, RawBatchClient # this is used as the default value for optional parameters @@ -36,31 +36,29 @@ def list( self, *, limit: typing.Optional[int] = None, - cursor: typing.Optional[int] = None, + cursor: typing.Optional[str] = None, status: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, - ) -> BatchListResponse: + ) -> SyncPager[Batch, BatchPage]: """ - List batches for the current project, optionally filtered by batch status. - Parameters ---------- limit : typing.Optional[int] - Maximum number of batches to return. + Page size - cursor : typing.Optional[int] - Pagination cursor from a previous response. + cursor : typing.Optional[str] + Opaque page cursor status : typing.Optional[str] - Batch status filter. + Batch status filter request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - BatchListResponse - Batch list + SyncPager[Batch, BatchPage] + OK Examples -------- @@ -69,42 +67,46 @@ def list( client = Zep( api_key="YOUR_API_KEY", ) - client.batch.list( + response = client.batch.list( limit=1, - cursor=1, + cursor="cursor", status="status", ) + for item in response: + yield item + # alternatively, you can paginate page-by-page + for page in response.iter_pages(): + yield page """ - _response = self._raw_client.list(limit=limit, cursor=cursor, status=status, request_options=request_options) - return _response.data + return self._raw_client.list(limit=limit, cursor=cursor, status=status, request_options=request_options) def create( self, *, - ignore_roles: typing.Optional[typing.Sequence[RoleType]] = OMIT, - metadata: typing.Optional[typing.Dict[str, typing.Optional[typing.Any]]] = OMIT, + ignore_roles: typing.Optional[typing.Sequence[str]] = OMIT, + metadata: typing.Optional[typing.Dict[str, typing.Any]] = OMIT, + strict_ontology: typing.Optional[bool] = OMIT, + idempotency_key: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, - ) -> BatchSummary: + ) -> Batch: """ - Create a draft batch that can be filled with graph episodes and thread messages. - Parameters ---------- - ignore_roles : typing.Optional[typing.Sequence[RoleType]] - Optional list of message role types to skip during graph ingestion for - thread_message items in this batch. The messages are still stored and - retained as context, but no graph extraction is performed for them. - Has no effect on graph_episode items. + ignore_roles : typing.Optional[typing.Sequence[str]] + + metadata : typing.Optional[typing.Dict[str, typing.Any]] - metadata : typing.Optional[typing.Dict[str, typing.Optional[typing.Any]]] + strict_ontology : typing.Optional[bool] + + idempotency_key : typing.Optional[str] request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - BatchSummary - Created batch + Batch + Created Examples -------- @@ -116,26 +118,28 @@ 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, + idempotency_key=idempotency_key, + request_options=request_options, ) return _response.data - def get(self, batch_id: str, *, request_options: typing.Optional[RequestOptions] = None) -> BatchSummary: + def get(self, batch_uuid: str, *, request_options: typing.Optional[RequestOptions] = None) -> Batch: """ - Get a batch summary, including runtime progress when the batch has been processed. - Parameters ---------- - batch_id : str - The batch ID. + batch_uuid : str + Batch UUID request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - BatchSummary - Batch summary + Batch + OK Examples -------- @@ -145,28 +149,33 @@ def get(self, batch_id: str, *, request_options: typing.Optional[RequestOptions] api_key="YOUR_API_KEY", ) client.batch.get( - batch_id="batchId", + batch_uuid="batch_uuid", ) """ - _response = self._raw_client.get(batch_id, request_options=request_options) + _response = self._raw_client.get(batch_uuid, request_options=request_options) return _response.data - def delete(self, batch_id: str, *, request_options: typing.Optional[RequestOptions] = None) -> SuccessResponse: + def delete( + self, + batch_uuid: str, + *, + idempotency_key: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> None: """ - Delete a draft or invalid unprocessed batch. Processed batches cannot be deleted. - Parameters ---------- - batch_id : str - The batch ID. + batch_uuid : str + Batch UUID + + idempotency_key : typing.Optional[str] request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - SuccessResponse - Deleted batch + None Examples -------- @@ -176,45 +185,41 @@ def delete(self, batch_id: str, *, request_options: typing.Optional[RequestOptio api_key="YOUR_API_KEY", ) client.batch.delete( - batch_id="batchId", + batch_uuid="batch_uuid", ) """ - _response = self._raw_client.delete(batch_id, request_options=request_options) + _response = self._raw_client.delete( + batch_uuid, idempotency_key=idempotency_key, request_options=request_options + ) return _response.data def list_items( self, - batch_id: str, + batch_uuid: str, *, limit: typing.Optional[int] = None, - cursor: typing.Optional[int] = None, - status: typing.Optional[str] = None, + cursor: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, - ) -> BatchItemListResponse: + ) -> SyncPager[BatchItem, BatchItemPage]: """ - List items in a batch, including derived runtime status when the batch has been processed. - Parameters ---------- - batch_id : str - The batch ID. + batch_uuid : str + Batch UUID limit : typing.Optional[int] - Maximum number of batch items to return. - - cursor : typing.Optional[int] - Pagination cursor from a previous response. + Page size - status : typing.Optional[str] - Batch item status filter. + cursor : typing.Optional[str] + Opaque page cursor request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - BatchItemListResponse - Batch item list + SyncPager[BatchItem, BatchItemPage] + OK Examples -------- @@ -223,78 +228,83 @@ def list_items( client = Zep( api_key="YOUR_API_KEY", ) - client.batch.list_items( - batch_id="batchId", + response = client.batch.list_items( + batch_uuid="batch_uuid", limit=1, - cursor=1, - status="status", + cursor="cursor", ) + for item in response: + yield item + # alternatively, you can paginate page-by-page + for page in response.iter_pages(): + yield page """ - _response = self._raw_client.list_items( - batch_id, limit=limit, cursor=cursor, status=status, request_options=request_options - ) - return _response.data + return self._raw_client.list_items(batch_uuid, limit=limit, cursor=cursor, request_options=request_options) - def add( + def add_items( self, - batch_id: str, + batch_uuid: str, *, - items: typing.Sequence[BatchAddItem], + items: typing.Optional[typing.Sequence[typing.Dict[str, typing.Any]]] = OMIT, + idempotency_key: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, - ) -> typing.List[BatchItemDetail]: + ) -> BatchItemsResponse: """ - Add graph episodes and thread messages to a draft batch. Items are appended in request order. - Parameters ---------- - batch_id : str - The batch ID. + batch_uuid : str + Batch UUID + + items : typing.Optional[typing.Sequence[typing.Dict[str, typing.Any]]] - items : typing.Sequence[BatchAddItem] + idempotency_key : typing.Optional[str] request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - typing.List[BatchItemDetail] - Added batch items + BatchItemsResponse + OK Examples -------- - from zep_cloud import BatchAddItem, Zep + from zep_cloud import Zep client = Zep( api_key="YOUR_API_KEY", ) - client.batch.add( - batch_id="batchId", - items=[ - BatchAddItem( - type="graph_episode", - ) - ], + client.batch.add_items( + batch_uuid="batch_uuid", ) """ - _response = self._raw_client.add(batch_id, items=items, request_options=request_options) + _response = self._raw_client.add_items( + batch_uuid, items=items, idempotency_key=idempotency_key, request_options=request_options + ) return _response.data - def process(self, batch_id: str, *, request_options: typing.Optional[RequestOptions] = None) -> BatchSummary: + def process( + self, + batch_uuid: str, + *, + idempotency_key: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> ProcessBatchResult: """ - Start processing a filled batch. Repeated calls return a conflict. - Parameters ---------- - batch_id : str - The batch ID. + batch_uuid : str + Batch UUID + + idempotency_key : typing.Optional[str] request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - BatchSummary - Batch processing state + ProcessBatchResult + Accepted Examples -------- @@ -304,10 +314,12 @@ def process(self, batch_id: str, *, request_options: typing.Optional[RequestOpti api_key="YOUR_API_KEY", ) client.batch.process( - batch_id="batchId", + batch_uuid="batch_uuid", ) """ - _response = self._raw_client.process(batch_id, request_options=request_options) + _response = self._raw_client.process( + batch_uuid, idempotency_key=idempotency_key, request_options=request_options + ) return _response.data @@ -330,31 +342,29 @@ async def list( self, *, limit: typing.Optional[int] = None, - cursor: typing.Optional[int] = None, + cursor: typing.Optional[str] = None, status: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, - ) -> BatchListResponse: + ) -> AsyncPager[Batch, BatchPage]: """ - List batches for the current project, optionally filtered by batch status. - Parameters ---------- limit : typing.Optional[int] - Maximum number of batches to return. + Page size - cursor : typing.Optional[int] - Pagination cursor from a previous response. + cursor : typing.Optional[str] + Opaque page cursor status : typing.Optional[str] - Batch status filter. + Batch status filter request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - BatchListResponse - Batch list + AsyncPager[Batch, BatchPage] + OK Examples -------- @@ -368,47 +378,50 @@ async def list( async def main() -> None: - await client.batch.list( + response = await client.batch.list( limit=1, - cursor=1, + cursor="cursor", status="status", ) + async for item in response: + yield item + + # alternatively, you can paginate page-by-page + async for page in response.iter_pages(): + yield page asyncio.run(main()) """ - _response = await self._raw_client.list( - limit=limit, cursor=cursor, status=status, request_options=request_options - ) - return _response.data + return await self._raw_client.list(limit=limit, cursor=cursor, status=status, request_options=request_options) async def create( self, *, - ignore_roles: typing.Optional[typing.Sequence[RoleType]] = OMIT, - metadata: typing.Optional[typing.Dict[str, typing.Optional[typing.Any]]] = OMIT, + ignore_roles: typing.Optional[typing.Sequence[str]] = OMIT, + metadata: typing.Optional[typing.Dict[str, typing.Any]] = OMIT, + strict_ontology: typing.Optional[bool] = OMIT, + idempotency_key: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, - ) -> BatchSummary: + ) -> Batch: """ - Create a draft batch that can be filled with graph episodes and thread messages. - Parameters ---------- - ignore_roles : typing.Optional[typing.Sequence[RoleType]] - Optional list of message role types to skip during graph ingestion for - thread_message items in this batch. The messages are still stored and - retained as context, but no graph extraction is performed for them. - Has no effect on graph_episode items. + ignore_roles : typing.Optional[typing.Sequence[str]] - metadata : typing.Optional[typing.Dict[str, typing.Optional[typing.Any]]] + metadata : typing.Optional[typing.Dict[str, typing.Any]] + + strict_ontology : typing.Optional[bool] + + idempotency_key : typing.Optional[str] request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - BatchSummary - Created batch + Batch + Created Examples -------- @@ -428,26 +441,28 @@ 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, + idempotency_key=idempotency_key, + request_options=request_options, ) return _response.data - async def get(self, batch_id: str, *, request_options: typing.Optional[RequestOptions] = None) -> BatchSummary: + async def get(self, batch_uuid: str, *, request_options: typing.Optional[RequestOptions] = None) -> Batch: """ - Get a batch summary, including runtime progress when the batch has been processed. - Parameters ---------- - batch_id : str - The batch ID. + batch_uuid : str + Batch UUID request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - BatchSummary - Batch summary + Batch + OK Examples -------- @@ -462,33 +477,36 @@ async def get(self, batch_id: str, *, request_options: typing.Optional[RequestOp async def main() -> None: await client.batch.get( - batch_id="batchId", + batch_uuid="batch_uuid", ) asyncio.run(main()) """ - _response = await self._raw_client.get(batch_id, request_options=request_options) + _response = await self._raw_client.get(batch_uuid, request_options=request_options) return _response.data async def delete( - self, batch_id: str, *, request_options: typing.Optional[RequestOptions] = None - ) -> SuccessResponse: + self, + batch_uuid: str, + *, + idempotency_key: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> None: """ - Delete a draft or invalid unprocessed batch. Processed batches cannot be deleted. - Parameters ---------- - batch_id : str - The batch ID. + batch_uuid : str + Batch UUID + + idempotency_key : typing.Optional[str] request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - SuccessResponse - Deleted batch + None Examples -------- @@ -503,48 +521,44 @@ async def delete( async def main() -> None: await client.batch.delete( - batch_id="batchId", + batch_uuid="batch_uuid", ) asyncio.run(main()) """ - _response = await self._raw_client.delete(batch_id, request_options=request_options) + _response = await self._raw_client.delete( + batch_uuid, idempotency_key=idempotency_key, request_options=request_options + ) return _response.data async def list_items( self, - batch_id: str, + batch_uuid: str, *, limit: typing.Optional[int] = None, - cursor: typing.Optional[int] = None, - status: typing.Optional[str] = None, + cursor: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, - ) -> BatchItemListResponse: + ) -> AsyncPager[BatchItem, BatchItemPage]: """ - List items in a batch, including derived runtime status when the batch has been processed. - Parameters ---------- - batch_id : str - The batch ID. + batch_uuid : str + Batch UUID limit : typing.Optional[int] - Maximum number of batch items to return. + Page size - cursor : typing.Optional[int] - Pagination cursor from a previous response. - - status : typing.Optional[str] - Batch item status filter. + cursor : typing.Optional[str] + Opaque page cursor request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - BatchItemListResponse - Batch item list + AsyncPager[BatchItem, BatchItemPage] + OK Examples -------- @@ -558,51 +572,56 @@ async def list_items( async def main() -> None: - await client.batch.list_items( - batch_id="batchId", + response = await client.batch.list_items( + batch_uuid="batch_uuid", limit=1, - cursor=1, - status="status", + cursor="cursor", ) + async for item in response: + yield item + + # alternatively, you can paginate page-by-page + async for page in response.iter_pages(): + yield page asyncio.run(main()) """ - _response = await self._raw_client.list_items( - batch_id, limit=limit, cursor=cursor, status=status, request_options=request_options + return await self._raw_client.list_items( + batch_uuid, limit=limit, cursor=cursor, request_options=request_options ) - return _response.data - async def add( + async def add_items( self, - batch_id: str, + batch_uuid: str, *, - items: typing.Sequence[BatchAddItem], + items: typing.Optional[typing.Sequence[typing.Dict[str, typing.Any]]] = OMIT, + idempotency_key: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, - ) -> typing.List[BatchItemDetail]: + ) -> BatchItemsResponse: """ - Add graph episodes and thread messages to a draft batch. Items are appended in request order. - Parameters ---------- - batch_id : str - The batch ID. + batch_uuid : str + Batch UUID - items : typing.Sequence[BatchAddItem] + items : typing.Optional[typing.Sequence[typing.Dict[str, typing.Any]]] + + idempotency_key : typing.Optional[str] request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - typing.List[BatchItemDetail] - Added batch items + BatchItemsResponse + OK Examples -------- import asyncio - from zep_cloud import AsyncZep, BatchAddItem + from zep_cloud import AsyncZep client = AsyncZep( api_key="YOUR_API_KEY", @@ -610,37 +629,40 @@ async def add( async def main() -> None: - await client.batch.add( - batch_id="batchId", - items=[ - BatchAddItem( - type="graph_episode", - ) - ], + await client.batch.add_items( + batch_uuid="batch_uuid", ) asyncio.run(main()) """ - _response = await self._raw_client.add(batch_id, items=items, request_options=request_options) + _response = await self._raw_client.add_items( + batch_uuid, items=items, idempotency_key=idempotency_key, request_options=request_options + ) return _response.data - async def process(self, batch_id: str, *, request_options: typing.Optional[RequestOptions] = None) -> BatchSummary: + async def process( + self, + batch_uuid: str, + *, + idempotency_key: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> ProcessBatchResult: """ - Start processing a filled batch. Repeated calls return a conflict. - Parameters ---------- - batch_id : str - The batch ID. + batch_uuid : str + Batch UUID + + idempotency_key : typing.Optional[str] request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - BatchSummary - Batch processing state + ProcessBatchResult + Accepted Examples -------- @@ -655,11 +677,13 @@ async def process(self, batch_id: str, *, request_options: typing.Optional[Reque async def main() -> None: await client.batch.process( - batch_id="batchId", + batch_uuid="batch_uuid", ) asyncio.run(main()) """ - _response = await self._raw_client.process(batch_id, request_options=request_options) + _response = await self._raw_client.process( + batch_uuid, idempotency_key=idempotency_key, 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..195c53ae 100644 --- a/src/zep_cloud/batch/raw_client.py +++ b/src/zep_cloud/batch/raw_client.py @@ -7,22 +7,21 @@ from ..core.client_wrapper import AsyncClientWrapper, SyncClientWrapper from ..core.http_response import AsyncHttpResponse, HttpResponse from ..core.jsonable_encoder import jsonable_encoder +from ..core.pagination import AsyncPager, SyncPager +from ..core.parse_error import ParsingError 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.conflict_error import ConflictError -from ..errors.forbidden_error import ForbiddenError -from ..errors.internal_server_error import InternalServerError from ..errors.not_found_error import NotFoundError +from ..errors.unauthorized_error import UnauthorizedError from ..types.api_error import ApiError as types_api_error_ApiError -from ..types.batch_add_item import BatchAddItem -from ..types.batch_item_detail import BatchItemDetail -from ..types.batch_item_list_response import BatchItemListResponse -from ..types.batch_list_response import BatchListResponse -from ..types.batch_summary import BatchSummary -from ..types.role_type import RoleType -from ..types.success_response import SuccessResponse +from ..types.batch import Batch +from ..types.batch_item import BatchItem +from ..types.batch_item_page import BatchItemPage +from ..types.batch_items_response import BatchItemsResponse +from ..types.batch_page import BatchPage +from ..types.process_batch_result import ProcessBatchResult +from pydantic import ValidationError # this is used as the default value for optional parameters OMIT = typing.cast(typing.Any, ...) @@ -36,31 +35,29 @@ def list( self, *, limit: typing.Optional[int] = None, - cursor: typing.Optional[int] = None, + cursor: typing.Optional[str] = None, status: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, - ) -> HttpResponse[BatchListResponse]: + ) -> SyncPager[Batch, BatchPage]: """ - List batches for the current project, optionally filtered by batch status. - Parameters ---------- limit : typing.Optional[int] - Maximum number of batches to return. + Page size - cursor : typing.Optional[int] - Pagination cursor from a previous response. + cursor : typing.Optional[str] + Opaque page cursor status : typing.Optional[str] - Batch status filter. + Batch status filter request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - HttpResponse[BatchListResponse] - Batch list + SyncPager[Batch, BatchPage] + OK """ _response = self._client_wrapper.httpx_client.request( "batches", @@ -74,27 +71,36 @@ def list( ) try: if 200 <= _response.status_code < 300: - _data = typing.cast( - BatchListResponse, + _parsed_response = typing.cast( + BatchPage, parse_obj_as( - type_=BatchListResponse, # type: ignore + type_=BatchPage, # type: ignore object_=_response.json(), ), ) - return HttpResponse(response=_response, data=_data) + _items = _parsed_response.items + _parsed_next = _parsed_response.next_cursor + _has_next = _parsed_next is not None and _parsed_next != "" + _get_next = lambda: self.list( + limit=limit, + cursor=_parsed_next, + status=status, + request_options=request_options, + ) + return SyncPager(has_next=_has_next, items=_items, get_next=_get_next, response=_parsed_response) if _response.status_code == 400: raise BadRequestError( headers=dict(_response.headers), body=typing.cast( - typing.Optional[typing.Any], + types_api_error_ApiError, parse_obj_as( - type_=typing.Optional[typing.Any], # type: ignore + type_=types_api_error_ApiError, # type: ignore object_=_response.json(), ), ), ) - if _response.status_code == 403: - raise ForbiddenError( + if _response.status_code == 401: + raise UnauthorizedError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -104,8 +110,8 @@ def list( ), ), ) - if _response.status_code == 500: - raise InternalServerError( + if _response.status_code == 404: + raise NotFoundError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -120,6 +126,10 @@ def list( raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response.text ) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response_json ) @@ -127,30 +137,30 @@ def list( def create( self, *, - ignore_roles: typing.Optional[typing.Sequence[RoleType]] = OMIT, - metadata: typing.Optional[typing.Dict[str, typing.Optional[typing.Any]]] = OMIT, + ignore_roles: typing.Optional[typing.Sequence[str]] = OMIT, + metadata: typing.Optional[typing.Dict[str, typing.Any]] = OMIT, + strict_ontology: typing.Optional[bool] = OMIT, + idempotency_key: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, - ) -> HttpResponse[BatchSummary]: + ) -> HttpResponse[Batch]: """ - Create a draft batch that can be filled with graph episodes and thread messages. - Parameters ---------- - ignore_roles : typing.Optional[typing.Sequence[RoleType]] - Optional list of message role types to skip during graph ingestion for - thread_message items in this batch. The messages are still stored and - retained as context, but no graph extraction is performed for them. - Has no effect on graph_episode items. + ignore_roles : typing.Optional[typing.Sequence[str]] + + metadata : typing.Optional[typing.Dict[str, typing.Any]] - metadata : typing.Optional[typing.Dict[str, typing.Optional[typing.Any]]] + strict_ontology : typing.Optional[bool] + + idempotency_key : typing.Optional[str] request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - HttpResponse[BatchSummary] - Created batch + HttpResponse[Batch] + Created """ _response = self._client_wrapper.httpx_client.request( "batches", @@ -158,9 +168,11 @@ def create( json={ "ignore_roles": ignore_roles, "metadata": metadata, + "strict_ontology": strict_ontology, }, headers={ "content-type": "application/json", + "Idempotency-Key": str(idempotency_key) if idempotency_key is not None else None, }, request_options=request_options, omit=OMIT, @@ -168,9 +180,9 @@ def create( try: if 200 <= _response.status_code < 300: _data = typing.cast( - BatchSummary, + Batch, parse_obj_as( - type_=BatchSummary, # type: ignore + type_=Batch, # type: ignore object_=_response.json(), ), ) @@ -179,15 +191,15 @@ def create( raise BadRequestError( headers=dict(_response.headers), body=typing.cast( - typing.Optional[typing.Any], + types_api_error_ApiError, parse_obj_as( - type_=typing.Optional[typing.Any], # type: ignore + type_=types_api_error_ApiError, # type: ignore object_=_response.json(), ), ), ) - if _response.status_code == 403: - raise ForbiddenError( + if _response.status_code == 401: + raise UnauthorizedError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -197,8 +209,8 @@ def create( ), ), ) - if _response.status_code == 500: - raise InternalServerError( + if _response.status_code == 404: + raise NotFoundError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -213,57 +225,46 @@ def create( raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response.text ) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response_json ) - def get( - self, batch_id: str, *, request_options: typing.Optional[RequestOptions] = None - ) -> HttpResponse[BatchSummary]: + def get(self, batch_uuid: str, *, request_options: typing.Optional[RequestOptions] = None) -> HttpResponse[Batch]: """ - Get a batch summary, including runtime progress when the batch has been processed. - Parameters ---------- - batch_id : str - The batch ID. + batch_uuid : str + Batch UUID request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - HttpResponse[BatchSummary] - Batch summary + HttpResponse[Batch] + OK """ _response = self._client_wrapper.httpx_client.request( - f"batches/{jsonable_encoder(batch_id)}", + f"batches/{jsonable_encoder(batch_uuid)}", method="GET", request_options=request_options, ) try: if 200 <= _response.status_code < 300: _data = typing.cast( - BatchSummary, + Batch, parse_obj_as( - type_=BatchSummary, # type: ignore + type_=Batch, # 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 == 403: - raise ForbiddenError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -273,8 +274,8 @@ def get( ), ), ) - if _response.status_code == 404: - raise NotFoundError( + if _response.status_code == 401: + raise UnauthorizedError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -284,8 +285,8 @@ def get( ), ), ) - if _response.status_code == 500: - raise InternalServerError( + if _response.status_code == 404: + raise NotFoundError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -300,68 +301,49 @@ def get( raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response.text ) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response_json ) def delete( - self, batch_id: str, *, request_options: typing.Optional[RequestOptions] = None - ) -> HttpResponse[SuccessResponse]: + self, + batch_uuid: str, + *, + idempotency_key: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[None]: """ - Delete a draft or invalid unprocessed batch. Processed batches cannot be deleted. - Parameters ---------- - batch_id : str - The batch ID. + batch_uuid : str + Batch UUID + + idempotency_key : typing.Optional[str] request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - HttpResponse[SuccessResponse] - Deleted batch + HttpResponse[None] """ _response = self._client_wrapper.httpx_client.request( - f"batches/{jsonable_encoder(batch_id)}", + f"batches/{jsonable_encoder(batch_uuid)}", method="DELETE", + headers={ + "Idempotency-Key": str(idempotency_key) if idempotency_key is not None else None, + }, request_options=request_options, ) try: if 200 <= _response.status_code < 300: - _data = typing.cast( - SuccessResponse, - parse_obj_as( - type_=SuccessResponse, # type: ignore - object_=_response.json(), - ), - ) - return HttpResponse(response=_response, data=_data) + return HttpResponse(response=_response, data=None) 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 == 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, @@ -371,8 +353,8 @@ def delete( ), ), ) - if _response.status_code == 409: - raise ConflictError( + if _response.status_code == 401: + raise UnauthorizedError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -382,8 +364,8 @@ def delete( ), ), ) - if _response.status_code == 500: - raise InternalServerError( + if _response.status_code == 404: + raise NotFoundError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -398,77 +380,72 @@ def delete( raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response.text ) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response_json ) def list_items( self, - batch_id: str, + batch_uuid: str, *, limit: typing.Optional[int] = None, - cursor: typing.Optional[int] = None, - status: typing.Optional[str] = None, + cursor: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, - ) -> HttpResponse[BatchItemListResponse]: + ) -> SyncPager[BatchItem, BatchItemPage]: """ - List items in a batch, including derived runtime status when the batch has been processed. - Parameters ---------- - batch_id : str - The batch ID. + batch_uuid : str + Batch UUID limit : typing.Optional[int] - Maximum number of batch items to return. + Page size - cursor : typing.Optional[int] - Pagination cursor from a previous response. - - status : typing.Optional[str] - Batch item status filter. + cursor : typing.Optional[str] + Opaque page cursor request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - HttpResponse[BatchItemListResponse] - Batch item list + SyncPager[BatchItem, BatchItemPage] + OK """ _response = self._client_wrapper.httpx_client.request( - f"batches/{jsonable_encoder(batch_id)}/items", + f"batches/{jsonable_encoder(batch_uuid)}/items", method="GET", params={ "limit": limit, "cursor": cursor, - "status": status, }, request_options=request_options, ) try: if 200 <= _response.status_code < 300: - _data = typing.cast( - BatchItemListResponse, + _parsed_response = typing.cast( + BatchItemPage, parse_obj_as( - type_=BatchItemListResponse, # type: ignore + type_=BatchItemPage, # type: ignore object_=_response.json(), ), ) - return HttpResponse(response=_response, data=_data) + _items = _parsed_response.items + _parsed_next = _parsed_response.next_cursor + _has_next = _parsed_next is not None and _parsed_next != "" + _get_next = lambda: self.list_items( + batch_uuid, + limit=limit, + cursor=_parsed_next, + request_options=request_options, + ) + return SyncPager(has_next=_has_next, items=_items, get_next=_get_next, response=_parsed_response) 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 == 403: - raise ForbiddenError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -478,8 +455,8 @@ def list_items( ), ), ) - if _response.status_code == 404: - raise NotFoundError( + if _response.status_code == 401: + raise UnauthorizedError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -489,8 +466,8 @@ def list_items( ), ), ) - if _response.status_code == 500: - raise InternalServerError( + if _response.status_code == 404: + raise NotFoundError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -505,45 +482,49 @@ def list_items( raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response.text ) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response_json ) - def add( + def add_items( self, - batch_id: str, + batch_uuid: str, *, - items: typing.Sequence[BatchAddItem], + items: typing.Optional[typing.Sequence[typing.Dict[str, typing.Any]]] = OMIT, + idempotency_key: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, - ) -> HttpResponse[typing.List[BatchItemDetail]]: + ) -> HttpResponse[BatchItemsResponse]: """ - Add graph episodes and thread messages to a draft batch. Items are appended in request order. - Parameters ---------- - batch_id : str - The batch ID. + batch_uuid : str + Batch UUID - items : typing.Sequence[BatchAddItem] + items : typing.Optional[typing.Sequence[typing.Dict[str, typing.Any]]] + + idempotency_key : typing.Optional[str] request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - HttpResponse[typing.List[BatchItemDetail]] - Added batch items + HttpResponse[BatchItemsResponse] + OK """ _response = self._client_wrapper.httpx_client.request( - f"batches/{jsonable_encoder(batch_id)}/items", + f"batches/{jsonable_encoder(batch_uuid)}/items", method="POST", json={ - "items": convert_and_respect_annotation_metadata( - object_=items, annotation=typing.Sequence[BatchAddItem], direction="write" - ), + "items": items, }, headers={ "content-type": "application/json", + "Idempotency-Key": str(idempotency_key) if idempotency_key is not None else None, }, request_options=request_options, omit=OMIT, @@ -551,37 +532,15 @@ def add( try: if 200 <= _response.status_code < 300: _data = typing.cast( - typing.List[BatchItemDetail], + BatchItemsResponse, parse_obj_as( - type_=typing.List[BatchItemDetail], # type: ignore + type_=BatchItemsResponse, # 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 == 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, @@ -591,8 +550,8 @@ def add( ), ), ) - if _response.status_code == 409: - raise ConflictError( + if _response.status_code == 401: + raise UnauthorizedError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -602,8 +561,8 @@ def add( ), ), ) - if _response.status_code == 500: - raise InternalServerError( + if _response.status_code == 404: + raise NotFoundError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -618,57 +577,57 @@ def add( raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response.text ) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response_json ) def process( - self, batch_id: str, *, request_options: typing.Optional[RequestOptions] = None - ) -> HttpResponse[BatchSummary]: + self, + batch_uuid: str, + *, + idempotency_key: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[ProcessBatchResult]: """ - Start processing a filled batch. Repeated calls return a conflict. - Parameters ---------- - batch_id : str - The batch ID. + batch_uuid : str + Batch UUID + + idempotency_key : typing.Optional[str] request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - HttpResponse[BatchSummary] - Batch processing state + HttpResponse[ProcessBatchResult] + Accepted """ _response = self._client_wrapper.httpx_client.request( - f"batches/{jsonable_encoder(batch_id)}/process", + f"batches/{jsonable_encoder(batch_uuid)}/process", method="POST", + headers={ + "Idempotency-Key": str(idempotency_key) if idempotency_key is not None else None, + }, request_options=request_options, ) try: if 200 <= _response.status_code < 300: _data = typing.cast( - BatchSummary, + ProcessBatchResult, parse_obj_as( - type_=BatchSummary, # type: ignore + type_=ProcessBatchResult, # 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 == 403: - raise ForbiddenError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -678,8 +637,8 @@ def process( ), ), ) - if _response.status_code == 404: - raise NotFoundError( + if _response.status_code == 401: + raise UnauthorizedError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -689,19 +648,8 @@ def process( ), ), ) - if _response.status_code == 409: - raise ConflictError( - 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( + if _response.status_code == 404: + raise NotFoundError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -716,6 +664,10 @@ def process( raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response.text ) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response_json ) @@ -729,31 +681,29 @@ async def list( self, *, limit: typing.Optional[int] = None, - cursor: typing.Optional[int] = None, + cursor: typing.Optional[str] = None, status: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, - ) -> AsyncHttpResponse[BatchListResponse]: + ) -> AsyncPager[Batch, BatchPage]: """ - List batches for the current project, optionally filtered by batch status. - Parameters ---------- limit : typing.Optional[int] - Maximum number of batches to return. + Page size - cursor : typing.Optional[int] - Pagination cursor from a previous response. + cursor : typing.Optional[str] + Opaque page cursor status : typing.Optional[str] - Batch status filter. + Batch status filter request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - AsyncHttpResponse[BatchListResponse] - Batch list + AsyncPager[Batch, BatchPage] + OK """ _response = await self._client_wrapper.httpx_client.request( "batches", @@ -767,27 +717,39 @@ async def list( ) try: if 200 <= _response.status_code < 300: - _data = typing.cast( - BatchListResponse, + _parsed_response = typing.cast( + BatchPage, parse_obj_as( - type_=BatchListResponse, # type: ignore + type_=BatchPage, # type: ignore object_=_response.json(), ), ) - return AsyncHttpResponse(response=_response, data=_data) + _items = _parsed_response.items + _parsed_next = _parsed_response.next_cursor + _has_next = _parsed_next is not None and _parsed_next != "" + + async def _get_next(): + return await self.list( + limit=limit, + cursor=_parsed_next, + status=status, + request_options=request_options, + ) + + return AsyncPager(has_next=_has_next, items=_items, get_next=_get_next, response=_parsed_response) if _response.status_code == 400: raise BadRequestError( headers=dict(_response.headers), body=typing.cast( - typing.Optional[typing.Any], + types_api_error_ApiError, parse_obj_as( - type_=typing.Optional[typing.Any], # type: ignore + type_=types_api_error_ApiError, # type: ignore object_=_response.json(), ), ), ) - if _response.status_code == 403: - raise ForbiddenError( + if _response.status_code == 401: + raise UnauthorizedError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -797,8 +759,8 @@ async def list( ), ), ) - if _response.status_code == 500: - raise InternalServerError( + if _response.status_code == 404: + raise NotFoundError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -813,6 +775,10 @@ async def list( raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response.text ) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response_json ) @@ -820,30 +786,30 @@ async def list( async def create( self, *, - ignore_roles: typing.Optional[typing.Sequence[RoleType]] = OMIT, - metadata: typing.Optional[typing.Dict[str, typing.Optional[typing.Any]]] = OMIT, + ignore_roles: typing.Optional[typing.Sequence[str]] = OMIT, + metadata: typing.Optional[typing.Dict[str, typing.Any]] = OMIT, + strict_ontology: typing.Optional[bool] = OMIT, + idempotency_key: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, - ) -> AsyncHttpResponse[BatchSummary]: + ) -> AsyncHttpResponse[Batch]: """ - Create a draft batch that can be filled with graph episodes and thread messages. - Parameters ---------- - ignore_roles : typing.Optional[typing.Sequence[RoleType]] - Optional list of message role types to skip during graph ingestion for - thread_message items in this batch. The messages are still stored and - retained as context, but no graph extraction is performed for them. - Has no effect on graph_episode items. + ignore_roles : typing.Optional[typing.Sequence[str]] + + metadata : typing.Optional[typing.Dict[str, typing.Any]] - metadata : typing.Optional[typing.Dict[str, typing.Optional[typing.Any]]] + strict_ontology : typing.Optional[bool] + + idempotency_key : typing.Optional[str] request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - AsyncHttpResponse[BatchSummary] - Created batch + AsyncHttpResponse[Batch] + Created """ _response = await self._client_wrapper.httpx_client.request( "batches", @@ -851,9 +817,11 @@ async def create( json={ "ignore_roles": ignore_roles, "metadata": metadata, + "strict_ontology": strict_ontology, }, headers={ "content-type": "application/json", + "Idempotency-Key": str(idempotency_key) if idempotency_key is not None else None, }, request_options=request_options, omit=OMIT, @@ -861,9 +829,9 @@ async def create( try: if 200 <= _response.status_code < 300: _data = typing.cast( - BatchSummary, + Batch, parse_obj_as( - type_=BatchSummary, # type: ignore + type_=Batch, # type: ignore object_=_response.json(), ), ) @@ -872,15 +840,15 @@ async def create( raise BadRequestError( headers=dict(_response.headers), body=typing.cast( - typing.Optional[typing.Any], + types_api_error_ApiError, parse_obj_as( - type_=typing.Optional[typing.Any], # type: ignore + type_=types_api_error_ApiError, # type: ignore object_=_response.json(), ), ), ) - if _response.status_code == 403: - raise ForbiddenError( + if _response.status_code == 401: + raise UnauthorizedError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -890,8 +858,8 @@ async def create( ), ), ) - if _response.status_code == 500: - raise InternalServerError( + if _response.status_code == 404: + raise NotFoundError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -906,57 +874,48 @@ async def create( raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response.text ) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response_json ) async def get( - self, batch_id: str, *, request_options: typing.Optional[RequestOptions] = None - ) -> AsyncHttpResponse[BatchSummary]: + self, batch_uuid: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[Batch]: """ - Get a batch summary, including runtime progress when the batch has been processed. - Parameters ---------- - batch_id : str - The batch ID. + batch_uuid : str + Batch UUID request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - AsyncHttpResponse[BatchSummary] - Batch summary + AsyncHttpResponse[Batch] + OK """ _response = await self._client_wrapper.httpx_client.request( - f"batches/{jsonable_encoder(batch_id)}", + f"batches/{jsonable_encoder(batch_uuid)}", method="GET", request_options=request_options, ) try: if 200 <= _response.status_code < 300: _data = typing.cast( - BatchSummary, + Batch, parse_obj_as( - type_=BatchSummary, # type: ignore + type_=Batch, # 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 == 403: - raise ForbiddenError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -966,8 +925,8 @@ async def get( ), ), ) - if _response.status_code == 404: - raise NotFoundError( + if _response.status_code == 401: + raise UnauthorizedError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -977,8 +936,8 @@ async def get( ), ), ) - if _response.status_code == 500: - raise InternalServerError( + if _response.status_code == 404: + raise NotFoundError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -993,68 +952,49 @@ async def get( raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response.text ) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response_json ) async def delete( - self, batch_id: str, *, request_options: typing.Optional[RequestOptions] = None - ) -> AsyncHttpResponse[SuccessResponse]: + self, + batch_uuid: str, + *, + idempotency_key: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[None]: """ - Delete a draft or invalid unprocessed batch. Processed batches cannot be deleted. - Parameters ---------- - batch_id : str - The batch ID. + batch_uuid : str + Batch UUID + + idempotency_key : typing.Optional[str] request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - AsyncHttpResponse[SuccessResponse] - Deleted batch + AsyncHttpResponse[None] """ _response = await self._client_wrapper.httpx_client.request( - f"batches/{jsonable_encoder(batch_id)}", + f"batches/{jsonable_encoder(batch_uuid)}", method="DELETE", + headers={ + "Idempotency-Key": str(idempotency_key) if idempotency_key is not None else None, + }, request_options=request_options, ) try: if 200 <= _response.status_code < 300: - _data = typing.cast( - SuccessResponse, - parse_obj_as( - type_=SuccessResponse, # type: ignore - object_=_response.json(), - ), - ) - return AsyncHttpResponse(response=_response, data=_data) + return AsyncHttpResponse(response=_response, data=None) 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 == 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, @@ -1064,8 +1004,8 @@ async def delete( ), ), ) - if _response.status_code == 409: - raise ConflictError( + if _response.status_code == 401: + raise UnauthorizedError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -1075,8 +1015,8 @@ async def delete( ), ), ) - if _response.status_code == 500: - raise InternalServerError( + if _response.status_code == 404: + raise NotFoundError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -1091,77 +1031,75 @@ async def delete( raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response.text ) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response_json ) async def list_items( self, - batch_id: str, + batch_uuid: str, *, limit: typing.Optional[int] = None, - cursor: typing.Optional[int] = None, - status: typing.Optional[str] = None, + cursor: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, - ) -> AsyncHttpResponse[BatchItemListResponse]: + ) -> AsyncPager[BatchItem, BatchItemPage]: """ - List items in a batch, including derived runtime status when the batch has been processed. - Parameters ---------- - batch_id : str - The batch ID. + batch_uuid : str + Batch UUID limit : typing.Optional[int] - Maximum number of batch items to return. + Page size - cursor : typing.Optional[int] - Pagination cursor from a previous response. - - status : typing.Optional[str] - Batch item status filter. + cursor : typing.Optional[str] + Opaque page cursor request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - AsyncHttpResponse[BatchItemListResponse] - Batch item list + AsyncPager[BatchItem, BatchItemPage] + OK """ _response = await self._client_wrapper.httpx_client.request( - f"batches/{jsonable_encoder(batch_id)}/items", + f"batches/{jsonable_encoder(batch_uuid)}/items", method="GET", params={ "limit": limit, "cursor": cursor, - "status": status, }, request_options=request_options, ) try: if 200 <= _response.status_code < 300: - _data = typing.cast( - BatchItemListResponse, + _parsed_response = typing.cast( + BatchItemPage, parse_obj_as( - type_=BatchItemListResponse, # type: ignore + type_=BatchItemPage, # type: ignore object_=_response.json(), ), ) - return AsyncHttpResponse(response=_response, data=_data) + _items = _parsed_response.items + _parsed_next = _parsed_response.next_cursor + _has_next = _parsed_next is not None and _parsed_next != "" + + async def _get_next(): + return await self.list_items( + batch_uuid, + limit=limit, + cursor=_parsed_next, + request_options=request_options, + ) + + return AsyncPager(has_next=_has_next, items=_items, get_next=_get_next, response=_parsed_response) 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 == 403: - raise ForbiddenError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -1171,8 +1109,8 @@ async def list_items( ), ), ) - if _response.status_code == 404: - raise NotFoundError( + if _response.status_code == 401: + raise UnauthorizedError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -1182,8 +1120,8 @@ async def list_items( ), ), ) - if _response.status_code == 500: - raise InternalServerError( + if _response.status_code == 404: + raise NotFoundError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -1198,45 +1136,49 @@ async def list_items( raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response.text ) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response_json ) - async def add( + async def add_items( self, - batch_id: str, + batch_uuid: str, *, - items: typing.Sequence[BatchAddItem], + items: typing.Optional[typing.Sequence[typing.Dict[str, typing.Any]]] = OMIT, + idempotency_key: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, - ) -> AsyncHttpResponse[typing.List[BatchItemDetail]]: + ) -> AsyncHttpResponse[BatchItemsResponse]: """ - Add graph episodes and thread messages to a draft batch. Items are appended in request order. - Parameters ---------- - batch_id : str - The batch ID. + batch_uuid : str + Batch UUID + + items : typing.Optional[typing.Sequence[typing.Dict[str, typing.Any]]] - items : typing.Sequence[BatchAddItem] + idempotency_key : typing.Optional[str] request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - AsyncHttpResponse[typing.List[BatchItemDetail]] - Added batch items + AsyncHttpResponse[BatchItemsResponse] + OK """ _response = await self._client_wrapper.httpx_client.request( - f"batches/{jsonable_encoder(batch_id)}/items", + f"batches/{jsonable_encoder(batch_uuid)}/items", method="POST", json={ - "items": convert_and_respect_annotation_metadata( - object_=items, annotation=typing.Sequence[BatchAddItem], direction="write" - ), + "items": items, }, headers={ "content-type": "application/json", + "Idempotency-Key": str(idempotency_key) if idempotency_key is not None else None, }, request_options=request_options, omit=OMIT, @@ -1244,37 +1186,15 @@ async def add( try: if 200 <= _response.status_code < 300: _data = typing.cast( - typing.List[BatchItemDetail], + BatchItemsResponse, parse_obj_as( - type_=typing.List[BatchItemDetail], # type: ignore + type_=BatchItemsResponse, # 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 == 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, @@ -1284,8 +1204,8 @@ async def add( ), ), ) - if _response.status_code == 409: - raise ConflictError( + if _response.status_code == 401: + raise UnauthorizedError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -1295,8 +1215,8 @@ async def add( ), ), ) - if _response.status_code == 500: - raise InternalServerError( + if _response.status_code == 404: + raise NotFoundError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -1311,57 +1231,57 @@ async def add( raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response.text ) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response_json ) async def process( - self, batch_id: str, *, request_options: typing.Optional[RequestOptions] = None - ) -> AsyncHttpResponse[BatchSummary]: + self, + batch_uuid: str, + *, + idempotency_key: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[ProcessBatchResult]: """ - Start processing a filled batch. Repeated calls return a conflict. - Parameters ---------- - batch_id : str - The batch ID. + batch_uuid : str + Batch UUID + + idempotency_key : typing.Optional[str] request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - AsyncHttpResponse[BatchSummary] - Batch processing state + AsyncHttpResponse[ProcessBatchResult] + Accepted """ _response = await self._client_wrapper.httpx_client.request( - f"batches/{jsonable_encoder(batch_id)}/process", + f"batches/{jsonable_encoder(batch_uuid)}/process", method="POST", + headers={ + "Idempotency-Key": str(idempotency_key) if idempotency_key is not None else None, + }, request_options=request_options, ) try: if 200 <= _response.status_code < 300: _data = typing.cast( - BatchSummary, + ProcessBatchResult, parse_obj_as( - type_=BatchSummary, # type: ignore + type_=ProcessBatchResult, # 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 == 403: - raise ForbiddenError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -1371,8 +1291,8 @@ async def process( ), ), ) - if _response.status_code == 404: - raise NotFoundError( + if _response.status_code == 401: + raise UnauthorizedError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -1382,19 +1302,8 @@ async def process( ), ), ) - if _response.status_code == 409: - raise ConflictError( - 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( + if _response.status_code == 404: + raise NotFoundError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -1409,6 +1318,10 @@ async def process( raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response.text ) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response_json ) diff --git a/src/zep_cloud/client.py b/src/zep_cloud/client.py index b793251d..658e8d46 100644 --- a/src/zep_cloud/client.py +++ b/src/zep_cloud/client.py @@ -1,59 +1,59 @@ +# This file was auto-generated by Fern from our API Definition. + import os import typing import httpx from .base_client import AsyncBaseClient, BaseClient +from .core.logging import LogConfig, Logger from .environment import ZepEnvironment -from .external_clients.graph import AsyncGraphClient, GraphClient -from .external_clients.user import AsyncUserClient, UserClient class Zep(BaseClient): def __init__( - self, - *, - base_url: typing.Optional[str] = None, - environment: ZepEnvironment = ZepEnvironment.DEFAULT, - api_key: typing.Optional[str] = os.getenv("ZEP_API_KEY"), - timeout: typing.Optional[float] = None, - follow_redirects: typing.Optional[bool] = None, - httpx_client: typing.Optional[httpx.Client] = None + self, + *, + base_url: typing.Optional[str] = None, + environment: ZepEnvironment = ZepEnvironment.DEFAULT, + api_key: typing.Optional[str] = os.getenv("ZEP_API_KEY"), + headers: typing.Optional[typing.Dict[str, str]] = None, + timeout: typing.Optional[float] = None, + follow_redirects: typing.Optional[bool] = True, + httpx_client: typing.Optional[httpx.Client] = None, + logging: typing.Optional[typing.Union[LogConfig, Logger]] = None, ): - env_api_url = os.getenv("ZEP_API_URL") - if env_api_url: - base_url = f"{env_api_url}/api/v2" super().__init__( base_url=base_url, environment=environment, api_key=api_key, + headers=headers, timeout=timeout, follow_redirects=follow_redirects, - httpx_client=httpx_client + httpx_client=httpx_client, + logging=logging, ) - self.user = UserClient(client_wrapper=self._client_wrapper) - self.graph = GraphClient(client_wrapper=self._client_wrapper) + class AsyncZep(AsyncBaseClient): def __init__( - self, - *, - base_url: typing.Optional[str] = None, - environment: ZepEnvironment = ZepEnvironment.DEFAULT, - api_key: typing.Optional[str] = os.getenv("ZEP_API_KEY"), - timeout: typing.Optional[float] = None, - follow_redirects: typing.Optional[bool] = None, - httpx_client: typing.Optional[httpx.AsyncClient] = None + self, + *, + base_url: typing.Optional[str] = None, + environment: ZepEnvironment = ZepEnvironment.DEFAULT, + api_key: typing.Optional[str] = os.getenv("ZEP_API_KEY"), + headers: typing.Optional[typing.Dict[str, str]] = None, + timeout: typing.Optional[float] = None, + follow_redirects: typing.Optional[bool] = True, + httpx_client: typing.Optional[httpx.AsyncClient] = None, + logging: typing.Optional[typing.Union[LogConfig, Logger]] = None, ): - env_api_url = os.getenv("ZEP_API_URL") - if env_api_url: - base_url = f"{env_api_url}/api/v2" super().__init__( base_url=base_url, environment=environment, api_key=api_key, + headers=headers, timeout=timeout, follow_redirects=follow_redirects, - httpx_client=httpx_client + httpx_client=httpx_client, + logging=logging, ) - self.user = AsyncUserClient(client_wrapper=self._client_wrapper) - self.graph = AsyncGraphClient(client_wrapper=self._client_wrapper) diff --git a/src/zep_cloud/context/client.py b/src/zep_cloud/context/client.py index 583c9edf..8ed77ced 100644 --- a/src/zep_cloud/context/client.py +++ b/src/zep_cloud/context/client.py @@ -3,10 +3,10 @@ import typing from ..core.client_wrapper import AsyncClientWrapper, SyncClientWrapper +from ..core.pagination import AsyncPager, SyncPager from ..core.request_options import RequestOptions -from ..types.context_template_response import ContextTemplateResponse -from ..types.list_context_templates_response import ListContextTemplatesResponse -from ..types.success_response import SuccessResponse +from ..types.context_template import ContextTemplate +from ..types.context_template_page import ContextTemplatePage from .raw_client import AsyncRawContextClient, RawContextClient # this is used as the default value for optional parameters @@ -28,21 +28,30 @@ def with_raw_response(self) -> RawContextClient: """ return self._raw_client - def list_context_templates( - self, *, request_options: typing.Optional[RequestOptions] = None - ) -> ListContextTemplatesResponse: + def create_template( + self, + *, + name: typing.Optional[str] = OMIT, + template: typing.Optional[str] = OMIT, + idempotency_key: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> ContextTemplate: """ - Lists all context templates. - Parameters ---------- + name : typing.Optional[str] + + template : typing.Optional[str] + + idempotency_key : typing.Optional[str] + request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - ListContextTemplatesResponse - The list of context templates. + ContextTemplate + Created Examples -------- @@ -51,32 +60,42 @@ def list_context_templates( client = Zep( api_key="YOUR_API_KEY", ) - client.context.list_context_templates() + client.context.create_template() """ - _response = self._raw_client.list_context_templates(request_options=request_options) + _response = self._raw_client.create_template( + name=name, template=template, idempotency_key=idempotency_key, request_options=request_options + ) return _response.data - def create_context_template( - self, *, template: str, template_id: str, request_options: typing.Optional[RequestOptions] = None - ) -> ContextTemplateResponse: + def list_templates( + self, + *, + limit: typing.Optional[int] = None, + cursor: typing.Optional[str] = None, + name: typing.Optional[str] = OMIT, + idempotency_key: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> SyncPager[ContextTemplate, ContextTemplatePage]: """ - Creates a new context template. - Parameters ---------- - template : str - The template content (max 1200 characters). + limit : typing.Optional[int] + Page size + + cursor : typing.Optional[str] + Opaque page cursor - template_id : str - Unique identifier for the template (max 100 characters). + name : typing.Optional[str] + + idempotency_key : typing.Optional[str] request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - ContextTemplateResponse - The created context template. + SyncPager[ContextTemplate, ContextTemplatePage] + OK Examples -------- @@ -85,34 +104,36 @@ def create_context_template( client = Zep( api_key="YOUR_API_KEY", ) - client.context.create_context_template( - template="template", - template_id="template_id", + response = client.context.list_templates( + limit=1, + cursor="cursor", ) + for item in response: + yield item + # alternatively, you can paginate page-by-page + for page in response.iter_pages(): + yield page """ - _response = self._raw_client.create_context_template( - template=template, template_id=template_id, request_options=request_options + return self._raw_client.list_templates( + limit=limit, cursor=cursor, name=name, idempotency_key=idempotency_key, request_options=request_options ) - return _response.data - def get_context_template( - self, template_id: str, *, request_options: typing.Optional[RequestOptions] = None - ) -> ContextTemplateResponse: + def get_template( + self, template_uuid: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> ContextTemplate: """ - Retrieves a context template by template_id. - Parameters ---------- - template_id : str - Template ID + template_uuid : str + Template UUID request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - ContextTemplateResponse - The context template. + ContextTemplate + OK Examples -------- @@ -121,34 +142,41 @@ def get_context_template( client = Zep( api_key="YOUR_API_KEY", ) - client.context.get_context_template( - template_id="template_id", + client.context.get_template( + template_uuid="template_uuid", ) """ - _response = self._raw_client.get_context_template(template_id, request_options=request_options) + _response = self._raw_client.get_template(template_uuid, request_options=request_options) return _response.data - def update_context_template( - self, template_id: str, *, template: str, request_options: typing.Optional[RequestOptions] = None - ) -> ContextTemplateResponse: + def update_template( + self, + template_uuid: str, + *, + name: typing.Optional[str] = OMIT, + template: typing.Optional[str] = OMIT, + idempotency_key: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> ContextTemplate: """ - Updates an existing context template by template_id. - Parameters ---------- - template_id : str - Template ID + template_uuid : str + Template UUID + + name : typing.Optional[str] + + template : typing.Optional[str] - template : str - The template content (max 1200 characters). + idempotency_key : typing.Optional[str] request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - ContextTemplateResponse - The updated context template. + ContextTemplate + OK Examples -------- @@ -157,34 +185,40 @@ def update_context_template( client = Zep( api_key="YOUR_API_KEY", ) - client.context.update_context_template( - template_id="template_id", - template="template", + client.context.update_template( + template_uuid="template_uuid", ) """ - _response = self._raw_client.update_context_template( - template_id, template=template, request_options=request_options + _response = self._raw_client.update_template( + template_uuid, + name=name, + template=template, + idempotency_key=idempotency_key, + request_options=request_options, ) return _response.data - def delete_context_template( - self, template_id: str, *, request_options: typing.Optional[RequestOptions] = None - ) -> SuccessResponse: + def delete_template( + self, + template_uuid: str, + *, + idempotency_key: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> None: """ - Deletes a context template by template_id. - Parameters ---------- - template_id : str - Template ID + template_uuid : str + Template UUID + + idempotency_key : typing.Optional[str] request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - SuccessResponse - Template deleted successfully + None Examples -------- @@ -193,11 +227,13 @@ def delete_context_template( client = Zep( api_key="YOUR_API_KEY", ) - client.context.delete_context_template( - template_id="template_id", + client.context.delete_template( + template_uuid="template_uuid", ) """ - _response = self._raw_client.delete_context_template(template_id, request_options=request_options) + _response = self._raw_client.delete_template( + template_uuid, idempotency_key=idempotency_key, request_options=request_options + ) return _response.data @@ -216,21 +252,30 @@ def with_raw_response(self) -> AsyncRawContextClient: """ return self._raw_client - async def list_context_templates( - self, *, request_options: typing.Optional[RequestOptions] = None - ) -> ListContextTemplatesResponse: + async def create_template( + self, + *, + name: typing.Optional[str] = OMIT, + template: typing.Optional[str] = OMIT, + idempotency_key: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> ContextTemplate: """ - Lists all context templates. - Parameters ---------- + name : typing.Optional[str] + + template : typing.Optional[str] + + idempotency_key : typing.Optional[str] + request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - ListContextTemplatesResponse - The list of context templates. + ContextTemplate + Created Examples -------- @@ -244,35 +289,45 @@ async def list_context_templates( async def main() -> None: - await client.context.list_context_templates() + await client.context.create_template() asyncio.run(main()) """ - _response = await self._raw_client.list_context_templates(request_options=request_options) + _response = await self._raw_client.create_template( + name=name, template=template, idempotency_key=idempotency_key, request_options=request_options + ) return _response.data - async def create_context_template( - self, *, template: str, template_id: str, request_options: typing.Optional[RequestOptions] = None - ) -> ContextTemplateResponse: + async def list_templates( + self, + *, + limit: typing.Optional[int] = None, + cursor: typing.Optional[str] = None, + name: typing.Optional[str] = OMIT, + idempotency_key: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncPager[ContextTemplate, ContextTemplatePage]: """ - Creates a new context template. - Parameters ---------- - template : str - The template content (max 1200 characters). + limit : typing.Optional[int] + Page size + + cursor : typing.Optional[str] + Opaque page cursor - template_id : str - Unique identifier for the template (max 100 characters). + name : typing.Optional[str] + + idempotency_key : typing.Optional[str] request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - ContextTemplateResponse - The created context template. + AsyncPager[ContextTemplate, ContextTemplatePage] + OK Examples -------- @@ -286,37 +341,40 @@ async def create_context_template( async def main() -> None: - await client.context.create_context_template( - template="template", - template_id="template_id", + response = await client.context.list_templates( + limit=1, + cursor="cursor", ) + async for item in response: + yield item + + # alternatively, you can paginate page-by-page + async for page in response.iter_pages(): + yield page asyncio.run(main()) """ - _response = await self._raw_client.create_context_template( - template=template, template_id=template_id, request_options=request_options + return await self._raw_client.list_templates( + limit=limit, cursor=cursor, name=name, idempotency_key=idempotency_key, request_options=request_options ) - return _response.data - async def get_context_template( - self, template_id: str, *, request_options: typing.Optional[RequestOptions] = None - ) -> ContextTemplateResponse: + async def get_template( + self, template_uuid: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> ContextTemplate: """ - Retrieves a context template by template_id. - Parameters ---------- - template_id : str - Template ID + template_uuid : str + Template UUID request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - ContextTemplateResponse - The context template. + ContextTemplate + OK Examples -------- @@ -330,37 +388,44 @@ async def get_context_template( async def main() -> None: - await client.context.get_context_template( - template_id="template_id", + await client.context.get_template( + template_uuid="template_uuid", ) asyncio.run(main()) """ - _response = await self._raw_client.get_context_template(template_id, request_options=request_options) + _response = await self._raw_client.get_template(template_uuid, request_options=request_options) return _response.data - async def update_context_template( - self, template_id: str, *, template: str, request_options: typing.Optional[RequestOptions] = None - ) -> ContextTemplateResponse: + async def update_template( + self, + template_uuid: str, + *, + name: typing.Optional[str] = OMIT, + template: typing.Optional[str] = OMIT, + idempotency_key: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> ContextTemplate: """ - Updates an existing context template by template_id. - Parameters ---------- - template_id : str - Template ID + template_uuid : str + Template UUID + + name : typing.Optional[str] - template : str - The template content (max 1200 characters). + template : typing.Optional[str] + + idempotency_key : typing.Optional[str] request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - ContextTemplateResponse - The updated context template. + ContextTemplate + OK Examples -------- @@ -374,37 +439,43 @@ async def update_context_template( async def main() -> None: - await client.context.update_context_template( - template_id="template_id", - template="template", + await client.context.update_template( + template_uuid="template_uuid", ) asyncio.run(main()) """ - _response = await self._raw_client.update_context_template( - template_id, template=template, request_options=request_options + _response = await self._raw_client.update_template( + template_uuid, + name=name, + template=template, + idempotency_key=idempotency_key, + request_options=request_options, ) return _response.data - async def delete_context_template( - self, template_id: str, *, request_options: typing.Optional[RequestOptions] = None - ) -> SuccessResponse: + async def delete_template( + self, + template_uuid: str, + *, + idempotency_key: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> None: """ - Deletes a context template by template_id. - Parameters ---------- - template_id : str - Template ID + template_uuid : str + Template UUID + + idempotency_key : typing.Optional[str] request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - SuccessResponse - Template deleted successfully + None Examples -------- @@ -418,12 +489,14 @@ async def delete_context_template( async def main() -> None: - await client.context.delete_context_template( - template_id="template_id", + await client.context.delete_template( + template_uuid="template_uuid", ) asyncio.run(main()) """ - _response = await self._raw_client.delete_context_template(template_id, request_options=request_options) + _response = await self._raw_client.delete_template( + template_uuid, idempotency_key=idempotency_key, request_options=request_options + ) return _response.data diff --git a/src/zep_cloud/context/raw_client.py b/src/zep_cloud/context/raw_client.py index 4fe59872..a843e543 100644 --- a/src/zep_cloud/context/raw_client.py +++ b/src/zep_cloud/context/raw_client.py @@ -7,15 +7,17 @@ from ..core.client_wrapper import AsyncClientWrapper, SyncClientWrapper from ..core.http_response import AsyncHttpResponse, HttpResponse from ..core.jsonable_encoder import jsonable_encoder +from ..core.pagination import AsyncPager, SyncPager +from ..core.parse_error import ParsingError from ..core.pydantic_utilities import parse_obj_as from ..core.request_options import RequestOptions from ..errors.bad_request_error import BadRequestError -from ..errors.internal_server_error import InternalServerError from ..errors.not_found_error import NotFoundError +from ..errors.unauthorized_error import UnauthorizedError from ..types.api_error import ApiError as types_api_error_ApiError -from ..types.context_template_response import ContextTemplateResponse -from ..types.list_context_templates_response import ListContextTemplatesResponse -from ..types.success_response import SuccessResponse +from ..types.context_template import ContextTemplate +from ..types.context_template_page import ContextTemplatePage +from pydantic import ValidationError # this is used as the default value for optional parameters OMIT = typing.cast(typing.Any, ...) @@ -25,33 +27,51 @@ class RawContextClient: def __init__(self, *, client_wrapper: SyncClientWrapper): self._client_wrapper = client_wrapper - def list_context_templates( - self, *, request_options: typing.Optional[RequestOptions] = None - ) -> HttpResponse[ListContextTemplatesResponse]: + def create_template( + self, + *, + name: typing.Optional[str] = OMIT, + template: typing.Optional[str] = OMIT, + idempotency_key: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[ContextTemplate]: """ - Lists all context templates. - Parameters ---------- + name : typing.Optional[str] + + template : typing.Optional[str] + + idempotency_key : typing.Optional[str] + request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - HttpResponse[ListContextTemplatesResponse] - The list of context templates. + HttpResponse[ContextTemplate] + Created """ _response = self._client_wrapper.httpx_client.request( "context-templates", - method="GET", + method="POST", + json={ + "name": name, + "template": template, + }, + headers={ + "content-type": "application/json", + "Idempotency-Key": str(idempotency_key) if idempotency_key is not None else None, + }, request_options=request_options, + omit=OMIT, ) try: if 200 <= _response.status_code < 300: _data = typing.cast( - ListContextTemplatesResponse, + ContextTemplate, parse_obj_as( - type_=ListContextTemplatesResponse, # type: ignore + type_=ContextTemplate, # type: ignore object_=_response.json(), ), ) @@ -60,15 +80,26 @@ def list_context_templates( raise BadRequestError( headers=dict(_response.headers), body=typing.cast( - typing.Optional[typing.Any], + types_api_error_ApiError, parse_obj_as( - type_=typing.Optional[typing.Any], # type: ignore + type_=types_api_error_ApiError, # type: ignore object_=_response.json(), ), ), ) - if _response.status_code == 500: - raise InternalServerError( + if _response.status_code == 401: + raise UnauthorizedError( + 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, @@ -83,68 +114,105 @@ def list_context_templates( raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response.text ) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response_json ) - def create_context_template( - self, *, template: str, template_id: str, request_options: typing.Optional[RequestOptions] = None - ) -> HttpResponse[ContextTemplateResponse]: + def list_templates( + self, + *, + limit: typing.Optional[int] = None, + cursor: typing.Optional[str] = None, + name: typing.Optional[str] = OMIT, + idempotency_key: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> SyncPager[ContextTemplate, ContextTemplatePage]: """ - Creates a new context template. - Parameters ---------- - template : str - The template content (max 1200 characters). + limit : typing.Optional[int] + Page size - template_id : str - Unique identifier for the template (max 100 characters). + cursor : typing.Optional[str] + Opaque page cursor + + name : typing.Optional[str] + + idempotency_key : typing.Optional[str] request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - HttpResponse[ContextTemplateResponse] - The created context template. + SyncPager[ContextTemplate, ContextTemplatePage] + OK """ _response = self._client_wrapper.httpx_client.request( - "context-templates", + "context-templates/list", method="POST", + params={ + "limit": limit, + "cursor": cursor, + }, json={ - "template": template, - "template_id": template_id, + "name": name, }, headers={ "content-type": "application/json", + "Idempotency-Key": str(idempotency_key) if idempotency_key is not None else None, }, request_options=request_options, omit=OMIT, ) try: if 200 <= _response.status_code < 300: - _data = typing.cast( - ContextTemplateResponse, + _parsed_response = typing.cast( + ContextTemplatePage, parse_obj_as( - type_=ContextTemplateResponse, # type: ignore + type_=ContextTemplatePage, # type: ignore object_=_response.json(), ), ) - return HttpResponse(response=_response, data=_data) + _items = _parsed_response.items + _parsed_next = _parsed_response.next_cursor + _has_next = _parsed_next is not None and _parsed_next != "" + _get_next = lambda: self.list_templates( + limit=limit, + cursor=_parsed_next, + name=name, + idempotency_key=idempotency_key, + request_options=request_options, + ) + return SyncPager(has_next=_has_next, items=_items, get_next=_get_next, response=_parsed_response) if _response.status_code == 400: raise BadRequestError( headers=dict(_response.headers), body=typing.cast( - typing.Optional[typing.Any], + types_api_error_ApiError, + parse_obj_as( + type_=types_api_error_ApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 401: + raise UnauthorizedError( + headers=dict(_response.headers), + body=typing.cast( + types_api_error_ApiError, parse_obj_as( - type_=typing.Optional[typing.Any], # type: ignore + type_=types_api_error_ApiError, # type: ignore object_=_response.json(), ), ), ) - if _response.status_code == 500: - raise InternalServerError( + if _response.status_code == 404: + raise NotFoundError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -159,40 +227,42 @@ def create_context_template( raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response.text ) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response_json ) - def get_context_template( - self, template_id: str, *, request_options: typing.Optional[RequestOptions] = None - ) -> HttpResponse[ContextTemplateResponse]: + def get_template( + self, template_uuid: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[ContextTemplate]: """ - Retrieves a context template by template_id. - Parameters ---------- - template_id : str - Template ID + template_uuid : str + Template UUID request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - HttpResponse[ContextTemplateResponse] - The context template. + HttpResponse[ContextTemplate] + OK """ _response = self._client_wrapper.httpx_client.request( - f"context-templates/{jsonable_encoder(template_id)}", + f"context-templates/{jsonable_encoder(template_uuid)}", method="GET", request_options=request_options, ) try: if 200 <= _response.status_code < 300: _data = typing.cast( - ContextTemplateResponse, + ContextTemplate, parse_obj_as( - type_=ContextTemplateResponse, # type: ignore + type_=ContextTemplate, # type: ignore object_=_response.json(), ), ) @@ -201,15 +271,15 @@ def get_context_template( raise BadRequestError( headers=dict(_response.headers), body=typing.cast( - typing.Optional[typing.Any], + types_api_error_ApiError, parse_obj_as( - type_=typing.Optional[typing.Any], # type: ignore + type_=types_api_error_ApiError, # type: ignore object_=_response.json(), ), ), ) - if _response.status_code == 404: - raise NotFoundError( + if _response.status_code == 401: + raise UnauthorizedError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -219,8 +289,8 @@ def get_context_template( ), ), ) - if _response.status_code == 500: - raise InternalServerError( + if _response.status_code == 404: + raise NotFoundError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -235,40 +305,53 @@ def get_context_template( raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response.text ) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response_json ) - def update_context_template( - self, template_id: str, *, template: str, request_options: typing.Optional[RequestOptions] = None - ) -> HttpResponse[ContextTemplateResponse]: + def update_template( + self, + template_uuid: str, + *, + name: typing.Optional[str] = OMIT, + template: typing.Optional[str] = OMIT, + idempotency_key: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[ContextTemplate]: """ - Updates an existing context template by template_id. - Parameters ---------- - template_id : str - Template ID + template_uuid : str + Template UUID + + name : typing.Optional[str] - template : str - The template content (max 1200 characters). + template : typing.Optional[str] + + idempotency_key : typing.Optional[str] request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - HttpResponse[ContextTemplateResponse] - The updated context template. + HttpResponse[ContextTemplate] + OK """ _response = self._client_wrapper.httpx_client.request( - f"context-templates/{jsonable_encoder(template_id)}", + f"context-templates/{jsonable_encoder(template_uuid)}", method="PUT", json={ + "name": name, "template": template, }, headers={ "content-type": "application/json", + "Idempotency-Key": str(idempotency_key) if idempotency_key is not None else None, }, request_options=request_options, omit=OMIT, @@ -276,9 +359,9 @@ def update_context_template( try: if 200 <= _response.status_code < 300: _data = typing.cast( - ContextTemplateResponse, + ContextTemplate, parse_obj_as( - type_=ContextTemplateResponse, # type: ignore + type_=ContextTemplate, # type: ignore object_=_response.json(), ), ) @@ -287,15 +370,15 @@ def update_context_template( raise BadRequestError( headers=dict(_response.headers), body=typing.cast( - typing.Optional[typing.Any], + types_api_error_ApiError, parse_obj_as( - type_=typing.Optional[typing.Any], # type: ignore + type_=types_api_error_ApiError, # type: ignore object_=_response.json(), ), ), ) - if _response.status_code == 404: - raise NotFoundError( + if _response.status_code == 401: + raise UnauthorizedError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -305,8 +388,8 @@ def update_context_template( ), ), ) - if _response.status_code == 500: - raise InternalServerError( + if _response.status_code == 404: + raise NotFoundError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -321,57 +404,60 @@ def update_context_template( raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response.text ) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response_json ) - def delete_context_template( - self, template_id: str, *, request_options: typing.Optional[RequestOptions] = None - ) -> HttpResponse[SuccessResponse]: + def delete_template( + self, + template_uuid: str, + *, + idempotency_key: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[None]: """ - Deletes a context template by template_id. - Parameters ---------- - template_id : str - Template ID + template_uuid : str + Template UUID + + idempotency_key : typing.Optional[str] request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - HttpResponse[SuccessResponse] - Template deleted successfully + HttpResponse[None] """ _response = self._client_wrapper.httpx_client.request( - f"context-templates/{jsonable_encoder(template_id)}", + f"context-templates/{jsonable_encoder(template_uuid)}", method="DELETE", + headers={ + "Idempotency-Key": str(idempotency_key) if idempotency_key is not None else None, + }, request_options=request_options, ) try: if 200 <= _response.status_code < 300: - _data = typing.cast( - SuccessResponse, - parse_obj_as( - type_=SuccessResponse, # type: ignore - object_=_response.json(), - ), - ) - return HttpResponse(response=_response, data=_data) + return HttpResponse(response=_response, data=None) if _response.status_code == 400: raise BadRequestError( headers=dict(_response.headers), body=typing.cast( - typing.Optional[typing.Any], + types_api_error_ApiError, parse_obj_as( - type_=typing.Optional[typing.Any], # type: ignore + type_=types_api_error_ApiError, # type: ignore object_=_response.json(), ), ), ) - if _response.status_code == 404: - raise NotFoundError( + if _response.status_code == 401: + raise UnauthorizedError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -381,8 +467,8 @@ def delete_context_template( ), ), ) - if _response.status_code == 500: - raise InternalServerError( + if _response.status_code == 404: + raise NotFoundError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -397,6 +483,10 @@ def delete_context_template( raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response.text ) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response_json ) @@ -406,33 +496,51 @@ class AsyncRawContextClient: def __init__(self, *, client_wrapper: AsyncClientWrapper): self._client_wrapper = client_wrapper - async def list_context_templates( - self, *, request_options: typing.Optional[RequestOptions] = None - ) -> AsyncHttpResponse[ListContextTemplatesResponse]: + async def create_template( + self, + *, + name: typing.Optional[str] = OMIT, + template: typing.Optional[str] = OMIT, + idempotency_key: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[ContextTemplate]: """ - Lists all context templates. - Parameters ---------- + name : typing.Optional[str] + + template : typing.Optional[str] + + idempotency_key : typing.Optional[str] + request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - AsyncHttpResponse[ListContextTemplatesResponse] - The list of context templates. + AsyncHttpResponse[ContextTemplate] + Created """ _response = await self._client_wrapper.httpx_client.request( "context-templates", - method="GET", + method="POST", + json={ + "name": name, + "template": template, + }, + headers={ + "content-type": "application/json", + "Idempotency-Key": str(idempotency_key) if idempotency_key is not None else None, + }, request_options=request_options, + omit=OMIT, ) try: if 200 <= _response.status_code < 300: _data = typing.cast( - ListContextTemplatesResponse, + ContextTemplate, parse_obj_as( - type_=ListContextTemplatesResponse, # type: ignore + type_=ContextTemplate, # type: ignore object_=_response.json(), ), ) @@ -441,15 +549,26 @@ async def list_context_templates( raise BadRequestError( headers=dict(_response.headers), body=typing.cast( - typing.Optional[typing.Any], + types_api_error_ApiError, parse_obj_as( - type_=typing.Optional[typing.Any], # type: ignore + type_=types_api_error_ApiError, # type: ignore object_=_response.json(), ), ), ) - if _response.status_code == 500: - raise InternalServerError( + if _response.status_code == 401: + raise UnauthorizedError( + 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, @@ -464,68 +583,108 @@ async def list_context_templates( raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response.text ) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response_json ) - async def create_context_template( - self, *, template: str, template_id: str, request_options: typing.Optional[RequestOptions] = None - ) -> AsyncHttpResponse[ContextTemplateResponse]: + async def list_templates( + self, + *, + limit: typing.Optional[int] = None, + cursor: typing.Optional[str] = None, + name: typing.Optional[str] = OMIT, + idempotency_key: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncPager[ContextTemplate, ContextTemplatePage]: """ - Creates a new context template. - Parameters ---------- - template : str - The template content (max 1200 characters). + limit : typing.Optional[int] + Page size + + cursor : typing.Optional[str] + Opaque page cursor - template_id : str - Unique identifier for the template (max 100 characters). + name : typing.Optional[str] + + idempotency_key : typing.Optional[str] request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - AsyncHttpResponse[ContextTemplateResponse] - The created context template. + AsyncPager[ContextTemplate, ContextTemplatePage] + OK """ _response = await self._client_wrapper.httpx_client.request( - "context-templates", + "context-templates/list", method="POST", + params={ + "limit": limit, + "cursor": cursor, + }, json={ - "template": template, - "template_id": template_id, + "name": name, }, headers={ "content-type": "application/json", + "Idempotency-Key": str(idempotency_key) if idempotency_key is not None else None, }, request_options=request_options, omit=OMIT, ) try: if 200 <= _response.status_code < 300: - _data = typing.cast( - ContextTemplateResponse, + _parsed_response = typing.cast( + ContextTemplatePage, parse_obj_as( - type_=ContextTemplateResponse, # type: ignore + type_=ContextTemplatePage, # type: ignore object_=_response.json(), ), ) - return AsyncHttpResponse(response=_response, data=_data) + _items = _parsed_response.items + _parsed_next = _parsed_response.next_cursor + _has_next = _parsed_next is not None and _parsed_next != "" + + async def _get_next(): + return await self.list_templates( + limit=limit, + cursor=_parsed_next, + name=name, + idempotency_key=idempotency_key, + request_options=request_options, + ) + + return AsyncPager(has_next=_has_next, items=_items, get_next=_get_next, response=_parsed_response) if _response.status_code == 400: raise BadRequestError( headers=dict(_response.headers), body=typing.cast( - typing.Optional[typing.Any], + types_api_error_ApiError, parse_obj_as( - type_=typing.Optional[typing.Any], # type: ignore + type_=types_api_error_ApiError, # type: ignore object_=_response.json(), ), ), ) - if _response.status_code == 500: - raise InternalServerError( + if _response.status_code == 401: + raise UnauthorizedError( + 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, @@ -540,40 +699,42 @@ async def create_context_template( raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response.text ) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response_json ) - async def get_context_template( - self, template_id: str, *, request_options: typing.Optional[RequestOptions] = None - ) -> AsyncHttpResponse[ContextTemplateResponse]: + async def get_template( + self, template_uuid: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[ContextTemplate]: """ - Retrieves a context template by template_id. - Parameters ---------- - template_id : str - Template ID + template_uuid : str + Template UUID request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - AsyncHttpResponse[ContextTemplateResponse] - The context template. + AsyncHttpResponse[ContextTemplate] + OK """ _response = await self._client_wrapper.httpx_client.request( - f"context-templates/{jsonable_encoder(template_id)}", + f"context-templates/{jsonable_encoder(template_uuid)}", method="GET", request_options=request_options, ) try: if 200 <= _response.status_code < 300: _data = typing.cast( - ContextTemplateResponse, + ContextTemplate, parse_obj_as( - type_=ContextTemplateResponse, # type: ignore + type_=ContextTemplate, # type: ignore object_=_response.json(), ), ) @@ -582,15 +743,15 @@ async def get_context_template( raise BadRequestError( headers=dict(_response.headers), body=typing.cast( - typing.Optional[typing.Any], + types_api_error_ApiError, parse_obj_as( - type_=typing.Optional[typing.Any], # type: ignore + type_=types_api_error_ApiError, # type: ignore object_=_response.json(), ), ), ) - if _response.status_code == 404: - raise NotFoundError( + if _response.status_code == 401: + raise UnauthorizedError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -600,8 +761,8 @@ async def get_context_template( ), ), ) - if _response.status_code == 500: - raise InternalServerError( + if _response.status_code == 404: + raise NotFoundError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -616,40 +777,53 @@ async def get_context_template( raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response.text ) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response_json ) - async def update_context_template( - self, template_id: str, *, template: str, request_options: typing.Optional[RequestOptions] = None - ) -> AsyncHttpResponse[ContextTemplateResponse]: + async def update_template( + self, + template_uuid: str, + *, + name: typing.Optional[str] = OMIT, + template: typing.Optional[str] = OMIT, + idempotency_key: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[ContextTemplate]: """ - Updates an existing context template by template_id. - Parameters ---------- - template_id : str - Template ID + template_uuid : str + Template UUID - template : str - The template content (max 1200 characters). + name : typing.Optional[str] + + template : typing.Optional[str] + + idempotency_key : typing.Optional[str] request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - AsyncHttpResponse[ContextTemplateResponse] - The updated context template. + AsyncHttpResponse[ContextTemplate] + OK """ _response = await self._client_wrapper.httpx_client.request( - f"context-templates/{jsonable_encoder(template_id)}", + f"context-templates/{jsonable_encoder(template_uuid)}", method="PUT", json={ + "name": name, "template": template, }, headers={ "content-type": "application/json", + "Idempotency-Key": str(idempotency_key) if idempotency_key is not None else None, }, request_options=request_options, omit=OMIT, @@ -657,9 +831,9 @@ async def update_context_template( try: if 200 <= _response.status_code < 300: _data = typing.cast( - ContextTemplateResponse, + ContextTemplate, parse_obj_as( - type_=ContextTemplateResponse, # type: ignore + type_=ContextTemplate, # type: ignore object_=_response.json(), ), ) @@ -668,15 +842,15 @@ async def update_context_template( raise BadRequestError( headers=dict(_response.headers), body=typing.cast( - typing.Optional[typing.Any], + types_api_error_ApiError, parse_obj_as( - type_=typing.Optional[typing.Any], # type: ignore + type_=types_api_error_ApiError, # type: ignore object_=_response.json(), ), ), ) - if _response.status_code == 404: - raise NotFoundError( + if _response.status_code == 401: + raise UnauthorizedError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -686,8 +860,8 @@ async def update_context_template( ), ), ) - if _response.status_code == 500: - raise InternalServerError( + if _response.status_code == 404: + raise NotFoundError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -702,57 +876,60 @@ async def update_context_template( raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response.text ) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response_json ) - async def delete_context_template( - self, template_id: str, *, request_options: typing.Optional[RequestOptions] = None - ) -> AsyncHttpResponse[SuccessResponse]: + async def delete_template( + self, + template_uuid: str, + *, + idempotency_key: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[None]: """ - Deletes a context template by template_id. - Parameters ---------- - template_id : str - Template ID + template_uuid : str + Template UUID + + idempotency_key : typing.Optional[str] request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - AsyncHttpResponse[SuccessResponse] - Template deleted successfully + AsyncHttpResponse[None] """ _response = await self._client_wrapper.httpx_client.request( - f"context-templates/{jsonable_encoder(template_id)}", + f"context-templates/{jsonable_encoder(template_uuid)}", method="DELETE", + headers={ + "Idempotency-Key": str(idempotency_key) if idempotency_key is not None else None, + }, request_options=request_options, ) try: if 200 <= _response.status_code < 300: - _data = typing.cast( - SuccessResponse, - parse_obj_as( - type_=SuccessResponse, # type: ignore - object_=_response.json(), - ), - ) - return AsyncHttpResponse(response=_response, data=_data) + return AsyncHttpResponse(response=_response, data=None) if _response.status_code == 400: raise BadRequestError( headers=dict(_response.headers), body=typing.cast( - typing.Optional[typing.Any], + types_api_error_ApiError, parse_obj_as( - type_=typing.Optional[typing.Any], # type: ignore + type_=types_api_error_ApiError, # type: ignore object_=_response.json(), ), ), ) - if _response.status_code == 404: - raise NotFoundError( + if _response.status_code == 401: + raise UnauthorizedError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -762,8 +939,8 @@ async def delete_context_template( ), ), ) - if _response.status_code == 500: - raise InternalServerError( + if _response.status_code == 404: + raise NotFoundError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -778,6 +955,10 @@ async def delete_context_template( raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response.text ) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response_json ) diff --git a/src/zep_cloud/core/__init__.py b/src/zep_cloud/core/__init__.py index 31bbb818..2a2b56e7 100644 --- a/src/zep_cloud/core/__init__.py +++ b/src/zep_cloud/core/__init__.py @@ -2,47 +2,125 @@ # isort: skip_file -from .api_error import ApiError -from .client_wrapper import AsyncClientWrapper, BaseClientWrapper, SyncClientWrapper -from .datetime_utils import serialize_datetime -from .file import File, convert_file_dict_to_httpx_tuples, with_content_type -from .http_client import AsyncHttpClient, HttpClient -from .http_response import AsyncHttpResponse, HttpResponse -from .jsonable_encoder import jsonable_encoder -from .pydantic_utilities import ( - IS_PYDANTIC_V2, - UniversalBaseModel, - UniversalRootModel, - parse_obj_as, - universal_field_validator, - universal_root_validator, - update_forward_refs, -) -from .query_encoder import encode_query -from .remove_none_from_dict import remove_none_from_dict -from .request_options import RequestOptions -from .serialization import FieldMetadata, convert_and_respect_annotation_metadata +import typing +from importlib import import_module + +if typing.TYPE_CHECKING: + from .api_error import ApiError + from .client_wrapper import AsyncClientWrapper, BaseClientWrapper, SyncClientWrapper + from .datetime_utils import Rfc2822DateTime, parse_rfc2822_datetime, serialize_datetime + from .file import File, convert_file_dict_to_httpx_tuples, with_content_type + from .http_client import AsyncHttpClient, HttpClient + from .http_response import AsyncHttpResponse, HttpResponse + from .jsonable_encoder import jsonable_encoder + from .logging import ConsoleLogger, ILogger, LogConfig, LogLevel, Logger, create_logger + from .pagination import AsyncPager, SyncPager + from .parse_error import ParsingError + from .pydantic_utilities import ( + IS_PYDANTIC_V2, + UniversalBaseModel, + UniversalRootModel, + parse_obj_as, + universal_field_validator, + universal_root_validator, + update_forward_refs, + ) + from .query_encoder import encode_query + from .remove_none_from_dict import remove_none_from_dict + from .request_options import RequestOptions + from .serialization import FieldMetadata, convert_and_respect_annotation_metadata +_dynamic_imports: typing.Dict[str, str] = { + "ApiError": ".api_error", + "AsyncClientWrapper": ".client_wrapper", + "AsyncHttpClient": ".http_client", + "AsyncHttpResponse": ".http_response", + "AsyncPager": ".pagination", + "BaseClientWrapper": ".client_wrapper", + "ConsoleLogger": ".logging", + "FieldMetadata": ".serialization", + "File": ".file", + "HttpClient": ".http_client", + "HttpResponse": ".http_response", + "ILogger": ".logging", + "IS_PYDANTIC_V2": ".pydantic_utilities", + "LogConfig": ".logging", + "LogLevel": ".logging", + "Logger": ".logging", + "ParsingError": ".parse_error", + "RequestOptions": ".request_options", + "Rfc2822DateTime": ".datetime_utils", + "SyncClientWrapper": ".client_wrapper", + "SyncPager": ".pagination", + "UniversalBaseModel": ".pydantic_utilities", + "UniversalRootModel": ".pydantic_utilities", + "convert_and_respect_annotation_metadata": ".serialization", + "convert_file_dict_to_httpx_tuples": ".file", + "create_logger": ".logging", + "encode_query": ".query_encoder", + "jsonable_encoder": ".jsonable_encoder", + "parse_obj_as": ".pydantic_utilities", + "parse_rfc2822_datetime": ".datetime_utils", + "remove_none_from_dict": ".remove_none_from_dict", + "serialize_datetime": ".datetime_utils", + "universal_field_validator": ".pydantic_utilities", + "universal_root_validator": ".pydantic_utilities", + "update_forward_refs": ".pydantic_utilities", + "with_content_type": ".file", +} + + +def __getattr__(attr_name: str) -> typing.Any: + module_name = _dynamic_imports.get(attr_name) + if module_name is None: + raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") + try: + module = import_module(module_name, __package__) + if module_name == f".{attr_name}": + return module + else: + return getattr(module, attr_name) + except ImportError as e: + raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e + except AttributeError as e: + raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e + + +def __dir__(): + lazy_attrs = list(_dynamic_imports.keys()) + return sorted(lazy_attrs) + __all__ = [ "ApiError", "AsyncClientWrapper", "AsyncHttpClient", "AsyncHttpResponse", + "AsyncPager", "BaseClientWrapper", + "ConsoleLogger", "FieldMetadata", "File", "HttpClient", "HttpResponse", + "ILogger", "IS_PYDANTIC_V2", + "LogConfig", + "LogLevel", + "Logger", + "ParsingError", "RequestOptions", + "Rfc2822DateTime", "SyncClientWrapper", + "SyncPager", "UniversalBaseModel", "UniversalRootModel", "convert_and_respect_annotation_metadata", "convert_file_dict_to_httpx_tuples", + "create_logger", "encode_query", "jsonable_encoder", "parse_obj_as", + "parse_rfc2822_datetime", "remove_none_from_dict", "serialize_datetime", "universal_field_validator", diff --git a/src/zep_cloud/core/client_wrapper.py b/src/zep_cloud/core/client_wrapper.py index dde7c3ad..4daaa20a 100644 --- a/src/zep_cloud/core/client_wrapper.py +++ b/src/zep_cloud/core/client_wrapper.py @@ -4,6 +4,7 @@ import httpx from .http_client import AsyncHttpClient, HttpClient +from .logging import LogConfig, Logger class BaseClientWrapper: @@ -14,18 +15,24 @@ def __init__( headers: typing.Optional[typing.Dict[str, str]] = None, base_url: str, timeout: typing.Optional[float] = None, + logging: typing.Optional[typing.Union[LogConfig, Logger]] = None, ): self.api_key = api_key self._headers = headers self._base_url = base_url self._timeout = timeout + self._logging = logging def get_headers(self) -> typing.Dict[str, str]: + import platform + headers: typing.Dict[str, str] = { - "User-Agent": "zep-cloud/3.28.0", + "User-Agent": "zep-cloud/4.0.0a1", "X-Fern-Language": "Python", + "X-Fern-Runtime": f"python/{platform.python_version()}", + "X-Fern-Platform": f"{platform.system().lower()}/{platform.release()}", "X-Fern-SDK-Name": "zep-cloud", - "X-Fern-SDK-Version": "3.28.0", + "X-Fern-SDK-Version": "4.0.0a1", **(self.get_custom_headers() or {}), } headers["Authorization"] = f"Api-Key {self.api_key}" @@ -49,14 +56,16 @@ def __init__( headers: typing.Optional[typing.Dict[str, str]] = None, base_url: str, timeout: typing.Optional[float] = None, + logging: typing.Optional[typing.Union[LogConfig, Logger]] = None, httpx_client: httpx.Client, ): - super().__init__(api_key=api_key, headers=headers, base_url=base_url, timeout=timeout) + super().__init__(api_key=api_key, headers=headers, base_url=base_url, timeout=timeout, logging=logging) self.httpx_client = HttpClient( httpx_client=httpx_client, base_headers=self.get_headers, base_timeout=self.get_timeout, base_url=self.get_base_url, + logging_config=self._logging, ) @@ -68,12 +77,24 @@ def __init__( headers: typing.Optional[typing.Dict[str, str]] = None, base_url: str, timeout: typing.Optional[float] = None, + logging: typing.Optional[typing.Union[LogConfig, Logger]] = None, + async_token: typing.Optional[typing.Callable[[], typing.Awaitable[str]]] = None, httpx_client: httpx.AsyncClient, ): - super().__init__(api_key=api_key, headers=headers, base_url=base_url, timeout=timeout) + super().__init__(api_key=api_key, headers=headers, base_url=base_url, timeout=timeout, logging=logging) + self._async_token = async_token self.httpx_client = AsyncHttpClient( httpx_client=httpx_client, base_headers=self.get_headers, base_timeout=self.get_timeout, base_url=self.get_base_url, + async_base_headers=self.async_get_headers, + logging_config=self._logging, ) + + async def async_get_headers(self) -> typing.Dict[str, str]: + headers = self.get_headers() + if self._async_token is not None: + token = await self._async_token() + headers["Authorization"] = f"Bearer {token}" + return headers diff --git a/src/zep_cloud/core/datetime_utils.py b/src/zep_cloud/core/datetime_utils.py index 7c9864a9..a12b2ad0 100644 --- a/src/zep_cloud/core/datetime_utils.py +++ b/src/zep_cloud/core/datetime_utils.py @@ -1,6 +1,48 @@ # This file was auto-generated by Fern from our API Definition. import datetime as dt +from email.utils import parsedate_to_datetime +from typing import Any + +import pydantic + +IS_PYDANTIC_V2 = pydantic.VERSION.startswith("2.") + + +def parse_rfc2822_datetime(v: Any) -> dt.datetime: + """ + Parse an RFC 2822 datetime string (e.g., "Wed, 02 Oct 2002 13:00:00 GMT") + into a datetime object. If the value is already a datetime, return it as-is. + Falls back to ISO 8601 parsing if RFC 2822 parsing fails. + """ + if isinstance(v, dt.datetime): + return v + if isinstance(v, str): + try: + return parsedate_to_datetime(v) + except Exception: + pass + # Fallback to ISO 8601 parsing + return dt.datetime.fromisoformat(v.replace("Z", "+00:00")) + raise ValueError(f"Expected str or datetime, got {type(v)}") + + +class Rfc2822DateTime(dt.datetime): + """A datetime subclass that parses RFC 2822 date strings. + + On Pydantic V1, uses __get_validators__ for pre-validation. + On Pydantic V2, uses __get_pydantic_core_schema__ for BeforeValidator-style parsing. + """ + + @classmethod + def __get_validators__(cls): # type: ignore[no-untyped-def] + yield parse_rfc2822_datetime + + @classmethod + def __get_pydantic_core_schema__(cls, _source_type: Any, _handler: Any) -> Any: # type: ignore[override] + from pydantic_core import core_schema + + return core_schema.no_info_before_validator_function(parse_rfc2822_datetime, core_schema.datetime_schema()) def serialize_datetime(v: dt.datetime) -> str: diff --git a/src/zep_cloud/core/force_multipart.py b/src/zep_cloud/core/force_multipart.py index ae24ccff..5440913f 100644 --- a/src/zep_cloud/core/force_multipart.py +++ b/src/zep_cloud/core/force_multipart.py @@ -1,7 +1,9 @@ # This file was auto-generated by Fern from our API Definition. +from typing import Any, Dict -class ForceMultipartDict(dict): + +class ForceMultipartDict(Dict[str, Any]): """ A dictionary subclass that always evaluates to True in boolean contexts. @@ -9,7 +11,7 @@ class ForceMultipartDict(dict): the dictionary is empty, which would normally evaluate to False. """ - def __bool__(self): + def __bool__(self) -> bool: return True diff --git a/src/zep_cloud/core/http_client.py b/src/zep_cloud/core/http_client.py index e4173f99..ee937589 100644 --- a/src/zep_cloud/core/http_client.py +++ b/src/zep_cloud/core/http_client.py @@ -5,7 +5,6 @@ import re import time import typing -import urllib.parse from contextlib import asynccontextmanager, contextmanager from random import random @@ -13,14 +12,15 @@ from .file import File, convert_file_dict_to_httpx_tuples from .force_multipart import FORCE_MULTIPART from .jsonable_encoder import jsonable_encoder +from .logging import LogConfig, Logger, create_logger from .query_encoder import encode_query -from .remove_none_from_dict import remove_none_from_dict +from .remove_none_from_dict import remove_none_from_dict as remove_none_from_dict from .request_options import RequestOptions from httpx._types import RequestFiles -INITIAL_RETRY_DELAY_SECONDS = 0.5 -MAX_RETRY_DELAY_SECONDS = 10 -MAX_RETRY_DELAY_SECONDS_FROM_HEADER = 30 +INITIAL_RETRY_DELAY_SECONDS = 1.0 +MAX_RETRY_DELAY_SECONDS = 60.0 +JITTER_FACTOR = 0.2 # 20% random jitter def _parse_retry_after(response_headers: httpx.Headers) -> typing.Optional[float]: @@ -64,6 +64,38 @@ def _parse_retry_after(response_headers: httpx.Headers) -> typing.Optional[float return seconds +def _add_positive_jitter(delay: float) -> float: + """Add positive jitter (0-20%) to prevent thundering herd.""" + jitter_multiplier = 1 + random() * JITTER_FACTOR + return delay * jitter_multiplier + + +def _add_symmetric_jitter(delay: float) -> float: + """Add symmetric jitter (±10%) for exponential backoff.""" + jitter_multiplier = 1 + (random() - 0.5) * JITTER_FACTOR + return delay * jitter_multiplier + + +def _parse_x_ratelimit_reset(response_headers: httpx.Headers) -> typing.Optional[float]: + """ + Parse the X-RateLimit-Reset header (Unix timestamp in seconds). + Returns seconds to wait, or None if header is missing/invalid. + """ + reset_time_str = response_headers.get("x-ratelimit-reset") + if reset_time_str is None: + return None + + try: + reset_time = int(reset_time_str) + delay = reset_time - time.time() + if delay > 0: + return delay + except (ValueError, TypeError): + pass + + return None + + def _retry_timeout(response: httpx.Response, retries: int) -> float: """ Determine the amount of time to wait before retrying a request. @@ -71,17 +103,19 @@ def _retry_timeout(response: httpx.Response, retries: int) -> float: with a jitter to determine the number of seconds to wait. """ - # If the API asks us to wait a certain amount of time (and it's a reasonable amount), just do what it says. + # 1. Check Retry-After header first retry_after = _parse_retry_after(response.headers) - if retry_after is not None and retry_after <= MAX_RETRY_DELAY_SECONDS_FROM_HEADER: - return retry_after + if retry_after is not None and retry_after > 0: + return min(retry_after, MAX_RETRY_DELAY_SECONDS) - # Apply exponential backoff, capped at MAX_RETRY_DELAY_SECONDS. - retry_delay = min(INITIAL_RETRY_DELAY_SECONDS * pow(2.0, retries), MAX_RETRY_DELAY_SECONDS) + # 2. Check X-RateLimit-Reset header (with positive jitter) + ratelimit_reset = _parse_x_ratelimit_reset(response.headers) + if ratelimit_reset is not None: + return _add_positive_jitter(min(ratelimit_reset, MAX_RETRY_DELAY_SECONDS)) - # Add a randomness / jitter to the retry delay to avoid overwhelming the server with retries. - timeout = retry_delay * (1 - 0.25 * random()) - return timeout if timeout >= 0 else 0 + # 3. Fall back to exponential backoff (with symmetric jitter) + backoff = min(INITIAL_RETRY_DELAY_SECONDS * pow(2.0, retries), MAX_RETRY_DELAY_SECONDS) + return _add_symmetric_jitter(backoff) def _should_retry(response: httpx.Response) -> bool: @@ -89,6 +123,71 @@ def _should_retry(response: httpx.Response) -> bool: return response.status_code >= 500 or response.status_code in retryable_400s +_SENSITIVE_HEADERS = frozenset( + { + "authorization", + "www-authenticate", + "x-api-key", + "api-key", + "apikey", + "x-api-token", + "x-auth-token", + "auth-token", + "cookie", + "set-cookie", + "proxy-authorization", + "proxy-authenticate", + "x-csrf-token", + "x-xsrf-token", + "x-session-token", + "x-access-token", + } +) + + +def _redact_headers(headers: typing.Dict[str, str]) -> typing.Dict[str, str]: + return {k: ("[REDACTED]" if k.lower() in _SENSITIVE_HEADERS else v) for k, v in headers.items()} + + +def _build_url(base_url: str, path: typing.Optional[str]) -> str: + """ + Build a full URL by joining a base URL with a path. + + This function correctly handles base URLs that contain path prefixes (e.g., tenant-based URLs) + by using string concatenation instead of urllib.parse.urljoin(), which would incorrectly + strip path components when the path starts with '/'. + + Example: + >>> _build_url("https://cloud.example.com/org/tenant/api", "/users") + 'https://cloud.example.com/org/tenant/api/users' + + Args: + base_url: The base URL, which may contain path prefixes. + path: The path to append. Can be None or empty string. + + Returns: + The full URL with base_url and path properly joined. + """ + if not path: + return base_url + return f"{base_url.rstrip('/')}/{path.lstrip('/')}" + + +def _maybe_filter_none_from_multipart_data( + data: typing.Optional[typing.Any], + request_files: typing.Optional[RequestFiles], + force_multipart: typing.Optional[bool], +) -> typing.Optional[typing.Any]: + """ + Filter None values from data body for multipart/form requests. + This prevents httpx from converting None to empty strings in multipart encoding. + Only applies when files are present or force_multipart is True. + """ + if data is not None and isinstance(data, typing.Mapping) and (request_files or force_multipart): + return remove_none_from_dict(data) + return data + + def remove_omit_from_dict( original: typing.Dict[str, typing.Optional[typing.Any]], omit: typing.Optional[typing.Any], @@ -143,8 +242,19 @@ def get_request_body( # If both data and json are None, we send json data in the event extra properties are specified json_body = maybe_filter_request_body(json, request_options, omit) - # If you have an empty JSON body, you should just send None - return (json_body if json_body != {} else None), data_body if data_body != {} else None + has_additional_body_parameters = bool( + request_options is not None and request_options.get("additional_body_parameters") + ) + + # Only collapse empty dict to None when the body was not explicitly provided + # and there are no additional body parameters. This preserves explicit empty + # bodies (e.g., when an endpoint has a request body type but all fields are optional). + if json_body == {} and json is None and not has_additional_body_parameters: + json_body = None + if data_body == {} and data is None and not has_additional_body_parameters: + data_body = None + + return json_body, data_body class HttpClient: @@ -155,11 +265,13 @@ def __init__( base_timeout: typing.Callable[[], typing.Optional[float]], base_headers: typing.Callable[[], typing.Dict[str, str]], base_url: typing.Optional[typing.Callable[[], str]] = None, + logging_config: typing.Optional[typing.Union[LogConfig, Logger]] = None, ): self.base_url = base_url self.base_timeout = base_timeout self.base_headers = base_headers self.httpx_client = httpx_client + self.logger = create_logger(logging_config) def get_base_url(self, maybe_base_url: typing.Optional[str]) -> str: base_url = maybe_base_url @@ -188,7 +300,7 @@ def request( ] = None, headers: typing.Optional[typing.Dict[str, typing.Any]] = None, request_options: typing.Optional[RequestOptions] = None, - retries: int = 2, + retries: int = 0, omit: typing.Optional[typing.Any] = None, force_multipart: typing.Optional[bool] = None, ) -> httpx.Response: @@ -210,35 +322,53 @@ def request( if (request_files is None or len(request_files) == 0) and force_multipart: request_files = FORCE_MULTIPART - response = self.httpx_client.request( - method=method, - url=urllib.parse.urljoin(f"{base_url}/", path), - headers=jsonable_encoder( + data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( remove_none_from_dict( - { - **self.base_headers(), - **(headers if headers is not None else {}), - **(request_options.get("additional_headers", {}) or {} if request_options is not None else {}), - } - ) - ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, ) ) - ), + ) + ) + + _request_url = _build_url(base_url, path) + _request_headers = jsonable_encoder( + remove_none_from_dict( + { + **self.base_headers(), + **(headers if headers is not None else {}), + **(request_options.get("additional_headers", {}) or {} if request_options is not None else {}), + } + ) + ) + + if self.logger.is_debug(): + self.logger.debug( + "Making HTTP request", + method=method, + url=_request_url, + headers=_redact_headers(_request_headers), + has_body=json_body is not None or data_body is not None, + ) + + response = self.httpx_client.request( + method=method, + url=_request_url, + headers=_request_headers, + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -246,9 +376,9 @@ def request( timeout=timeout, ) - max_retries: int = request_options.get("max_retries", 0) if request_options is not None else 0 + max_retries: int = request_options.get("max_retries", 2) if request_options is not None else 2 if _should_retry(response=response): - if max_retries > retries: + if retries < max_retries: time.sleep(_retry_timeout(response=response, retries=retries)) return self.request( path=path, @@ -264,6 +394,24 @@ def request( omit=omit, ) + if self.logger.is_debug(): + if 200 <= response.status_code < 400: + self.logger.debug( + "HTTP request succeeded", + method=method, + url=_request_url, + status_code=response.status_code, + ) + + if self.logger.is_error(): + if response.status_code >= 400: + self.logger.error( + "HTTP request failed with error status", + method=method, + url=_request_url, + status_code=response.status_code, + ) + return response @contextmanager @@ -285,7 +433,7 @@ def stream( ] = None, headers: typing.Optional[typing.Dict[str, typing.Any]] = None, request_options: typing.Optional[RequestOptions] = None, - retries: int = 2, + retries: int = 0, omit: typing.Optional[typing.Any] = None, force_multipart: typing.Optional[bool] = None, ) -> typing.Iterator[httpx.Response]: @@ -307,35 +455,52 @@ def stream( json_body, data_body = get_request_body(json=json, data=data, request_options=request_options, omit=omit) - with self.httpx_client.stream( - method=method, - url=urllib.parse.urljoin(f"{base_url}/", path), - headers=jsonable_encoder( + data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( remove_none_from_dict( - { - **self.base_headers(), - **(headers if headers is not None else {}), - **(request_options.get("additional_headers", {}) if request_options is not None else {}), - } - ) - ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit, - ) + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit, ) ) - ), + ) + ) + + _request_url = _build_url(base_url, path) + _request_headers = jsonable_encoder( + remove_none_from_dict( + { + **self.base_headers(), + **(headers if headers is not None else {}), + **(request_options.get("additional_headers", {}) if request_options is not None else {}), + } + ) + ) + + if self.logger.is_debug(): + self.logger.debug( + "Making streaming HTTP request", + method=method, + url=_request_url, + headers=_redact_headers(_request_headers), + ) + + with self.httpx_client.stream( + method=method, + url=_request_url, + headers=_request_headers, + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -353,11 +518,20 @@ def __init__( base_timeout: typing.Callable[[], typing.Optional[float]], base_headers: typing.Callable[[], typing.Dict[str, str]], base_url: typing.Optional[typing.Callable[[], str]] = None, + async_base_headers: typing.Optional[typing.Callable[[], typing.Awaitable[typing.Dict[str, str]]]] = None, + logging_config: typing.Optional[typing.Union[LogConfig, Logger]] = None, ): self.base_url = base_url self.base_timeout = base_timeout self.base_headers = base_headers + self.async_base_headers = async_base_headers self.httpx_client = httpx_client + self.logger = create_logger(logging_config) + + async def _get_headers(self) -> typing.Dict[str, str]: + if self.async_base_headers is not None: + return await self.async_base_headers() + return self.base_headers() def get_base_url(self, maybe_base_url: typing.Optional[str]) -> str: base_url = maybe_base_url @@ -386,7 +560,7 @@ async def request( ] = None, headers: typing.Optional[typing.Dict[str, typing.Any]] = None, request_options: typing.Optional[RequestOptions] = None, - retries: int = 2, + retries: int = 0, omit: typing.Optional[typing.Any] = None, force_multipart: typing.Optional[bool] = None, ) -> httpx.Response: @@ -408,36 +582,56 @@ async def request( json_body, data_body = get_request_body(json=json, data=data, request_options=request_options, omit=omit) - # Add the input to each of these and do None-safety checks - response = await self.httpx_client.request( - method=method, - url=urllib.parse.urljoin(f"{base_url}/", path), - headers=jsonable_encoder( + data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + + # Get headers (supports async token providers) + _headers = await self._get_headers() + + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( remove_none_from_dict( - { - **self.base_headers(), - **(headers if headers is not None else {}), - **(request_options.get("additional_headers", {}) or {} if request_options is not None else {}), - } - ) - ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, ) ) - ), + ) + ) + + _request_url = _build_url(base_url, path) + _request_headers = jsonable_encoder( + remove_none_from_dict( + { + **_headers, + **(headers if headers is not None else {}), + **(request_options.get("additional_headers", {}) or {} if request_options is not None else {}), + } + ) + ) + + if self.logger.is_debug(): + self.logger.debug( + "Making HTTP request", + method=method, + url=_request_url, + headers=_redact_headers(_request_headers), + has_body=json_body is not None or data_body is not None, + ) + + response = await self.httpx_client.request( + method=method, + url=_request_url, + headers=_request_headers, + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -445,9 +639,9 @@ async def request( timeout=timeout, ) - max_retries: int = request_options.get("max_retries", 0) if request_options is not None else 0 + max_retries: int = request_options.get("max_retries", 2) if request_options is not None else 2 if _should_retry(response=response): - if max_retries > retries: + if retries < max_retries: await asyncio.sleep(_retry_timeout(response=response, retries=retries)) return await self.request( path=path, @@ -462,6 +656,25 @@ async def request( retries=retries + 1, omit=omit, ) + + if self.logger.is_debug(): + if 200 <= response.status_code < 400: + self.logger.debug( + "HTTP request succeeded", + method=method, + url=_request_url, + status_code=response.status_code, + ) + + if self.logger.is_error(): + if response.status_code >= 400: + self.logger.error( + "HTTP request failed with error status", + method=method, + url=_request_url, + status_code=response.status_code, + ) + return response @asynccontextmanager @@ -483,7 +696,7 @@ async def stream( ] = None, headers: typing.Optional[typing.Dict[str, typing.Any]] = None, request_options: typing.Optional[RequestOptions] = None, - retries: int = 2, + retries: int = 0, omit: typing.Optional[typing.Any] = None, force_multipart: typing.Optional[bool] = None, ) -> typing.AsyncIterator[httpx.Response]: @@ -505,35 +718,55 @@ async def stream( json_body, data_body = get_request_body(json=json, data=data, request_options=request_options, omit=omit) - async with self.httpx_client.stream( - method=method, - url=urllib.parse.urljoin(f"{base_url}/", path), - headers=jsonable_encoder( + data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + + # Get headers (supports async token providers) + _headers = await self._get_headers() + + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( remove_none_from_dict( - { - **self.base_headers(), - **(headers if headers is not None else {}), - **(request_options.get("additional_headers", {}) if request_options is not None else {}), - } - ) - ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit=omit, - ) + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit=omit, ) ) - ), + ) + ) + + _request_url = _build_url(base_url, path) + _request_headers = jsonable_encoder( + remove_none_from_dict( + { + **_headers, + **(headers if headers is not None else {}), + **(request_options.get("additional_headers", {}) if request_options is not None else {}), + } + ) + ) + + if self.logger.is_debug(): + self.logger.debug( + "Making streaming HTTP request", + method=method, + url=_request_url, + headers=_redact_headers(_request_headers), + ) + + async with self.httpx_client.stream( + method=method, + url=_request_url, + headers=_request_headers, + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, diff --git a/src/zep_cloud/core/http_response.py b/src/zep_cloud/core/http_response.py index 48a1798a..00bb1096 100644 --- a/src/zep_cloud/core/http_response.py +++ b/src/zep_cloud/core/http_response.py @@ -4,12 +4,12 @@ import httpx +# Generic to represent the underlying type of the data wrapped by the HTTP response. T = TypeVar("T") -"""Generic to represent the underlying type of the data wrapped by the HTTP response.""" class BaseHttpResponse: - """Minimalist HTTP response wrapper that exposes response headers.""" + """Minimalist HTTP response wrapper that exposes response headers and status code.""" _response: httpx.Response @@ -20,6 +20,10 @@ def __init__(self, response: httpx.Response): def headers(self) -> Dict[str, str]: return dict(self._response.headers) + @property + def status_code(self) -> int: + return self._response.status_code + class HttpResponse(Generic[T], BaseHttpResponse): """HTTP response wrapper that exposes response headers and data.""" diff --git a/src/zep_cloud/core/http_sse/__init__.py b/src/zep_cloud/core/http_sse/__init__.py new file mode 100644 index 00000000..730e5a33 --- /dev/null +++ b/src/zep_cloud/core/http_sse/__init__.py @@ -0,0 +1,42 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + +import typing +from importlib import import_module + +if typing.TYPE_CHECKING: + from ._api import EventSource, aconnect_sse, connect_sse + from ._exceptions import SSEError + from ._models import ServerSentEvent +_dynamic_imports: typing.Dict[str, str] = { + "EventSource": "._api", + "SSEError": "._exceptions", + "ServerSentEvent": "._models", + "aconnect_sse": "._api", + "connect_sse": "._api", +} + + +def __getattr__(attr_name: str) -> typing.Any: + module_name = _dynamic_imports.get(attr_name) + if module_name is None: + raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") + try: + module = import_module(module_name, __package__) + if module_name == f".{attr_name}": + return module + else: + return getattr(module, attr_name) + except ImportError as e: + raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e + except AttributeError as e: + raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e + + +def __dir__(): + lazy_attrs = list(_dynamic_imports.keys()) + return sorted(lazy_attrs) + + +__all__ = ["EventSource", "SSEError", "ServerSentEvent", "aconnect_sse", "connect_sse"] diff --git a/src/zep_cloud/core/http_sse/_api.py b/src/zep_cloud/core/http_sse/_api.py new file mode 100644 index 00000000..f900b3b6 --- /dev/null +++ b/src/zep_cloud/core/http_sse/_api.py @@ -0,0 +1,112 @@ +# This file was auto-generated by Fern from our API Definition. + +import re +from contextlib import asynccontextmanager, contextmanager +from typing import Any, AsyncGenerator, AsyncIterator, Iterator, cast + +import httpx +from ._decoders import SSEDecoder +from ._exceptions import SSEError +from ._models import ServerSentEvent + + +class EventSource: + def __init__(self, response: httpx.Response) -> None: + self._response = response + + def _check_content_type(self) -> None: + content_type = self._response.headers.get("content-type", "").partition(";")[0] + if "text/event-stream" not in content_type: + raise SSEError( + f"Expected response header Content-Type to contain 'text/event-stream', got {content_type!r}" + ) + + def _get_charset(self) -> str: + """Extract charset from Content-Type header, fallback to UTF-8.""" + content_type = self._response.headers.get("content-type", "") + + # Parse charset parameter using regex + charset_match = re.search(r"charset=([^;\s]+)", content_type, re.IGNORECASE) + if charset_match: + charset = charset_match.group(1).strip("\"'") + # Validate that it's a known encoding + try: + # Test if the charset is valid by trying to encode/decode + "test".encode(charset).decode(charset) + return charset + except (LookupError, UnicodeError): + # If charset is invalid, fall back to UTF-8 + pass + + # Default to UTF-8 if no charset specified or invalid charset + return "utf-8" + + @property + def response(self) -> httpx.Response: + return self._response + + def iter_sse(self) -> Iterator[ServerSentEvent]: + self._check_content_type() + decoder = SSEDecoder() + charset = self._get_charset() + + buffer = "" + for chunk in self._response.iter_bytes(): + # Decode chunk using detected charset + text_chunk = chunk.decode(charset, errors="replace") + buffer += text_chunk + + # Process complete lines + while "\n" in buffer: + line, buffer = buffer.split("\n", 1) + line = line.rstrip("\r") + sse = decoder.decode(line) + # when we reach a "\n\n" => line = '' + # => decoder will attempt to return an SSE Event + if sse is not None: + yield sse + + # Process any remaining data in buffer + if buffer.strip(): + line = buffer.rstrip("\r") + sse = decoder.decode(line) + if sse is not None: + yield sse + + async def aiter_sse(self) -> AsyncGenerator[ServerSentEvent, None]: + self._check_content_type() + decoder = SSEDecoder() + lines = cast(AsyncGenerator[str, None], self._response.aiter_lines()) + try: + async for line in lines: + line = line.rstrip("\n") + sse = decoder.decode(line) + if sse is not None: + yield sse + finally: + await lines.aclose() + + +@contextmanager +def connect_sse(client: httpx.Client, method: str, url: str, **kwargs: Any) -> Iterator[EventSource]: + headers = kwargs.pop("headers", {}) + headers["Accept"] = "text/event-stream" + headers["Cache-Control"] = "no-store" + + with client.stream(method, url, headers=headers, **kwargs) as response: + yield EventSource(response) + + +@asynccontextmanager +async def aconnect_sse( + client: httpx.AsyncClient, + method: str, + url: str, + **kwargs: Any, +) -> AsyncIterator[EventSource]: + headers = kwargs.pop("headers", {}) + headers["Accept"] = "text/event-stream" + headers["Cache-Control"] = "no-store" + + async with client.stream(method, url, headers=headers, **kwargs) as response: + yield EventSource(response) diff --git a/src/zep_cloud/core/http_sse/_decoders.py b/src/zep_cloud/core/http_sse/_decoders.py new file mode 100644 index 00000000..339b0890 --- /dev/null +++ b/src/zep_cloud/core/http_sse/_decoders.py @@ -0,0 +1,61 @@ +# This file was auto-generated by Fern from our API Definition. + +from typing import List, Optional + +from ._models import ServerSentEvent + + +class SSEDecoder: + def __init__(self) -> None: + self._event = "" + self._data: List[str] = [] + self._last_event_id = "" + self._retry: Optional[int] = None + + def decode(self, line: str) -> Optional[ServerSentEvent]: + # See: https://html.spec.whatwg.org/multipage/server-sent-events.html#event-stream-interpretation # noqa: E501 + + if not line: + if not self._event and not self._data and not self._last_event_id and self._retry is None: + return None + + sse = ServerSentEvent( + event=self._event, + data="\n".join(self._data), + id=self._last_event_id, + retry=self._retry, + ) + + # NOTE: as per the SSE spec, do not reset last_event_id. + self._event = "" + self._data = [] + self._retry = None + + return sse + + if line.startswith(":"): + return None + + fieldname, _, value = line.partition(":") + + if value.startswith(" "): + value = value[1:] + + if fieldname == "event": + self._event = value + elif fieldname == "data": + self._data.append(value) + elif fieldname == "id": + if "\0" in value: + pass + else: + self._last_event_id = value + elif fieldname == "retry": + try: + self._retry = int(value) + except (TypeError, ValueError): + pass + else: + pass # Field is ignored. + + return None diff --git a/src/zep_cloud/core/http_sse/_exceptions.py b/src/zep_cloud/core/http_sse/_exceptions.py new file mode 100644 index 00000000..81605a8a --- /dev/null +++ b/src/zep_cloud/core/http_sse/_exceptions.py @@ -0,0 +1,7 @@ +# This file was auto-generated by Fern from our API Definition. + +import httpx + + +class SSEError(httpx.TransportError): + pass diff --git a/src/zep_cloud/core/http_sse/_models.py b/src/zep_cloud/core/http_sse/_models.py new file mode 100644 index 00000000..1af57f8f --- /dev/null +++ b/src/zep_cloud/core/http_sse/_models.py @@ -0,0 +1,17 @@ +# This file was auto-generated by Fern from our API Definition. + +import json +from dataclasses import dataclass +from typing import Any, Optional + + +@dataclass(frozen=True) +class ServerSentEvent: + event: str = "message" + data: str = "" + id: str = "" + retry: Optional[int] = None + + def json(self) -> Any: + """Parse the data field as JSON.""" + return json.loads(self.data) diff --git a/src/zep_cloud/core/jsonable_encoder.py b/src/zep_cloud/core/jsonable_encoder.py index afee3662..f8beaeaf 100644 --- a/src/zep_cloud/core/jsonable_encoder.py +++ b/src/zep_cloud/core/jsonable_encoder.py @@ -30,6 +30,10 @@ def jsonable_encoder(obj: Any, custom_encoder: Optional[Dict[Any, Callable[[Any], Any]]] = None) -> Any: custom_encoder = custom_encoder or {} + # Generated SDKs use Ellipsis (`...`) as the sentinel value for "OMIT". + # OMIT values should be excluded from serialized payloads. + if obj is Ellipsis: + return None if custom_encoder: if type(obj) in custom_encoder: return custom_encoder[type(obj)](obj) @@ -70,6 +74,8 @@ def jsonable_encoder(obj: Any, custom_encoder: Optional[Dict[Any, Callable[[Any] allowed_keys = set(obj.keys()) for key, value in obj.items(): if key in allowed_keys: + if value is Ellipsis: + continue encoded_key = jsonable_encoder(key, custom_encoder=custom_encoder) encoded_value = jsonable_encoder(value, custom_encoder=custom_encoder) encoded_dict[encoded_key] = encoded_value @@ -77,6 +83,8 @@ def jsonable_encoder(obj: Any, custom_encoder: Optional[Dict[Any, Callable[[Any] if isinstance(obj, (list, set, frozenset, GeneratorType, tuple)): encoded_list = [] for item in obj: + if item is Ellipsis: + continue encoded_list.append(jsonable_encoder(item, custom_encoder=custom_encoder)) return encoded_list diff --git a/src/zep_cloud/core/logging.py b/src/zep_cloud/core/logging.py new file mode 100644 index 00000000..e5e57245 --- /dev/null +++ b/src/zep_cloud/core/logging.py @@ -0,0 +1,107 @@ +# This file was auto-generated by Fern from our API Definition. + +import logging +import typing + +LogLevel = typing.Literal["debug", "info", "warn", "error"] + +_LOG_LEVEL_MAP: typing.Dict[LogLevel, int] = { + "debug": 1, + "info": 2, + "warn": 3, + "error": 4, +} + + +class ILogger(typing.Protocol): + def debug(self, message: str, **kwargs: typing.Any) -> None: ... + def info(self, message: str, **kwargs: typing.Any) -> None: ... + def warn(self, message: str, **kwargs: typing.Any) -> None: ... + def error(self, message: str, **kwargs: typing.Any) -> None: ... + + +class ConsoleLogger: + _logger: logging.Logger + + def __init__(self) -> None: + self._logger = logging.getLogger("fern") + if not self._logger.handlers: + handler = logging.StreamHandler() + handler.setFormatter(logging.Formatter("%(levelname)s - %(message)s")) + self._logger.addHandler(handler) + self._logger.setLevel(logging.DEBUG) + + def debug(self, message: str, **kwargs: typing.Any) -> None: + self._logger.debug(message, extra=kwargs) + + def info(self, message: str, **kwargs: typing.Any) -> None: + self._logger.info(message, extra=kwargs) + + def warn(self, message: str, **kwargs: typing.Any) -> None: + self._logger.warning(message, extra=kwargs) + + def error(self, message: str, **kwargs: typing.Any) -> None: + self._logger.error(message, extra=kwargs) + + +class LogConfig(typing.TypedDict, total=False): + level: LogLevel + logger: ILogger + silent: bool + + +class Logger: + _level: int + _logger: ILogger + _silent: bool + + def __init__(self, *, level: LogLevel, logger: ILogger, silent: bool) -> None: + self._level = _LOG_LEVEL_MAP[level] + self._logger = logger + self._silent = silent + + def _should_log(self, level: LogLevel) -> bool: + return not self._silent and self._level <= _LOG_LEVEL_MAP[level] + + def is_debug(self) -> bool: + return self._should_log("debug") + + def is_info(self) -> bool: + return self._should_log("info") + + def is_warn(self) -> bool: + return self._should_log("warn") + + def is_error(self) -> bool: + return self._should_log("error") + + def debug(self, message: str, **kwargs: typing.Any) -> None: + if self.is_debug(): + self._logger.debug(message, **kwargs) + + def info(self, message: str, **kwargs: typing.Any) -> None: + if self.is_info(): + self._logger.info(message, **kwargs) + + def warn(self, message: str, **kwargs: typing.Any) -> None: + if self.is_warn(): + self._logger.warn(message, **kwargs) + + def error(self, message: str, **kwargs: typing.Any) -> None: + if self.is_error(): + self._logger.error(message, **kwargs) + + +_default_logger: Logger = Logger(level="info", logger=ConsoleLogger(), silent=True) + + +def create_logger(config: typing.Optional[typing.Union[LogConfig, Logger]] = None) -> Logger: + if config is None: + return _default_logger + if isinstance(config, Logger): + return config + return Logger( + level=config.get("level", "info"), + logger=config.get("logger", ConsoleLogger()), + silent=config.get("silent", True), + ) diff --git a/src/zep_cloud/core/pagination.py b/src/zep_cloud/core/pagination.py new file mode 100644 index 00000000..760b0899 --- /dev/null +++ b/src/zep_cloud/core/pagination.py @@ -0,0 +1,82 @@ +# This file was auto-generated by Fern from our API Definition. + +from __future__ import annotations + +from dataclasses import dataclass +from typing import AsyncIterator, Awaitable, Callable, Generic, Iterator, List, Optional, TypeVar + +# Generic to represent the underlying type of the results within a page +T = TypeVar("T") +# Generic to represent the type of the API response +R = TypeVar("R") + + +# SDKs implement a Page ABC per-pagination request, the endpoint then returns a pager that wraps this type +# for example, an endpoint will return SyncPager[UserPage] where UserPage implements the Page ABC. ex: +# +# SyncPager( +# has_next=response.list_metadata.after is not None, +# items=response.data, +# # This should be the outer function that returns the SyncPager again +# get_next=lambda: list(..., cursor: response.cursor) (or list(..., offset: offset + 1)) +# ) + + +@dataclass(frozen=True) +class SyncPager(Generic[T, R]): + get_next: Optional[Callable[[], Optional[SyncPager[T, R]]]] + has_next: bool + items: Optional[List[T]] + response: R + + # Here we type ignore the iterator to avoid a mypy error + # caused by the type conflict with Pydanitc's __iter__ method + # brought in by extending the base model + def __iter__(self) -> Iterator[T]: # type: ignore[override] + for page in self.iter_pages(): + if page.items is not None: + yield from page.items + + def iter_pages(self) -> Iterator[SyncPager[T, R]]: + page: Optional[SyncPager[T, R]] = self + while page is not None: + yield page + + if not page.has_next or page.get_next is None: + return + + page = page.get_next() + if page is None or page.items is None or len(page.items) == 0: + return + + def next_page(self) -> Optional[SyncPager[T, R]]: + return self.get_next() if self.get_next is not None else None + + +@dataclass(frozen=True) +class AsyncPager(Generic[T, R]): + get_next: Optional[Callable[[], Awaitable[Optional[AsyncPager[T, R]]]]] + has_next: bool + items: Optional[List[T]] + response: R + + async def __aiter__(self) -> AsyncIterator[T]: + async for page in self.iter_pages(): + if page.items is not None: + for item in page.items: + yield item + + async def iter_pages(self) -> AsyncIterator[AsyncPager[T, R]]: + page: Optional[AsyncPager[T, R]] = self + while page is not None: + yield page + + if not page.has_next or page.get_next is None: + return + + page = await page.get_next() + if page is None or page.items is None or len(page.items) == 0: + return + + async def next_page(self) -> Optional[AsyncPager[T, R]]: + return await self.get_next() if self.get_next is not None else None diff --git a/src/zep_cloud/core/parse_error.py b/src/zep_cloud/core/parse_error.py new file mode 100644 index 00000000..4527c6a8 --- /dev/null +++ b/src/zep_cloud/core/parse_error.py @@ -0,0 +1,36 @@ +# This file was auto-generated by Fern from our API Definition. + +from typing import Any, Dict, Optional + + +class ParsingError(Exception): + """ + Raised when the SDK fails to parse/validate a response from the server. + This typically indicates that the server returned a response whose shape + does not match the expected schema. + """ + + headers: Optional[Dict[str, str]] + status_code: Optional[int] + body: Any + cause: Optional[Exception] + + def __init__( + self, + *, + headers: Optional[Dict[str, str]] = None, + status_code: Optional[int] = None, + body: Any = None, + cause: Optional[Exception] = None, + ) -> None: + self.headers = headers + self.status_code = status_code + self.body = body + self.cause = cause + super().__init__() + if cause is not None: + self.__cause__ = cause + + def __str__(self) -> str: + cause_str = f", cause: {self.cause}" if self.cause is not None else "" + return f"headers: {self.headers}, status_code: {self.status_code}, body: {self.body}{cause_str}" diff --git a/src/zep_cloud/core/pydantic_utilities.py b/src/zep_cloud/core/pydantic_utilities.py index 7db29500..fea3a08d 100644 --- a/src/zep_cloud/core/pydantic_utilities.py +++ b/src/zep_cloud/core/pydantic_utilities.py @@ -2,30 +2,129 @@ # nopycln: file import datetime as dt +import inspect +import json +import logging from collections import defaultdict -from typing import Any, Callable, ClassVar, Dict, List, Mapping, Optional, Set, Tuple, Type, TypeVar, Union, cast +from dataclasses import asdict +from typing import ( + TYPE_CHECKING, + Any, + Callable, + ClassVar, + Dict, + List, + Mapping, + Optional, + Set, + Tuple, + Type, + TypeVar, + Union, + cast, +) import pydantic +import typing_extensions +from pydantic.fields import FieldInfo as _FieldInfo + +_logger = logging.getLogger(__name__) + +if TYPE_CHECKING: + from .http_sse._models import ServerSentEvent IS_PYDANTIC_V2 = pydantic.VERSION.startswith("2.") if IS_PYDANTIC_V2: - from pydantic.v1.datetime_parse import parse_date as parse_date - from pydantic.v1.datetime_parse import parse_datetime as parse_datetime - from pydantic.v1.fields import ModelField as ModelField - from pydantic.v1.json import ENCODERS_BY_TYPE as encoders_by_type # type: ignore[attr-defined] - from pydantic.v1.typing import get_args as get_args - from pydantic.v1.typing import get_origin as get_origin - from pydantic.v1.typing import is_literal_type as is_literal_type - from pydantic.v1.typing import is_union as is_union + _datetime_adapter = pydantic.TypeAdapter(dt.datetime) # type: ignore[attr-defined] + _date_adapter = pydantic.TypeAdapter(dt.date) # type: ignore[attr-defined] + + def parse_datetime(value: Any) -> dt.datetime: # type: ignore[misc] + if isinstance(value, dt.datetime): + return value + return _datetime_adapter.validate_python(value) + + def parse_date(value: Any) -> dt.date: # type: ignore[misc] + if isinstance(value, dt.datetime): + return value.date() + if isinstance(value, dt.date): + return value + return _date_adapter.validate_python(value) + + # Avoid importing from pydantic.v1 to maintain Python 3.14 compatibility. + from typing import get_args as get_args # type: ignore[assignment] + from typing import get_origin as get_origin # type: ignore[assignment] + + def is_literal_type(tp: Optional[Type[Any]]) -> bool: # type: ignore[misc] + return typing_extensions.get_origin(tp) is typing_extensions.Literal + + def is_union(tp: Optional[Type[Any]]) -> bool: # type: ignore[misc] + return tp is Union or typing_extensions.get_origin(tp) is Union # type: ignore[comparison-overlap] + + # Inline encoders_by_type to avoid importing from pydantic.v1.json + import re as _re + from collections import deque as _deque + from decimal import Decimal as _Decimal + from enum import Enum as _Enum + from ipaddress import ( + IPv4Address as _IPv4Address, + ) + from ipaddress import ( + IPv4Interface as _IPv4Interface, + ) + from ipaddress import ( + IPv4Network as _IPv4Network, + ) + from ipaddress import ( + IPv6Address as _IPv6Address, + ) + from ipaddress import ( + IPv6Interface as _IPv6Interface, + ) + from ipaddress import ( + IPv6Network as _IPv6Network, + ) + from pathlib import Path as _Path + from types import GeneratorType as _GeneratorType + from uuid import UUID as _UUID + + from pydantic.fields import FieldInfo as ModelField # type: ignore[no-redef, assignment] + + def _decimal_encoder(dec_value: Any) -> Any: + if dec_value.as_tuple().exponent >= 0: + return int(dec_value) + return float(dec_value) + + encoders_by_type: Dict[Type[Any], Callable[[Any], Any]] = { # type: ignore[no-redef] + bytes: lambda o: o.decode(), + dt.date: lambda o: o.isoformat(), + dt.datetime: lambda o: o.isoformat(), + dt.time: lambda o: o.isoformat(), + dt.timedelta: lambda td: td.total_seconds(), + _Decimal: _decimal_encoder, + _Enum: lambda o: o.value, + frozenset: list, + _deque: list, + _GeneratorType: list, + _IPv4Address: str, + _IPv4Interface: str, + _IPv4Network: str, + _IPv6Address: str, + _IPv6Interface: str, + _IPv6Network: str, + _Path: str, + _re.Pattern: lambda o: o.pattern, + set: list, + _UUID: str, + } else: from pydantic.datetime_parse import parse_date as parse_date # type: ignore[no-redef] from pydantic.datetime_parse import parse_datetime as parse_datetime # type: ignore[no-redef] - from pydantic.fields import ModelField as ModelField # type: ignore[attr-defined, no-redef] + from pydantic.fields import ModelField as ModelField # type: ignore[attr-defined, no-redef, assignment] from pydantic.json import ENCODERS_BY_TYPE as encoders_by_type # type: ignore[no-redef] from pydantic.typing import get_args as get_args # type: ignore[no-redef] from pydantic.typing import get_origin as get_origin # type: ignore[no-redef] - from pydantic.typing import is_literal_type as is_literal_type # type: ignore[no-redef] + from pydantic.typing import is_literal_type as is_literal_type # type: ignore[no-redef, assignment] from pydantic.typing import is_union as is_union # type: ignore[no-redef] from .datetime_utils import serialize_datetime @@ -36,8 +135,212 @@ Model = TypeVar("Model", bound=pydantic.BaseModel) +def _get_discriminator_and_variants(type_: Type[Any]) -> Tuple[Optional[str], Optional[List[Type[Any]]]]: + """ + Extract the discriminator field name and union variants from a discriminated union type. + Supports Annotated[Union[...], Field(discriminator=...)] patterns. + Returns (discriminator, variants) or (None, None) if not a discriminated union. + """ + origin = typing_extensions.get_origin(type_) + + if origin is typing_extensions.Annotated: + args = typing_extensions.get_args(type_) + if len(args) >= 2: + inner_type = args[0] + # Check annotations for discriminator + discriminator = None + for annotation in args[1:]: + if hasattr(annotation, "discriminator"): + discriminator = getattr(annotation, "discriminator", None) + break + + if discriminator: + inner_origin = typing_extensions.get_origin(inner_type) + if inner_origin is Union: + variants = list(typing_extensions.get_args(inner_type)) + return discriminator, variants + return None, None + + +def _get_field_annotation(model: Type[Any], field_name: str) -> Optional[Type[Any]]: + """Get the type annotation of a field from a Pydantic model.""" + if IS_PYDANTIC_V2: + fields = getattr(model, "model_fields", {}) + field_info = fields.get(field_name) + if field_info: + return cast(Optional[Type[Any]], field_info.annotation) + else: + fields = getattr(model, "__fields__", {}) + field_info = fields.get(field_name) + if field_info: + return cast(Optional[Type[Any]], field_info.outer_type_) + return None + + +def _find_variant_by_discriminator( + variants: List[Type[Any]], + discriminator: str, + discriminator_value: Any, +) -> Optional[Type[Any]]: + """Find the union variant that matches the discriminator value.""" + for variant in variants: + if not (inspect.isclass(variant) and issubclass(variant, pydantic.BaseModel)): + continue + + disc_annotation = _get_field_annotation(variant, discriminator) + if disc_annotation and is_literal_type(disc_annotation): + literal_args = get_args(disc_annotation) + if literal_args and literal_args[0] == discriminator_value: + return variant + return None + + +def _is_string_type(type_: Type[Any]) -> bool: + """Check if a type is str or Optional[str].""" + if type_ is str: + return True + + origin = typing_extensions.get_origin(type_) + if origin is Union: + args = typing_extensions.get_args(type_) + # Optional[str] = Union[str, None] + non_none_args = [a for a in args if a is not type(None)] + if len(non_none_args) == 1 and non_none_args[0] is str: + return True + + return False + + +def parse_sse_obj(sse: "ServerSentEvent", type_: Type[T]) -> T: + """ + Parse a ServerSentEvent into the appropriate type. + + Handles two scenarios based on where the discriminator field is located: + + 1. Data-level discrimination: The discriminator (e.g., 'type') is inside the 'data' payload. + The union describes the data content, not the SSE envelope. + -> Returns: json.loads(data) parsed into the type + + Example: ChatStreamResponse with discriminator='type' + Input: ServerSentEvent(event="message", data='{"type": "content-delta", ...}', id="") + Output: ContentDeltaEvent (parsed from data, SSE envelope stripped) + + 2. Event-level discrimination: The discriminator (e.g., 'event') is at the SSE event level. + The union describes the full SSE event structure. + -> Returns: SSE envelope with 'data' field JSON-parsed only if the variant expects non-string + + Example: JobStreamResponse with discriminator='event' + Input: ServerSentEvent(event="ERROR", data='{"code": "FAILED", ...}', id="123") + Output: JobStreamResponse_Error with data as ErrorData object + + But for variants where data is str (like STATUS_UPDATE): + Input: ServerSentEvent(event="STATUS_UPDATE", data='{"status": "processing"}', id="1") + Output: JobStreamResponse_StatusUpdate with data as string (not parsed) + + Args: + sse: The ServerSentEvent object to parse + type_: The target discriminated union type + + Returns: + The parsed object of type T + + Note: + This function is only available in SDK contexts where http_sse module exists. + """ + sse_event = asdict(sse) + discriminator, variants = _get_discriminator_and_variants(type_) + + if discriminator is None or variants is None: + # Not a discriminated union - parse the data field as JSON + data_value = sse_event.get("data") + if isinstance(data_value, str) and data_value: + try: + parsed_data = json.loads(data_value) + return parse_obj_as(type_, parsed_data) + except json.JSONDecodeError as e: + _logger.warning( + "Failed to parse SSE data field as JSON: %s, data: %s", + e, + data_value[:100] if len(data_value) > 100 else data_value, + ) + return parse_obj_as(type_, sse_event) + + data_value = sse_event.get("data") + + # Check if discriminator is at the top level (event-level discrimination) + if discriminator in sse_event: + # Case 2: Event-level discrimination + # Find the matching variant to check if 'data' field needs JSON parsing + disc_value = sse_event.get(discriminator) + matching_variant = _find_variant_by_discriminator(variants, discriminator, disc_value) + + if matching_variant is not None: + # Check what type the variant expects for 'data' + data_type = _get_field_annotation(matching_variant, "data") + if data_type is not None and not _is_string_type(data_type): + # Variant expects non-string data - parse JSON + if isinstance(data_value, str) and data_value: + try: + parsed_data = json.loads(data_value) + new_object = dict(sse_event) + new_object["data"] = parsed_data + return parse_obj_as(type_, new_object) + except json.JSONDecodeError as e: + _logger.warning( + "Failed to parse SSE data field as JSON for event-level discrimination: %s, data: %s", + e, + data_value[:100] if len(data_value) > 100 else data_value, + ) + # Either no matching variant, data is string type, or JSON parse failed + return parse_obj_as(type_, sse_event) + + else: + # Case 1: Data-level discrimination + # The discriminator is inside the data payload - extract and parse data only + if isinstance(data_value, str) and data_value: + try: + parsed_data = json.loads(data_value) + return parse_obj_as(type_, parsed_data) + except json.JSONDecodeError as e: + _logger.warning( + "Failed to parse SSE data field as JSON for data-level discrimination: %s, data: %s", + e, + data_value[:100] if len(data_value) > 100 else data_value, + ) + return parse_obj_as(type_, sse_event) + + def parse_obj_as(type_: Type[T], object_: Any) -> T: - dealiased_object = convert_and_respect_annotation_metadata(object_=object_, annotation=type_, direction="read") + # convert_and_respect_annotation_metadata is required for TypedDict aliasing. + # + # For Pydantic models, whether we should pre-dealias depends on how the model encodes aliasing: + # - If the model uses real Pydantic aliases (pydantic.Field(alias=...)), then we must pass wire keys through + # unchanged so Pydantic can validate them. + # - If the model encodes aliasing only via FieldMetadata annotations, then we MUST pre-dealias because Pydantic + # will not recognize those aliases during validation. + if inspect.isclass(type_) and issubclass(type_, pydantic.BaseModel): + has_pydantic_aliases = False + if IS_PYDANTIC_V2: + for field_name, field_info in getattr(type_, "model_fields", {}).items(): # type: ignore[attr-defined] + alias = getattr(field_info, "alias", None) + if alias is not None and alias != field_name: + has_pydantic_aliases = True + break + else: + for field in getattr(type_, "__fields__", {}).values(): + alias = getattr(field, "alias", None) + name = getattr(field, "name", None) + if alias is not None and name is not None and alias != name: + has_pydantic_aliases = True + break + + dealiased_object = ( + object_ + if has_pydantic_aliases + else convert_and_respect_annotation_metadata(object_=object_, annotation=type_, direction="read") + ) + else: + dealiased_object = convert_and_respect_annotation_metadata(object_=object_, annotation=type_, direction="read") if IS_PYDANTIC_V2: adapter = pydantic.TypeAdapter(type_) # type: ignore[attr-defined] return adapter.validate_python(dealiased_object) @@ -59,9 +362,46 @@ class UniversalBaseModel(pydantic.BaseModel): protected_namespaces=(), ) + @pydantic.model_validator(mode="before") # type: ignore[attr-defined] + @classmethod + def _coerce_field_names_to_aliases(cls, data: Any) -> Any: + """ + Accept Python field names in input by rewriting them to their Pydantic aliases, + while avoiding silent collisions when a key could refer to multiple fields. + """ + if not isinstance(data, Mapping): + return data + + fields = getattr(cls, "model_fields", {}) # type: ignore[attr-defined] + name_to_alias: Dict[str, str] = {} + alias_to_name: Dict[str, str] = {} + + for name, field_info in fields.items(): + alias = getattr(field_info, "alias", None) or name + name_to_alias[name] = alias + if alias != name: + alias_to_name[alias] = name + + # Detect ambiguous keys: a key that is an alias for one field and a name for another. + ambiguous_keys = set(alias_to_name.keys()).intersection(set(name_to_alias.keys())) + for key in ambiguous_keys: + if key in data and name_to_alias[key] not in data: + raise ValueError( + f"Ambiguous input key '{key}': it is both a field name and an alias. " + "Provide the explicit alias key to disambiguate." + ) + + original_keys = set(data.keys()) + rewritten: Dict[str, Any] = dict(data) + for name, alias in name_to_alias.items(): + if alias != name and name in original_keys and alias not in rewritten: + rewritten[alias] = rewritten.pop(name) + + return rewritten + @pydantic.model_serializer(mode="plain", when_used="json") # type: ignore[attr-defined] def serialize_model(self) -> Any: # type: ignore[name-defined] - serialized = self.model_dump() + serialized = self.dict() # type: ignore[attr-defined] data = {k: serialize_datetime(v) if isinstance(v, dt.datetime) else v for k, v in serialized.items()} return data @@ -71,6 +411,40 @@ class Config: smart_union = True json_encoders = {dt.datetime: serialize_datetime} + @pydantic.root_validator(pre=True) + def _coerce_field_names_to_aliases(cls, values: Any) -> Any: + """ + Pydantic v1 equivalent of _coerce_field_names_to_aliases. + """ + if not isinstance(values, Mapping): + return values + + fields = getattr(cls, "__fields__", {}) + name_to_alias: Dict[str, str] = {} + alias_to_name: Dict[str, str] = {} + + for name, field in fields.items(): + alias = getattr(field, "alias", None) or name + name_to_alias[name] = alias + if alias != name: + alias_to_name[alias] = name + + ambiguous_keys = set(alias_to_name.keys()).intersection(set(name_to_alias.keys())) + for key in ambiguous_keys: + if key in values and name_to_alias[key] not in values: + raise ValueError( + f"Ambiguous input key '{key}': it is both a field name and an alias. " + "Provide the explicit alias key to disambiguate." + ) + + original_keys = set(values.keys()) + rewritten: Dict[str, Any] = dict(values) + for name, alias in name_to_alias.items(): + if alias != name and name in original_keys and alias not in rewritten: + rewritten[alias] = rewritten.pop(name) + + return rewritten + @classmethod def model_construct(cls: Type["Model"], _fields_set: Optional[Set[str]] = None, **values: Any) -> "Model": dealiased_object = convert_and_respect_annotation_metadata(object_=values, annotation=cls, direction="read") @@ -147,7 +521,10 @@ def dict(self, **kwargs: Any) -> Dict[str, Any]: dict_dump = super().dict(**kwargs_with_defaults_exclude_unset_include_fields) - return convert_and_respect_annotation_metadata(object_=dict_dump, annotation=self.__class__, direction="write") + return cast( + Dict[str, Any], + convert_and_respect_annotation_metadata(object_=dict_dump, annotation=self.__class__, direction="write"), + ) def _union_list_of_pydantic_dicts(source: List[Any], destination: List[Any]) -> List[Any]: @@ -217,7 +594,9 @@ def universal_root_validator( ) -> Callable[[AnyCallable], AnyCallable]: def decorator(func: AnyCallable) -> AnyCallable: if IS_PYDANTIC_V2: - return cast(AnyCallable, pydantic.model_validator(mode="before" if pre else "after")(func)) # type: ignore[attr-defined] + # In Pydantic v2, for RootModel we always use "before" mode + # The custom validators transform the input value before the model is created + return cast(AnyCallable, pydantic.model_validator(mode="before")(func)) # type: ignore[attr-defined] return cast(AnyCallable, pydantic.root_validator(pre=pre)(func)) # type: ignore[call-overload] return decorator @@ -232,7 +611,7 @@ def decorator(func: AnyCallable) -> AnyCallable: return decorator -PydanticField = Union[ModelField, pydantic.fields.FieldInfo] +PydanticField = Union[ModelField, _FieldInfo] def _get_model_fields(model: Type["Model"]) -> Mapping[str, PydanticField]: diff --git a/src/zep_cloud/environment.py b/src/zep_cloud/environment.py index b8424b82..f5a5d282 100644 --- a/src/zep_cloud/environment.py +++ b/src/zep_cloud/environment.py @@ -4,4 +4,4 @@ class ZepEnvironment(enum.Enum): - DEFAULT = "https://api.getzep.com/api/v2" + DEFAULT = "https://api.getzep.com/api/v4" diff --git a/src/zep_cloud/errors/__init__.py b/src/zep_cloud/errors/__init__.py index 28fad1dd..370c6048 100644 --- a/src/zep_cloud/errors/__init__.py +++ b/src/zep_cloud/errors/__init__.py @@ -2,10 +2,39 @@ # isort: skip_file -from .bad_request_error import BadRequestError -from .conflict_error import ConflictError -from .forbidden_error import ForbiddenError -from .internal_server_error import InternalServerError -from .not_found_error import NotFoundError +import typing +from importlib import import_module -__all__ = ["BadRequestError", "ConflictError", "ForbiddenError", "InternalServerError", "NotFoundError"] +if typing.TYPE_CHECKING: + from .bad_request_error import BadRequestError + from .not_found_error import NotFoundError + from .unauthorized_error import UnauthorizedError +_dynamic_imports: typing.Dict[str, str] = { + "BadRequestError": ".bad_request_error", + "NotFoundError": ".not_found_error", + "UnauthorizedError": ".unauthorized_error", +} + + +def __getattr__(attr_name: str) -> typing.Any: + module_name = _dynamic_imports.get(attr_name) + if module_name is None: + raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") + try: + module = import_module(module_name, __package__) + if module_name == f".{attr_name}": + return module + else: + return getattr(module, attr_name) + except ImportError as e: + raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e + except AttributeError as e: + raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e + + +def __dir__(): + lazy_attrs = list(_dynamic_imports.keys()) + return sorted(lazy_attrs) + + +__all__ = ["BadRequestError", "NotFoundError", "UnauthorizedError"] diff --git a/src/zep_cloud/errors/bad_request_error.py b/src/zep_cloud/errors/bad_request_error.py index baf5be4f..3cf1d9ca 100644 --- a/src/zep_cloud/errors/bad_request_error.py +++ b/src/zep_cloud/errors/bad_request_error.py @@ -2,9 +2,10 @@ import typing -from ..core.api_error import ApiError +from ..core.api_error import ApiError as core_api_error_ApiError +from ..types.api_error import ApiError as types_api_error_ApiError -class BadRequestError(ApiError): - def __init__(self, body: typing.Optional[typing.Any], headers: typing.Optional[typing.Dict[str, str]] = None): +class BadRequestError(core_api_error_ApiError): + def __init__(self, body: types_api_error_ApiError, headers: typing.Optional[typing.Dict[str, str]] = None): super().__init__(status_code=400, headers=headers, body=body) diff --git a/src/zep_cloud/errors/forbidden_error.py b/src/zep_cloud/errors/forbidden_error.py deleted file mode 100644 index 80e15e0f..00000000 --- a/src/zep_cloud/errors/forbidden_error.py +++ /dev/null @@ -1,11 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -from ..core.api_error import ApiError as core_api_error_ApiError -from ..types.api_error import ApiError as types_api_error_ApiError - - -class ForbiddenError(core_api_error_ApiError): - def __init__(self, body: types_api_error_ApiError, headers: typing.Optional[typing.Dict[str, str]] = None): - super().__init__(status_code=403, headers=headers, body=body) diff --git a/src/zep_cloud/errors/internal_server_error.py b/src/zep_cloud/errors/internal_server_error.py deleted file mode 100644 index 42082d1f..00000000 --- a/src/zep_cloud/errors/internal_server_error.py +++ /dev/null @@ -1,11 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -from ..core.api_error import ApiError as core_api_error_ApiError -from ..types.api_error import ApiError as types_api_error_ApiError - - -class InternalServerError(core_api_error_ApiError): - def __init__(self, body: types_api_error_ApiError, headers: typing.Optional[typing.Dict[str, str]] = None): - super().__init__(status_code=500, headers=headers, body=body) diff --git a/src/zep_cloud/errors/conflict_error.py b/src/zep_cloud/errors/unauthorized_error.py similarity index 74% rename from src/zep_cloud/errors/conflict_error.py rename to src/zep_cloud/errors/unauthorized_error.py index a1954586..da573a2e 100644 --- a/src/zep_cloud/errors/conflict_error.py +++ b/src/zep_cloud/errors/unauthorized_error.py @@ -6,6 +6,6 @@ from ..types.api_error import ApiError as types_api_error_ApiError -class ConflictError(core_api_error_ApiError): +class UnauthorizedError(core_api_error_ApiError): def __init__(self, body: types_api_error_ApiError, headers: typing.Optional[typing.Dict[str, str]] = None): - super().__init__(status_code=409, headers=headers, body=body) + super().__init__(status_code=401, headers=headers, body=body) diff --git a/src/zep_cloud/external_clients/graph.py b/src/zep_cloud/external_clients/graph.py deleted file mode 100644 index 3c650657..00000000 --- a/src/zep_cloud/external_clients/graph.py +++ /dev/null @@ -1,457 +0,0 @@ -import typing - -from zep_cloud import EdgeType, EntityEdgeSourceTarget -from zep_cloud.core.client_wrapper import AsyncClientWrapper, SyncClientWrapper -from zep_cloud.external_clients.ontology import ( - EdgeModel, - edge_model_to_api_schema, - entity_model_to_api_schema, -) -from zep_cloud.graph.client import AsyncGraphClient as AsyncBaseGraphClient -from zep_cloud.graph.client import GraphClient as BaseGraphClient -from zep_cloud.types import EntityType - -if typing.TYPE_CHECKING: - from zep_cloud.external_clients.ontology import EntityModel -from zep_cloud.core.request_options import RequestOptions - - -class GraphClient(BaseGraphClient): - def __init__(self, *, client_wrapper: SyncClientWrapper): - super().__init__(client_wrapper=client_wrapper) - - def set_ontology( - self, - entities: dict[str, "EntityModel"], - edges: typing.Optional[ - dict[ - str, - typing.Union[ - "EdgeModel", - typing.Tuple["EdgeModel", typing.List[EntityEdgeSourceTarget]], - ], - ] - ] = None, - user_ids: typing.Optional[typing.List[str]] = None, - graph_ids: typing.Optional[typing.List[str]] = None, - request_options: typing.Optional[RequestOptions] = None, - ): - """ - Sets the entity and edge types for a project, replacing any existing ones. - - Parameters - ---------- - entities : dict[str, "EntityModel"] - Entity type definitions. - - edges : typing.Optional[dict[str, typing.Union["EdgeModel", typing.Tuple["EdgeModel", typing.List[EntityEdgeSourceTarget]]]]] - Edge type definitions. - - user_ids : typing.Optional[typing.List[str]] - - The user identifiers for which to set the ontology. - - graph_ids : typing.Optional[typing.List[str]] - The graph identifiers for which to set the ontology. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Examples - -------- - - class Destination(EntityModel): - - \"""A destination is a place that travelers visit.\""" - destination_name: EntityText = Field( - description="The name of the destination", - default=None - ) - country: EntityText = Field( - description="The country of the destination", - default=None - ) - region: EntityText = Field( - description="The region of the destination", - default=None - ) - description: EntityText = Field( - description="A description of the destination", - default=None - ) - - - class TravelingTo(EdgeModel): - - \"""An edge representing a traveler going to a destination.\""" - travel_date: EntityText = Field( - description="The date of travel to this destination", - default=None - ) - purpose: EntityText = Field( - description="The purpose of travel (Business, Leisure, etc.)", - default=None - ) - - client.graph.set_ontology( - entities={ - "Destination": Destination, - }, - edges={ - "TRAVELING_TO": ( - TravelingTo, - [ - EntityEdgeSourceTarget( - source="User", - target="Destination" - ) - ] - ), - } - ) - """ - return self.set_entity_types( - entities=entities, - edges=edges, - user_ids=user_ids, - graph_ids=graph_ids, - request_options=request_options, - ) - - def set_entity_types( - self, - entities: dict[str, "EntityModel"], - edges: typing.Optional[ - dict[ - str, - typing.Union[ - "EdgeModel", - typing.Tuple["EdgeModel", typing.List[EntityEdgeSourceTarget]], - ], - ] - ] = None, - user_ids: typing.Optional[typing.List[str]] = None, - graph_ids: typing.Optional[typing.List[str]] = None, - request_options: typing.Optional[RequestOptions] = None, - ): - """ - Sets the entity and edge types for a project, replacing any existing ones. - - Parameters - ---------- - entities : dict[str, "EntityModel"] - - edges : typing.Optional[dict[str, typing.Union["EdgeModel", typing.Tuple["EdgeModel", typing.List[EntityEdgeSourceTarget]]]]] - - user_ids : typing.Optional[typing.List[str]] - - The user identifiers for which to set the ontology. - - graph_ids : typing.Optional[typing.List[str]] - The graph identifiers for which to set the ontology. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Examples - -------- - - class Destination(EntityModel): - - \"""A destination is a place that travelers visit.\""" - destination_name: EntityText = Field( - description="The name of the destination", - default=None - ) - country: EntityText = Field( - description="The country of the destination", - default=None - ) - region: EntityText = Field( - description="The region of the destination", - default=None - ) - description: EntityText = Field( - description="A description of the destination", - default=None - ) - - - class TravelingTo(EdgeModel): - - \"""An edge representing a traveler going to a destination.\""" - travel_date: EntityText = Field( - description="The date of travel to this destination", - default=None - ) - purpose: EntityText = Field( - description="The purpose of travel (Business, Leisure, etc.)", - default=None - ) - - client.graph.set_entity_types( - entities={ - "Destination": Destination, - }, - edges={ - "TRAVELING_TO": ( - TravelingTo, - [ - EntityEdgeSourceTarget( - source="User", - target="Destination" - ) - ] - ), - } - ) - """ - api_entity_types: list[EntityType] = [] - api_edge_types: list[EdgeType] = [] - - for name, entity in entities.items(): - entity_dict = entity_model_to_api_schema(entity, name) - api_entity_types.append(EntityType(**entity_dict)) - - if edges: - for name, edge_data in edges.items(): - # Handle both EdgeModel directly and tuple of (model, source_targets) - if isinstance(edge_data, tuple): - edge_model, source_targets = edge_data - else: - edge_model = edge_data - source_targets = None - - edge_dict = edge_model_to_api_schema(edge_model, name) - if source_targets: - edge_dict["source_targets"] = [st.dict() for st in source_targets] - api_edge_types.append(EdgeType(**edge_dict)) - res = self.set_entity_types_internal( - entity_types=api_entity_types, - edge_types=api_edge_types, - user_ids=user_ids, - graph_ids=graph_ids, - request_options=request_options, - ) - return res - - -class AsyncGraphClient(AsyncBaseGraphClient): - def __init__(self, *, client_wrapper: AsyncClientWrapper): - super().__init__(client_wrapper=client_wrapper) - - async def set_ontology( - self, - entities: dict[str, "EntityModel"], - edges: typing.Optional[ - dict[ - str, - typing.Union[ - "EdgeModel", - typing.Tuple["EdgeModel", typing.List[EntityEdgeSourceTarget]], - ], - ] - ] = None, - user_ids: typing.Optional[typing.List[str]] = None, - graph_ids: typing.Optional[typing.List[str]] = None, - request_options: typing.Optional[RequestOptions] = None, - ): - """ - Sets the entity and edge types for a project, replacing any existing ones. - - Parameters - ---------- - entities : dict[str, "EntityModel"] - Entity type definitions. - - edges : typing.Optional[dict[str, typing.Union["EdgeModel", typing.Tuple["EdgeModel", typing.List[EntityEdgeSourceTarget]]]]] - Edge type definitions. - - user_ids : typing.Optional[typing.List[str]] - - The user identifiers for which to set the ontology. - - graph_ids : typing.Optional[typing.List[str]] - The graph identifiers for which to set the ontology. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Examples - -------- - - class Destination(EntityModel): - - \"""A destination is a place that travelers visit.\""" - destination_name: EntityText = Field( - description="The name of the destination", - default=None - ) - country: EntityText = Field( - description="The country of the destination", - default=None - ) - region: EntityText = Field( - description="The region of the destination", - default=None - ) - description: EntityText = Field( - description="A description of the destination", - default=None - ) - - - class TravelingTo(EdgeModel): - - \"""An edge representing a traveler going to a destination.\""" - travel_date: EntityText = Field( - description="The date of travel to this destination", - default=None - ) - purpose: EntityText = Field( - description="The purpose of travel (Business, Leisure, etc.)", - default=None - ) - - await client.graph.set_ontology( - entities={ - "Destination": Destination, - }, - edges={ - "TRAVELING_TO": ( - TravelingTo, - [ - EntityEdgeSourceTarget( - source="User", - target="Destination" - ) - ] - ), - } - ) - """ - return await self.set_entity_types( - entities=entities, - edges=edges, - request_options=request_options, - user_ids=user_ids, - graph_ids=graph_ids - ) - - async def set_entity_types( - self, - entities: dict[str, "EntityModel"], - edges: typing.Optional[ - dict[ - str, - typing.Union[ - "EdgeModel", - typing.Tuple["EdgeModel", typing.List[EntityEdgeSourceTarget]], - ], - ] - ] = None, - user_ids: typing.Optional[typing.List[str]] = None, - graph_ids: typing.Optional[typing.List[str]] = None, - request_options: typing.Optional[RequestOptions] = None, - ): - """ - Sets the entity and edge types for a project, replacing any existing ones. - - Parameters - ---------- - entities : dict[str, "EntityModel"] - - edges : typing.Optional[dict[str, typing.Union["EdgeModel", typing.Tuple["EdgeModel", typing.List[EntityEdgeSourceTarget]]]]] - - user_ids : typing.Optional[typing.List[str]] - - The user identifiers for which to set the ontology. - - graph_ids : typing.Optional[typing.List[str]] - The graph identifiers for which to set the ontology. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Examples - -------- - - class Destination(EntityModel): - - \"""A destination is a place that travelers visit.\""" - destination_name: EntityText = Field( - description="The name of the destination", - default=None - ) - country: EntityText = Field( - description="The country of the destination", - default=None - ) - region: EntityText = Field( - description="The region of the destination", - default=None - ) - description: EntityText = Field( - description="A description of the destination", - default=None - ) - - - class TravelingTo(EdgeModel): - - \"""An edge representing a traveler going to a destination.\""" - travel_date: EntityText = Field( - description="The date of travel to this destination", - default=None - ) - purpose: EntityText = Field( - description="The purpose of travel (Business, Leisure, etc.)", - default=None - ) - - await client.graph.set_entity_types( - entities={ - "Destination": Destination, - }, - edges={ - "TRAVELING_TO": ( - TravelingTo, - [ - EntityEdgeSourceTarget( - source="User", - target="Destination" - ) - ] - ), - } - ) - """ - api_entity_types: list[EntityType] = [] - api_edge_types: list[EdgeType] = [] - - for name, entity in entities.items(): - entity_dict = entity_model_to_api_schema(entity, name) - api_entity_types.append(EntityType(**entity_dict)) - - if edges: - for name, edge_data in edges.items(): - # Handle both EdgeModel directly and tuple of (model, source_targets) - if isinstance(edge_data, tuple): - edge_model, source_targets = edge_data - else: - edge_model = edge_data - source_targets = None - - edge_dict = edge_model_to_api_schema(edge_model, name) - if source_targets: - edge_dict["source_targets"] = [st.dict() for st in source_targets] - api_edge_types.append(EdgeType(**edge_dict)) - - res = await self.set_entity_types_internal( - entity_types=api_entity_types, - edge_types=api_edge_types, - user_ids=user_ids, - graph_ids=graph_ids, - request_options=request_options, - ) - return res diff --git a/src/zep_cloud/external_clients/ontology.py b/src/zep_cloud/external_clients/ontology.py deleted file mode 100644 index c15d99a6..00000000 --- a/src/zep_cloud/external_clients/ontology.py +++ /dev/null @@ -1,168 +0,0 @@ -import typing -from enum import Enum - -from pydantic import BaseModel, Field, WithJsonSchema -from pydantic.json_schema import GenerateJsonSchema, JsonSchemaValue -from pydantic_core import core_schema -from typing_extensions import Annotated - - -class EntityPropertyType(Enum): - Text = "Text" - Int = "Int" - Float = "Float" - Boolean = "Boolean" - - -class EntityField(BaseModel): - """Base class for entity field definitions""" - - description: str - type: EntityPropertyType - - -class EntityBaseText(EntityField): - """Entity field with Text type""" - - type: EntityPropertyType = EntityPropertyType.Text - - -class EntityBaseInt(EntityField): - """Entity field with Int type""" - - type: EntityPropertyType = EntityPropertyType.Int - - -class EntityBaseFloat(EntityField): - """Entity field with Float type""" - - type: EntityPropertyType = EntityPropertyType.Float - - -class EntityBaseBoolean(EntityField): - """Entity field with Boolean type""" - - type: EntityPropertyType = EntityPropertyType.Boolean - - -# Annotated types for entity properties -# These types are used to define the properties of entity and edge models -# Each type includes: -# 1. The base Python type (str, int, float, bool) -# 2. A default value of None -# 3. JSON schema information for serialization - -EntityText = Annotated[ - typing.Optional[str], - Field(default=None), - WithJsonSchema(EntityBaseText.model_json_schema(), mode="serialization"), -] - -EntityInt = Annotated[ - typing.Optional[int], - Field(default=None), - WithJsonSchema(EntityBaseInt.model_json_schema(), mode="serialization"), -] - -EntityFloat = Annotated[ - typing.Optional[float], - Field(default=None), - WithJsonSchema(EntityBaseFloat.model_json_schema(), mode="serialization"), -] - -EntityBoolean = Annotated[ - typing.Optional[bool], - Field(default=None), - WithJsonSchema(EntityBaseBoolean.model_json_schema(), mode="serialization"), -] - - -class _CustomJsonSchema(GenerateJsonSchema): - """ - _CustomJsonSchema is a helper class that flattens and removes nullable as these aren't relevant to the entity schema - and this simplifies server-side deserialization - """ - - def nullable_schema(self, schema: core_schema.CoreSchema) -> JsonSchemaValue: - return self.generate_inner(schema["schema"]) - - -class _BaseSchemaModel(BaseModel): - """Base class for models that need custom JSON schema generation""" - - @classmethod - def model_json_schema(cls, *args, **kwargs): - kwargs["schema_generator"] = _CustomJsonSchema - return super().model_json_schema(*args, **kwargs) - - -class EntityModel(_BaseSchemaModel): - """Entity model for representing entity types""" - - pass - - -class EdgeModel(_BaseSchemaModel): - """Edge model for representing edge types""" - - pass - - -def _model_to_api_schema_common( - model_class: typing.Union["EntityModel", "EdgeModel"], - name: str, - is_edge: bool = False, -) -> dict[str, typing.Any]: - """Common function to convert a Pydantic Model to a JSON schema for API EntityType or EdgeType""" - - schema = model_class.model_json_schema() - - # Define the type with proper typings for properties as a list of dictionaries - result_type: dict[str, typing.Any] = { - "name": name, - "description": model_class.__doc__.strip() if model_class.__doc__ else "", - "properties": [], - } - - # Add source_targets field for edge types - if is_edge: - result_type["source_targets"] = [] - - for field_name, field_schema in schema.get("properties", {}).items(): - if "type" not in field_schema: - continue - - property_type = field_schema.get("type") - type_mapping = { - "string": "Text", - "integer": "Int", - "number": "Float", - "boolean": "Boolean", - } - - if property_type in type_mapping: - property_type = type_mapping[property_type] - else: - raise ValueError(f"Unsupported property type: {property_type}") - - description = field_schema.get("description", "") - - result_type["properties"].append( - {"name": field_name, "type": property_type, "description": description} - ) - - return result_type - - -def entity_model_to_api_schema( - model_class: "EntityModel", name: str -) -> dict[str, typing.Any]: - """Convert a Pydantic EntityModel to a JSON schema for API EntityType""" - return _model_to_api_schema_common(model_class, name, is_edge=False) - - -def edge_model_to_api_schema( - model_class: "EdgeModel", name: str -) -> dict[str, typing.Any]: - """Convert a Pydantic EdgeModel to a JSON schema for API EntityEdge""" - return _model_to_api_schema_common(model_class, name, is_edge=True) diff --git a/src/zep_cloud/external_clients/user.py b/src/zep_cloud/external_clients/user.py deleted file mode 100644 index df91516b..00000000 --- a/src/zep_cloud/external_clients/user.py +++ /dev/null @@ -1,13 +0,0 @@ -from zep_cloud.core.client_wrapper import AsyncClientWrapper, SyncClientWrapper -from zep_cloud.user.client import AsyncUserClient as AsyncBaseUserClient -from zep_cloud.user.client import UserClient as BaseUserClient - - -class UserClient(BaseUserClient): - def __init__(self, *, client_wrapper: SyncClientWrapper): - super().__init__(client_wrapper=client_wrapper) - - -class AsyncUserClient(AsyncBaseUserClient): - def __init__(self, *, client_wrapper: AsyncClientWrapper): - super().__init__(client_wrapper=client_wrapper) diff --git a/src/zep_cloud/graph/__init__.py b/src/zep_cloud/graph/__init__.py index 49f0683f..a82cd113 100644 --- a/src/zep_cloud/graph/__init__.py +++ b/src/zep_cloud/graph/__init__.py @@ -2,6 +2,40 @@ # isort: skip_file -from . import edge, episode, node, observation, thread_summary +import typing +from importlib import import_module -__all__ = ["edge", "episode", "node", "observation", "thread_summary"] +if typing.TYPE_CHECKING: + from . import document_summary, edge, episode, node, observation, thread_summary +_dynamic_imports: typing.Dict[str, str] = { + "document_summary": ".document_summary", + "edge": ".edge", + "episode": ".episode", + "node": ".node", + "observation": ".observation", + "thread_summary": ".thread_summary", +} + + +def __getattr__(attr_name: str) -> typing.Any: + module_name = _dynamic_imports.get(attr_name) + if module_name is None: + raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") + try: + module = import_module(module_name, __package__) + if module_name == f".{attr_name}": + return module + else: + return getattr(module, attr_name) + except ImportError as e: + raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e + except AttributeError as e: + raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e + + +def __dir__(): + lazy_attrs = list(_dynamic_imports.keys()) + return sorted(lazy_attrs) + + +__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..5bd06b59 100644 --- a/src/zep_cloud/graph/client.py +++ b/src/zep_cloud/graph/client.py @@ -1,40 +1,43 @@ # This file was auto-generated by Fern from our API Definition. +from __future__ import annotations + import typing from ..core.client_wrapper import AsyncClientWrapper, SyncClientWrapper +from ..core.pagination import AsyncPager, SyncPager from ..core.request_options import RequestOptions -from ..types.add_node_item import AddNodeItem -from ..types.add_nodes_response import AddNodesResponse -from ..types.add_triple_response import AddTripleResponse -from ..types.clone_graph_response import CloneGraphResponse -from ..types.custom_instruction import CustomInstruction -from ..types.detect_config import DetectConfig -from ..types.detect_patterns_response import DetectPatternsResponse +from ..types.async_result import AsyncResult +from ..types.clone_graph_result import CloneGraphResult +from ..types.edge import Edge +from ..types.edge_page import EdgePage from ..types.edge_type import EdgeType from ..types.entity_type import EntityType -from ..types.entity_type_response import EntityTypeResponse from ..types.episode import Episode -from ..types.episode_data import EpisodeData +from ..types.episode_page import EpisodePage from ..types.graph import Graph -from ..types.graph_data_type import GraphDataType -from ..types.graph_list_response import GraphListResponse -from ..types.graph_search_results import GraphSearchResults -from ..types.graph_search_scope import GraphSearchScope -from ..types.graph_subgraph_response import GraphSubgraphResponse -from ..types.list_custom_instructions_response import ListCustomInstructionsResponse -from ..types.pattern_seeds import PatternSeeds -from ..types.recency_weight import RecencyWeight -from ..types.reranker import Reranker -from ..types.search_filters import SearchFilters -from ..types.success_response import SuccessResponse -from .edge.client import AsyncEdgeClient, EdgeClient -from .episode.client import AsyncEpisodeClient, EpisodeClient -from .node.client import AsyncNodeClient, NodeClient -from .observation.client import AsyncObservationClient, ObservationClient +from ..types.graph_context_response import GraphContextResponse +from ..types.graph_delete_result import GraphDeleteResult +from ..types.graph_page import GraphPage +from ..types.instructions import Instructions +from ..types.node import Node +from ..types.node_page import NodePage +from ..types.observation import Observation +from ..types.observation_page import ObservationPage +from ..types.observation_steering import ObservationSteering +from ..types.ontology import Ontology +from ..types.subgraph_response import SubgraphResponse +from ..types.thread_summary import ThreadSummary +from ..types.thread_summary_page import ThreadSummaryPage from .raw_client import AsyncRawGraphClient, RawGraphClient -from .thread_summary.client import AsyncThreadSummaryClient, ThreadSummaryClient +if typing.TYPE_CHECKING: + 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 + from .observation.client import AsyncObservationClient, ObservationClient + from .thread_summary.client import AsyncThreadSummaryClient, ThreadSummaryClient # this is used as the default value for optional parameters OMIT = typing.cast(typing.Any, ...) @@ -42,15 +45,13 @@ class GraphClient: def __init__(self, *, client_wrapper: SyncClientWrapper): self._raw_client = RawGraphClient(client_wrapper=client_wrapper) - self.edge = EdgeClient(client_wrapper=client_wrapper) - - self.episode = EpisodeClient(client_wrapper=client_wrapper) - - self.node = NodeClient(client_wrapper=client_wrapper) - - self.observation = ObservationClient(client_wrapper=client_wrapper) - - self.thread_summary = ThreadSummaryClient(client_wrapper=client_wrapper) + self._client_wrapper = client_wrapper + self._document_summary: typing.Optional[DocumentSummaryClient] = None + self._episode: typing.Optional[EpisodeClient] = None + self._edge: typing.Optional[EdgeClient] = None + self._node: typing.Optional[NodeClient] = None + self._observation: typing.Optional[ObservationClient] = None + self._thread_summary: typing.Optional[ThreadSummaryClient] = None @property def with_raw_response(self) -> RawGraphClient: @@ -63,31 +64,36 @@ def with_raw_response(self) -> RawGraphClient: """ return self._raw_client - def list_custom_instructions( + def create( self, *, - user_id: typing.Optional[str] = None, - graph_id: typing.Optional[str] = None, + description: typing.Optional[str] = OMIT, + graph_id: typing.Optional[str] = OMIT, + name: typing.Optional[str] = OMIT, + time_zone: typing.Optional[str] = OMIT, + idempotency_key: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, - ) -> ListCustomInstructionsResponse: + ) -> Graph: """ - Lists all custom instructions for a project, user, or graph. - Parameters ---------- - user_id : typing.Optional[str] - User ID to get user-specific instructions + description : typing.Optional[str] graph_id : typing.Optional[str] - Graph ID to get graph-specific instructions + + name : typing.Optional[str] + + time_zone : typing.Optional[str] + + idempotency_key : typing.Optional[str] request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - ListCustomInstructionsResponse - The list of instructions. + Graph + Created Examples -------- @@ -96,96 +102,112 @@ def list_custom_instructions( client = Zep( api_key="YOUR_API_KEY", ) - client.graph.list_custom_instructions( - user_id="user_id", - graph_id="graph_id", - ) + client.graph.create() """ - _response = self._raw_client.list_custom_instructions( - user_id=user_id, graph_id=graph_id, request_options=request_options + _response = self._raw_client.create( + description=description, + graph_id=graph_id, + name=name, + time_zone=time_zone, + idempotency_key=idempotency_key, + request_options=request_options, ) return _response.data - def add_custom_instructions( + def list( self, *, - instructions: typing.Sequence[CustomInstruction], - graph_ids: typing.Optional[typing.Sequence[str]] = OMIT, - user_ids: typing.Optional[typing.Sequence[str]] = OMIT, + limit: typing.Optional[int] = None, + cursor: typing.Optional[str] = None, + order_by: typing.Optional[str] = None, + order: typing.Optional[str] = None, + search: typing.Optional[str] = OMIT, + idempotency_key: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, - ) -> SuccessResponse: + ) -> SyncPager[Graph, GraphPage]: """ - Adds new custom instructions for graphs without removing existing ones. If user_ids or graph_ids is empty, adds to project-wide default instructions. - Parameters ---------- - instructions : typing.Sequence[CustomInstruction] - Instructions to add to the graph. + limit : typing.Optional[int] + Page size + + cursor : typing.Optional[str] + Opaque page cursor - graph_ids : typing.Optional[typing.Sequence[str]] - Graph IDs to add the instructions to. If empty, the instructions are added to the project-wide default. + order_by : typing.Optional[str] + Sort field + + order : typing.Optional[str] + asc or desc + + search : typing.Optional[str] - user_ids : typing.Optional[typing.Sequence[str]] - User IDs to add the instructions to. If empty, the instructions are added to the project-wide default. + idempotency_key : typing.Optional[str] request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - SuccessResponse - Instructions added successfully + SyncPager[Graph, GraphPage] + OK Examples -------- - from zep_cloud import CustomInstruction, Zep + from zep_cloud import Zep client = Zep( api_key="YOUR_API_KEY", ) - client.graph.add_custom_instructions( - instructions=[ - CustomInstruction( - name="name", - text="text", - ) - ], + response = client.graph.list( + limit=1, + cursor="cursor", + order_by="order_by", + order="order", ) + for item in response: + yield item + # alternatively, you can paginate page-by-page + for page in response.iter_pages(): + yield page """ - _response = self._raw_client.add_custom_instructions( - instructions=instructions, graph_ids=graph_ids, user_ids=user_ids, request_options=request_options + return self._raw_client.list( + limit=limit, + cursor=cursor, + order_by=order_by, + order=order, + search=search, + idempotency_key=idempotency_key, + request_options=request_options, ) - return _response.data - def delete_custom_instructions( + def lookup( self, *, - graph_ids: typing.Optional[typing.Sequence[str]] = OMIT, - instruction_names: typing.Optional[typing.Sequence[str]] = OMIT, - user_ids: typing.Optional[typing.Sequence[str]] = OMIT, + graph_id: typing.Optional[str] = OMIT, + thread_id: typing.Optional[str] = OMIT, + user_id: typing.Optional[str] = OMIT, + idempotency_key: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, - ) -> SuccessResponse: + ) -> Graph: """ - Deletes custom instructions for graphs or project wide defaults. - Parameters ---------- - graph_ids : typing.Optional[typing.Sequence[str]] - Determines which group graphs will have their custom instructions deleted. If no graphs are provided, the project-wide custom instructions will be affected. + graph_id : typing.Optional[str] - instruction_names : typing.Optional[typing.Sequence[str]] - Unique identifier for the instructions to be deleted. If empty deletes all instructions. + thread_id : typing.Optional[str] - user_ids : typing.Optional[typing.Sequence[str]] - Determines which user graphs will have their custom instructions deleted. If no users are provided, the project-wide custom instructions will be affected. + user_id : typing.Optional[str] + + idempotency_key : typing.Optional[str] request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - SuccessResponse - Instructions deleted successfully + Graph + OK Examples -------- @@ -194,38 +216,31 @@ def delete_custom_instructions( client = Zep( api_key="YOUR_API_KEY", ) - client.graph.delete_custom_instructions() + client.graph.lookup() """ - _response = self._raw_client.delete_custom_instructions( - graph_ids=graph_ids, instruction_names=instruction_names, user_ids=user_ids, request_options=request_options + _response = self._raw_client.lookup( + graph_id=graph_id, + thread_id=thread_id, + user_id=user_id, + idempotency_key=idempotency_key, + request_options=request_options, ) return _response.data - def list_entity_types( - self, - *, - user_id: typing.Optional[str] = None, - graph_id: typing.Optional[str] = None, - request_options: typing.Optional[RequestOptions] = None, - ) -> EntityTypeResponse: + def get(self, graph_uuid: str, *, request_options: typing.Optional[RequestOptions] = None) -> Graph: """ - Returns all entity types for a project, user, or graph. - Parameters ---------- - user_id : typing.Optional[str] - User ID to get user-specific entity types - - graph_id : typing.Optional[str] - Graph ID to get graph-specific entity types + graph_uuid : str + Graph UUID request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - EntityTypeResponse - The list of entity types. + Graph + OK Examples -------- @@ -234,45 +249,35 @@ def list_entity_types( client = Zep( api_key="YOUR_API_KEY", ) - client.graph.list_entity_types( - user_id="user_id", - graph_id="graph_id", + client.graph.get( + graph_uuid="graph_uuid", ) """ - _response = self._raw_client.list_entity_types( - user_id=user_id, graph_id=graph_id, request_options=request_options - ) + _response = self._raw_client.get(graph_uuid, request_options=request_options) return _response.data - def set_entity_types_internal( + def delete( self, + graph_uuid: str, *, - edge_types: typing.Optional[typing.Sequence[EdgeType]] = OMIT, - entity_types: typing.Optional[typing.Sequence[EntityType]] = OMIT, - graph_ids: typing.Optional[typing.Sequence[str]] = OMIT, - user_ids: typing.Optional[typing.Sequence[str]] = OMIT, + idempotency_key: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, - ) -> SuccessResponse: + ) -> GraphDeleteResult: """ - Sets the entity types for multiple users and graphs, replacing any existing ones. - Parameters ---------- - edge_types : typing.Optional[typing.Sequence[EdgeType]] + graph_uuid : str + Graph UUID - entity_types : typing.Optional[typing.Sequence[EntityType]] - - graph_ids : typing.Optional[typing.Sequence[str]] - - user_ids : typing.Optional[typing.Sequence[str]] + idempotency_key : typing.Optional[str] request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - SuccessResponse - Entity types set successfully + GraphDeleteResult + Accepted Examples -------- @@ -281,62 +286,49 @@ def set_entity_types_internal( client = Zep( api_key="YOUR_API_KEY", ) - client.graph.set_entity_types_internal() + client.graph.delete( + graph_uuid="graph_uuid", + ) """ - _response = self._raw_client.set_entity_types_internal( - edge_types=edge_types, - entity_types=entity_types, - graph_ids=graph_ids, - user_ids=user_ids, - request_options=request_options, + _response = self._raw_client.delete( + graph_uuid, idempotency_key=idempotency_key, request_options=request_options ) return _response.data - def add( + def update( self, + graph_uuid: str, *, - data: str, - type: GraphDataType, - created_at: 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, - strict_ontology: typing.Optional[bool] = OMIT, - user_id: typing.Optional[str] = OMIT, + description: typing.Optional[str] = OMIT, + name: typing.Optional[str] = OMIT, + time_zone: typing.Optional[str] = OMIT, + idempotency_key: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, - ) -> Episode: + ) -> Graph: """ - Add data to the graph. - Parameters ---------- - data : str - - type : GraphDataType - - created_at : typing.Optional[str] - - 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. + graph_uuid : str + Graph UUID - metadata : typing.Optional[typing.Dict[str, typing.Optional[typing.Any]]] - Optional metadata key-value pairs. Max 10 keys. Values must be strings, numbers, booleans, or arrays of scalars. + description : typing.Optional[str] + Omit to leave unchanged, send JSON null to clear, or send a value to set. - source_description : typing.Optional[str] + name : typing.Optional[str] + Omit to leave unchanged, send JSON null to clear, or send a value to set. - strict_ontology : typing.Optional[bool] - When true, prevents extraction of generic Entity nodes that do not match the configured ontology. + time_zone : typing.Optional[str] + Omit to leave unchanged, send JSON null to clear, or send a value to set. - user_id : typing.Optional[str] - User ID is the ID of the user to which the data will be added. If not adding to a user graph, please use graph_id field instead. + idempotency_key : typing.Optional[str] request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - Episode - Added episode + Graph + OK Examples -------- @@ -345,191 +337,146 @@ def add( client = Zep( api_key="YOUR_API_KEY", ) - client.graph.add( - data="data", - type="text", + client.graph.update( + graph_uuid="graph_uuid", ) """ - _response = self._raw_client.add( - data=data, - type=type, - created_at=created_at, - graph_id=graph_id, - metadata=metadata, - source_description=source_description, - strict_ontology=strict_ontology, - user_id=user_id, + _response = self._raw_client.update( + graph_uuid, + description=description, + name=name, + time_zone=time_zone, + idempotency_key=idempotency_key, request_options=request_options, ) return _response.data - def add_batch( + def clone( self, + graph_uuid: str, *, - episodes: typing.Sequence[EpisodeData], - graph_id: typing.Optional[str] = OMIT, - strict_ontology: typing.Optional[bool] = OMIT, - user_id: typing.Optional[str] = OMIT, + target_graph_id: typing.Optional[str] = OMIT, + idempotency_key: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, - ) -> typing.List[Episode]: + ) -> CloneGraphResult: """ - Deprecated. Use the [Batch API](/adding-batch-data) (`client.batch.*`) instead. - - Adds data to the graph in batch mode, processing episodes concurrently. - Parameters ---------- - episodes : typing.Sequence[EpisodeData] + graph_uuid : str + Graph UUID - 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. - - strict_ontology : typing.Optional[bool] - When true, prevents extraction of generic Entity nodes that do not match the configured ontology. + target_graph_id : typing.Optional[str] - user_id : typing.Optional[str] - User ID is the ID of the user to which the data will be added. If not adding to a user graph, please use graph_id field instead. + idempotency_key : typing.Optional[str] request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - typing.List[Episode] - Added episodes + CloneGraphResult + Accepted Examples -------- - from zep_cloud import EpisodeData, Zep + from zep_cloud import Zep client = Zep( api_key="YOUR_API_KEY", ) - client.graph.add_batch( - episodes=[ - EpisodeData( - data="data", - type="text", - ) - ], + client.graph.clone( + graph_uuid="graph_uuid", ) """ - _response = self._raw_client.add_batch( - episodes=episodes, - graph_id=graph_id, - strict_ontology=strict_ontology, - user_id=user_id, + _response = self._raw_client.clone( + graph_uuid, + target_graph_id=target_graph_id, + idempotency_key=idempotency_key, request_options=request_options, ) return _response.data - def add_fact_triple( + def get_context( self, + graph_uuid: str, *, - fact: str, - fact_name: str, - created_at: typing.Optional[str] = OMIT, - edge_attributes: typing.Optional[typing.Dict[str, typing.Optional[typing.Any]]] = OMIT, - expired_at: typing.Optional[str] = OMIT, - graph_id: typing.Optional[str] = OMIT, - invalid_at: typing.Optional[str] = OMIT, - metadata: typing.Optional[typing.Dict[str, typing.Optional[typing.Any]]] = OMIT, - source_node_attributes: typing.Optional[typing.Dict[str, typing.Optional[typing.Any]]] = OMIT, - source_node_labels: typing.Optional[typing.Sequence[str]] = OMIT, - source_node_name: typing.Optional[str] = OMIT, - source_node_summary: typing.Optional[str] = OMIT, - source_node_uuid: typing.Optional[str] = OMIT, - target_node_attributes: typing.Optional[typing.Dict[str, typing.Optional[typing.Any]]] = OMIT, - target_node_labels: typing.Optional[typing.Sequence[str]] = OMIT, - target_node_name: typing.Optional[str] = OMIT, - target_node_summary: typing.Optional[str] = OMIT, - target_node_uuid: typing.Optional[str] = OMIT, - user_id: typing.Optional[str] = OMIT, - valid_at: typing.Optional[str] = OMIT, + filters: typing.Optional[typing.Dict[str, typing.Any]] = OMIT, + include_results: typing.Optional[bool] = OMIT, + max_characters: typing.Optional[int] = OMIT, + query: typing.Optional[str] = OMIT, + recency_bias: typing.Optional[str] = OMIT, + template_uuid: typing.Optional[str] = OMIT, + idempotency_key: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, - ) -> AddTripleResponse: + ) -> GraphContextResponse: """ - Add a fact triple for a user or group - Parameters ---------- - fact : str - The fact relating the two nodes that this edge represents - - fact_name : str - The name of the edge to add. Should be all caps using snake case (eg RELATES_TO) - - created_at : typing.Optional[str] - The timestamp of the message - - edge_attributes : typing.Optional[typing.Dict[str, typing.Optional[typing.Any]]] - Additional attributes of the edge. Values must be scalar types (string, number, boolean, or null). - Nested objects and arrays are not allowed. - - expired_at : typing.Optional[str] - The time (if any) at which the edge expires - - graph_id : typing.Optional[str] - - invalid_at : typing.Optional[str] - The time (if any) at which the fact stops being true + graph_uuid : str + Graph UUID - metadata : typing.Optional[typing.Dict[str, typing.Optional[typing.Any]]] - Optional metadata key-value pairs for the shadow episode created for this fact triple. - Max 10 keys. Values must be strings, numbers, or booleans. + filters : typing.Optional[typing.Dict[str, typing.Any]] - source_node_attributes : typing.Optional[typing.Dict[str, typing.Optional[typing.Any]]] - Additional attributes of the source node. Values must be scalar types (string, number, boolean, or null). - Nested objects and arrays are not allowed. + include_results : typing.Optional[bool] - source_node_labels : typing.Optional[typing.Sequence[str]] - The labels for the source node. At most one entity-type label may be - provided so that manually-added triples remain consistent with automatic - episode extraction, which assigns one best-match entity type per node. - The base "Entity" label is added implicitly by the graph layer on save - and does not need to be supplied here. - - source_node_name : typing.Optional[str] - The name of the source node to add + max_characters : typing.Optional[int] - source_node_summary : typing.Optional[str] - The summary of the source node to add + query : typing.Optional[str] - source_node_uuid : typing.Optional[str] - The source node uuid + recency_bias : typing.Optional[str] - target_node_attributes : typing.Optional[typing.Dict[str, typing.Optional[typing.Any]]] - Additional attributes of the target node. Values must be scalar types (string, number, boolean, or null). - Nested objects and arrays are not allowed. + template_uuid : typing.Optional[str] - target_node_labels : typing.Optional[typing.Sequence[str]] - The labels for the target node. At most one entity-type label may be - provided so that manually-added triples remain consistent with automatic - episode extraction, which assigns one best-match entity type per node. - The base "Entity" label is added implicitly by the graph layer on save - and does not need to be supplied here. + idempotency_key : typing.Optional[str] - target_node_name : typing.Optional[str] - The name of the target node to add + request_options : typing.Optional[RequestOptions] + Request-specific configuration. - target_node_summary : typing.Optional[str] - The summary of the target node to add + Returns + ------- + GraphContextResponse + OK - target_node_uuid : typing.Optional[str] - The target node uuid + Examples + -------- + from zep_cloud import Zep - user_id : typing.Optional[str] + client = Zep( + api_key="YOUR_API_KEY", + ) + client.graph.get_context( + graph_uuid="graph_uuid", + ) + """ + _response = self._raw_client.get_context( + graph_uuid, + filters=filters, + include_results=include_results, + max_characters=max_characters, + query=query, + recency_bias=recency_bias, + template_uuid=template_uuid, + idempotency_key=idempotency_key, + request_options=request_options, + ) + return _response.data - valid_at : typing.Optional[str] - The time at which the fact becomes true + def get_instructions( + self, graph_uuid: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> Instructions: + """ + Parameters + ---------- + graph_uuid : str + Graph UUID request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - AddTripleResponse - Resulting triple + Instructions + OK Examples -------- @@ -538,69 +485,41 @@ def add_fact_triple( client = Zep( api_key="YOUR_API_KEY", ) - client.graph.add_fact_triple( - fact="fact", - fact_name="fact_name", + client.graph.get_instructions( + graph_uuid="graph_uuid", ) """ - _response = self._raw_client.add_fact_triple( - fact=fact, - fact_name=fact_name, - created_at=created_at, - edge_attributes=edge_attributes, - expired_at=expired_at, - graph_id=graph_id, - invalid_at=invalid_at, - metadata=metadata, - source_node_attributes=source_node_attributes, - source_node_labels=source_node_labels, - source_node_name=source_node_name, - source_node_summary=source_node_summary, - source_node_uuid=source_node_uuid, - target_node_attributes=target_node_attributes, - target_node_labels=target_node_labels, - target_node_name=target_node_name, - target_node_summary=target_node_summary, - target_node_uuid=target_node_uuid, - user_id=user_id, - valid_at=valid_at, - request_options=request_options, - ) + _response = self._raw_client.get_instructions(graph_uuid, request_options=request_options) return _response.data - def clone( + def set_instructions( self, + graph_uuid: str, *, - source_graph_id: typing.Optional[str] = OMIT, - source_user_id: typing.Optional[str] = OMIT, - target_graph_id: typing.Optional[str] = OMIT, - target_user_id: typing.Optional[str] = OMIT, + inherited: typing.Optional[bool] = OMIT, + instructions: typing.Optional[typing.Sequence[typing.Dict[str, typing.Any]]] = OMIT, + idempotency_key: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, - ) -> CloneGraphResponse: + ) -> Instructions: """ - Clone a user or group graph. - Parameters ---------- - source_graph_id : typing.Optional[str] - source_graph_id is the ID of the graph to be cloned. Required if source_user_id is not provided + graph_uuid : str + Graph UUID - source_user_id : typing.Optional[str] - user_id of the user whose graph is being cloned. Required if source_graph_id is not provided + inherited : typing.Optional[bool] - target_graph_id : typing.Optional[str] - target_graph_id is the ID to be set on the cloned graph. Must not point to an existing graph. Required if target_user_id is not provided. + instructions : typing.Optional[typing.Sequence[typing.Dict[str, typing.Any]]] - target_user_id : typing.Optional[str] - user_id to be set on the cloned user. Must not point to an existing user. Required if target_graph_id is not provided. + idempotency_key : typing.Optional[str] request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - CloneGraphResponse - Response object containing graph_id or user_id pointing to the new graph + Instructions + OK Examples -------- @@ -609,47 +528,35 @@ def clone( client = Zep( api_key="YOUR_API_KEY", ) - client.graph.clone() + client.graph.set_instructions( + graph_uuid="graph_uuid", + ) """ - _response = self._raw_client.clone( - source_graph_id=source_graph_id, - source_user_id=source_user_id, - target_graph_id=target_graph_id, - target_user_id=target_user_id, + _response = self._raw_client.set_instructions( + graph_uuid, + inherited=inherited, + instructions=instructions, + idempotency_key=idempotency_key, request_options=request_options, ) return _response.data - def create( - self, - *, - graph_id: str, - description: typing.Optional[str] = OMIT, - name: typing.Optional[str] = OMIT, - time_zone: typing.Optional[str] = OMIT, - request_options: typing.Optional[RequestOptions] = None, - ) -> Graph: + def get_observation_steering( + self, graph_uuid: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> ObservationSteering: """ - Creates a new graph. - Parameters ---------- - graph_id : str - - description : typing.Optional[str] - - name : typing.Optional[str] - - time_zone : typing.Optional[str] - The graph's IANA time zone. Stored on its group-backed subject. + graph_uuid : str + Graph UUID request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - Graph - The added graph + ObservationSteering + OK Examples -------- @@ -658,52 +565,44 @@ def create( client = Zep( api_key="YOUR_API_KEY", ) - client.graph.create( - graph_id="graph_id", + client.graph.get_observation_steering( + graph_uuid="graph_uuid", ) """ - _response = self._raw_client.create( - graph_id=graph_id, description=description, name=name, time_zone=time_zone, request_options=request_options - ) + _response = self._raw_client.get_observation_steering(graph_uuid, request_options=request_options) return _response.data - def list_all( + def set_observation_steering( self, + graph_uuid: str, *, - page_number: typing.Optional[int] = None, - page_size: typing.Optional[int] = None, - search: typing.Optional[str] = None, - order_by: typing.Optional[str] = None, - asc: typing.Optional[bool] = None, + inherited: typing.Optional[bool] = OMIT, + instruction: typing.Optional[str] = OMIT, + types: typing.Optional[typing.Sequence[typing.Dict[str, typing.Any]]] = OMIT, + idempotency_key: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, - ) -> GraphListResponse: + ) -> ObservationSteering: """ - Returns all graphs. In order to list users, use user.list_ordered instead - Parameters ---------- - page_number : typing.Optional[int] - Page number for pagination, starting from 1. + graph_uuid : str + Graph UUID - page_size : typing.Optional[int] - Number of graphs to retrieve per page (default 50, range 1-100; explicit 0 is invalid). + inherited : typing.Optional[bool] - search : typing.Optional[str] - Search term for filtering graphs by graph_id, name, or description. Queries longer than 200 Unicode code points after whitespace normalization are invalid. + instruction : typing.Optional[str] - order_by : typing.Optional[str] - Column to sort by (created_at, graph_id, name). + types : typing.Optional[typing.Sequence[typing.Dict[str, typing.Any]]] - asc : typing.Optional[bool] - Sort in ascending order. + idempotency_key : typing.Optional[str] request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - GraphListResponse - Successfully retrieved list of graphs. + ObservationSteering + OK Examples -------- @@ -712,141 +611,80 @@ def list_all( client = Zep( api_key="YOUR_API_KEY", ) - client.graph.list_all( - page_number=1, - page_size=1, - search="search", - order_by="order_by", - asc=True, + client.graph.set_observation_steering( + graph_uuid="graph_uuid", ) """ - _response = self._raw_client.list_all( - page_number=page_number, - page_size=page_size, - search=search, - order_by=order_by, - asc=asc, + _response = self._raw_client.set_observation_steering( + graph_uuid, + inherited=inherited, + instruction=instruction, + types=types, + idempotency_key=idempotency_key, request_options=request_options, ) return _response.data - def add_nodes( - self, - *, - nodes: typing.Sequence[AddNodeItem], - graph_id: typing.Optional[str] = OMIT, - user_id: typing.Optional[str] = OMIT, - request_options: typing.Optional[RequestOptions] = None, - ) -> AddNodesResponse: + def get_ontology(self, graph_uuid: str, *, request_options: typing.Optional[RequestOptions] = None) -> Ontology: """ - Add entity nodes to a user or graph directly, without episode ingestion. Up to 100 nodes per request. - Parameters ---------- - nodes : typing.Sequence[AddNodeItem] - The nodes to add. 1 to 100 items. - - graph_id : typing.Optional[str] - - user_id : typing.Optional[str] + graph_uuid : str + Graph UUID request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - AddNodesResponse - Accepted + Ontology + OK Examples -------- - from zep_cloud import AddNodeItem, Zep + from zep_cloud import Zep client = Zep( api_key="YOUR_API_KEY", ) - client.graph.add_nodes( - nodes=[ - AddNodeItem( - name="name", - ) - ], + client.graph.get_ontology( + graph_uuid="graph_uuid", ) """ - _response = self._raw_client.add_nodes( - nodes=nodes, graph_id=graph_id, user_id=user_id, request_options=request_options - ) + _response = self._raw_client.get_ontology(graph_uuid, request_options=request_options) return _response.data - def detect_patterns( + def set_ontology( self, + graph_uuid: str, *, - detect: typing.Optional[DetectConfig] = OMIT, - edge_limit: typing.Optional[int] = OMIT, - graph_id: typing.Optional[str] = OMIT, - limit: typing.Optional[int] = OMIT, - min_occurrences: typing.Optional[int] = OMIT, - query: typing.Optional[str] = OMIT, - query_limit: typing.Optional[int] = OMIT, - recency_weight: typing.Optional[RecencyWeight] = OMIT, - search_filters: typing.Optional[SearchFilters] = OMIT, - seeds: typing.Optional[PatternSeeds] = OMIT, - user_id: typing.Optional[str] = OMIT, + edge_types: typing.Optional[typing.Sequence[EdgeType]] = OMIT, + entity_types: typing.Optional[typing.Sequence[EntityType]] = OMIT, + inherited: typing.Optional[bool] = OMIT, + idempotency_key: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, - ) -> DetectPatternsResponse: + ) -> Ontology: """ - 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, - detects triple-frequency patterns, and returns resolved edges ranked by relevance. - Parameters ---------- - detect : typing.Optional[DetectConfig] - Which pattern types to detect with type-specific configuration. - Omit to detect all types with defaults. Ignored when query is set. - - edge_limit : typing.Optional[int] - Max resolved edges per pattern. Default: 10, Max: 100. Only used with query. - - graph_id : typing.Optional[str] - Graph ID when detecting patterns on a named graph - - limit : typing.Optional[int] - Max patterns to return. Default: 50, Max: 200 - - min_occurrences : typing.Optional[int] - Minimum occurrence count to report a pattern. Default: 2 - - query : typing.Optional[str] - Search query for discovering seed nodes via hybrid search. - When set, forces triple-frequency detection only and enables edge resolution - with cross-encoder reranking. Mutually exclusive with seeds. - - query_limit : typing.Optional[int] - Max seed nodes from search. Default: 10, Max: 50. Only used with query. + graph_uuid : str + Graph UUID - recency_weight : typing.Optional[RecencyWeight] - Exponential half-life decay applied to edge created_at timestamps. - Valid values: none, 7_days, 30_days, 90_days. Default: none + edge_types : typing.Optional[typing.Sequence[EdgeType]] - search_filters : typing.Optional[SearchFilters] - Filters which edges/nodes participate in pattern detection. - Reuses the same filter format as /graph/search. + entity_types : typing.Optional[typing.Sequence[EntityType]] - seeds : typing.Optional[PatternSeeds] - Seed selection. If omitted, analyzes the entire graph. Mutually exclusive with query. + inherited : typing.Optional[bool] - user_id : typing.Optional[str] - User ID when detecting patterns on a user graph + idempotency_key : typing.Optional[str] request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - DetectPatternsResponse - Detected patterns + Ontology + OK Examples -------- @@ -855,95 +693,68 @@ def detect_patterns( client = Zep( api_key="YOUR_API_KEY", ) - client.graph.detect_patterns() + client.graph.set_ontology( + graph_uuid="graph_uuid", + ) """ - _response = self._raw_client.detect_patterns( - detect=detect, - edge_limit=edge_limit, - graph_id=graph_id, - limit=limit, - min_occurrences=min_occurrences, - query=query, - query_limit=query_limit, - recency_weight=recency_weight, - search_filters=search_filters, - seeds=seeds, - user_id=user_id, + _response = self._raw_client.set_ontology( + graph_uuid, + edge_types=edge_types, + entity_types=entity_types, + inherited=inherited, + idempotency_key=idempotency_key, request_options=request_options, ) return _response.data - def search( + def search_edges( self, + graph_uuid: str, *, - query: str, + limit: typing.Optional[int] = None, + cursor: typing.Optional[str] = None, bfs_origin_node_uuids: typing.Optional[typing.Sequence[str]] = OMIT, center_node_uuid: typing.Optional[str] = OMIT, - graph_id: typing.Optional[str] = OMIT, - limit: typing.Optional[int] = OMIT, - max_characters: typing.Optional[int] = OMIT, + filters: typing.Optional[typing.Dict[str, typing.Any]] = OMIT, mmr_lambda: typing.Optional[float] = OMIT, - reranker: typing.Optional[Reranker] = OMIT, - return_raw_results: typing.Optional[bool] = OMIT, - scope: typing.Optional[GraphSearchScope] = OMIT, - search_filters: typing.Optional[SearchFilters] = OMIT, - user_id: typing.Optional[str] = OMIT, + query: typing.Optional[str] = OMIT, + reranker: typing.Optional[str] = OMIT, + idempotency_key: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, - ) -> GraphSearchResults: + ) -> SyncPager[Edge, EdgePage]: """ - Perform a graph search query. - Parameters ---------- - query : str - The string to search for (required) + graph_uuid : str + Graph UUID - bfs_origin_node_uuids : typing.Optional[typing.Sequence[str]] - Nodes that are the origins of the BFS searches + limit : typing.Optional[int] + Page size - center_node_uuid : typing.Optional[str] - Node to rerank around for node distance reranking + cursor : typing.Optional[str] + Opaque page cursor - graph_id : typing.Optional[str] - The graph_id to search in. When searching user graph, please use user_id instead. + bfs_origin_node_uuids : typing.Optional[typing.Sequence[str]] - limit : typing.Optional[int] - The maximum number of facts to retrieve for non-auto scopes. Defaults to 10. Limited to 50. Ignored when scope=auto. + center_node_uuid : typing.Optional[str] - max_characters : typing.Optional[int] - Maximum total characters across all selected results when scope=auto. Defaults to 2500. Limited to 50000. + filters : typing.Optional[typing.Dict[str, typing.Any]] mmr_lambda : typing.Optional[float] - weighting for maximal marginal relevance - - reranker : typing.Optional[Reranker] - Defaults to RRF. Ignored when scope=auto except node_distance and episode_mentions are rejected; - auto search always uses RRF retrieval and applies its own internal rerank after retrieval. - episode_mentions ranks edge candidates by how many of the episodes listed - in search_filters.episode_uuids mention them; without episode_uuids it has - no effect and results are ranked as if no reranker were specified. - return_raw_results : typing.Optional[bool] - When scope=auto, include the selected raw graph results alongside the materialized context block. - For graph-service-backed auto mode, selected raw results may include episodes, - edges, nodes, observations, and thread_summaries. - - scope : typing.Optional[GraphSearchScope] - Defaults to Edges. + query : typing.Optional[str] - search_filters : typing.Optional[SearchFilters] - Search filters to apply to the search + reranker : typing.Optional[str] - user_id : typing.Optional[str] - The user_id when searching user graph. If not searching user graph, please use graph_id instead. + idempotency_key : typing.Optional[str] request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - GraphSearchResults - Graph search results or auto-context block + SyncPager[Edge, EdgePage] + OK Examples -------- @@ -952,85 +763,79 @@ def search( client = Zep( api_key="YOUR_API_KEY", ) - client.graph.search( - query="query", + response = client.graph.search_edges( + graph_uuid="graph_uuid", + limit=1, + cursor="cursor", ) + for item in response: + yield item + # alternatively, you can paginate page-by-page + for page in response.iter_pages(): + yield page """ - _response = self._raw_client.search( - query=query, + return self._raw_client.search_edges( + graph_uuid, + limit=limit, + cursor=cursor, bfs_origin_node_uuids=bfs_origin_node_uuids, center_node_uuid=center_node_uuid, - graph_id=graph_id, - limit=limit, - max_characters=max_characters, + filters=filters, mmr_lambda=mmr_lambda, + query=query, reranker=reranker, - return_raw_results=return_raw_results, - scope=scope, - search_filters=search_filters, - user_id=user_id, + idempotency_key=idempotency_key, request_options=request_options, ) - return _response.data - def get_subgraph( + def search_episodes( self, + graph_uuid: str, *, - seed_node_uuids: typing.Sequence[str], - depth: typing.Optional[int] = OMIT, - direction: typing.Optional[str] = OMIT, - graph_id: typing.Optional[str] = OMIT, - max_edges: typing.Optional[int] = OMIT, - max_nodes: typing.Optional[int] = OMIT, - search_filters: typing.Optional[SearchFilters] = OMIT, - user_id: typing.Optional[str] = OMIT, + limit: typing.Optional[int] = None, + cursor: typing.Optional[str] = None, + bfs_origin_node_uuids: typing.Optional[typing.Sequence[str]] = OMIT, + center_node_uuid: typing.Optional[str] = OMIT, + filters: typing.Optional[typing.Dict[str, typing.Any]] = OMIT, + mmr_lambda: typing.Optional[float] = OMIT, + query: typing.Optional[str] = OMIT, + reranker: typing.Optional[str] = OMIT, + idempotency_key: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, - ) -> GraphSubgraphResponse: + ) -> SyncPager[Episode, EpisodePage]: """ - Returns the bounded neighborhood of a set of seed nodes as a single {nodes, edges} payload: breadth-first expansion up to a caller-specified depth, subject to explicit budgets, with explicit truncation reporting. - Parameters ---------- - seed_node_uuids : typing.Sequence[str] - Seed node UUIDs to expand from, in traversal-priority order: seeds are - admitted before any expansion, in this order, and count toward - max_nodes first. 1-20 entries, required. Seeds that do not exist in - the target graph are ignored, not an error. + graph_uuid : str + Graph UUID - depth : typing.Optional[int] - Maximum traversal depth from the seeds. 1-3. Defaults to 1. + limit : typing.Optional[int] + Page size - direction : typing.Optional[str] - Edge orientation followed during expansion, relative to each frontier - node: "in" | "out" | "both". Defaults to "both". + cursor : typing.Optional[str] + Opaque page cursor - graph_id : typing.Optional[str] - graph_id identifies the target named graph. Exactly one of user_id or - graph_id is required. + bfs_origin_node_uuids : typing.Optional[typing.Sequence[str]] - max_edges : typing.Optional[int] - Maximum number of edges in the response. 1-1000. Defaults to 200. + center_node_uuid : typing.Optional[str] - max_nodes : typing.Optional[int] - Maximum number of nodes in the response, including admitted seeds. - 1-500. Defaults to 100. + filters : typing.Optional[typing.Dict[str, typing.Any]] + + mmr_lambda : typing.Optional[float] - 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). + query : typing.Optional[str] - user_id : typing.Optional[str] - user_id identifies the target user graph. Exactly one of user_id or - graph_id is required. + reranker : typing.Optional[str] + + idempotency_key : typing.Optional[str] request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - GraphSubgraphResponse - Subgraph + SyncPager[Episode, EpisodePage] + OK Examples -------- @@ -1039,39 +844,79 @@ def get_subgraph( client = Zep( api_key="YOUR_API_KEY", ) - client.graph.get_subgraph( - seed_node_uuids=["seed_node_uuids"], + response = client.graph.search_episodes( + graph_uuid="graph_uuid", + limit=1, + cursor="cursor", ) + for item in response: + yield item + # alternatively, you can paginate page-by-page + for page in response.iter_pages(): + yield page """ - _response = self._raw_client.get_subgraph( - seed_node_uuids=seed_node_uuids, - depth=depth, - direction=direction, - graph_id=graph_id, - max_edges=max_edges, - max_nodes=max_nodes, - search_filters=search_filters, - user_id=user_id, + return self._raw_client.search_episodes( + graph_uuid, + limit=limit, + cursor=cursor, + bfs_origin_node_uuids=bfs_origin_node_uuids, + center_node_uuid=center_node_uuid, + filters=filters, + mmr_lambda=mmr_lambda, + query=query, + reranker=reranker, + idempotency_key=idempotency_key, request_options=request_options, ) - return _response.data - def get(self, graph_id: str, *, request_options: typing.Optional[RequestOptions] = None) -> Graph: + def search_nodes( + self, + graph_uuid: str, + *, + limit: typing.Optional[int] = None, + cursor: typing.Optional[str] = None, + bfs_origin_node_uuids: typing.Optional[typing.Sequence[str]] = OMIT, + center_node_uuid: typing.Optional[str] = OMIT, + filters: typing.Optional[typing.Dict[str, typing.Any]] = OMIT, + mmr_lambda: typing.Optional[float] = OMIT, + query: typing.Optional[str] = OMIT, + reranker: typing.Optional[str] = OMIT, + idempotency_key: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> SyncPager[Node, NodePage]: """ - Returns a graph. - Parameters ---------- - graph_id : str - The graph_id of the graph to get. + graph_uuid : str + Graph UUID + + limit : typing.Optional[int] + Page size + + cursor : typing.Optional[str] + Opaque page cursor + + bfs_origin_node_uuids : typing.Optional[typing.Sequence[str]] + + center_node_uuid : typing.Optional[str] + + filters : typing.Optional[typing.Dict[str, typing.Any]] + + mmr_lambda : typing.Optional[float] + + query : typing.Optional[str] + + reranker : typing.Optional[str] + + idempotency_key : typing.Optional[str] request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - Graph - The graph that was retrieved. + SyncPager[Node, NodePage] + OK Examples -------- @@ -1080,29 +925,79 @@ def get(self, graph_id: str, *, request_options: typing.Optional[RequestOptions] client = Zep( api_key="YOUR_API_KEY", ) - client.graph.get( - graph_id="graphId", + response = client.graph.search_nodes( + graph_uuid="graph_uuid", + limit=1, + cursor="cursor", ) + for item in response: + yield item + # alternatively, you can paginate page-by-page + for page in response.iter_pages(): + yield page """ - _response = self._raw_client.get(graph_id, request_options=request_options) - return _response.data + return self._raw_client.search_nodes( + graph_uuid, + limit=limit, + cursor=cursor, + bfs_origin_node_uuids=bfs_origin_node_uuids, + center_node_uuid=center_node_uuid, + filters=filters, + mmr_lambda=mmr_lambda, + query=query, + reranker=reranker, + idempotency_key=idempotency_key, + request_options=request_options, + ) - def delete(self, graph_id: str, *, request_options: typing.Optional[RequestOptions] = None) -> SuccessResponse: + def search_observations( + self, + graph_uuid: str, + *, + limit: typing.Optional[int] = None, + cursor: typing.Optional[str] = None, + bfs_origin_node_uuids: typing.Optional[typing.Sequence[str]] = OMIT, + center_node_uuid: typing.Optional[str] = OMIT, + filters: typing.Optional[typing.Dict[str, typing.Any]] = OMIT, + mmr_lambda: typing.Optional[float] = OMIT, + query: typing.Optional[str] = OMIT, + reranker: typing.Optional[str] = OMIT, + idempotency_key: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> SyncPager[Observation, ObservationPage]: """ - Deletes a graph. If you would like to delete a user graph, make sure to use user.delete instead. - Parameters ---------- - graph_id : str - Graph ID + graph_uuid : str + Graph UUID + + limit : typing.Optional[int] + Page size + + cursor : typing.Optional[str] + Opaque page cursor + + bfs_origin_node_uuids : typing.Optional[typing.Sequence[str]] + + center_node_uuid : typing.Optional[str] + + filters : typing.Optional[typing.Dict[str, typing.Any]] + + mmr_lambda : typing.Optional[float] + + query : typing.Optional[str] + + reranker : typing.Optional[str] + + idempotency_key : typing.Optional[str] request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - SuccessResponse - Deleted + SyncPager[Observation, ObservationPage] + OK Examples -------- @@ -1111,44 +1006,152 @@ def delete(self, graph_id: str, *, request_options: typing.Optional[RequestOptio client = Zep( api_key="YOUR_API_KEY", ) - client.graph.delete( - graph_id="graphId", + response = client.graph.search_observations( + graph_uuid="graph_uuid", + limit=1, + cursor="cursor", ) + for item in response: + yield item + # alternatively, you can paginate page-by-page + for page in response.iter_pages(): + yield page """ - _response = self._raw_client.delete(graph_id, request_options=request_options) - return _response.data + return self._raw_client.search_observations( + graph_uuid, + limit=limit, + cursor=cursor, + bfs_origin_node_uuids=bfs_origin_node_uuids, + center_node_uuid=center_node_uuid, + filters=filters, + mmr_lambda=mmr_lambda, + query=query, + reranker=reranker, + idempotency_key=idempotency_key, + request_options=request_options, + ) - def update( + def search_thread_summaries( self, - graph_id: str, + graph_uuid: str, *, - description: typing.Optional[str] = OMIT, - name: typing.Optional[str] = OMIT, - time_zone: typing.Optional[str] = OMIT, + limit: typing.Optional[int] = None, + cursor: typing.Optional[str] = None, + bfs_origin_node_uuids: typing.Optional[typing.Sequence[str]] = OMIT, + center_node_uuid: typing.Optional[str] = OMIT, + filters: typing.Optional[typing.Dict[str, typing.Any]] = OMIT, + mmr_lambda: typing.Optional[float] = OMIT, + query: typing.Optional[str] = OMIT, + reranker: typing.Optional[str] = OMIT, + idempotency_key: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, - ) -> Graph: + ) -> SyncPager[ThreadSummary, ThreadSummaryPage]: """ - Updates information about a graph. + Parameters + ---------- + graph_uuid : str + Graph UUID + + limit : typing.Optional[int] + Page size + + cursor : typing.Optional[str] + Opaque page cursor + + bfs_origin_node_uuids : typing.Optional[typing.Sequence[str]] + + center_node_uuid : typing.Optional[str] + + filters : typing.Optional[typing.Dict[str, typing.Any]] + + mmr_lambda : typing.Optional[float] + + query : typing.Optional[str] + + reranker : typing.Optional[str] + + idempotency_key : typing.Optional[str] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + Returns + ------- + SyncPager[ThreadSummary, ThreadSummaryPage] + OK + + Examples + -------- + from zep_cloud import Zep + + client = Zep( + api_key="YOUR_API_KEY", + ) + response = client.graph.search_thread_summaries( + graph_uuid="graph_uuid", + limit=1, + cursor="cursor", + ) + for item in response: + yield item + # alternatively, you can paginate page-by-page + for page in response.iter_pages(): + yield page + """ + return self._raw_client.search_thread_summaries( + graph_uuid, + limit=limit, + cursor=cursor, + bfs_origin_node_uuids=bfs_origin_node_uuids, + center_node_uuid=center_node_uuid, + filters=filters, + mmr_lambda=mmr_lambda, + query=query, + reranker=reranker, + idempotency_key=idempotency_key, + request_options=request_options, + ) + + def get_subgraph( + self, + graph_uuid: str, + *, + depth: typing.Optional[int] = OMIT, + direction: typing.Optional[str] = OMIT, + filters: typing.Optional[typing.Dict[str, typing.Any]] = OMIT, + max_edges: typing.Optional[int] = OMIT, + max_nodes: typing.Optional[int] = OMIT, + seed_node_uuids: typing.Optional[typing.Sequence[str]] = OMIT, + idempotency_key: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> SubgraphResponse: + """ Parameters ---------- - graph_id : str - Graph ID + graph_uuid : str + Graph UUID - description : typing.Optional[str] + depth : typing.Optional[int] - name : typing.Optional[str] + direction : typing.Optional[str] - time_zone : typing.Optional[str] - The graph's IANA time zone. Stored on its group-backed subject. + filters : typing.Optional[typing.Dict[str, typing.Any]] + + max_edges : typing.Optional[int] + + max_nodes : typing.Optional[int] + + seed_node_uuids : typing.Optional[typing.Sequence[str]] + + idempotency_key : typing.Optional[str] request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - Graph - The updated graph object + SubgraphResponse + OK Examples -------- @@ -1157,31 +1160,45 @@ def update( client = Zep( api_key="YOUR_API_KEY", ) - client.graph.update( - graph_id="graphId", + client.graph.get_subgraph( + graph_uuid="graph_uuid", ) """ - _response = self._raw_client.update( - graph_id, description=description, name=name, time_zone=time_zone, request_options=request_options + _response = self._raw_client.get_subgraph( + graph_uuid, + depth=depth, + direction=direction, + filters=filters, + max_edges=max_edges, + max_nodes=max_nodes, + seed_node_uuids=seed_node_uuids, + idempotency_key=idempotency_key, + request_options=request_options, ) return _response.data - def warm(self, graph_id: str, *, request_options: typing.Optional[RequestOptions] = None) -> SuccessResponse: + def warm( + self, + graph_uuid: str, + *, + idempotency_key: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncResult: """ - Hints Zep to warm a graph for low-latency search - Parameters ---------- - graph_id : str - The graph_id of the graph to warm. + graph_uuid : str + Graph UUID + + idempotency_key : typing.Optional[str] request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - SuccessResponse - Warm hint accepted + AsyncResult + Accepted Examples -------- @@ -1191,25 +1208,71 @@ def warm(self, graph_id: str, *, request_options: typing.Optional[RequestOptions api_key="YOUR_API_KEY", ) client.graph.warm( - graph_id="graphId", + graph_uuid="graph_uuid", ) """ - _response = self._raw_client.warm(graph_id, request_options=request_options) + _response = self._raw_client.warm(graph_uuid, idempotency_key=idempotency_key, request_options=request_options) return _response.data + @property + def document_summary(self): + if self._document_summary is None: + from .document_summary.client import DocumentSummaryClient # noqa: E402 -class AsyncGraphClient: - def __init__(self, *, client_wrapper: AsyncClientWrapper): - self._raw_client = AsyncRawGraphClient(client_wrapper=client_wrapper) - self.edge = AsyncEdgeClient(client_wrapper=client_wrapper) + self._document_summary = DocumentSummaryClient(client_wrapper=self._client_wrapper) + return self._document_summary + + @property + def episode(self): + if self._episode is None: + from .episode.client import EpisodeClient # noqa: E402 + + self._episode = EpisodeClient(client_wrapper=self._client_wrapper) + return self._episode + + @property + def edge(self): + if self._edge is None: + from .edge.client import EdgeClient # noqa: E402 + + self._edge = EdgeClient(client_wrapper=self._client_wrapper) + return self._edge + + @property + def node(self): + if self._node is None: + from .node.client import NodeClient # noqa: E402 + + self._node = NodeClient(client_wrapper=self._client_wrapper) + return self._node + + @property + def observation(self): + if self._observation is None: + from .observation.client import ObservationClient # noqa: E402 - self.episode = AsyncEpisodeClient(client_wrapper=client_wrapper) + self._observation = ObservationClient(client_wrapper=self._client_wrapper) + return self._observation - self.node = AsyncNodeClient(client_wrapper=client_wrapper) + @property + def thread_summary(self): + if self._thread_summary is None: + from .thread_summary.client import ThreadSummaryClient # noqa: E402 - self.observation = AsyncObservationClient(client_wrapper=client_wrapper) + self._thread_summary = ThreadSummaryClient(client_wrapper=self._client_wrapper) + return self._thread_summary - self.thread_summary = AsyncThreadSummaryClient(client_wrapper=client_wrapper) + +class AsyncGraphClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._raw_client = AsyncRawGraphClient(client_wrapper=client_wrapper) + self._client_wrapper = client_wrapper + self._document_summary: typing.Optional[AsyncDocumentSummaryClient] = None + self._episode: typing.Optional[AsyncEpisodeClient] = None + self._edge: typing.Optional[AsyncEdgeClient] = None + self._node: typing.Optional[AsyncNodeClient] = None + self._observation: typing.Optional[AsyncObservationClient] = None + self._thread_summary: typing.Optional[AsyncThreadSummaryClient] = None @property def with_raw_response(self) -> AsyncRawGraphClient: @@ -1222,31 +1285,36 @@ def with_raw_response(self) -> AsyncRawGraphClient: """ return self._raw_client - async def list_custom_instructions( + async def create( self, *, - user_id: typing.Optional[str] = None, - graph_id: typing.Optional[str] = None, + description: typing.Optional[str] = OMIT, + graph_id: typing.Optional[str] = OMIT, + name: typing.Optional[str] = OMIT, + time_zone: typing.Optional[str] = OMIT, + idempotency_key: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, - ) -> ListCustomInstructionsResponse: + ) -> Graph: """ - Lists all custom instructions for a project, user, or graph. - Parameters ---------- - user_id : typing.Optional[str] - User ID to get user-specific instructions + description : typing.Optional[str] graph_id : typing.Optional[str] - Graph ID to get graph-specific instructions + + name : typing.Optional[str] + + time_zone : typing.Optional[str] + + idempotency_key : typing.Optional[str] request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - ListCustomInstructionsResponse - The list of instructions. + Graph + Created Examples -------- @@ -1260,54 +1328,64 @@ async def list_custom_instructions( async def main() -> None: - await client.graph.list_custom_instructions( - user_id="user_id", - graph_id="graph_id", - ) + await client.graph.create() asyncio.run(main()) """ - _response = await self._raw_client.list_custom_instructions( - user_id=user_id, graph_id=graph_id, request_options=request_options + _response = await self._raw_client.create( + description=description, + graph_id=graph_id, + name=name, + time_zone=time_zone, + idempotency_key=idempotency_key, + request_options=request_options, ) return _response.data - async def add_custom_instructions( + async def list( self, *, - instructions: typing.Sequence[CustomInstruction], - graph_ids: typing.Optional[typing.Sequence[str]] = OMIT, - user_ids: typing.Optional[typing.Sequence[str]] = OMIT, + limit: typing.Optional[int] = None, + cursor: typing.Optional[str] = None, + order_by: typing.Optional[str] = None, + order: typing.Optional[str] = None, + search: typing.Optional[str] = OMIT, + idempotency_key: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, - ) -> SuccessResponse: + ) -> AsyncPager[Graph, GraphPage]: """ - Adds new custom instructions for graphs without removing existing ones. If user_ids or graph_ids is empty, adds to project-wide default instructions. - Parameters ---------- - instructions : typing.Sequence[CustomInstruction] - Instructions to add to the graph. + limit : typing.Optional[int] + Page size + + cursor : typing.Optional[str] + Opaque page cursor + + order_by : typing.Optional[str] + Sort field - graph_ids : typing.Optional[typing.Sequence[str]] - Graph IDs to add the instructions to. If empty, the instructions are added to the project-wide default. + order : typing.Optional[str] + asc or desc - user_ids : typing.Optional[typing.Sequence[str]] - User IDs to add the instructions to. If empty, the instructions are added to the project-wide default. + search : typing.Optional[str] + + idempotency_key : typing.Optional[str] request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - SuccessResponse - Instructions added successfully + AsyncPager[Graph, GraphPage] + OK Examples -------- import asyncio - from zep_cloud import AsyncZep, CustomInstruction + from zep_cloud import AsyncZep client = AsyncZep( api_key="YOUR_API_KEY", @@ -1315,52 +1393,59 @@ async def add_custom_instructions( async def main() -> None: - await client.graph.add_custom_instructions( - instructions=[ - CustomInstruction( - name="name", - text="text", - ) - ], + response = await client.graph.list( + limit=1, + cursor="cursor", + order_by="order_by", + order="order", ) + async for item in response: + yield item + + # alternatively, you can paginate page-by-page + async for page in response.iter_pages(): + yield page asyncio.run(main()) """ - _response = await self._raw_client.add_custom_instructions( - instructions=instructions, graph_ids=graph_ids, user_ids=user_ids, request_options=request_options + return await self._raw_client.list( + limit=limit, + cursor=cursor, + order_by=order_by, + order=order, + search=search, + idempotency_key=idempotency_key, + request_options=request_options, ) - return _response.data - async def delete_custom_instructions( + async def lookup( self, *, - graph_ids: typing.Optional[typing.Sequence[str]] = OMIT, - instruction_names: typing.Optional[typing.Sequence[str]] = OMIT, - user_ids: typing.Optional[typing.Sequence[str]] = OMIT, + graph_id: typing.Optional[str] = OMIT, + thread_id: typing.Optional[str] = OMIT, + user_id: typing.Optional[str] = OMIT, + idempotency_key: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, - ) -> SuccessResponse: + ) -> Graph: """ - Deletes custom instructions for graphs or project wide defaults. - Parameters ---------- - graph_ids : typing.Optional[typing.Sequence[str]] - Determines which group graphs will have their custom instructions deleted. If no graphs are provided, the project-wide custom instructions will be affected. + graph_id : typing.Optional[str] - instruction_names : typing.Optional[typing.Sequence[str]] - Unique identifier for the instructions to be deleted. If empty deletes all instructions. + thread_id : typing.Optional[str] - user_ids : typing.Optional[typing.Sequence[str]] - Determines which user graphs will have their custom instructions deleted. If no users are provided, the project-wide custom instructions will be affected. + user_id : typing.Optional[str] + + idempotency_key : typing.Optional[str] request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - SuccessResponse - Instructions deleted successfully + Graph + OK Examples -------- @@ -1374,41 +1459,34 @@ async def delete_custom_instructions( async def main() -> None: - await client.graph.delete_custom_instructions() + await client.graph.lookup() asyncio.run(main()) """ - _response = await self._raw_client.delete_custom_instructions( - graph_ids=graph_ids, instruction_names=instruction_names, user_ids=user_ids, request_options=request_options + _response = await self._raw_client.lookup( + graph_id=graph_id, + thread_id=thread_id, + user_id=user_id, + idempotency_key=idempotency_key, + request_options=request_options, ) return _response.data - async def list_entity_types( - self, - *, - user_id: typing.Optional[str] = None, - graph_id: typing.Optional[str] = None, - request_options: typing.Optional[RequestOptions] = None, - ) -> EntityTypeResponse: + async def get(self, graph_uuid: str, *, request_options: typing.Optional[RequestOptions] = None) -> Graph: """ - Returns all entity types for a project, user, or graph. - Parameters ---------- - user_id : typing.Optional[str] - User ID to get user-specific entity types - - graph_id : typing.Optional[str] - Graph ID to get graph-specific entity types + graph_uuid : str + Graph UUID request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - EntityTypeResponse - The list of entity types. + Graph + OK Examples -------- @@ -1422,48 +1500,38 @@ async def list_entity_types( async def main() -> None: - await client.graph.list_entity_types( - user_id="user_id", - graph_id="graph_id", + await client.graph.get( + graph_uuid="graph_uuid", ) asyncio.run(main()) """ - _response = await self._raw_client.list_entity_types( - user_id=user_id, graph_id=graph_id, request_options=request_options - ) + _response = await self._raw_client.get(graph_uuid, request_options=request_options) return _response.data - async def set_entity_types_internal( + async def delete( self, + graph_uuid: str, *, - edge_types: typing.Optional[typing.Sequence[EdgeType]] = OMIT, - entity_types: typing.Optional[typing.Sequence[EntityType]] = OMIT, - graph_ids: typing.Optional[typing.Sequence[str]] = OMIT, - user_ids: typing.Optional[typing.Sequence[str]] = OMIT, + idempotency_key: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, - ) -> SuccessResponse: + ) -> GraphDeleteResult: """ - Sets the entity types for multiple users and graphs, replacing any existing ones. - Parameters ---------- - edge_types : typing.Optional[typing.Sequence[EdgeType]] - - entity_types : typing.Optional[typing.Sequence[EntityType]] - - graph_ids : typing.Optional[typing.Sequence[str]] + graph_uuid : str + Graph UUID - user_ids : typing.Optional[typing.Sequence[str]] + idempotency_key : typing.Optional[str] request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - SuccessResponse - Entity types set successfully + GraphDeleteResult + Accepted Examples -------- @@ -1477,65 +1545,52 @@ async def set_entity_types_internal( async def main() -> None: - await client.graph.set_entity_types_internal() + await client.graph.delete( + graph_uuid="graph_uuid", + ) asyncio.run(main()) """ - _response = await self._raw_client.set_entity_types_internal( - edge_types=edge_types, - entity_types=entity_types, - graph_ids=graph_ids, - user_ids=user_ids, - request_options=request_options, + _response = await self._raw_client.delete( + graph_uuid, idempotency_key=idempotency_key, request_options=request_options ) return _response.data - async def add( + async def update( self, + graph_uuid: str, *, - data: str, - type: GraphDataType, - created_at: 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, - strict_ontology: typing.Optional[bool] = OMIT, - user_id: typing.Optional[str] = OMIT, + description: typing.Optional[str] = OMIT, + name: typing.Optional[str] = OMIT, + time_zone: typing.Optional[str] = OMIT, + idempotency_key: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, - ) -> Episode: + ) -> Graph: """ - Add data to the graph. - Parameters ---------- - data : str - - type : GraphDataType - - created_at : typing.Optional[str] + graph_uuid : str + Graph UUID - 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. - - metadata : typing.Optional[typing.Dict[str, typing.Optional[typing.Any]]] - Optional metadata key-value pairs. Max 10 keys. Values must be strings, numbers, booleans, or arrays of scalars. + description : typing.Optional[str] + Omit to leave unchanged, send JSON null to clear, or send a value to set. - source_description : typing.Optional[str] + name : typing.Optional[str] + Omit to leave unchanged, send JSON null to clear, or send a value to set. - strict_ontology : typing.Optional[bool] - When true, prevents extraction of generic Entity nodes that do not match the configured ontology. + time_zone : typing.Optional[str] + Omit to leave unchanged, send JSON null to clear, or send a value to set. - user_id : typing.Optional[str] - User ID is the ID of the user to which the data will be added. If not adding to a user graph, please use graph_id field instead. + idempotency_key : typing.Optional[str] request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - Episode - Added episode + Graph + OK Examples -------- @@ -1549,67 +1604,54 @@ async def add( async def main() -> None: - await client.graph.add( - data="data", - type="text", + await client.graph.update( + graph_uuid="graph_uuid", ) asyncio.run(main()) """ - _response = await self._raw_client.add( - data=data, - type=type, - created_at=created_at, - graph_id=graph_id, - metadata=metadata, - source_description=source_description, - strict_ontology=strict_ontology, - user_id=user_id, + _response = await self._raw_client.update( + graph_uuid, + description=description, + name=name, + time_zone=time_zone, + idempotency_key=idempotency_key, request_options=request_options, ) return _response.data - async def add_batch( + async def clone( self, + graph_uuid: str, *, - episodes: typing.Sequence[EpisodeData], - graph_id: typing.Optional[str] = OMIT, - strict_ontology: typing.Optional[bool] = OMIT, - user_id: typing.Optional[str] = OMIT, + target_graph_id: typing.Optional[str] = OMIT, + idempotency_key: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, - ) -> typing.List[Episode]: + ) -> CloneGraphResult: """ - Deprecated. Use the [Batch API](/adding-batch-data) (`client.batch.*`) instead. - - Adds data to the graph in batch mode, processing episodes concurrently. - Parameters ---------- - episodes : typing.Sequence[EpisodeData] - - 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. + graph_uuid : str + Graph UUID - strict_ontology : typing.Optional[bool] - When true, prevents extraction of generic Entity nodes that do not match the configured ontology. + target_graph_id : typing.Optional[str] - user_id : typing.Optional[str] - User ID is the ID of the user to which the data will be added. If not adding to a user graph, please use graph_id field instead. + idempotency_key : typing.Optional[str] request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - typing.List[Episode] - Added episodes + CloneGraphResult + Accepted Examples -------- import asyncio - from zep_cloud import AsyncZep, EpisodeData + from zep_cloud import AsyncZep client = AsyncZep( api_key="YOUR_API_KEY", @@ -1617,134 +1659,110 @@ async def add_batch( async def main() -> None: - await client.graph.add_batch( - episodes=[ - EpisodeData( - data="data", - type="text", - ) - ], + await client.graph.clone( + graph_uuid="graph_uuid", ) asyncio.run(main()) """ - _response = await self._raw_client.add_batch( - episodes=episodes, - graph_id=graph_id, - strict_ontology=strict_ontology, - user_id=user_id, + _response = await self._raw_client.clone( + graph_uuid, + target_graph_id=target_graph_id, + idempotency_key=idempotency_key, request_options=request_options, ) return _response.data - async def add_fact_triple( + async def get_context( self, + graph_uuid: str, *, - fact: str, - fact_name: str, - created_at: typing.Optional[str] = OMIT, - edge_attributes: typing.Optional[typing.Dict[str, typing.Optional[typing.Any]]] = OMIT, - expired_at: typing.Optional[str] = OMIT, - graph_id: typing.Optional[str] = OMIT, - invalid_at: typing.Optional[str] = OMIT, - metadata: typing.Optional[typing.Dict[str, typing.Optional[typing.Any]]] = OMIT, - source_node_attributes: typing.Optional[typing.Dict[str, typing.Optional[typing.Any]]] = OMIT, - source_node_labels: typing.Optional[typing.Sequence[str]] = OMIT, - source_node_name: typing.Optional[str] = OMIT, - source_node_summary: typing.Optional[str] = OMIT, - source_node_uuid: typing.Optional[str] = OMIT, - target_node_attributes: typing.Optional[typing.Dict[str, typing.Optional[typing.Any]]] = OMIT, - target_node_labels: typing.Optional[typing.Sequence[str]] = OMIT, - target_node_name: typing.Optional[str] = OMIT, - target_node_summary: typing.Optional[str] = OMIT, - target_node_uuid: typing.Optional[str] = OMIT, - user_id: typing.Optional[str] = OMIT, - valid_at: typing.Optional[str] = OMIT, + filters: typing.Optional[typing.Dict[str, typing.Any]] = OMIT, + include_results: typing.Optional[bool] = OMIT, + max_characters: typing.Optional[int] = OMIT, + query: typing.Optional[str] = OMIT, + recency_bias: typing.Optional[str] = OMIT, + template_uuid: typing.Optional[str] = OMIT, + idempotency_key: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, - ) -> AddTripleResponse: + ) -> GraphContextResponse: """ - Add a fact triple for a user or group - Parameters ---------- - fact : str - The fact relating the two nodes that this edge represents - - fact_name : str - The name of the edge to add. Should be all caps using snake case (eg RELATES_TO) - - created_at : typing.Optional[str] - The timestamp of the message + graph_uuid : str + Graph UUID - edge_attributes : typing.Optional[typing.Dict[str, typing.Optional[typing.Any]]] - Additional attributes of the edge. Values must be scalar types (string, number, boolean, or null). - Nested objects and arrays are not allowed. + filters : typing.Optional[typing.Dict[str, typing.Any]] - expired_at : typing.Optional[str] - The time (if any) at which the edge expires + include_results : typing.Optional[bool] - graph_id : typing.Optional[str] + max_characters : typing.Optional[int] - invalid_at : typing.Optional[str] - The time (if any) at which the fact stops being true + query : typing.Optional[str] - metadata : typing.Optional[typing.Dict[str, typing.Optional[typing.Any]]] - Optional metadata key-value pairs for the shadow episode created for this fact triple. - Max 10 keys. Values must be strings, numbers, or booleans. + recency_bias : typing.Optional[str] - source_node_attributes : typing.Optional[typing.Dict[str, typing.Optional[typing.Any]]] - Additional attributes of the source node. Values must be scalar types (string, number, boolean, or null). - Nested objects and arrays are not allowed. + template_uuid : typing.Optional[str] - source_node_labels : typing.Optional[typing.Sequence[str]] - The labels for the source node. At most one entity-type label may be - provided so that manually-added triples remain consistent with automatic - episode extraction, which assigns one best-match entity type per node. - The base "Entity" label is added implicitly by the graph layer on save - and does not need to be supplied here. + idempotency_key : typing.Optional[str] - source_node_name : typing.Optional[str] - The name of the source node to add + request_options : typing.Optional[RequestOptions] + Request-specific configuration. - source_node_summary : typing.Optional[str] - The summary of the source node to add + Returns + ------- + GraphContextResponse + OK - source_node_uuid : typing.Optional[str] - The source node uuid + Examples + -------- + import asyncio - target_node_attributes : typing.Optional[typing.Dict[str, typing.Optional[typing.Any]]] - Additional attributes of the target node. Values must be scalar types (string, number, boolean, or null). - Nested objects and arrays are not allowed. + from zep_cloud import AsyncZep - target_node_labels : typing.Optional[typing.Sequence[str]] - The labels for the target node. At most one entity-type label may be - provided so that manually-added triples remain consistent with automatic - episode extraction, which assigns one best-match entity type per node. - The base "Entity" label is added implicitly by the graph layer on save - and does not need to be supplied here. + client = AsyncZep( + api_key="YOUR_API_KEY", + ) - target_node_name : typing.Optional[str] - The name of the target node to add - target_node_summary : typing.Optional[str] - The summary of the target node to add + async def main() -> None: + await client.graph.get_context( + graph_uuid="graph_uuid", + ) - target_node_uuid : typing.Optional[str] - The target node uuid - user_id : typing.Optional[str] + asyncio.run(main()) + """ + _response = await self._raw_client.get_context( + graph_uuid, + filters=filters, + include_results=include_results, + max_characters=max_characters, + query=query, + recency_bias=recency_bias, + template_uuid=template_uuid, + idempotency_key=idempotency_key, + request_options=request_options, + ) + return _response.data - valid_at : typing.Optional[str] - The time at which the fact becomes true + async def get_instructions( + self, graph_uuid: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> Instructions: + """ + Parameters + ---------- + graph_uuid : str + Graph UUID request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - AddTripleResponse - Resulting triple + Instructions + OK Examples -------- @@ -1758,72 +1776,44 @@ async def add_fact_triple( async def main() -> None: - await client.graph.add_fact_triple( - fact="fact", - fact_name="fact_name", + await client.graph.get_instructions( + graph_uuid="graph_uuid", ) asyncio.run(main()) """ - _response = await self._raw_client.add_fact_triple( - fact=fact, - fact_name=fact_name, - created_at=created_at, - edge_attributes=edge_attributes, - expired_at=expired_at, - graph_id=graph_id, - invalid_at=invalid_at, - metadata=metadata, - source_node_attributes=source_node_attributes, - source_node_labels=source_node_labels, - source_node_name=source_node_name, - source_node_summary=source_node_summary, - source_node_uuid=source_node_uuid, - target_node_attributes=target_node_attributes, - target_node_labels=target_node_labels, - target_node_name=target_node_name, - target_node_summary=target_node_summary, - target_node_uuid=target_node_uuid, - user_id=user_id, - valid_at=valid_at, - request_options=request_options, - ) + _response = await self._raw_client.get_instructions(graph_uuid, request_options=request_options) return _response.data - async def clone( + async def set_instructions( self, + graph_uuid: str, *, - source_graph_id: typing.Optional[str] = OMIT, - source_user_id: typing.Optional[str] = OMIT, - target_graph_id: typing.Optional[str] = OMIT, - target_user_id: typing.Optional[str] = OMIT, + inherited: typing.Optional[bool] = OMIT, + instructions: typing.Optional[typing.Sequence[typing.Dict[str, typing.Any]]] = OMIT, + idempotency_key: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, - ) -> CloneGraphResponse: + ) -> Instructions: """ - Clone a user or group graph. - Parameters ---------- - source_graph_id : typing.Optional[str] - source_graph_id is the ID of the graph to be cloned. Required if source_user_id is not provided + graph_uuid : str + Graph UUID - source_user_id : typing.Optional[str] - user_id of the user whose graph is being cloned. Required if source_graph_id is not provided + inherited : typing.Optional[bool] - target_graph_id : typing.Optional[str] - target_graph_id is the ID to be set on the cloned graph. Must not point to an existing graph. Required if target_user_id is not provided. + instructions : typing.Optional[typing.Sequence[typing.Dict[str, typing.Any]]] - target_user_id : typing.Optional[str] - user_id to be set on the cloned user. Must not point to an existing user. Required if target_graph_id is not provided. + idempotency_key : typing.Optional[str] request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - CloneGraphResponse - Response object containing graph_id or user_id pointing to the new graph + Instructions + OK Examples -------- @@ -1837,50 +1827,92 @@ async def clone( async def main() -> None: - await client.graph.clone() + await client.graph.set_instructions( + graph_uuid="graph_uuid", + ) asyncio.run(main()) """ - _response = await self._raw_client.clone( - source_graph_id=source_graph_id, - source_user_id=source_user_id, - target_graph_id=target_graph_id, - target_user_id=target_user_id, + _response = await self._raw_client.set_instructions( + graph_uuid, + inherited=inherited, + instructions=instructions, + idempotency_key=idempotency_key, request_options=request_options, ) return _response.data - async def create( + async def get_observation_steering( + self, graph_uuid: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> ObservationSteering: + """ + Parameters + ---------- + graph_uuid : str + Graph UUID + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + ObservationSteering + OK + + Examples + -------- + import asyncio + + from zep_cloud import AsyncZep + + client = AsyncZep( + api_key="YOUR_API_KEY", + ) + + + async def main() -> None: + await client.graph.get_observation_steering( + graph_uuid="graph_uuid", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.get_observation_steering(graph_uuid, request_options=request_options) + return _response.data + + async def set_observation_steering( self, + graph_uuid: str, *, - graph_id: str, - description: typing.Optional[str] = OMIT, - name: typing.Optional[str] = OMIT, - time_zone: typing.Optional[str] = OMIT, + inherited: typing.Optional[bool] = OMIT, + instruction: typing.Optional[str] = OMIT, + types: typing.Optional[typing.Sequence[typing.Dict[str, typing.Any]]] = OMIT, + idempotency_key: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, - ) -> Graph: + ) -> ObservationSteering: """ - Creates a new graph. - Parameters ---------- - graph_id : str + graph_uuid : str + Graph UUID - description : typing.Optional[str] + inherited : typing.Optional[bool] + + instruction : typing.Optional[str] - name : typing.Optional[str] + types : typing.Optional[typing.Sequence[typing.Dict[str, typing.Any]]] - time_zone : typing.Optional[str] - The graph's IANA time zone. Stored on its group-backed subject. + idempotency_key : typing.Optional[str] request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - Graph - The added graph + ObservationSteering + OK Examples -------- @@ -1894,55 +1926,39 @@ async def create( async def main() -> None: - await client.graph.create( - graph_id="graph_id", + await client.graph.set_observation_steering( + graph_uuid="graph_uuid", ) asyncio.run(main()) """ - _response = await self._raw_client.create( - graph_id=graph_id, description=description, name=name, time_zone=time_zone, request_options=request_options + _response = await self._raw_client.set_observation_steering( + graph_uuid, + inherited=inherited, + instruction=instruction, + types=types, + idempotency_key=idempotency_key, + request_options=request_options, ) return _response.data - async def list_all( - self, - *, - page_number: typing.Optional[int] = None, - page_size: typing.Optional[int] = None, - search: typing.Optional[str] = None, - order_by: typing.Optional[str] = None, - asc: typing.Optional[bool] = None, - request_options: typing.Optional[RequestOptions] = None, - ) -> GraphListResponse: + async def get_ontology( + self, graph_uuid: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> Ontology: """ - Returns all graphs. In order to list users, use user.list_ordered instead - Parameters ---------- - page_number : typing.Optional[int] - Page number for pagination, starting from 1. - - page_size : typing.Optional[int] - Number of graphs to retrieve per page (default 50, range 1-100; explicit 0 is invalid). - - search : typing.Optional[str] - Search term for filtering graphs by graph_id, name, or description. Queries longer than 200 Unicode code points after whitespace normalization are invalid. - - order_by : typing.Optional[str] - Column to sort by (created_at, graph_id, name). - - asc : typing.Optional[bool] - Sort in ascending order. + graph_uuid : str + Graph UUID request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - GraphListResponse - Successfully retrieved list of graphs. + Ontology + OK Examples -------- @@ -1956,60 +1972,53 @@ async def list_all( async def main() -> None: - await client.graph.list_all( - page_number=1, - page_size=1, - search="search", - order_by="order_by", - asc=True, + await client.graph.get_ontology( + graph_uuid="graph_uuid", ) asyncio.run(main()) """ - _response = await self._raw_client.list_all( - page_number=page_number, - page_size=page_size, - search=search, - order_by=order_by, - asc=asc, - request_options=request_options, - ) + _response = await self._raw_client.get_ontology(graph_uuid, request_options=request_options) return _response.data - async def add_nodes( + async def set_ontology( self, + graph_uuid: str, *, - nodes: typing.Sequence[AddNodeItem], - graph_id: typing.Optional[str] = OMIT, - user_id: typing.Optional[str] = OMIT, + edge_types: typing.Optional[typing.Sequence[EdgeType]] = OMIT, + entity_types: typing.Optional[typing.Sequence[EntityType]] = OMIT, + inherited: typing.Optional[bool] = OMIT, + idempotency_key: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, - ) -> AddNodesResponse: + ) -> Ontology: """ - Add entity nodes to a user or graph directly, without episode ingestion. Up to 100 nodes per request. - Parameters ---------- - nodes : typing.Sequence[AddNodeItem] - The nodes to add. 1 to 100 items. + graph_uuid : str + Graph UUID - graph_id : typing.Optional[str] + edge_types : typing.Optional[typing.Sequence[EdgeType]] - user_id : typing.Optional[str] + entity_types : typing.Optional[typing.Sequence[EntityType]] + + inherited : typing.Optional[bool] + + idempotency_key : typing.Optional[str] request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - AddNodesResponse - Accepted + Ontology + OK Examples -------- import asyncio - from zep_cloud import AddNodeItem, AsyncZep + from zep_cloud import AsyncZep client = AsyncZep( api_key="YOUR_API_KEY", @@ -2017,91 +2026,71 @@ async def add_nodes( async def main() -> None: - await client.graph.add_nodes( - nodes=[ - AddNodeItem( - name="name", - ) - ], + await client.graph.set_ontology( + graph_uuid="graph_uuid", ) asyncio.run(main()) """ - _response = await self._raw_client.add_nodes( - nodes=nodes, graph_id=graph_id, user_id=user_id, request_options=request_options + _response = await self._raw_client.set_ontology( + graph_uuid, + edge_types=edge_types, + entity_types=entity_types, + inherited=inherited, + idempotency_key=idempotency_key, + request_options=request_options, ) return _response.data - async def detect_patterns( + async def search_edges( self, + graph_uuid: str, *, - detect: typing.Optional[DetectConfig] = OMIT, - edge_limit: typing.Optional[int] = OMIT, - graph_id: typing.Optional[str] = OMIT, - limit: typing.Optional[int] = OMIT, - min_occurrences: typing.Optional[int] = OMIT, + limit: typing.Optional[int] = None, + cursor: typing.Optional[str] = None, + bfs_origin_node_uuids: typing.Optional[typing.Sequence[str]] = OMIT, + center_node_uuid: typing.Optional[str] = OMIT, + filters: typing.Optional[typing.Dict[str, typing.Any]] = OMIT, + mmr_lambda: typing.Optional[float] = OMIT, query: typing.Optional[str] = OMIT, - query_limit: typing.Optional[int] = OMIT, - recency_weight: typing.Optional[RecencyWeight] = OMIT, - search_filters: typing.Optional[SearchFilters] = OMIT, - seeds: typing.Optional[PatternSeeds] = OMIT, - user_id: typing.Optional[str] = OMIT, + reranker: typing.Optional[str] = OMIT, + idempotency_key: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, - ) -> DetectPatternsResponse: + ) -> AsyncPager[Edge, EdgePage]: """ - 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, - detects triple-frequency patterns, and returns resolved edges ranked by relevance. - Parameters ---------- - detect : typing.Optional[DetectConfig] - Which pattern types to detect with type-specific configuration. - Omit to detect all types with defaults. Ignored when query is set. - - edge_limit : typing.Optional[int] - Max resolved edges per pattern. Default: 10, Max: 100. Only used with query. - - graph_id : typing.Optional[str] - Graph ID when detecting patterns on a named graph + graph_uuid : str + Graph UUID limit : typing.Optional[int] - Max patterns to return. Default: 50, Max: 200 + Page size - min_occurrences : typing.Optional[int] - Minimum occurrence count to report a pattern. Default: 2 + cursor : typing.Optional[str] + Opaque page cursor - query : typing.Optional[str] - Search query for discovering seed nodes via hybrid search. - When set, forces triple-frequency detection only and enables edge resolution - with cross-encoder reranking. Mutually exclusive with seeds. + bfs_origin_node_uuids : typing.Optional[typing.Sequence[str]] - query_limit : typing.Optional[int] - Max seed nodes from search. Default: 10, Max: 50. Only used with query. + center_node_uuid : typing.Optional[str] - recency_weight : typing.Optional[RecencyWeight] - Exponential half-life decay applied to edge created_at timestamps. - Valid values: none, 7_days, 30_days, 90_days. Default: none + filters : typing.Optional[typing.Dict[str, typing.Any]] - search_filters : typing.Optional[SearchFilters] - Filters which edges/nodes participate in pattern detection. - Reuses the same filter format as /graph/search. + mmr_lambda : typing.Optional[float] - seeds : typing.Optional[PatternSeeds] - Seed selection. If omitted, analyzes the entire graph. Mutually exclusive with query. + query : typing.Optional[str] - user_id : typing.Optional[str] - User ID when detecting patterns on a user graph + reranker : typing.Optional[str] + + idempotency_key : typing.Optional[str] request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - DetectPatternsResponse - Detected patterns + AsyncPager[Edge, EdgePage] + OK Examples -------- @@ -2115,98 +2104,83 @@ async def detect_patterns( async def main() -> None: - await client.graph.detect_patterns() + response = await client.graph.search_edges( + graph_uuid="graph_uuid", + limit=1, + cursor="cursor", + ) + async for item in response: + yield item + + # alternatively, you can paginate page-by-page + async for page in response.iter_pages(): + yield page asyncio.run(main()) """ - _response = await self._raw_client.detect_patterns( - detect=detect, - edge_limit=edge_limit, - graph_id=graph_id, + return await self._raw_client.search_edges( + graph_uuid, limit=limit, - min_occurrences=min_occurrences, + cursor=cursor, + bfs_origin_node_uuids=bfs_origin_node_uuids, + center_node_uuid=center_node_uuid, + filters=filters, + mmr_lambda=mmr_lambda, query=query, - query_limit=query_limit, - recency_weight=recency_weight, - search_filters=search_filters, - seeds=seeds, - user_id=user_id, + reranker=reranker, + idempotency_key=idempotency_key, request_options=request_options, ) - return _response.data - async def search( + async def search_episodes( self, + graph_uuid: str, *, - query: str, + limit: typing.Optional[int] = None, + cursor: typing.Optional[str] = None, bfs_origin_node_uuids: typing.Optional[typing.Sequence[str]] = OMIT, center_node_uuid: typing.Optional[str] = OMIT, - graph_id: typing.Optional[str] = OMIT, - limit: typing.Optional[int] = OMIT, - max_characters: typing.Optional[int] = OMIT, + filters: typing.Optional[typing.Dict[str, typing.Any]] = OMIT, mmr_lambda: typing.Optional[float] = OMIT, - reranker: typing.Optional[Reranker] = OMIT, - return_raw_results: typing.Optional[bool] = OMIT, - scope: typing.Optional[GraphSearchScope] = OMIT, - search_filters: typing.Optional[SearchFilters] = OMIT, - user_id: typing.Optional[str] = OMIT, + query: typing.Optional[str] = OMIT, + reranker: typing.Optional[str] = OMIT, + idempotency_key: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, - ) -> GraphSearchResults: + ) -> AsyncPager[Episode, EpisodePage]: """ - Perform a graph search query. - Parameters ---------- - query : str - The string to search for (required) + graph_uuid : str + Graph UUID - bfs_origin_node_uuids : typing.Optional[typing.Sequence[str]] - Nodes that are the origins of the BFS searches + limit : typing.Optional[int] + Page size - center_node_uuid : typing.Optional[str] - Node to rerank around for node distance reranking + cursor : typing.Optional[str] + Opaque page cursor - graph_id : typing.Optional[str] - The graph_id to search in. When searching user graph, please use user_id instead. + bfs_origin_node_uuids : typing.Optional[typing.Sequence[str]] - limit : typing.Optional[int] - The maximum number of facts to retrieve for non-auto scopes. Defaults to 10. Limited to 50. Ignored when scope=auto. + center_node_uuid : typing.Optional[str] - max_characters : typing.Optional[int] - Maximum total characters across all selected results when scope=auto. Defaults to 2500. Limited to 50000. + filters : typing.Optional[typing.Dict[str, typing.Any]] mmr_lambda : typing.Optional[float] - weighting for maximal marginal relevance - - reranker : typing.Optional[Reranker] - Defaults to RRF. Ignored when scope=auto except node_distance and episode_mentions are rejected; - auto search always uses RRF retrieval and applies its own internal rerank after retrieval. - episode_mentions ranks edge candidates by how many of the episodes listed - in search_filters.episode_uuids mention them; without episode_uuids it has - no effect and results are ranked as if no reranker were specified. - return_raw_results : typing.Optional[bool] - When scope=auto, include the selected raw graph results alongside the materialized context block. - For graph-service-backed auto mode, selected raw results may include episodes, - edges, nodes, observations, and thread_summaries. - - scope : typing.Optional[GraphSearchScope] - Defaults to Edges. + query : typing.Optional[str] - search_filters : typing.Optional[SearchFilters] - Search filters to apply to the search + reranker : typing.Optional[str] - user_id : typing.Optional[str] - The user_id when searching user graph. If not searching user graph, please use graph_id instead. + idempotency_key : typing.Optional[str] request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - GraphSearchResults - Graph search results or auto-context block + AsyncPager[Episode, EpisodePage] + OK Examples -------- @@ -2220,88 +2194,83 @@ async def search( async def main() -> None: - await client.graph.search( - query="query", + response = await client.graph.search_episodes( + graph_uuid="graph_uuid", + limit=1, + cursor="cursor", ) + async for item in response: + yield item + + # alternatively, you can paginate page-by-page + async for page in response.iter_pages(): + yield page asyncio.run(main()) """ - _response = await self._raw_client.search( - query=query, + return await self._raw_client.search_episodes( + graph_uuid, + limit=limit, + cursor=cursor, bfs_origin_node_uuids=bfs_origin_node_uuids, center_node_uuid=center_node_uuid, - graph_id=graph_id, - limit=limit, - max_characters=max_characters, + filters=filters, mmr_lambda=mmr_lambda, + query=query, reranker=reranker, - return_raw_results=return_raw_results, - scope=scope, - search_filters=search_filters, - user_id=user_id, + idempotency_key=idempotency_key, request_options=request_options, ) - return _response.data - async def get_subgraph( + async def search_nodes( self, + graph_uuid: str, *, - seed_node_uuids: typing.Sequence[str], - depth: typing.Optional[int] = OMIT, - direction: typing.Optional[str] = OMIT, - graph_id: typing.Optional[str] = OMIT, - max_edges: typing.Optional[int] = OMIT, - max_nodes: typing.Optional[int] = OMIT, - search_filters: typing.Optional[SearchFilters] = OMIT, - user_id: typing.Optional[str] = OMIT, + limit: typing.Optional[int] = None, + cursor: typing.Optional[str] = None, + bfs_origin_node_uuids: typing.Optional[typing.Sequence[str]] = OMIT, + center_node_uuid: typing.Optional[str] = OMIT, + filters: typing.Optional[typing.Dict[str, typing.Any]] = OMIT, + mmr_lambda: typing.Optional[float] = OMIT, + query: typing.Optional[str] = OMIT, + reranker: typing.Optional[str] = OMIT, + idempotency_key: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, - ) -> GraphSubgraphResponse: + ) -> AsyncPager[Node, NodePage]: """ - Returns the bounded neighborhood of a set of seed nodes as a single {nodes, edges} payload: breadth-first expansion up to a caller-specified depth, subject to explicit budgets, with explicit truncation reporting. - Parameters ---------- - seed_node_uuids : typing.Sequence[str] - Seed node UUIDs to expand from, in traversal-priority order: seeds are - admitted before any expansion, in this order, and count toward - max_nodes first. 1-20 entries, required. Seeds that do not exist in - the target graph are ignored, not an error. + graph_uuid : str + Graph UUID - depth : typing.Optional[int] - Maximum traversal depth from the seeds. 1-3. Defaults to 1. + limit : typing.Optional[int] + Page size - direction : typing.Optional[str] - Edge orientation followed during expansion, relative to each frontier - node: "in" | "out" | "both". Defaults to "both". + cursor : typing.Optional[str] + Opaque page cursor - graph_id : typing.Optional[str] - graph_id identifies the target named graph. Exactly one of user_id or - graph_id is required. + bfs_origin_node_uuids : typing.Optional[typing.Sequence[str]] - max_edges : typing.Optional[int] - Maximum number of edges in the response. 1-1000. Defaults to 200. + center_node_uuid : typing.Optional[str] - max_nodes : typing.Optional[int] - Maximum number of nodes in the response, including admitted seeds. - 1-500. Defaults to 100. + filters : typing.Optional[typing.Dict[str, typing.Any]] - 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). + mmr_lambda : typing.Optional[float] - user_id : typing.Optional[str] - user_id identifies the target user graph. Exactly one of user_id or - graph_id is required. + query : typing.Optional[str] + + reranker : typing.Optional[str] + + idempotency_key : typing.Optional[str] request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - GraphSubgraphResponse - Subgraph + AsyncPager[Node, NodePage] + OK Examples -------- @@ -2315,42 +2284,83 @@ async def get_subgraph( async def main() -> None: - await client.graph.get_subgraph( - seed_node_uuids=["seed_node_uuids"], + response = await client.graph.search_nodes( + graph_uuid="graph_uuid", + limit=1, + cursor="cursor", ) + async for item in response: + yield item + + # alternatively, you can paginate page-by-page + async for page in response.iter_pages(): + yield page asyncio.run(main()) """ - _response = await self._raw_client.get_subgraph( - seed_node_uuids=seed_node_uuids, - depth=depth, - direction=direction, - graph_id=graph_id, - max_edges=max_edges, - max_nodes=max_nodes, - search_filters=search_filters, - user_id=user_id, + return await self._raw_client.search_nodes( + graph_uuid, + limit=limit, + cursor=cursor, + bfs_origin_node_uuids=bfs_origin_node_uuids, + center_node_uuid=center_node_uuid, + filters=filters, + mmr_lambda=mmr_lambda, + query=query, + reranker=reranker, + idempotency_key=idempotency_key, request_options=request_options, ) - return _response.data - async def get(self, graph_id: str, *, request_options: typing.Optional[RequestOptions] = None) -> Graph: + async def search_observations( + self, + graph_uuid: str, + *, + limit: typing.Optional[int] = None, + cursor: typing.Optional[str] = None, + bfs_origin_node_uuids: typing.Optional[typing.Sequence[str]] = OMIT, + center_node_uuid: typing.Optional[str] = OMIT, + filters: typing.Optional[typing.Dict[str, typing.Any]] = OMIT, + mmr_lambda: typing.Optional[float] = OMIT, + query: typing.Optional[str] = OMIT, + reranker: typing.Optional[str] = OMIT, + idempotency_key: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncPager[Observation, ObservationPage]: """ - Returns a graph. - Parameters ---------- - graph_id : str - The graph_id of the graph to get. + graph_uuid : str + Graph UUID + + limit : typing.Optional[int] + Page size + + cursor : typing.Optional[str] + Opaque page cursor + + bfs_origin_node_uuids : typing.Optional[typing.Sequence[str]] + + center_node_uuid : typing.Optional[str] + + filters : typing.Optional[typing.Dict[str, typing.Any]] + + mmr_lambda : typing.Optional[float] + + query : typing.Optional[str] + + reranker : typing.Optional[str] + + idempotency_key : typing.Optional[str] request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - Graph - The graph that was retrieved. + AsyncPager[Observation, ObservationPage] + OK Examples -------- @@ -2364,34 +2374,83 @@ async def get(self, graph_id: str, *, request_options: typing.Optional[RequestOp async def main() -> None: - await client.graph.get( - graph_id="graphId", + response = await client.graph.search_observations( + graph_uuid="graph_uuid", + limit=1, + cursor="cursor", ) + async for item in response: + yield item + + # alternatively, you can paginate page-by-page + async for page in response.iter_pages(): + yield page asyncio.run(main()) """ - _response = await self._raw_client.get(graph_id, request_options=request_options) - return _response.data + return await self._raw_client.search_observations( + graph_uuid, + limit=limit, + cursor=cursor, + bfs_origin_node_uuids=bfs_origin_node_uuids, + center_node_uuid=center_node_uuid, + filters=filters, + mmr_lambda=mmr_lambda, + query=query, + reranker=reranker, + idempotency_key=idempotency_key, + request_options=request_options, + ) - async def delete( - self, graph_id: str, *, request_options: typing.Optional[RequestOptions] = None - ) -> SuccessResponse: + async def search_thread_summaries( + self, + graph_uuid: str, + *, + limit: typing.Optional[int] = None, + cursor: typing.Optional[str] = None, + bfs_origin_node_uuids: typing.Optional[typing.Sequence[str]] = OMIT, + center_node_uuid: typing.Optional[str] = OMIT, + filters: typing.Optional[typing.Dict[str, typing.Any]] = OMIT, + mmr_lambda: typing.Optional[float] = OMIT, + query: typing.Optional[str] = OMIT, + reranker: typing.Optional[str] = OMIT, + idempotency_key: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncPager[ThreadSummary, ThreadSummaryPage]: """ - Deletes a graph. If you would like to delete a user graph, make sure to use user.delete instead. - Parameters ---------- - graph_id : str - Graph ID + graph_uuid : str + Graph UUID + + limit : typing.Optional[int] + Page size + + cursor : typing.Optional[str] + Opaque page cursor + + bfs_origin_node_uuids : typing.Optional[typing.Sequence[str]] + + center_node_uuid : typing.Optional[str] + + filters : typing.Optional[typing.Dict[str, typing.Any]] + + mmr_lambda : typing.Optional[float] + + query : typing.Optional[str] + + reranker : typing.Optional[str] + + idempotency_key : typing.Optional[str] request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - SuccessResponse - Deleted + AsyncPager[ThreadSummary, ThreadSummaryPage] + OK Examples -------- @@ -2405,47 +2464,75 @@ async def delete( async def main() -> None: - await client.graph.delete( - graph_id="graphId", + response = await client.graph.search_thread_summaries( + graph_uuid="graph_uuid", + limit=1, + cursor="cursor", ) + async for item in response: + yield item + + # alternatively, you can paginate page-by-page + async for page in response.iter_pages(): + yield page asyncio.run(main()) """ - _response = await self._raw_client.delete(graph_id, request_options=request_options) - return _response.data + return await self._raw_client.search_thread_summaries( + graph_uuid, + limit=limit, + cursor=cursor, + bfs_origin_node_uuids=bfs_origin_node_uuids, + center_node_uuid=center_node_uuid, + filters=filters, + mmr_lambda=mmr_lambda, + query=query, + reranker=reranker, + idempotency_key=idempotency_key, + request_options=request_options, + ) - async def update( + async def get_subgraph( self, - graph_id: str, + graph_uuid: str, *, - description: typing.Optional[str] = OMIT, - name: typing.Optional[str] = OMIT, - time_zone: typing.Optional[str] = OMIT, + depth: typing.Optional[int] = OMIT, + direction: typing.Optional[str] = OMIT, + filters: typing.Optional[typing.Dict[str, typing.Any]] = OMIT, + max_edges: typing.Optional[int] = OMIT, + max_nodes: typing.Optional[int] = OMIT, + seed_node_uuids: typing.Optional[typing.Sequence[str]] = OMIT, + idempotency_key: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, - ) -> Graph: + ) -> SubgraphResponse: """ - Updates information about a graph. - Parameters ---------- - graph_id : str - Graph ID + graph_uuid : str + Graph UUID - description : typing.Optional[str] + depth : typing.Optional[int] - name : typing.Optional[str] + direction : typing.Optional[str] - time_zone : typing.Optional[str] - The graph's IANA time zone. Stored on its group-backed subject. + filters : typing.Optional[typing.Dict[str, typing.Any]] + + max_edges : typing.Optional[int] + + max_nodes : typing.Optional[int] + + seed_node_uuids : typing.Optional[typing.Sequence[str]] + + idempotency_key : typing.Optional[str] request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - Graph - The updated graph object + SubgraphResponse + OK Examples -------- @@ -2459,34 +2546,48 @@ async def update( async def main() -> None: - await client.graph.update( - graph_id="graphId", + await client.graph.get_subgraph( + graph_uuid="graph_uuid", ) asyncio.run(main()) """ - _response = await self._raw_client.update( - graph_id, description=description, name=name, time_zone=time_zone, request_options=request_options + _response = await self._raw_client.get_subgraph( + graph_uuid, + depth=depth, + direction=direction, + filters=filters, + max_edges=max_edges, + max_nodes=max_nodes, + seed_node_uuids=seed_node_uuids, + idempotency_key=idempotency_key, + request_options=request_options, ) return _response.data - async def warm(self, graph_id: str, *, request_options: typing.Optional[RequestOptions] = None) -> SuccessResponse: + async def warm( + self, + graph_uuid: str, + *, + idempotency_key: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncResult: """ - Hints Zep to warm a graph for low-latency search - Parameters ---------- - graph_id : str - The graph_id of the graph to warm. + graph_uuid : str + Graph UUID + + idempotency_key : typing.Optional[str] request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - SuccessResponse - Warm hint accepted + AsyncResult + Accepted Examples -------- @@ -2501,11 +2602,61 @@ async def warm(self, graph_id: str, *, request_options: typing.Optional[RequestO async def main() -> None: await client.graph.warm( - graph_id="graphId", + graph_uuid="graph_uuid", ) asyncio.run(main()) """ - _response = await self._raw_client.warm(graph_id, request_options=request_options) + _response = await self._raw_client.warm( + graph_uuid, idempotency_key=idempotency_key, request_options=request_options + ) return _response.data + + @property + def document_summary(self): + if self._document_summary is None: + from .document_summary.client import AsyncDocumentSummaryClient # noqa: E402 + + self._document_summary = AsyncDocumentSummaryClient(client_wrapper=self._client_wrapper) + return self._document_summary + + @property + def episode(self): + if self._episode is None: + from .episode.client import AsyncEpisodeClient # noqa: E402 + + self._episode = AsyncEpisodeClient(client_wrapper=self._client_wrapper) + return self._episode + + @property + def edge(self): + if self._edge is None: + from .edge.client import AsyncEdgeClient # noqa: E402 + + self._edge = AsyncEdgeClient(client_wrapper=self._client_wrapper) + return self._edge + + @property + def node(self): + if self._node is None: + from .node.client import AsyncNodeClient # noqa: E402 + + self._node = AsyncNodeClient(client_wrapper=self._client_wrapper) + return self._node + + @property + def observation(self): + if self._observation is None: + from .observation.client import AsyncObservationClient # noqa: E402 + + self._observation = AsyncObservationClient(client_wrapper=self._client_wrapper) + return self._observation + + @property + def thread_summary(self): + if self._thread_summary is None: + from .thread_summary.client import AsyncThreadSummaryClient # noqa: E402 + + self._thread_summary = AsyncThreadSummaryClient(client_wrapper=self._client_wrapper) + return self._thread_summary diff --git a/src/zep_cloud/user_group/__init__.py b/src/zep_cloud/graph/document_summary/__init__.py similarity index 100% rename from src/zep_cloud/user_group/__init__.py rename to src/zep_cloud/graph/document_summary/__init__.py 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..da3c2d40 --- /dev/null +++ b/src/zep_cloud/graph/document_summary/client.py @@ -0,0 +1,176 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from ...core.client_wrapper import AsyncClientWrapper, SyncClientWrapper +from ...core.pagination import AsyncPager, SyncPager +from ...core.request_options import RequestOptions +from ...types.document_summary import DocumentSummary +from ...types.document_summary_page import DocumentSummaryPage +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 list( + self, + graph_uuid: str, + *, + limit: typing.Optional[int] = None, + cursor: typing.Optional[str] = None, + filters: typing.Optional[typing.Dict[str, typing.Any]] = OMIT, + idempotency_key: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> SyncPager[DocumentSummary, DocumentSummaryPage]: + """ + Parameters + ---------- + graph_uuid : str + Graph UUID + + limit : typing.Optional[int] + Page size + + cursor : typing.Optional[str] + Opaque page cursor + + filters : typing.Optional[typing.Dict[str, typing.Any]] + + idempotency_key : typing.Optional[str] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + SyncPager[DocumentSummary, DocumentSummaryPage] + OK + + Examples + -------- + from zep_cloud import Zep + + client = Zep( + api_key="YOUR_API_KEY", + ) + response = client.graph.document_summary.list( + graph_uuid="graph_uuid", + limit=1, + cursor="cursor", + ) + for item in response: + yield item + # alternatively, you can paginate page-by-page + for page in response.iter_pages(): + yield page + """ + return self._raw_client.list( + graph_uuid, + limit=limit, + cursor=cursor, + filters=filters, + idempotency_key=idempotency_key, + request_options=request_options, + ) + + +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 list( + self, + graph_uuid: str, + *, + limit: typing.Optional[int] = None, + cursor: typing.Optional[str] = None, + filters: typing.Optional[typing.Dict[str, typing.Any]] = OMIT, + idempotency_key: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncPager[DocumentSummary, DocumentSummaryPage]: + """ + Parameters + ---------- + graph_uuid : str + Graph UUID + + limit : typing.Optional[int] + Page size + + cursor : typing.Optional[str] + Opaque page cursor + + filters : typing.Optional[typing.Dict[str, typing.Any]] + + idempotency_key : typing.Optional[str] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncPager[DocumentSummary, DocumentSummaryPage] + OK + + Examples + -------- + import asyncio + + from zep_cloud import AsyncZep + + client = AsyncZep( + api_key="YOUR_API_KEY", + ) + + + async def main() -> None: + response = await client.graph.document_summary.list( + graph_uuid="graph_uuid", + limit=1, + cursor="cursor", + ) + async for item in response: + yield item + + # alternatively, you can paginate page-by-page + async for page in response.iter_pages(): + yield page + + + asyncio.run(main()) + """ + return await self._raw_client.list( + graph_uuid, + limit=limit, + cursor=cursor, + filters=filters, + idempotency_key=idempotency_key, + request_options=request_options, + ) 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..a02ba67f --- /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.jsonable_encoder import jsonable_encoder +from ...core.pagination import AsyncPager, SyncPager +from ...core.parse_error import ParsingError +from ...core.pydantic_utilities import parse_obj_as +from ...core.request_options import RequestOptions +from ...errors.bad_request_error import BadRequestError +from ...errors.not_found_error import NotFoundError +from ...errors.unauthorized_error import UnauthorizedError +from ...types.api_error import ApiError as types_api_error_ApiError +from ...types.document_summary import DocumentSummary +from ...types.document_summary_page import DocumentSummaryPage +from pydantic import ValidationError + +# 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 list( + self, + graph_uuid: str, + *, + limit: typing.Optional[int] = None, + cursor: typing.Optional[str] = None, + filters: typing.Optional[typing.Dict[str, typing.Any]] = OMIT, + idempotency_key: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> SyncPager[DocumentSummary, DocumentSummaryPage]: + """ + Parameters + ---------- + graph_uuid : str + Graph UUID + + limit : typing.Optional[int] + Page size + + cursor : typing.Optional[str] + Opaque page cursor + + filters : typing.Optional[typing.Dict[str, typing.Any]] + + idempotency_key : typing.Optional[str] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + SyncPager[DocumentSummary, DocumentSummaryPage] + OK + """ + _response = self._client_wrapper.httpx_client.request( + f"graphs/{jsonable_encoder(graph_uuid)}/document-summaries/list", + method="POST", + params={ + "limit": limit, + "cursor": cursor, + }, + json={ + "filters": filters, + }, + headers={ + "content-type": "application/json", + "Idempotency-Key": str(idempotency_key) if idempotency_key is not None else None, + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _parsed_response = typing.cast( + DocumentSummaryPage, + parse_obj_as( + type_=DocumentSummaryPage, # type: ignore + object_=_response.json(), + ), + ) + _items = _parsed_response.items + _parsed_next = _parsed_response.next_cursor + _has_next = _parsed_next is not None and _parsed_next != "" + _get_next = lambda: self.list( + graph_uuid, + limit=limit, + cursor=_parsed_next, + filters=filters, + idempotency_key=idempotency_key, + request_options=request_options, + ) + return SyncPager(has_next=_has_next, items=_items, get_next=_get_next, response=_parsed_response) + if _response.status_code == 400: + raise BadRequestError( + 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 == 401: + raise UnauthorizedError( + 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(), + ), + ), + ) + _response_json = _response.json() + except JSONDecodeError: + raise core_api_error_ApiError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.text + ) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) + 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 list( + self, + graph_uuid: str, + *, + limit: typing.Optional[int] = None, + cursor: typing.Optional[str] = None, + filters: typing.Optional[typing.Dict[str, typing.Any]] = OMIT, + idempotency_key: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncPager[DocumentSummary, DocumentSummaryPage]: + """ + Parameters + ---------- + graph_uuid : str + Graph UUID + + limit : typing.Optional[int] + Page size + + cursor : typing.Optional[str] + Opaque page cursor + + filters : typing.Optional[typing.Dict[str, typing.Any]] + + idempotency_key : typing.Optional[str] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncPager[DocumentSummary, DocumentSummaryPage] + OK + """ + _response = await self._client_wrapper.httpx_client.request( + f"graphs/{jsonable_encoder(graph_uuid)}/document-summaries/list", + method="POST", + params={ + "limit": limit, + "cursor": cursor, + }, + json={ + "filters": filters, + }, + headers={ + "content-type": "application/json", + "Idempotency-Key": str(idempotency_key) if idempotency_key is not None else None, + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _parsed_response = typing.cast( + DocumentSummaryPage, + parse_obj_as( + type_=DocumentSummaryPage, # type: ignore + object_=_response.json(), + ), + ) + _items = _parsed_response.items + _parsed_next = _parsed_response.next_cursor + _has_next = _parsed_next is not None and _parsed_next != "" + + async def _get_next(): + return await self.list( + graph_uuid, + limit=limit, + cursor=_parsed_next, + filters=filters, + idempotency_key=idempotency_key, + request_options=request_options, + ) + + return AsyncPager(has_next=_has_next, items=_items, get_next=_get_next, response=_parsed_response) + if _response.status_code == 400: + raise BadRequestError( + 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 == 401: + raise UnauthorizedError( + 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(), + ), + ), + ) + _response_json = _response.json() + except JSONDecodeError: + raise core_api_error_ApiError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.text + ) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) + raise core_api_error_ApiError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response_json + ) diff --git a/src/zep_cloud/graph/edge/client.py b/src/zep_cloud/graph/edge/client.py index 2835015e..3a2bacd6 100644 --- a/src/zep_cloud/graph/edge/client.py +++ b/src/zep_cloud/graph/edge/client.py @@ -3,10 +3,12 @@ import typing from ...core.client_wrapper import AsyncClientWrapper, SyncClientWrapper +from ...core.pagination import AsyncPager, SyncPager from ...core.request_options import RequestOptions -from ...types.entity_edge import EntityEdge -from ...types.search_filters import SearchFilters -from ...types.success_response import SuccessResponse +from ...types.add_edge_result import AddEdgeResult +from ...types.async_result import AsyncResult +from ...types.edge import Edge +from ...types.edge_page import EdgePage from .raw_client import AsyncRawEdgeClient, RawEdgeClient # this is used as the default value for optional parameters @@ -28,54 +30,55 @@ def with_raw_response(self) -> RawEdgeClient: """ return self._raw_client - def get_by_graph_id( + def add( self, - graph_id: str, + graph_uuid: 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, + attributes: typing.Optional[typing.Dict[str, typing.Any]] = OMIT, + expired_at: typing.Optional[str] = OMIT, + fact: typing.Optional[str] = OMIT, + fact_name: typing.Optional[str] = OMIT, + invalid_at: typing.Optional[str] = OMIT, + metadata: typing.Optional[typing.Dict[str, typing.Any]] = OMIT, + source_node: typing.Optional[typing.Dict[str, typing.Any]] = OMIT, + target_node: typing.Optional[typing.Dict[str, typing.Any]] = OMIT, + valid_at: typing.Optional[str] = OMIT, + idempotency_key: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, - ) -> typing.List[EntityEdge]: + ) -> AddEdgeResult: """ - Returns all edges for a graph. - Parameters ---------- - graph_id : str - Graph ID + graph_uuid : str + Graph UUID - 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. + attributes : typing.Optional[typing.Dict[str, typing.Any]] + + expired_at : typing.Optional[str] - direction : typing.Optional[str] - Sort direction. One of "asc" or "desc" (default "desc"). + fact : typing.Optional[str] - filters : typing.Optional[SearchFilters] - Optional filters applied to the listed artifacts. Reuses the graph.search filter type. + fact_name : typing.Optional[str] - limit : typing.Optional[int] - Maximum number of items to return + invalid_at : typing.Optional[str] - order_by : typing.Optional[str] - Field to sort by. One of "created_at", "valid_at", or "uuid" (default "uuid"). + metadata : typing.Optional[typing.Dict[str, typing.Any]] - uuid_cursor : typing.Optional[str] - UUID based cursor, used for pagination. Should be the UUID of the last item in the previous page. + source_node : typing.Optional[typing.Dict[str, typing.Any]] + + target_node : typing.Optional[typing.Dict[str, typing.Any]] + + valid_at : typing.Optional[str] - Deprecated: prefer Cursor, the opaque cursor returned via the Zep-Next-Cursor response header. + idempotency_key : typing.Optional[str] request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - typing.List[EntityEdge] - Edges + AddEdgeResult + Accepted Examples -------- @@ -84,70 +87,59 @@ def get_by_graph_id( client = Zep( api_key="YOUR_API_KEY", ) - client.graph.edge.get_by_graph_id( - graph_id="graph_id", + client.graph.edge.add( + graph_uuid="graph_uuid", ) """ - _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, + _response = self._raw_client.add( + graph_uuid, + attributes=attributes, + expired_at=expired_at, + fact=fact, + fact_name=fact_name, + invalid_at=invalid_at, + metadata=metadata, + source_node=source_node, + target_node=target_node, + valid_at=valid_at, + idempotency_key=idempotency_key, request_options=request_options, ) return _response.data - def get_by_user_id( + def list( self, - user_id: str, + graph_uuid: 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, + limit: typing.Optional[int] = None, + cursor: typing.Optional[str] = None, + filters: typing.Optional[typing.Dict[str, typing.Any]] = OMIT, + idempotency_key: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, - ) -> typing.List[EntityEdge]: + ) -> SyncPager[Edge, EdgePage]: """ - Returns all edges for a user. - Parameters ---------- - user_id : str - User 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. + graph_uuid : str + Graph UUID limit : typing.Optional[int] - Maximum number of items to return + Page size - order_by : typing.Optional[str] - Field to sort by. One of "created_at", "valid_at", or "uuid" (default "uuid"). + cursor : typing.Optional[str] + Opaque page cursor - uuid_cursor : typing.Optional[str] - UUID based cursor, used for pagination. Should be the UUID of the last item in the previous page. + filters : typing.Optional[typing.Dict[str, typing.Any]] - Deprecated: prefer Cursor, the opaque cursor returned via the Zep-Next-Cursor response header. + idempotency_key : typing.Optional[str] request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - typing.List[EntityEdge] - Edges + SyncPager[Edge, EdgePage] + OK Examples -------- @@ -156,29 +148,34 @@ def get_by_user_id( client = Zep( api_key="YOUR_API_KEY", ) - client.graph.edge.get_by_user_id( - user_id="user_id", + response = client.graph.edge.list( + graph_uuid="graph_uuid", + limit=1, + cursor="cursor", ) + for item in response: + yield item + # alternatively, you can paginate page-by-page + for page in response.iter_pages(): + yield page """ - _response = self._raw_client.get_by_user_id( - user_id, + return self._raw_client.list( + graph_uuid, + limit=limit, cursor=cursor, - direction=direction, filters=filters, - limit=limit, - order_by=order_by, - uuid_cursor=uuid_cursor, + idempotency_key=idempotency_key, request_options=request_options, ) - return _response.data - def get(self, uuid_: str, *, request_options: typing.Optional[RequestOptions] = None) -> EntityEdge: + def get(self, graph_uuid: str, edge_uuid: str, *, request_options: typing.Optional[RequestOptions] = None) -> Edge: """ - Returns a specific edge by its UUID. - Parameters ---------- - uuid_ : str + graph_uuid : str + Graph UUID + + edge_uuid : str Edge UUID request_options : typing.Optional[RequestOptions] @@ -186,8 +183,8 @@ def get(self, uuid_: str, *, request_options: typing.Optional[RequestOptions] = Returns ------- - EntityEdge - Edge + Edge + OK Examples -------- @@ -197,28 +194,39 @@ def get(self, uuid_: str, *, request_options: typing.Optional[RequestOptions] = api_key="YOUR_API_KEY", ) client.graph.edge.get( - uuid_="uuid", + graph_uuid="graph_uuid", + edge_uuid="edge_uuid", ) """ - _response = self._raw_client.get(uuid_, request_options=request_options) + _response = self._raw_client.get(graph_uuid, edge_uuid, request_options=request_options) return _response.data - def delete(self, uuid_: str, *, request_options: typing.Optional[RequestOptions] = None) -> SuccessResponse: + def delete( + self, + graph_uuid: str, + edge_uuid: str, + *, + idempotency_key: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncResult: """ - Deletes an edge by UUID. - Parameters ---------- - uuid_ : str + graph_uuid : str + Graph UUID + + edge_uuid : str Edge UUID + idempotency_key : typing.Optional[str] + request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - SuccessResponse - Edge deleted + AsyncResult + Accepted Examples -------- @@ -228,57 +236,48 @@ def delete(self, uuid_: str, *, request_options: typing.Optional[RequestOptions] api_key="YOUR_API_KEY", ) client.graph.edge.delete( - uuid_="uuid", + graph_uuid="graph_uuid", + edge_uuid="edge_uuid", ) """ - _response = self._raw_client.delete(uuid_, request_options=request_options) + _response = self._raw_client.delete( + graph_uuid, edge_uuid, idempotency_key=idempotency_key, request_options=request_options + ) return _response.data def update( self, - uuid_: str, + graph_uuid: str, + edge_uuid: str, *, - attributes: typing.Optional[typing.Dict[str, typing.Optional[typing.Any]]] = OMIT, - expired_at: typing.Optional[str] = OMIT, + attributes: typing.Optional[typing.Dict[str, typing.Any]] = OMIT, fact: typing.Optional[str] = OMIT, - invalid_at: typing.Optional[str] = OMIT, - name: typing.Optional[str] = OMIT, - valid_at: typing.Optional[str] = OMIT, + idempotency_key: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, - ) -> EntityEdge: + ) -> Edge: """ - Updates an entity edge by UUID. - Parameters ---------- - uuid_ : str + graph_uuid : str + Graph UUID + + edge_uuid : str Edge UUID - attributes : typing.Optional[typing.Dict[str, typing.Optional[typing.Any]]] - Updated attributes. Merged with existing attributes. Set a key to null to delete it. - - expired_at : typing.Optional[str] - Updated time at which the edge expires + attributes : typing.Optional[typing.Dict[str, typing.Any]] fact : typing.Optional[str] - Updated fact for the edge - - invalid_at : typing.Optional[str] - Updated time at which the fact stopped being true + Omit to leave unchanged, send JSON null to clear, or send a value to set. - name : typing.Optional[str] - Updated name (relationship type) for the edge - - valid_at : typing.Optional[str] - Updated time at which the fact becomes true + idempotency_key : typing.Optional[str] request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - EntityEdge - Updated edge + Edge + OK Examples -------- @@ -288,17 +287,16 @@ def update( api_key="YOUR_API_KEY", ) client.graph.edge.update( - uuid_="uuid", + graph_uuid="graph_uuid", + edge_uuid="edge_uuid", ) """ _response = self._raw_client.update( - uuid_, + graph_uuid, + edge_uuid, attributes=attributes, - expired_at=expired_at, fact=fact, - invalid_at=invalid_at, - name=name, - valid_at=valid_at, + idempotency_key=idempotency_key, request_options=request_options, ) return _response.data @@ -319,54 +317,55 @@ def with_raw_response(self) -> AsyncRawEdgeClient: """ return self._raw_client - async def get_by_graph_id( + async def add( self, - graph_id: str, + graph_uuid: 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, + attributes: typing.Optional[typing.Dict[str, typing.Any]] = OMIT, + expired_at: typing.Optional[str] = OMIT, + fact: typing.Optional[str] = OMIT, + fact_name: typing.Optional[str] = OMIT, + invalid_at: typing.Optional[str] = OMIT, + metadata: typing.Optional[typing.Dict[str, typing.Any]] = OMIT, + source_node: typing.Optional[typing.Dict[str, typing.Any]] = OMIT, + target_node: typing.Optional[typing.Dict[str, typing.Any]] = OMIT, + valid_at: typing.Optional[str] = OMIT, + idempotency_key: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, - ) -> typing.List[EntityEdge]: + ) -> AddEdgeResult: """ - Returns all edges for a graph. - Parameters ---------- - graph_id : str - Graph ID + graph_uuid : str + Graph UUID - 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. + attributes : typing.Optional[typing.Dict[str, typing.Any]] + + expired_at : typing.Optional[str] - direction : typing.Optional[str] - Sort direction. One of "asc" or "desc" (default "desc"). + fact : typing.Optional[str] - filters : typing.Optional[SearchFilters] - Optional filters applied to the listed artifacts. Reuses the graph.search filter type. + fact_name : typing.Optional[str] - limit : typing.Optional[int] - Maximum number of items to return + invalid_at : typing.Optional[str] - order_by : typing.Optional[str] - Field to sort by. One of "created_at", "valid_at", or "uuid" (default "uuid"). + metadata : typing.Optional[typing.Dict[str, typing.Any]] - uuid_cursor : typing.Optional[str] - UUID based cursor, used for pagination. Should be the UUID of the last item in the previous page. + source_node : typing.Optional[typing.Dict[str, typing.Any]] - Deprecated: prefer Cursor, the opaque cursor returned via the Zep-Next-Cursor response header. + target_node : typing.Optional[typing.Dict[str, typing.Any]] + + valid_at : typing.Optional[str] + + idempotency_key : typing.Optional[str] request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - typing.List[EntityEdge] - Edges + AddEdgeResult + Accepted Examples -------- @@ -380,73 +379,62 @@ async def get_by_graph_id( async def main() -> None: - await client.graph.edge.get_by_graph_id( - graph_id="graph_id", + await client.graph.edge.add( + graph_uuid="graph_uuid", ) 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, + _response = await self._raw_client.add( + graph_uuid, + attributes=attributes, + expired_at=expired_at, + fact=fact, + fact_name=fact_name, + invalid_at=invalid_at, + metadata=metadata, + source_node=source_node, + target_node=target_node, + valid_at=valid_at, + idempotency_key=idempotency_key, request_options=request_options, ) return _response.data - async def get_by_user_id( + async def list( self, - user_id: str, + graph_uuid: 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, + limit: typing.Optional[int] = None, + cursor: typing.Optional[str] = None, + filters: typing.Optional[typing.Dict[str, typing.Any]] = OMIT, + idempotency_key: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, - ) -> typing.List[EntityEdge]: + ) -> AsyncPager[Edge, EdgePage]: """ - Returns all edges for a user. - Parameters ---------- - user_id : str - User 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. + graph_uuid : str + Graph UUID limit : typing.Optional[int] - Maximum number of items to return + Page size - order_by : typing.Optional[str] - Field to sort by. One of "created_at", "valid_at", or "uuid" (default "uuid"). + cursor : typing.Optional[str] + Opaque page cursor - uuid_cursor : typing.Optional[str] - UUID based cursor, used for pagination. Should be the UUID of the last item in the previous page. + filters : typing.Optional[typing.Dict[str, typing.Any]] - Deprecated: prefer Cursor, the opaque cursor returned via the Zep-Next-Cursor response header. + idempotency_key : typing.Optional[str] request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - typing.List[EntityEdge] - Edges + AsyncPager[Edge, EdgePage] + OK Examples -------- @@ -460,32 +448,40 @@ async def get_by_user_id( async def main() -> None: - await client.graph.edge.get_by_user_id( - user_id="user_id", + response = await client.graph.edge.list( + graph_uuid="graph_uuid", + limit=1, + cursor="cursor", ) + async for item in response: + yield item + + # alternatively, you can paginate page-by-page + async for page in response.iter_pages(): + yield page asyncio.run(main()) """ - _response = await self._raw_client.get_by_user_id( - user_id, + return await self._raw_client.list( + graph_uuid, + limit=limit, cursor=cursor, - direction=direction, filters=filters, - limit=limit, - order_by=order_by, - uuid_cursor=uuid_cursor, + idempotency_key=idempotency_key, request_options=request_options, ) - return _response.data - async def get(self, uuid_: str, *, request_options: typing.Optional[RequestOptions] = None) -> EntityEdge: + async def get( + self, graph_uuid: str, edge_uuid: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> Edge: """ - Returns a specific edge by its UUID. - Parameters ---------- - uuid_ : str + graph_uuid : str + Graph UUID + + edge_uuid : str Edge UUID request_options : typing.Optional[RequestOptions] @@ -493,8 +489,8 @@ async def get(self, uuid_: str, *, request_options: typing.Optional[RequestOptio Returns ------- - EntityEdge - Edge + Edge + OK Examples -------- @@ -509,31 +505,42 @@ async def get(self, uuid_: str, *, request_options: typing.Optional[RequestOptio async def main() -> None: await client.graph.edge.get( - uuid_="uuid", + graph_uuid="graph_uuid", + edge_uuid="edge_uuid", ) asyncio.run(main()) """ - _response = await self._raw_client.get(uuid_, request_options=request_options) + _response = await self._raw_client.get(graph_uuid, edge_uuid, request_options=request_options) return _response.data - async def delete(self, uuid_: str, *, request_options: typing.Optional[RequestOptions] = None) -> SuccessResponse: + async def delete( + self, + graph_uuid: str, + edge_uuid: str, + *, + idempotency_key: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncResult: """ - Deletes an edge by UUID. - Parameters ---------- - uuid_ : str + graph_uuid : str + Graph UUID + + edge_uuid : str Edge UUID + idempotency_key : typing.Optional[str] + request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - SuccessResponse - Edge deleted + AsyncResult + Accepted Examples -------- @@ -548,60 +555,51 @@ async def delete(self, uuid_: str, *, request_options: typing.Optional[RequestOp async def main() -> None: await client.graph.edge.delete( - uuid_="uuid", + graph_uuid="graph_uuid", + edge_uuid="edge_uuid", ) asyncio.run(main()) """ - _response = await self._raw_client.delete(uuid_, request_options=request_options) + _response = await self._raw_client.delete( + graph_uuid, edge_uuid, idempotency_key=idempotency_key, request_options=request_options + ) return _response.data async def update( self, - uuid_: str, + graph_uuid: str, + edge_uuid: str, *, - attributes: typing.Optional[typing.Dict[str, typing.Optional[typing.Any]]] = OMIT, - expired_at: typing.Optional[str] = OMIT, + attributes: typing.Optional[typing.Dict[str, typing.Any]] = OMIT, fact: typing.Optional[str] = OMIT, - invalid_at: typing.Optional[str] = OMIT, - name: typing.Optional[str] = OMIT, - valid_at: typing.Optional[str] = OMIT, + idempotency_key: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, - ) -> EntityEdge: + ) -> Edge: """ - Updates an entity edge by UUID. - Parameters ---------- - uuid_ : str - Edge UUID + graph_uuid : str + Graph UUID - attributes : typing.Optional[typing.Dict[str, typing.Optional[typing.Any]]] - Updated attributes. Merged with existing attributes. Set a key to null to delete it. + edge_uuid : str + Edge UUID - expired_at : typing.Optional[str] - Updated time at which the edge expires + attributes : typing.Optional[typing.Dict[str, typing.Any]] fact : typing.Optional[str] - Updated fact for the edge + Omit to leave unchanged, send JSON null to clear, or send a value to set. - invalid_at : typing.Optional[str] - Updated time at which the fact stopped being true - - name : typing.Optional[str] - Updated name (relationship type) for the edge - - valid_at : typing.Optional[str] - Updated time at which the fact becomes true + idempotency_key : typing.Optional[str] request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - EntityEdge - Updated edge + Edge + OK Examples -------- @@ -616,20 +614,19 @@ async def update( async def main() -> None: await client.graph.edge.update( - uuid_="uuid", + graph_uuid="graph_uuid", + edge_uuid="edge_uuid", ) asyncio.run(main()) """ _response = await self._raw_client.update( - uuid_, + graph_uuid, + edge_uuid, attributes=attributes, - expired_at=expired_at, fact=fact, - invalid_at=invalid_at, - name=name, - valid_at=valid_at, + idempotency_key=idempotency_key, request_options=request_options, ) return _response.data diff --git a/src/zep_cloud/graph/edge/raw_client.py b/src/zep_cloud/graph/edge/raw_client.py index 4375c21b..f9062a76 100644 --- a/src/zep_cloud/graph/edge/raw_client.py +++ b/src/zep_cloud/graph/edge/raw_client.py @@ -7,16 +7,19 @@ from ...core.client_wrapper import AsyncClientWrapper, SyncClientWrapper from ...core.http_response import AsyncHttpResponse, HttpResponse from ...core.jsonable_encoder import jsonable_encoder +from ...core.pagination import AsyncPager, SyncPager +from ...core.parse_error import ParsingError 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 ...errors.unauthorized_error import UnauthorizedError +from ...types.add_edge_result import AddEdgeResult from ...types.api_error import ApiError as types_api_error_ApiError -from ...types.entity_edge import EntityEdge -from ...types.search_filters import SearchFilters -from ...types.success_response import SuccessResponse +from ...types.async_result import AsyncResult +from ...types.edge import Edge +from ...types.edge_page import EdgePage +from pydantic import ValidationError # this is used as the default value for optional parameters OMIT = typing.cast(typing.Any, ...) @@ -26,67 +29,73 @@ class RawEdgeClient: def __init__(self, *, client_wrapper: SyncClientWrapper): self._client_wrapper = client_wrapper - def get_by_graph_id( + def add( self, - graph_id: str, + graph_uuid: 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, + attributes: typing.Optional[typing.Dict[str, typing.Any]] = OMIT, + expired_at: typing.Optional[str] = OMIT, + fact: typing.Optional[str] = OMIT, + fact_name: typing.Optional[str] = OMIT, + invalid_at: typing.Optional[str] = OMIT, + metadata: typing.Optional[typing.Dict[str, typing.Any]] = OMIT, + source_node: typing.Optional[typing.Dict[str, typing.Any]] = OMIT, + target_node: typing.Optional[typing.Dict[str, typing.Any]] = OMIT, + valid_at: typing.Optional[str] = OMIT, + idempotency_key: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, - ) -> HttpResponse[typing.List[EntityEdge]]: + ) -> HttpResponse[AddEdgeResult]: """ - Returns all edges for a graph. - Parameters ---------- - graph_id : str - Graph ID + graph_uuid : str + Graph UUID - 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. + attributes : typing.Optional[typing.Dict[str, typing.Any]] - direction : typing.Optional[str] - Sort direction. One of "asc" or "desc" (default "desc"). + expired_at : typing.Optional[str] - filters : typing.Optional[SearchFilters] - Optional filters applied to the listed artifacts. Reuses the graph.search filter type. + fact : typing.Optional[str] - limit : typing.Optional[int] - Maximum number of items to return + fact_name : typing.Optional[str] + + invalid_at : typing.Optional[str] - order_by : typing.Optional[str] - Field to sort by. One of "created_at", "valid_at", or "uuid" (default "uuid"). + metadata : typing.Optional[typing.Dict[str, typing.Any]] - uuid_cursor : typing.Optional[str] - UUID based cursor, used for pagination. Should be the UUID of the last item in the previous page. + source_node : typing.Optional[typing.Dict[str, typing.Any]] - Deprecated: prefer Cursor, the opaque cursor returned via the Zep-Next-Cursor response header. + target_node : typing.Optional[typing.Dict[str, typing.Any]] + + valid_at : typing.Optional[str] + + idempotency_key : typing.Optional[str] request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - HttpResponse[typing.List[EntityEdge]] - Edges + HttpResponse[AddEdgeResult] + Accepted """ _response = self._client_wrapper.httpx_client.request( - f"graph/edge/graph/{jsonable_encoder(graph_id)}", + f"graphs/{jsonable_encoder(graph_uuid)}/edges", 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, + "attributes": attributes, + "expired_at": expired_at, + "fact": fact, + "fact_name": fact_name, + "invalid_at": invalid_at, + "metadata": metadata, + "source_node": source_node, + "target_node": target_node, + "valid_at": valid_at, + }, + headers={ + "content-type": "application/json", + "Idempotency-Key": str(idempotency_key) if idempotency_key is not None else None, }, request_options=request_options, omit=OMIT, @@ -94,9 +103,9 @@ def get_by_graph_id( try: if 200 <= _response.status_code < 300: _data = typing.cast( - typing.List[EntityEdge], + AddEdgeResult, parse_obj_as( - type_=typing.List[EntityEdge], # type: ignore + type_=AddEdgeResult, # type: ignore object_=_response.json(), ), ) @@ -105,15 +114,26 @@ def get_by_graph_id( raise BadRequestError( headers=dict(_response.headers), body=typing.cast( - typing.Optional[typing.Any], + types_api_error_ApiError, parse_obj_as( - type_=typing.Optional[typing.Any], # type: ignore + type_=types_api_error_ApiError, # type: ignore object_=_response.json(), ), ), ) - if _response.status_code == 500: - raise InternalServerError( + if _response.status_code == 401: + raise UnauthorizedError( + 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, @@ -128,98 +148,110 @@ def get_by_graph_id( raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response.text ) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response_json ) - def get_by_user_id( + def list( self, - user_id: str, + graph_uuid: 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, + limit: typing.Optional[int] = None, + cursor: typing.Optional[str] = None, + filters: typing.Optional[typing.Dict[str, typing.Any]] = OMIT, + idempotency_key: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, - ) -> HttpResponse[typing.List[EntityEdge]]: + ) -> SyncPager[Edge, EdgePage]: """ - Returns all edges for a user. - Parameters ---------- - user_id : str - User 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. + graph_uuid : str + Graph UUID limit : typing.Optional[int] - Maximum number of items to return + Page size - order_by : typing.Optional[str] - Field to sort by. One of "created_at", "valid_at", or "uuid" (default "uuid"). + cursor : typing.Optional[str] + Opaque page cursor - uuid_cursor : typing.Optional[str] - UUID based cursor, used for pagination. Should be the UUID of the last item in the previous page. + filters : typing.Optional[typing.Dict[str, typing.Any]] - Deprecated: prefer Cursor, the opaque cursor returned via the Zep-Next-Cursor response header. + idempotency_key : typing.Optional[str] request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - HttpResponse[typing.List[EntityEdge]] - Edges + SyncPager[Edge, EdgePage] + OK """ _response = self._client_wrapper.httpx_client.request( - f"graph/edge/user/{jsonable_encoder(user_id)}", + f"graphs/{jsonable_encoder(graph_uuid)}/edges/list", method="POST", - json={ - "cursor": cursor, - "direction": direction, - "filters": convert_and_respect_annotation_metadata( - object_=filters, annotation=SearchFilters, direction="write" - ), + params={ "limit": limit, - "order_by": order_by, - "uuid_cursor": uuid_cursor, + "cursor": cursor, + }, + json={ + "filters": filters, + }, + headers={ + "content-type": "application/json", + "Idempotency-Key": str(idempotency_key) if idempotency_key is not None else None, }, request_options=request_options, omit=OMIT, ) try: if 200 <= _response.status_code < 300: - _data = typing.cast( - typing.List[EntityEdge], + _parsed_response = typing.cast( + EdgePage, parse_obj_as( - type_=typing.List[EntityEdge], # type: ignore + type_=EdgePage, # type: ignore object_=_response.json(), ), ) - return HttpResponse(response=_response, data=_data) + _items = _parsed_response.items + _parsed_next = _parsed_response.next_cursor + _has_next = _parsed_next is not None and _parsed_next != "" + _get_next = lambda: self.list( + graph_uuid, + limit=limit, + cursor=_parsed_next, + filters=filters, + idempotency_key=idempotency_key, + request_options=request_options, + ) + return SyncPager(has_next=_has_next, items=_items, get_next=_get_next, response=_parsed_response) if _response.status_code == 400: raise BadRequestError( headers=dict(_response.headers), body=typing.cast( - typing.Optional[typing.Any], + types_api_error_ApiError, + parse_obj_as( + type_=types_api_error_ApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 401: + raise UnauthorizedError( + headers=dict(_response.headers), + body=typing.cast( + types_api_error_ApiError, parse_obj_as( - type_=typing.Optional[typing.Any], # type: ignore + type_=types_api_error_ApiError, # type: ignore object_=_response.json(), ), ), ) - if _response.status_code == 500: - raise InternalServerError( + if _response.status_code == 404: + raise NotFoundError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -234,17 +266,24 @@ def get_by_user_id( raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response.text ) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response_json ) - def get(self, uuid_: str, *, request_options: typing.Optional[RequestOptions] = None) -> HttpResponse[EntityEdge]: + def get( + self, graph_uuid: str, edge_uuid: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[Edge]: """ - Returns a specific edge by its UUID. - Parameters ---------- - uuid_ : str + graph_uuid : str + Graph UUID + + edge_uuid : str Edge UUID request_options : typing.Optional[RequestOptions] @@ -252,20 +291,20 @@ def get(self, uuid_: str, *, request_options: typing.Optional[RequestOptions] = Returns ------- - HttpResponse[EntityEdge] - Edge + HttpResponse[Edge] + OK """ _response = self._client_wrapper.httpx_client.request( - f"graph/edge/{jsonable_encoder(uuid_)}", + f"graphs/{jsonable_encoder(graph_uuid)}/edges/{jsonable_encoder(edge_uuid)}", method="GET", request_options=request_options, ) try: if 200 <= _response.status_code < 300: _data = typing.cast( - EntityEdge, + Edge, parse_obj_as( - type_=EntityEdge, # type: ignore + type_=Edge, # type: ignore object_=_response.json(), ), ) @@ -274,15 +313,15 @@ def get(self, uuid_: str, *, request_options: typing.Optional[RequestOptions] = raise BadRequestError( headers=dict(_response.headers), body=typing.cast( - typing.Optional[typing.Any], + types_api_error_ApiError, parse_obj_as( - type_=typing.Optional[typing.Any], # type: ignore + type_=types_api_error_ApiError, # type: ignore object_=_response.json(), ), ), ) - if _response.status_code == 404: - raise NotFoundError( + if _response.status_code == 401: + raise UnauthorizedError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -292,8 +331,8 @@ def get(self, uuid_: str, *, request_options: typing.Optional[RequestOptions] = ), ), ) - if _response.status_code == 500: - raise InternalServerError( + if _response.status_code == 404: + raise NotFoundError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -308,40 +347,55 @@ def get(self, uuid_: str, *, request_options: typing.Optional[RequestOptions] = raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response.text ) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response_json ) def delete( - self, uuid_: str, *, request_options: typing.Optional[RequestOptions] = None - ) -> HttpResponse[SuccessResponse]: + self, + graph_uuid: str, + edge_uuid: str, + *, + idempotency_key: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[AsyncResult]: """ - Deletes an edge by UUID. - Parameters ---------- - uuid_ : str + graph_uuid : str + Graph UUID + + edge_uuid : str Edge UUID + idempotency_key : typing.Optional[str] + request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - HttpResponse[SuccessResponse] - Edge deleted + HttpResponse[AsyncResult] + Accepted """ _response = self._client_wrapper.httpx_client.request( - f"graph/edge/{jsonable_encoder(uuid_)}", + f"graphs/{jsonable_encoder(graph_uuid)}/edges/{jsonable_encoder(edge_uuid)}", method="DELETE", + headers={ + "Idempotency-Key": str(idempotency_key) if idempotency_key is not None else None, + }, request_options=request_options, ) try: if 200 <= _response.status_code < 300: _data = typing.cast( - SuccessResponse, + AsyncResult, parse_obj_as( - type_=SuccessResponse, # type: ignore + type_=AsyncResult, # type: ignore object_=_response.json(), ), ) @@ -350,15 +404,15 @@ def delete( raise BadRequestError( headers=dict(_response.headers), body=typing.cast( - typing.Optional[typing.Any], + types_api_error_ApiError, parse_obj_as( - type_=typing.Optional[typing.Any], # type: ignore + type_=types_api_error_ApiError, # type: ignore object_=_response.json(), ), ), ) - if _response.status_code == 404: - raise NotFoundError( + if _response.status_code == 401: + raise UnauthorizedError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -368,8 +422,8 @@ def delete( ), ), ) - if _response.status_code == 500: - raise InternalServerError( + if _response.status_code == 404: + raise NotFoundError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -384,69 +438,58 @@ def delete( raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response.text ) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response_json ) def update( self, - uuid_: str, + graph_uuid: str, + edge_uuid: str, *, - attributes: typing.Optional[typing.Dict[str, typing.Optional[typing.Any]]] = OMIT, - expired_at: typing.Optional[str] = OMIT, + attributes: typing.Optional[typing.Dict[str, typing.Any]] = OMIT, fact: typing.Optional[str] = OMIT, - invalid_at: typing.Optional[str] = OMIT, - name: typing.Optional[str] = OMIT, - valid_at: typing.Optional[str] = OMIT, + idempotency_key: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, - ) -> HttpResponse[EntityEdge]: + ) -> HttpResponse[Edge]: """ - Updates an entity edge by UUID. - Parameters ---------- - uuid_ : str - Edge UUID + graph_uuid : str + Graph UUID - attributes : typing.Optional[typing.Dict[str, typing.Optional[typing.Any]]] - Updated attributes. Merged with existing attributes. Set a key to null to delete it. + edge_uuid : str + Edge UUID - expired_at : typing.Optional[str] - Updated time at which the edge expires + attributes : typing.Optional[typing.Dict[str, typing.Any]] fact : typing.Optional[str] - Updated fact for the edge + Omit to leave unchanged, send JSON null to clear, or send a value to set. - invalid_at : typing.Optional[str] - Updated time at which the fact stopped being true - - name : typing.Optional[str] - Updated name (relationship type) for the edge - - valid_at : typing.Optional[str] - Updated time at which the fact becomes true + idempotency_key : typing.Optional[str] request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - HttpResponse[EntityEdge] - Updated edge + HttpResponse[Edge] + OK """ _response = self._client_wrapper.httpx_client.request( - f"graph/edge/{jsonable_encoder(uuid_)}", + f"graphs/{jsonable_encoder(graph_uuid)}/edges/{jsonable_encoder(edge_uuid)}", method="PATCH", json={ "attributes": attributes, - "expired_at": expired_at, "fact": fact, - "invalid_at": invalid_at, - "name": name, - "valid_at": valid_at, }, headers={ "content-type": "application/json", + "Idempotency-Key": str(idempotency_key) if idempotency_key is not None else None, }, request_options=request_options, omit=OMIT, @@ -454,9 +497,9 @@ def update( try: if 200 <= _response.status_code < 300: _data = typing.cast( - EntityEdge, + Edge, parse_obj_as( - type_=EntityEdge, # type: ignore + type_=Edge, # type: ignore object_=_response.json(), ), ) @@ -465,15 +508,15 @@ def update( raise BadRequestError( headers=dict(_response.headers), body=typing.cast( - typing.Optional[typing.Any], + types_api_error_ApiError, parse_obj_as( - type_=typing.Optional[typing.Any], # type: ignore + type_=types_api_error_ApiError, # type: ignore object_=_response.json(), ), ), ) - if _response.status_code == 404: - raise NotFoundError( + if _response.status_code == 401: + raise UnauthorizedError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -483,8 +526,8 @@ def update( ), ), ) - if _response.status_code == 500: - raise InternalServerError( + if _response.status_code == 404: + raise NotFoundError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -499,6 +542,10 @@ def update( raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response.text ) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response_json ) @@ -508,67 +555,73 @@ class AsyncRawEdgeClient: def __init__(self, *, client_wrapper: AsyncClientWrapper): self._client_wrapper = client_wrapper - async def get_by_graph_id( + async def add( self, - graph_id: str, + graph_uuid: 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, + attributes: typing.Optional[typing.Dict[str, typing.Any]] = OMIT, + expired_at: typing.Optional[str] = OMIT, + fact: typing.Optional[str] = OMIT, + fact_name: typing.Optional[str] = OMIT, + invalid_at: typing.Optional[str] = OMIT, + metadata: typing.Optional[typing.Dict[str, typing.Any]] = OMIT, + source_node: typing.Optional[typing.Dict[str, typing.Any]] = OMIT, + target_node: typing.Optional[typing.Dict[str, typing.Any]] = OMIT, + valid_at: typing.Optional[str] = OMIT, + idempotency_key: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, - ) -> AsyncHttpResponse[typing.List[EntityEdge]]: + ) -> AsyncHttpResponse[AddEdgeResult]: """ - Returns all edges for a graph. - Parameters ---------- - graph_id : str - Graph ID + graph_uuid : str + Graph UUID - 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. + attributes : typing.Optional[typing.Dict[str, typing.Any]] - direction : typing.Optional[str] - Sort direction. One of "asc" or "desc" (default "desc"). + expired_at : typing.Optional[str] - filters : typing.Optional[SearchFilters] - Optional filters applied to the listed artifacts. Reuses the graph.search filter type. + fact : typing.Optional[str] - limit : typing.Optional[int] - Maximum number of items to return + fact_name : typing.Optional[str] + + invalid_at : typing.Optional[str] - order_by : typing.Optional[str] - Field to sort by. One of "created_at", "valid_at", or "uuid" (default "uuid"). + metadata : typing.Optional[typing.Dict[str, typing.Any]] - uuid_cursor : typing.Optional[str] - UUID based cursor, used for pagination. Should be the UUID of the last item in the previous page. + source_node : typing.Optional[typing.Dict[str, typing.Any]] - Deprecated: prefer Cursor, the opaque cursor returned via the Zep-Next-Cursor response header. + target_node : typing.Optional[typing.Dict[str, typing.Any]] + + valid_at : typing.Optional[str] + + idempotency_key : typing.Optional[str] request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - AsyncHttpResponse[typing.List[EntityEdge]] - Edges + AsyncHttpResponse[AddEdgeResult] + Accepted """ _response = await self._client_wrapper.httpx_client.request( - f"graph/edge/graph/{jsonable_encoder(graph_id)}", + f"graphs/{jsonable_encoder(graph_uuid)}/edges", 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, + "attributes": attributes, + "expired_at": expired_at, + "fact": fact, + "fact_name": fact_name, + "invalid_at": invalid_at, + "metadata": metadata, + "source_node": source_node, + "target_node": target_node, + "valid_at": valid_at, + }, + headers={ + "content-type": "application/json", + "Idempotency-Key": str(idempotency_key) if idempotency_key is not None else None, }, request_options=request_options, omit=OMIT, @@ -576,9 +629,9 @@ async def get_by_graph_id( try: if 200 <= _response.status_code < 300: _data = typing.cast( - typing.List[EntityEdge], + AddEdgeResult, parse_obj_as( - type_=typing.List[EntityEdge], # type: ignore + type_=AddEdgeResult, # type: ignore object_=_response.json(), ), ) @@ -587,15 +640,26 @@ async def get_by_graph_id( raise BadRequestError( headers=dict(_response.headers), body=typing.cast( - typing.Optional[typing.Any], + types_api_error_ApiError, parse_obj_as( - type_=typing.Optional[typing.Any], # type: ignore + type_=types_api_error_ApiError, # type: ignore object_=_response.json(), ), ), ) - if _response.status_code == 500: - raise InternalServerError( + if _response.status_code == 401: + raise UnauthorizedError( + 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, @@ -610,98 +674,113 @@ async def get_by_graph_id( raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response.text ) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response_json ) - async def get_by_user_id( + async def list( self, - user_id: str, + graph_uuid: 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, + limit: typing.Optional[int] = None, + cursor: typing.Optional[str] = None, + filters: typing.Optional[typing.Dict[str, typing.Any]] = OMIT, + idempotency_key: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, - ) -> AsyncHttpResponse[typing.List[EntityEdge]]: + ) -> AsyncPager[Edge, EdgePage]: """ - Returns all edges for a user. - Parameters ---------- - user_id : str - User 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. + graph_uuid : str + Graph UUID limit : typing.Optional[int] - Maximum number of items to return + Page size - order_by : typing.Optional[str] - Field to sort by. One of "created_at", "valid_at", or "uuid" (default "uuid"). + cursor : typing.Optional[str] + Opaque page cursor - uuid_cursor : typing.Optional[str] - UUID based cursor, used for pagination. Should be the UUID of the last item in the previous page. + filters : typing.Optional[typing.Dict[str, typing.Any]] - Deprecated: prefer Cursor, the opaque cursor returned via the Zep-Next-Cursor response header. + idempotency_key : typing.Optional[str] request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - AsyncHttpResponse[typing.List[EntityEdge]] - Edges + AsyncPager[Edge, EdgePage] + OK """ _response = await self._client_wrapper.httpx_client.request( - f"graph/edge/user/{jsonable_encoder(user_id)}", + f"graphs/{jsonable_encoder(graph_uuid)}/edges/list", method="POST", - json={ - "cursor": cursor, - "direction": direction, - "filters": convert_and_respect_annotation_metadata( - object_=filters, annotation=SearchFilters, direction="write" - ), + params={ "limit": limit, - "order_by": order_by, - "uuid_cursor": uuid_cursor, + "cursor": cursor, + }, + json={ + "filters": filters, + }, + headers={ + "content-type": "application/json", + "Idempotency-Key": str(idempotency_key) if idempotency_key is not None else None, }, request_options=request_options, omit=OMIT, ) try: if 200 <= _response.status_code < 300: - _data = typing.cast( - typing.List[EntityEdge], + _parsed_response = typing.cast( + EdgePage, parse_obj_as( - type_=typing.List[EntityEdge], # type: ignore + type_=EdgePage, # type: ignore object_=_response.json(), ), ) - return AsyncHttpResponse(response=_response, data=_data) + _items = _parsed_response.items + _parsed_next = _parsed_response.next_cursor + _has_next = _parsed_next is not None and _parsed_next != "" + + async def _get_next(): + return await self.list( + graph_uuid, + limit=limit, + cursor=_parsed_next, + filters=filters, + idempotency_key=idempotency_key, + request_options=request_options, + ) + + return AsyncPager(has_next=_has_next, items=_items, get_next=_get_next, response=_parsed_response) if _response.status_code == 400: raise BadRequestError( headers=dict(_response.headers), body=typing.cast( - typing.Optional[typing.Any], + types_api_error_ApiError, parse_obj_as( - type_=typing.Optional[typing.Any], # type: ignore + type_=types_api_error_ApiError, # type: ignore object_=_response.json(), ), ), ) - if _response.status_code == 500: - raise InternalServerError( + if _response.status_code == 401: + raise UnauthorizedError( + 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, @@ -716,19 +795,24 @@ async def get_by_user_id( raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response.text ) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response_json ) async def get( - self, uuid_: str, *, request_options: typing.Optional[RequestOptions] = None - ) -> AsyncHttpResponse[EntityEdge]: + self, graph_uuid: str, edge_uuid: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[Edge]: """ - Returns a specific edge by its UUID. - Parameters ---------- - uuid_ : str + graph_uuid : str + Graph UUID + + edge_uuid : str Edge UUID request_options : typing.Optional[RequestOptions] @@ -736,20 +820,20 @@ async def get( Returns ------- - AsyncHttpResponse[EntityEdge] - Edge + AsyncHttpResponse[Edge] + OK """ _response = await self._client_wrapper.httpx_client.request( - f"graph/edge/{jsonable_encoder(uuid_)}", + f"graphs/{jsonable_encoder(graph_uuid)}/edges/{jsonable_encoder(edge_uuid)}", method="GET", request_options=request_options, ) try: if 200 <= _response.status_code < 300: _data = typing.cast( - EntityEdge, + Edge, parse_obj_as( - type_=EntityEdge, # type: ignore + type_=Edge, # type: ignore object_=_response.json(), ), ) @@ -758,15 +842,15 @@ async def get( raise BadRequestError( headers=dict(_response.headers), body=typing.cast( - typing.Optional[typing.Any], + types_api_error_ApiError, parse_obj_as( - type_=typing.Optional[typing.Any], # type: ignore + type_=types_api_error_ApiError, # type: ignore object_=_response.json(), ), ), ) - if _response.status_code == 404: - raise NotFoundError( + if _response.status_code == 401: + raise UnauthorizedError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -776,8 +860,8 @@ async def get( ), ), ) - if _response.status_code == 500: - raise InternalServerError( + if _response.status_code == 404: + raise NotFoundError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -792,40 +876,55 @@ async def get( raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response.text ) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response_json ) async def delete( - self, uuid_: str, *, request_options: typing.Optional[RequestOptions] = None - ) -> AsyncHttpResponse[SuccessResponse]: + self, + graph_uuid: str, + edge_uuid: str, + *, + idempotency_key: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[AsyncResult]: """ - Deletes an edge by UUID. - Parameters ---------- - uuid_ : str + graph_uuid : str + Graph UUID + + edge_uuid : str Edge UUID + idempotency_key : typing.Optional[str] + request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - AsyncHttpResponse[SuccessResponse] - Edge deleted + AsyncHttpResponse[AsyncResult] + Accepted """ _response = await self._client_wrapper.httpx_client.request( - f"graph/edge/{jsonable_encoder(uuid_)}", + f"graphs/{jsonable_encoder(graph_uuid)}/edges/{jsonable_encoder(edge_uuid)}", method="DELETE", + headers={ + "Idempotency-Key": str(idempotency_key) if idempotency_key is not None else None, + }, request_options=request_options, ) try: if 200 <= _response.status_code < 300: _data = typing.cast( - SuccessResponse, + AsyncResult, parse_obj_as( - type_=SuccessResponse, # type: ignore + type_=AsyncResult, # type: ignore object_=_response.json(), ), ) @@ -834,15 +933,15 @@ async def delete( raise BadRequestError( headers=dict(_response.headers), body=typing.cast( - typing.Optional[typing.Any], + types_api_error_ApiError, parse_obj_as( - type_=typing.Optional[typing.Any], # type: ignore + type_=types_api_error_ApiError, # type: ignore object_=_response.json(), ), ), ) - if _response.status_code == 404: - raise NotFoundError( + if _response.status_code == 401: + raise UnauthorizedError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -852,8 +951,8 @@ async def delete( ), ), ) - if _response.status_code == 500: - raise InternalServerError( + if _response.status_code == 404: + raise NotFoundError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -868,69 +967,58 @@ async def delete( raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response.text ) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response_json ) async def update( self, - uuid_: str, + graph_uuid: str, + edge_uuid: str, *, - attributes: typing.Optional[typing.Dict[str, typing.Optional[typing.Any]]] = OMIT, - expired_at: typing.Optional[str] = OMIT, + attributes: typing.Optional[typing.Dict[str, typing.Any]] = OMIT, fact: typing.Optional[str] = OMIT, - invalid_at: typing.Optional[str] = OMIT, - name: typing.Optional[str] = OMIT, - valid_at: typing.Optional[str] = OMIT, + idempotency_key: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, - ) -> AsyncHttpResponse[EntityEdge]: + ) -> AsyncHttpResponse[Edge]: """ - Updates an entity edge by UUID. - Parameters ---------- - uuid_ : str - Edge UUID + graph_uuid : str + Graph UUID - attributes : typing.Optional[typing.Dict[str, typing.Optional[typing.Any]]] - Updated attributes. Merged with existing attributes. Set a key to null to delete it. + edge_uuid : str + Edge UUID - expired_at : typing.Optional[str] - Updated time at which the edge expires + attributes : typing.Optional[typing.Dict[str, typing.Any]] fact : typing.Optional[str] - Updated fact for the edge + Omit to leave unchanged, send JSON null to clear, or send a value to set. - invalid_at : typing.Optional[str] - Updated time at which the fact stopped being true - - name : typing.Optional[str] - Updated name (relationship type) for the edge - - valid_at : typing.Optional[str] - Updated time at which the fact becomes true + idempotency_key : typing.Optional[str] request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - AsyncHttpResponse[EntityEdge] - Updated edge + AsyncHttpResponse[Edge] + OK """ _response = await self._client_wrapper.httpx_client.request( - f"graph/edge/{jsonable_encoder(uuid_)}", + f"graphs/{jsonable_encoder(graph_uuid)}/edges/{jsonable_encoder(edge_uuid)}", method="PATCH", json={ "attributes": attributes, - "expired_at": expired_at, "fact": fact, - "invalid_at": invalid_at, - "name": name, - "valid_at": valid_at, }, headers={ "content-type": "application/json", + "Idempotency-Key": str(idempotency_key) if idempotency_key is not None else None, }, request_options=request_options, omit=OMIT, @@ -938,9 +1026,9 @@ async def update( try: if 200 <= _response.status_code < 300: _data = typing.cast( - EntityEdge, + Edge, parse_obj_as( - type_=EntityEdge, # type: ignore + type_=Edge, # type: ignore object_=_response.json(), ), ) @@ -949,15 +1037,15 @@ async def update( raise BadRequestError( headers=dict(_response.headers), body=typing.cast( - typing.Optional[typing.Any], + types_api_error_ApiError, parse_obj_as( - type_=typing.Optional[typing.Any], # type: ignore + type_=types_api_error_ApiError, # type: ignore object_=_response.json(), ), ), ) - if _response.status_code == 404: - raise NotFoundError( + if _response.status_code == 401: + raise UnauthorizedError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -967,8 +1055,8 @@ async def update( ), ), ) - if _response.status_code == 500: - raise InternalServerError( + if _response.status_code == 404: + raise NotFoundError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -983,6 +1071,10 @@ async def update( raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response.text ) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) 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..f27053d3 100644 --- a/src/zep_cloud/graph/episode/client.py +++ b/src/zep_cloud/graph/episode/client.py @@ -3,11 +3,12 @@ import typing from ...core.client_wrapper import AsyncClientWrapper, SyncClientWrapper +from ...core.pagination import AsyncPager, SyncPager from ...core.request_options import RequestOptions +from ...types.add_episode_result import AddEpisodeResult +from ...types.async_result import AsyncResult from ...types.episode import Episode -from ...types.episode_mentions import EpisodeMentions -from ...types.episode_response import EpisodeResponse -from ...types.success_response import SuccessResponse +from ...types.episode_page import EpisodePage from .raw_client import AsyncRawEpisodeClient, RawEpisodeClient # this is used as the default value for optional parameters @@ -29,31 +30,37 @@ def with_raw_response(self) -> RawEpisodeClient: """ return self._raw_client - def get_by_graph_id( + def list_for_document( self, - graph_id: str, + graph_uuid: str, + document_id: str, *, - lastn: typing.Optional[int] = None, + limit: typing.Optional[int] = None, + cursor: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, - ) -> EpisodeResponse: + ) -> SyncPager[Episode, EpisodePage]: """ - Returns episodes by graph id. - Parameters ---------- - graph_id : str - Graph ID + graph_uuid : str + Graph UUID + + document_id : str + Document ID + + limit : typing.Optional[int] + Page size - lastn : typing.Optional[int] - The number of most recent episodes to retrieve. + cursor : typing.Optional[str] + Opaque page cursor request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - EpisodeResponse - Episodes + SyncPager[Episode, EpisodePage] + OK Examples -------- @@ -62,106 +69,65 @@ def get_by_graph_id( client = Zep( api_key="YOUR_API_KEY", ) - client.graph.episode.get_by_graph_id( - graph_id="graph_id", - lastn=1, + response = client.graph.episode.list_for_document( + graph_uuid="graph_uuid", + document_id="document_id", + limit=1, + cursor="cursor", + ) + for item in response: + yield item + # alternatively, you can paginate page-by-page + for page in response.iter_pages(): + yield page + """ + return self._raw_client.list_for_document( + graph_uuid, document_id, limit=limit, cursor=cursor, request_options=request_options ) - """ - _response = self._raw_client.get_by_graph_id(graph_id, lastn=lastn, request_options=request_options) - return _response.data - def list_by_graph_id( + def add( self, - graph_id: str, + graph_uuid: str, *, - cursor: typing.Optional[str] = OMIT, - direction: typing.Optional[str] = OMIT, - limit: typing.Optional[int] = OMIT, - mentioned_node_uuids: typing.Optional[typing.Sequence[str]] = OMIT, - order_by: typing.Optional[str] = OMIT, + created_at: typing.Optional[str] = OMIT, + data: typing.Optional[str] = OMIT, + document_id: typing.Optional[str] = OMIT, + metadata: typing.Optional[typing.Dict[str, typing.Any]] = OMIT, + source_description: typing.Optional[str] = OMIT, + strict_ontology: typing.Optional[bool] = OMIT, + type: typing.Optional[str] = OMIT, + idempotency_key: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, - ) -> typing.List[Episode]: + ) -> AddEpisodeResult: """ - Returns a paginated, filterable list of episodes for a graph. - Parameters ---------- - graph_id : str - Graph ID + graph_uuid : str + Graph UUID - cursor : typing.Optional[str] - Opaque cursor for pagination, obtained from the Zep-Next-Cursor - response header of the previous page. + created_at : typing.Optional[str] - direction : typing.Optional[str] - Sort direction. One of "asc" or "desc". Defaults to "desc". + data : typing.Optional[str] - 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. + document_id : typing.Optional[str] - 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. + metadata : typing.Optional[typing.Dict[str, typing.Any]] - order_by : typing.Optional[str] - Field to sort by. One of "uuid" or "created_at". Defaults to "uuid". + source_description : typing.Optional[str] - request_options : typing.Optional[RequestOptions] - Request-specific configuration. + strict_ontology : typing.Optional[bool] - Returns - ------- - typing.List[Episode] - Episodes + type : typing.Optional[str] - Examples - -------- - from zep_cloud import Zep - - client = Zep( - api_key="YOUR_API_KEY", - ) - client.graph.episode.list_by_graph_id( - graph_id="graph_id", - ) - """ - _response = self._raw_client.list_by_graph_id( - graph_id, - cursor=cursor, - direction=direction, - limit=limit, - mentioned_node_uuids=mentioned_node_uuids, - order_by=order_by, - request_options=request_options, - ) - return _response.data - - def get_by_user_id( - self, - user_id: str, - *, - lastn: typing.Optional[int] = None, - request_options: typing.Optional[RequestOptions] = None, - ) -> EpisodeResponse: - """ - Returns episodes by user id. - - Parameters - ---------- - user_id : str - User ID - - lastn : typing.Optional[int] - The number of most recent episodes entries to retrieve. + idempotency_key : typing.Optional[str] request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - EpisodeResponse - Episodes + AddEpisodeResult + Accepted Examples -------- @@ -170,58 +136,57 @@ def get_by_user_id( client = Zep( api_key="YOUR_API_KEY", ) - client.graph.episode.get_by_user_id( - user_id="user_id", - lastn=1, + client.graph.episode.add( + graph_uuid="graph_uuid", ) """ - _response = self._raw_client.get_by_user_id(user_id, lastn=lastn, request_options=request_options) + _response = self._raw_client.add( + graph_uuid, + created_at=created_at, + data=data, + document_id=document_id, + metadata=metadata, + source_description=source_description, + strict_ontology=strict_ontology, + type=type, + idempotency_key=idempotency_key, + request_options=request_options, + ) return _response.data - def list_by_user_id( + def list( self, - user_id: str, + graph_uuid: str, *, - cursor: typing.Optional[str] = OMIT, - direction: typing.Optional[str] = OMIT, - limit: typing.Optional[int] = OMIT, - mentioned_node_uuids: typing.Optional[typing.Sequence[str]] = OMIT, - order_by: typing.Optional[str] = OMIT, + limit: typing.Optional[int] = None, + cursor: typing.Optional[str] = None, + filters: typing.Optional[typing.Dict[str, typing.Any]] = OMIT, + idempotency_key: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, - ) -> typing.List[Episode]: + ) -> SyncPager[Episode, EpisodePage]: """ - Returns a paginated, filterable list of episodes for a user's graph. - Parameters ---------- - user_id : str - User ID - - cursor : typing.Optional[str] - Opaque cursor for pagination, obtained from the Zep-Next-Cursor - response header of the previous page. - - direction : typing.Optional[str] - Sort direction. One of "asc" or "desc". Defaults to "desc". + graph_uuid : str + Graph UUID 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. + Page size - 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. + cursor : typing.Optional[str] + Opaque page cursor - order_by : typing.Optional[str] - Field to sort by. One of "uuid" or "created_at". Defaults to "uuid". + filters : typing.Optional[typing.Dict[str, typing.Any]] + + idempotency_key : typing.Optional[str] request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - typing.List[Episode] - Episodes + SyncPager[Episode, EpisodePage] + OK Examples -------- @@ -230,28 +195,36 @@ def list_by_user_id( client = Zep( api_key="YOUR_API_KEY", ) - client.graph.episode.list_by_user_id( - user_id="user_id", + response = client.graph.episode.list( + graph_uuid="graph_uuid", + limit=1, + cursor="cursor", ) - """ - _response = self._raw_client.list_by_user_id( - user_id, - cursor=cursor, - direction=direction, + for item in response: + yield item + # alternatively, you can paginate page-by-page + for page in response.iter_pages(): + yield page + """ + return self._raw_client.list( + graph_uuid, limit=limit, - mentioned_node_uuids=mentioned_node_uuids, - order_by=order_by, + cursor=cursor, + filters=filters, + idempotency_key=idempotency_key, request_options=request_options, ) - return _response.data - def get(self, uuid_: str, *, request_options: typing.Optional[RequestOptions] = None) -> Episode: + def get( + self, graph_uuid: str, episode_uuid: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> Episode: """ - Returns episodes by UUID - Parameters ---------- - uuid_ : str + graph_uuid : str + Graph UUID + + episode_uuid : str Episode UUID request_options : typing.Optional[RequestOptions] @@ -260,7 +233,7 @@ def get(self, uuid_: str, *, request_options: typing.Optional[RequestOptions] = Returns ------- Episode - Episode + OK Examples -------- @@ -270,28 +243,39 @@ def get(self, uuid_: str, *, request_options: typing.Optional[RequestOptions] = api_key="YOUR_API_KEY", ) client.graph.episode.get( - uuid_="uuid", + graph_uuid="graph_uuid", + episode_uuid="episode_uuid", ) """ - _response = self._raw_client.get(uuid_, request_options=request_options) + _response = self._raw_client.get(graph_uuid, episode_uuid, request_options=request_options) return _response.data - def delete(self, uuid_: str, *, request_options: typing.Optional[RequestOptions] = None) -> SuccessResponse: + def delete( + self, + graph_uuid: str, + episode_uuid: str, + *, + idempotency_key: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncResult: """ - Deletes an episode by its UUID. - Parameters ---------- - uuid_ : str + graph_uuid : str + Graph UUID + + episode_uuid : str Episode UUID + idempotency_key : typing.Optional[str] + request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - SuccessResponse - Episode deleted + AsyncResult + Accepted Examples -------- @@ -301,29 +285,36 @@ def delete(self, uuid_: str, *, request_options: typing.Optional[RequestOptions] api_key="YOUR_API_KEY", ) client.graph.episode.delete( - uuid_="uuid", + graph_uuid="graph_uuid", + episode_uuid="episode_uuid", ) """ - _response = self._raw_client.delete(uuid_, request_options=request_options) + _response = self._raw_client.delete( + graph_uuid, episode_uuid, idempotency_key=idempotency_key, request_options=request_options + ) return _response.data def update( self, - uuid_: str, + graph_uuid: str, + episode_uuid: str, *, - metadata: typing.Dict[str, typing.Optional[typing.Any]], + metadata: typing.Optional[typing.Dict[str, typing.Any]] = OMIT, + idempotency_key: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, ) -> Episode: """ - Update episode metadata with merge semantics. Supplied keys overwrite or add to existing metadata; keys set to null are removed. - Parameters ---------- - uuid_ : str + graph_uuid : str + Graph UUID + + episode_uuid : str Episode UUID - metadata : typing.Dict[str, typing.Optional[typing.Any]] - Updated metadata. Merged with existing metadata: supplied keys overwrite/add, keys set to null are removed. Maximum 10 keys. Values must be scalars (string, number, boolean, null) or arrays of scalars. + metadata : typing.Optional[typing.Dict[str, typing.Any]] + + idempotency_key : typing.Optional[str] request_options : typing.Optional[RequestOptions] Request-specific configuration. @@ -331,7 +322,7 @@ def update( Returns ------- Episode - Updated episode + OK Examples -------- @@ -341,44 +332,17 @@ def update( api_key="YOUR_API_KEY", ) client.graph.episode.update( - uuid_="uuid", - metadata={"key": "value"}, + graph_uuid="graph_uuid", + episode_uuid="episode_uuid", ) """ - _response = self._raw_client.update(uuid_, metadata=metadata, request_options=request_options) - return _response.data - - def get_nodes_and_edges( - self, uuid_: str, *, request_options: typing.Optional[RequestOptions] = None - ) -> EpisodeMentions: - """ - Deprecated. Use edge and node listing with `filters.episode_uuids` instead. Returns nodes and edges mentioned in an episode, subject to an internal cap; responses reduced by that cap set the Zep-Truncated header. - - Parameters - ---------- - uuid_ : str - Episode uuid - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - EpisodeMentions - Edges and nodes mentioned in an episode - - Examples - -------- - from zep_cloud import Zep - - client = Zep( - api_key="YOUR_API_KEY", - ) - client.graph.episode.get_nodes_and_edges( - uuid_="uuid", + _response = self._raw_client.update( + graph_uuid, + episode_uuid, + metadata=metadata, + idempotency_key=idempotency_key, + request_options=request_options, ) - """ - _response = self._raw_client.get_nodes_and_edges(uuid_, request_options=request_options) return _response.data @@ -397,31 +361,37 @@ def with_raw_response(self) -> AsyncRawEpisodeClient: """ return self._raw_client - async def get_by_graph_id( + async def list_for_document( self, - graph_id: str, + graph_uuid: str, + document_id: str, *, - lastn: typing.Optional[int] = None, + limit: typing.Optional[int] = None, + cursor: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, - ) -> EpisodeResponse: + ) -> AsyncPager[Episode, EpisodePage]: """ - Returns episodes by graph id. - Parameters ---------- - graph_id : str - Graph ID + graph_uuid : str + Graph UUID + + document_id : str + Document ID - lastn : typing.Optional[int] - The number of most recent episodes to retrieve. + limit : typing.Optional[int] + Page size + + cursor : typing.Optional[str] + Opaque page cursor request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - EpisodeResponse - Episodes + AsyncPager[Episode, EpisodePage] + OK Examples -------- @@ -435,117 +405,69 @@ async def get_by_graph_id( async def main() -> None: - await client.graph.episode.get_by_graph_id( - graph_id="graph_id", - lastn=1, + response = await client.graph.episode.list_for_document( + graph_uuid="graph_uuid", + document_id="document_id", + limit=1, + cursor="cursor", ) + async for item in response: + yield item + + # alternatively, you can paginate page-by-page + async for page in response.iter_pages(): + yield page asyncio.run(main()) """ - _response = await self._raw_client.get_by_graph_id(graph_id, lastn=lastn, request_options=request_options) - return _response.data + return await self._raw_client.list_for_document( + graph_uuid, document_id, limit=limit, cursor=cursor, request_options=request_options + ) - async def list_by_graph_id( + async def add( self, - graph_id: str, + graph_uuid: str, *, - cursor: typing.Optional[str] = OMIT, - direction: typing.Optional[str] = OMIT, - limit: typing.Optional[int] = OMIT, - mentioned_node_uuids: typing.Optional[typing.Sequence[str]] = OMIT, - order_by: typing.Optional[str] = OMIT, + created_at: typing.Optional[str] = OMIT, + data: typing.Optional[str] = OMIT, + document_id: typing.Optional[str] = OMIT, + metadata: typing.Optional[typing.Dict[str, typing.Any]] = OMIT, + source_description: typing.Optional[str] = OMIT, + strict_ontology: typing.Optional[bool] = OMIT, + type: typing.Optional[str] = OMIT, + idempotency_key: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, - ) -> typing.List[Episode]: + ) -> AddEpisodeResult: """ - Returns a paginated, filterable list of episodes for a graph. - 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. - - direction : typing.Optional[str] - Sort direction. One of "asc" or "desc". Defaults to "desc". - - 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. + graph_uuid : str + Graph UUID - 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. + created_at : typing.Optional[str] - order_by : typing.Optional[str] - Field to sort by. One of "uuid" or "created_at". Defaults to "uuid". - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - typing.List[Episode] - Episodes + data : typing.Optional[str] - Examples - -------- - import asyncio + document_id : typing.Optional[str] - from zep_cloud import AsyncZep + metadata : typing.Optional[typing.Dict[str, typing.Any]] - client = AsyncZep( - api_key="YOUR_API_KEY", - ) + source_description : typing.Optional[str] + strict_ontology : typing.Optional[bool] - async def main() -> None: - await client.graph.episode.list_by_graph_id( - graph_id="graph_id", - ) + type : typing.Optional[str] - - asyncio.run(main()) - """ - _response = await self._raw_client.list_by_graph_id( - graph_id, - cursor=cursor, - direction=direction, - limit=limit, - mentioned_node_uuids=mentioned_node_uuids, - order_by=order_by, - request_options=request_options, - ) - return _response.data - - async def get_by_user_id( - self, - user_id: str, - *, - lastn: typing.Optional[int] = None, - request_options: typing.Optional[RequestOptions] = None, - ) -> EpisodeResponse: - """ - Returns episodes by user id. - - Parameters - ---------- - user_id : str - User ID - - lastn : typing.Optional[int] - The number of most recent episodes entries to retrieve. + idempotency_key : typing.Optional[str] request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - EpisodeResponse - Episodes + AddEpisodeResult + Accepted Examples -------- @@ -559,61 +481,60 @@ async def get_by_user_id( async def main() -> None: - await client.graph.episode.get_by_user_id( - user_id="user_id", - lastn=1, + await client.graph.episode.add( + graph_uuid="graph_uuid", ) asyncio.run(main()) """ - _response = await self._raw_client.get_by_user_id(user_id, lastn=lastn, request_options=request_options) + _response = await self._raw_client.add( + graph_uuid, + created_at=created_at, + data=data, + document_id=document_id, + metadata=metadata, + source_description=source_description, + strict_ontology=strict_ontology, + type=type, + idempotency_key=idempotency_key, + request_options=request_options, + ) return _response.data - async def list_by_user_id( + async def list( self, - user_id: str, + graph_uuid: str, *, - cursor: typing.Optional[str] = OMIT, - direction: typing.Optional[str] = OMIT, - limit: typing.Optional[int] = OMIT, - mentioned_node_uuids: typing.Optional[typing.Sequence[str]] = OMIT, - order_by: typing.Optional[str] = OMIT, + limit: typing.Optional[int] = None, + cursor: typing.Optional[str] = None, + filters: typing.Optional[typing.Dict[str, typing.Any]] = OMIT, + idempotency_key: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, - ) -> typing.List[Episode]: + ) -> AsyncPager[Episode, EpisodePage]: """ - Returns a paginated, filterable list of episodes for a user's graph. - Parameters ---------- - user_id : str - User ID - - cursor : typing.Optional[str] - Opaque cursor for pagination, obtained from the Zep-Next-Cursor - response header of the previous page. - - direction : typing.Optional[str] - Sort direction. One of "asc" or "desc". Defaults to "desc". + graph_uuid : str + Graph UUID 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. + Page size - 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. + cursor : typing.Optional[str] + Opaque page cursor - order_by : typing.Optional[str] - Field to sort by. One of "uuid" or "created_at". Defaults to "uuid". + filters : typing.Optional[typing.Dict[str, typing.Any]] + + idempotency_key : typing.Optional[str] request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - typing.List[Episode] - Episodes + AsyncPager[Episode, EpisodePage] + OK Examples -------- @@ -627,31 +548,40 @@ async def list_by_user_id( async def main() -> None: - await client.graph.episode.list_by_user_id( - user_id="user_id", + response = await client.graph.episode.list( + graph_uuid="graph_uuid", + limit=1, + cursor="cursor", ) + async for item in response: + yield item + + # alternatively, you can paginate page-by-page + async for page in response.iter_pages(): + yield page asyncio.run(main()) """ - _response = await self._raw_client.list_by_user_id( - user_id, - cursor=cursor, - direction=direction, + return await self._raw_client.list( + graph_uuid, limit=limit, - mentioned_node_uuids=mentioned_node_uuids, - order_by=order_by, + cursor=cursor, + filters=filters, + idempotency_key=idempotency_key, request_options=request_options, ) - return _response.data - async def get(self, uuid_: str, *, request_options: typing.Optional[RequestOptions] = None) -> Episode: + async def get( + self, graph_uuid: str, episode_uuid: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> Episode: """ - Returns episodes by UUID - Parameters ---------- - uuid_ : str + graph_uuid : str + Graph UUID + + episode_uuid : str Episode UUID request_options : typing.Optional[RequestOptions] @@ -660,7 +590,7 @@ async def get(self, uuid_: str, *, request_options: typing.Optional[RequestOptio Returns ------- Episode - Episode + OK Examples -------- @@ -675,31 +605,42 @@ async def get(self, uuid_: str, *, request_options: typing.Optional[RequestOptio async def main() -> None: await client.graph.episode.get( - uuid_="uuid", + graph_uuid="graph_uuid", + episode_uuid="episode_uuid", ) asyncio.run(main()) """ - _response = await self._raw_client.get(uuid_, request_options=request_options) + _response = await self._raw_client.get(graph_uuid, episode_uuid, request_options=request_options) return _response.data - async def delete(self, uuid_: str, *, request_options: typing.Optional[RequestOptions] = None) -> SuccessResponse: + async def delete( + self, + graph_uuid: str, + episode_uuid: str, + *, + idempotency_key: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncResult: """ - Deletes an episode by its UUID. - Parameters ---------- - uuid_ : str + graph_uuid : str + Graph UUID + + episode_uuid : str Episode UUID + idempotency_key : typing.Optional[str] + request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - SuccessResponse - Episode deleted + AsyncResult + Accepted Examples -------- @@ -714,32 +655,39 @@ async def delete(self, uuid_: str, *, request_options: typing.Optional[RequestOp async def main() -> None: await client.graph.episode.delete( - uuid_="uuid", + graph_uuid="graph_uuid", + episode_uuid="episode_uuid", ) asyncio.run(main()) """ - _response = await self._raw_client.delete(uuid_, request_options=request_options) + _response = await self._raw_client.delete( + graph_uuid, episode_uuid, idempotency_key=idempotency_key, request_options=request_options + ) return _response.data async def update( self, - uuid_: str, + graph_uuid: str, + episode_uuid: str, *, - metadata: typing.Dict[str, typing.Optional[typing.Any]], + metadata: typing.Optional[typing.Dict[str, typing.Any]] = OMIT, + idempotency_key: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, ) -> Episode: """ - Update episode metadata with merge semantics. Supplied keys overwrite or add to existing metadata; keys set to null are removed. - Parameters ---------- - uuid_ : str + graph_uuid : str + Graph UUID + + episode_uuid : str Episode UUID - metadata : typing.Dict[str, typing.Optional[typing.Any]] - Updated metadata. Merged with existing metadata: supplied keys overwrite/add, keys set to null are removed. Maximum 10 keys. Values must be scalars (string, number, boolean, null) or arrays of scalars. + metadata : typing.Optional[typing.Dict[str, typing.Any]] + + idempotency_key : typing.Optional[str] request_options : typing.Optional[RequestOptions] Request-specific configuration. @@ -747,7 +695,7 @@ async def update( Returns ------- Episode - Updated episode + OK Examples -------- @@ -762,53 +710,18 @@ async def update( async def main() -> None: await client.graph.episode.update( - uuid_="uuid", - metadata={"key": "value"}, + graph_uuid="graph_uuid", + episode_uuid="episode_uuid", ) asyncio.run(main()) """ - _response = await self._raw_client.update(uuid_, metadata=metadata, request_options=request_options) - return _response.data - - async def get_nodes_and_edges( - self, uuid_: str, *, request_options: typing.Optional[RequestOptions] = None - ) -> EpisodeMentions: - """ - Deprecated. Use edge and node listing with `filters.episode_uuids` instead. Returns nodes and edges mentioned in an episode, subject to an internal cap; responses reduced by that cap set the Zep-Truncated header. - - Parameters - ---------- - uuid_ : str - Episode uuid - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - EpisodeMentions - Edges and nodes mentioned in an episode - - Examples - -------- - import asyncio - - from zep_cloud import AsyncZep - - client = AsyncZep( - api_key="YOUR_API_KEY", + _response = await self._raw_client.update( + graph_uuid, + episode_uuid, + metadata=metadata, + idempotency_key=idempotency_key, + request_options=request_options, ) - - - async def main() -> None: - await client.graph.episode.get_nodes_and_edges( - uuid_="uuid", - ) - - - asyncio.run(main()) - """ - _response = await self._raw_client.get_nodes_and_edges(uuid_, request_options=request_options) return _response.data diff --git a/src/zep_cloud/graph/episode/raw_client.py b/src/zep_cloud/graph/episode/raw_client.py index 0c449529..fa405b2e 100644 --- a/src/zep_cloud/graph/episode/raw_client.py +++ b/src/zep_cloud/graph/episode/raw_client.py @@ -7,17 +7,19 @@ from ...core.client_wrapper import AsyncClientWrapper, SyncClientWrapper from ...core.http_response import AsyncHttpResponse, HttpResponse from ...core.jsonable_encoder import jsonable_encoder +from ...core.pagination import AsyncPager, SyncPager +from ...core.parse_error import ParsingError from ...core.pydantic_utilities import parse_obj_as from ...core.request_options import RequestOptions from ...errors.bad_request_error import BadRequestError -from ...errors.forbidden_error import ForbiddenError -from ...errors.internal_server_error import InternalServerError from ...errors.not_found_error import NotFoundError +from ...errors.unauthorized_error import UnauthorizedError +from ...types.add_episode_result import AddEpisodeResult from ...types.api_error import ApiError as types_api_error_ApiError +from ...types.async_result import AsyncResult from ...types.episode import Episode -from ...types.episode_mentions import EpisodeMentions -from ...types.episode_response import EpisodeResponse -from ...types.success_response import SuccessResponse +from ...types.episode_page import EpisodePage +from pydantic import ValidationError # this is used as the default value for optional parameters OMIT = typing.cast(typing.Any, ...) @@ -27,63 +29,91 @@ class RawEpisodeClient: def __init__(self, *, client_wrapper: SyncClientWrapper): self._client_wrapper = client_wrapper - def get_by_graph_id( + def list_for_document( self, - graph_id: str, + graph_uuid: str, + document_id: str, *, - lastn: typing.Optional[int] = None, + limit: typing.Optional[int] = None, + cursor: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, - ) -> HttpResponse[EpisodeResponse]: + ) -> SyncPager[Episode, EpisodePage]: """ - Returns episodes by graph id. - Parameters ---------- - graph_id : str - Graph ID + graph_uuid : str + Graph UUID + + document_id : str + Document ID - lastn : typing.Optional[int] - The number of most recent episodes to retrieve. + limit : typing.Optional[int] + Page size + + cursor : typing.Optional[str] + Opaque page cursor request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - HttpResponse[EpisodeResponse] - Episodes + SyncPager[Episode, EpisodePage] + OK """ _response = self._client_wrapper.httpx_client.request( - f"graph/episodes/graph/{jsonable_encoder(graph_id)}", + f"graphs/{jsonable_encoder(graph_uuid)}/documents/{jsonable_encoder(document_id)}/episodes", method="GET", params={ - "lastn": lastn, + "limit": limit, + "cursor": cursor, }, request_options=request_options, ) try: if 200 <= _response.status_code < 300: - _data = typing.cast( - EpisodeResponse, + _parsed_response = typing.cast( + EpisodePage, parse_obj_as( - type_=EpisodeResponse, # type: ignore + type_=EpisodePage, # type: ignore object_=_response.json(), ), ) - return HttpResponse(response=_response, data=_data) + _items = _parsed_response.items + _parsed_next = _parsed_response.next_cursor + _has_next = _parsed_next is not None and _parsed_next != "" + _get_next = lambda: self.list_for_document( + graph_uuid, + document_id, + limit=limit, + cursor=_parsed_next, + request_options=request_options, + ) + return SyncPager(has_next=_has_next, items=_items, get_next=_get_next, response=_parsed_response) if _response.status_code == 400: raise BadRequestError( headers=dict(_response.headers), body=typing.cast( - typing.Optional[typing.Any], + types_api_error_ApiError, parse_obj_as( - type_=typing.Optional[typing.Any], # type: ignore + type_=types_api_error_ApiError, # type: ignore object_=_response.json(), ), ), ) - if _response.status_code == 500: - raise InternalServerError( + if _response.status_code == 401: + raise UnauthorizedError( + 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, @@ -98,64 +128,73 @@ def get_by_graph_id( raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response.text ) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response_json ) - def list_by_graph_id( + def add( self, - graph_id: str, + graph_uuid: str, *, - cursor: typing.Optional[str] = OMIT, - direction: typing.Optional[str] = OMIT, - limit: typing.Optional[int] = OMIT, - mentioned_node_uuids: typing.Optional[typing.Sequence[str]] = OMIT, - order_by: typing.Optional[str] = OMIT, + created_at: typing.Optional[str] = OMIT, + data: typing.Optional[str] = OMIT, + document_id: typing.Optional[str] = OMIT, + metadata: typing.Optional[typing.Dict[str, typing.Any]] = OMIT, + source_description: typing.Optional[str] = OMIT, + strict_ontology: typing.Optional[bool] = OMIT, + type: typing.Optional[str] = OMIT, + idempotency_key: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, - ) -> HttpResponse[typing.List[Episode]]: + ) -> HttpResponse[AddEpisodeResult]: """ - Returns a paginated, filterable list of episodes for a graph. - Parameters ---------- - graph_id : str - Graph ID + graph_uuid : str + Graph UUID - cursor : typing.Optional[str] - Opaque cursor for pagination, obtained from the Zep-Next-Cursor - response header of the previous page. + created_at : typing.Optional[str] - direction : typing.Optional[str] - Sort direction. One of "asc" or "desc". Defaults to "desc". + data : typing.Optional[str] - 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. + document_id : typing.Optional[str] - 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. + metadata : typing.Optional[typing.Dict[str, typing.Any]] - order_by : typing.Optional[str] - Field to sort by. One of "uuid" or "created_at". Defaults to "uuid". + source_description : typing.Optional[str] + + strict_ontology : typing.Optional[bool] + + type : typing.Optional[str] + + idempotency_key : typing.Optional[str] request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - HttpResponse[typing.List[Episode]] - Episodes + HttpResponse[AddEpisodeResult] + Accepted """ _response = self._client_wrapper.httpx_client.request( - f"graph/episodes/graph/{jsonable_encoder(graph_id)}", + f"graphs/{jsonable_encoder(graph_uuid)}/episodes", method="POST", json={ - "cursor": cursor, - "direction": direction, - "limit": limit, - "mentioned_node_uuids": mentioned_node_uuids, - "order_by": order_by, + "created_at": created_at, + "data": data, + "document_id": document_id, + "metadata": metadata, + "source_description": source_description, + "strict_ontology": strict_ontology, + "type": type, + }, + headers={ + "content-type": "application/json", + "Idempotency-Key": str(idempotency_key) if idempotency_key is not None else None, }, request_options=request_options, omit=OMIT, @@ -163,26 +202,15 @@ def list_by_graph_id( try: if 200 <= _response.status_code < 300: _data = typing.cast( - typing.List[Episode], + AddEpisodeResult, parse_obj_as( - type_=typing.List[Episode], # type: ignore + type_=AddEpisodeResult, # 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 == 500: - raise InternalServerError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -192,72 +220,19 @@ def list_by_graph_id( ), ), ) - _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_by_user_id( - self, - user_id: str, - *, - lastn: typing.Optional[int] = None, - request_options: typing.Optional[RequestOptions] = None, - ) -> HttpResponse[EpisodeResponse]: - """ - Returns episodes by user id. - - Parameters - ---------- - user_id : str - User ID - - lastn : typing.Optional[int] - The number of most recent episodes entries to retrieve. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - HttpResponse[EpisodeResponse] - Episodes - """ - _response = self._client_wrapper.httpx_client.request( - f"graph/episodes/user/{jsonable_encoder(user_id)}", - method="GET", - params={ - "lastn": lastn, - }, - 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( + if _response.status_code == 401: + raise UnauthorizedError( headers=dict(_response.headers), body=typing.cast( - typing.Optional[typing.Any], + types_api_error_ApiError, parse_obj_as( - type_=typing.Optional[typing.Any], # type: ignore + type_=types_api_error_ApiError, # type: ignore object_=_response.json(), ), ), ) - if _response.status_code == 500: - raise InternalServerError( + if _response.status_code == 404: + raise NotFoundError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -272,91 +247,110 @@ def get_by_user_id( raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response.text ) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response_json ) - def list_by_user_id( + def list( self, - user_id: str, + graph_uuid: str, *, - cursor: typing.Optional[str] = OMIT, - direction: typing.Optional[str] = OMIT, - limit: typing.Optional[int] = OMIT, - mentioned_node_uuids: typing.Optional[typing.Sequence[str]] = OMIT, - order_by: typing.Optional[str] = OMIT, + limit: typing.Optional[int] = None, + cursor: typing.Optional[str] = None, + filters: typing.Optional[typing.Dict[str, typing.Any]] = OMIT, + idempotency_key: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, - ) -> HttpResponse[typing.List[Episode]]: + ) -> SyncPager[Episode, EpisodePage]: """ - Returns a paginated, filterable list of episodes for a user's graph. - Parameters ---------- - user_id : str - User ID - - cursor : typing.Optional[str] - Opaque cursor for pagination, obtained from the Zep-Next-Cursor - response header of the previous page. - - direction : typing.Optional[str] - Sort direction. One of "asc" or "desc". Defaults to "desc". + graph_uuid : str + Graph UUID 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. + Page size + + cursor : typing.Optional[str] + Opaque page cursor - 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. + filters : typing.Optional[typing.Dict[str, typing.Any]] - order_by : typing.Optional[str] - Field to sort by. One of "uuid" or "created_at". Defaults to "uuid". + idempotency_key : typing.Optional[str] request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - HttpResponse[typing.List[Episode]] - Episodes + SyncPager[Episode, EpisodePage] + OK """ _response = self._client_wrapper.httpx_client.request( - f"graph/episodes/user/{jsonable_encoder(user_id)}", + f"graphs/{jsonable_encoder(graph_uuid)}/episodes/list", method="POST", - json={ - "cursor": cursor, - "direction": direction, + params={ "limit": limit, - "mentioned_node_uuids": mentioned_node_uuids, - "order_by": order_by, + "cursor": cursor, + }, + json={ + "filters": filters, + }, + headers={ + "content-type": "application/json", + "Idempotency-Key": str(idempotency_key) if idempotency_key is not None else None, }, request_options=request_options, omit=OMIT, ) try: if 200 <= _response.status_code < 300: - _data = typing.cast( - typing.List[Episode], + _parsed_response = typing.cast( + EpisodePage, parse_obj_as( - type_=typing.List[Episode], # type: ignore + type_=EpisodePage, # type: ignore object_=_response.json(), ), ) - return HttpResponse(response=_response, data=_data) + _items = _parsed_response.items + _parsed_next = _parsed_response.next_cursor + _has_next = _parsed_next is not None and _parsed_next != "" + _get_next = lambda: self.list( + graph_uuid, + limit=limit, + cursor=_parsed_next, + filters=filters, + idempotency_key=idempotency_key, + request_options=request_options, + ) + return SyncPager(has_next=_has_next, items=_items, get_next=_get_next, response=_parsed_response) if _response.status_code == 400: raise BadRequestError( headers=dict(_response.headers), body=typing.cast( - typing.Optional[typing.Any], + types_api_error_ApiError, + parse_obj_as( + type_=types_api_error_ApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 401: + raise UnauthorizedError( + headers=dict(_response.headers), + body=typing.cast( + types_api_error_ApiError, parse_obj_as( - type_=typing.Optional[typing.Any], # type: ignore + type_=types_api_error_ApiError, # type: ignore object_=_response.json(), ), ), ) - if _response.status_code == 500: - raise InternalServerError( + if _response.status_code == 404: + raise NotFoundError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -371,17 +365,24 @@ def list_by_user_id( raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response.text ) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response_json ) - def get(self, uuid_: str, *, request_options: typing.Optional[RequestOptions] = None) -> HttpResponse[Episode]: + def get( + self, graph_uuid: str, episode_uuid: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[Episode]: """ - Returns episodes by UUID - Parameters ---------- - uuid_ : str + graph_uuid : str + Graph UUID + + episode_uuid : str Episode UUID request_options : typing.Optional[RequestOptions] @@ -390,10 +391,10 @@ def get(self, uuid_: str, *, request_options: typing.Optional[RequestOptions] = Returns ------- HttpResponse[Episode] - Episode + OK """ _response = self._client_wrapper.httpx_client.request( - f"graph/episodes/{jsonable_encoder(uuid_)}", + f"graphs/{jsonable_encoder(graph_uuid)}/episodes/{jsonable_encoder(episode_uuid)}", method="GET", request_options=request_options, ) @@ -411,15 +412,26 @@ def get(self, uuid_: str, *, request_options: typing.Optional[RequestOptions] = raise BadRequestError( headers=dict(_response.headers), body=typing.cast( - typing.Optional[typing.Any], + types_api_error_ApiError, parse_obj_as( - type_=typing.Optional[typing.Any], # type: ignore + type_=types_api_error_ApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 401: + raise UnauthorizedError( + 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( + if _response.status_code == 404: + raise NotFoundError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -434,40 +446,55 @@ def get(self, uuid_: str, *, request_options: typing.Optional[RequestOptions] = raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response.text ) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response_json ) def delete( - self, uuid_: str, *, request_options: typing.Optional[RequestOptions] = None - ) -> HttpResponse[SuccessResponse]: + self, + graph_uuid: str, + episode_uuid: str, + *, + idempotency_key: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[AsyncResult]: """ - Deletes an episode by its UUID. - Parameters ---------- - uuid_ : str + graph_uuid : str + Graph UUID + + episode_uuid : str Episode UUID + idempotency_key : typing.Optional[str] + request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - HttpResponse[SuccessResponse] - Episode deleted + HttpResponse[AsyncResult] + Accepted """ _response = self._client_wrapper.httpx_client.request( - f"graph/episodes/{jsonable_encoder(uuid_)}", + f"graphs/{jsonable_encoder(graph_uuid)}/episodes/{jsonable_encoder(episode_uuid)}", method="DELETE", + headers={ + "Idempotency-Key": str(idempotency_key) if idempotency_key is not None else None, + }, request_options=request_options, ) try: if 200 <= _response.status_code < 300: _data = typing.cast( - SuccessResponse, + AsyncResult, parse_obj_as( - type_=SuccessResponse, # type: ignore + type_=AsyncResult, # type: ignore object_=_response.json(), ), ) @@ -476,15 +503,15 @@ def delete( raise BadRequestError( headers=dict(_response.headers), body=typing.cast( - typing.Optional[typing.Any], + types_api_error_ApiError, parse_obj_as( - type_=typing.Optional[typing.Any], # type: ignore + type_=types_api_error_ApiError, # type: ignore object_=_response.json(), ), ), ) - if _response.status_code == 404: - raise NotFoundError( + if _response.status_code == 401: + raise UnauthorizedError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -494,8 +521,8 @@ def delete( ), ), ) - if _response.status_code == 500: - raise InternalServerError( + if _response.status_code == 404: + raise NotFoundError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -510,27 +537,35 @@ def delete( raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response.text ) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response_json ) def update( self, - uuid_: str, + graph_uuid: str, + episode_uuid: str, *, - metadata: typing.Dict[str, typing.Optional[typing.Any]], + metadata: typing.Optional[typing.Dict[str, typing.Any]] = OMIT, + idempotency_key: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, ) -> HttpResponse[Episode]: """ - Update episode metadata with merge semantics. Supplied keys overwrite or add to existing metadata; keys set to null are removed. - Parameters ---------- - uuid_ : str + graph_uuid : str + Graph UUID + + episode_uuid : str Episode UUID - metadata : typing.Dict[str, typing.Optional[typing.Any]] - Updated metadata. Merged with existing metadata: supplied keys overwrite/add, keys set to null are removed. Maximum 10 keys. Values must be scalars (string, number, boolean, null) or arrays of scalars. + metadata : typing.Optional[typing.Dict[str, typing.Any]] + + idempotency_key : typing.Optional[str] request_options : typing.Optional[RequestOptions] Request-specific configuration. @@ -538,16 +573,17 @@ def update( Returns ------- HttpResponse[Episode] - Updated episode + OK """ _response = self._client_wrapper.httpx_client.request( - f"graph/episodes/{jsonable_encoder(uuid_)}", + f"graphs/{jsonable_encoder(graph_uuid)}/episodes/{jsonable_encoder(episode_uuid)}", method="PATCH", json={ "metadata": metadata, }, headers={ "content-type": "application/json", + "Idempotency-Key": str(idempotency_key) if idempotency_key is not None else None, }, request_options=request_options, omit=OMIT, @@ -564,17 +600,6 @@ def update( 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 == 403: - raise ForbiddenError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -584,8 +609,8 @@ def update( ), ), ) - if _response.status_code == 404: - raise NotFoundError( + if _response.status_code == 401: + raise UnauthorizedError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -595,8 +620,8 @@ def update( ), ), ) - if _response.status_code == 500: - raise InternalServerError( + if _response.status_code == 404: + raise NotFoundError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -611,70 +636,9 @@ def update( 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_nodes_and_edges( - self, uuid_: str, *, request_options: typing.Optional[RequestOptions] = None - ) -> HttpResponse[EpisodeMentions]: - """ - Deprecated. Use edge and node listing with `filters.episode_uuids` instead. Returns nodes and edges mentioned in an episode, subject to an internal cap; responses reduced by that cap set the Zep-Truncated header. - - Parameters - ---------- - uuid_ : str - Episode uuid - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - HttpResponse[EpisodeMentions] - Edges and nodes mentioned in an episode - """ - _response = self._client_wrapper.httpx_client.request( - f"graph/episodes/{jsonable_encoder(uuid_)}/mentions", - method="GET", - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - _data = typing.cast( - EpisodeMentions, - parse_obj_as( - type_=EpisodeMentions, # 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 == 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 + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e ) raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response_json @@ -685,63 +649,94 @@ class AsyncRawEpisodeClient: def __init__(self, *, client_wrapper: AsyncClientWrapper): self._client_wrapper = client_wrapper - async def get_by_graph_id( + async def list_for_document( self, - graph_id: str, + graph_uuid: str, + document_id: str, *, - lastn: typing.Optional[int] = None, + limit: typing.Optional[int] = None, + cursor: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, - ) -> AsyncHttpResponse[EpisodeResponse]: + ) -> AsyncPager[Episode, EpisodePage]: """ - Returns episodes by graph id. - Parameters ---------- - graph_id : str - Graph ID + graph_uuid : str + Graph UUID - lastn : typing.Optional[int] - The number of most recent episodes to retrieve. + document_id : str + Document ID + + limit : typing.Optional[int] + Page size + + cursor : typing.Optional[str] + Opaque page cursor request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - AsyncHttpResponse[EpisodeResponse] - Episodes + AsyncPager[Episode, EpisodePage] + OK """ _response = await self._client_wrapper.httpx_client.request( - f"graph/episodes/graph/{jsonable_encoder(graph_id)}", + f"graphs/{jsonable_encoder(graph_uuid)}/documents/{jsonable_encoder(document_id)}/episodes", method="GET", params={ - "lastn": lastn, + "limit": limit, + "cursor": cursor, }, request_options=request_options, ) try: if 200 <= _response.status_code < 300: - _data = typing.cast( - EpisodeResponse, + _parsed_response = typing.cast( + EpisodePage, parse_obj_as( - type_=EpisodeResponse, # type: ignore + type_=EpisodePage, # type: ignore object_=_response.json(), ), ) - return AsyncHttpResponse(response=_response, data=_data) + _items = _parsed_response.items + _parsed_next = _parsed_response.next_cursor + _has_next = _parsed_next is not None and _parsed_next != "" + + async def _get_next(): + return await self.list_for_document( + graph_uuid, + document_id, + limit=limit, + cursor=_parsed_next, + request_options=request_options, + ) + + return AsyncPager(has_next=_has_next, items=_items, get_next=_get_next, response=_parsed_response) if _response.status_code == 400: raise BadRequestError( headers=dict(_response.headers), body=typing.cast( - typing.Optional[typing.Any], + types_api_error_ApiError, parse_obj_as( - type_=typing.Optional[typing.Any], # type: ignore + type_=types_api_error_ApiError, # type: ignore object_=_response.json(), ), ), ) - if _response.status_code == 500: - raise InternalServerError( + if _response.status_code == 401: + raise UnauthorizedError( + 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, @@ -756,64 +751,73 @@ async def get_by_graph_id( raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response.text ) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response_json ) - async def list_by_graph_id( + async def add( self, - graph_id: str, + graph_uuid: str, *, - cursor: typing.Optional[str] = OMIT, - direction: typing.Optional[str] = OMIT, - limit: typing.Optional[int] = OMIT, - mentioned_node_uuids: typing.Optional[typing.Sequence[str]] = OMIT, - order_by: typing.Optional[str] = OMIT, + created_at: typing.Optional[str] = OMIT, + data: typing.Optional[str] = OMIT, + document_id: typing.Optional[str] = OMIT, + metadata: typing.Optional[typing.Dict[str, typing.Any]] = OMIT, + source_description: typing.Optional[str] = OMIT, + strict_ontology: typing.Optional[bool] = OMIT, + type: typing.Optional[str] = OMIT, + idempotency_key: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, - ) -> AsyncHttpResponse[typing.List[Episode]]: + ) -> AsyncHttpResponse[AddEpisodeResult]: """ - Returns a paginated, filterable list of episodes for a graph. - Parameters ---------- - graph_id : str - Graph ID + graph_uuid : str + Graph UUID - cursor : typing.Optional[str] - Opaque cursor for pagination, obtained from the Zep-Next-Cursor - response header of the previous page. + created_at : typing.Optional[str] - direction : typing.Optional[str] - Sort direction. One of "asc" or "desc". Defaults to "desc". + data : typing.Optional[str] - 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. + document_id : typing.Optional[str] - 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. + metadata : typing.Optional[typing.Dict[str, typing.Any]] - order_by : typing.Optional[str] - Field to sort by. One of "uuid" or "created_at". Defaults to "uuid". + source_description : typing.Optional[str] + + strict_ontology : typing.Optional[bool] + + type : typing.Optional[str] + + idempotency_key : typing.Optional[str] request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - AsyncHttpResponse[typing.List[Episode]] - Episodes + AsyncHttpResponse[AddEpisodeResult] + Accepted """ _response = await self._client_wrapper.httpx_client.request( - f"graph/episodes/graph/{jsonable_encoder(graph_id)}", + f"graphs/{jsonable_encoder(graph_uuid)}/episodes", method="POST", json={ - "cursor": cursor, - "direction": direction, - "limit": limit, - "mentioned_node_uuids": mentioned_node_uuids, - "order_by": order_by, + "created_at": created_at, + "data": data, + "document_id": document_id, + "metadata": metadata, + "source_description": source_description, + "strict_ontology": strict_ontology, + "type": type, + }, + headers={ + "content-type": "application/json", + "Idempotency-Key": str(idempotency_key) if idempotency_key is not None else None, }, request_options=request_options, omit=OMIT, @@ -821,26 +825,15 @@ async def list_by_graph_id( try: if 200 <= _response.status_code < 300: _data = typing.cast( - typing.List[Episode], + AddEpisodeResult, parse_obj_as( - type_=typing.List[Episode], # type: ignore + type_=AddEpisodeResult, # 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 == 500: - raise InternalServerError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -850,72 +843,19 @@ async def list_by_graph_id( ), ), ) - _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_by_user_id( - self, - user_id: str, - *, - lastn: typing.Optional[int] = None, - request_options: typing.Optional[RequestOptions] = None, - ) -> AsyncHttpResponse[EpisodeResponse]: - """ - Returns episodes by user id. - - Parameters - ---------- - user_id : str - User ID - - lastn : typing.Optional[int] - The number of most recent episodes entries to retrieve. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - AsyncHttpResponse[EpisodeResponse] - Episodes - """ - _response = await self._client_wrapper.httpx_client.request( - f"graph/episodes/user/{jsonable_encoder(user_id)}", - method="GET", - params={ - "lastn": lastn, - }, - 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( + if _response.status_code == 401: + raise UnauthorizedError( headers=dict(_response.headers), body=typing.cast( - typing.Optional[typing.Any], + types_api_error_ApiError, parse_obj_as( - type_=typing.Optional[typing.Any], # type: ignore + type_=types_api_error_ApiError, # type: ignore object_=_response.json(), ), ), ) - if _response.status_code == 500: - raise InternalServerError( + if _response.status_code == 404: + raise NotFoundError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -930,91 +870,113 @@ async def get_by_user_id( raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response.text ) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response_json ) - async def list_by_user_id( + async def list( self, - user_id: str, + graph_uuid: str, *, - cursor: typing.Optional[str] = OMIT, - direction: typing.Optional[str] = OMIT, - limit: typing.Optional[int] = OMIT, - mentioned_node_uuids: typing.Optional[typing.Sequence[str]] = OMIT, - order_by: typing.Optional[str] = OMIT, + limit: typing.Optional[int] = None, + cursor: typing.Optional[str] = None, + filters: typing.Optional[typing.Dict[str, typing.Any]] = OMIT, + idempotency_key: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, - ) -> AsyncHttpResponse[typing.List[Episode]]: + ) -> AsyncPager[Episode, EpisodePage]: """ - Returns a paginated, filterable list of episodes for a user's graph. - Parameters ---------- - user_id : str - User ID - - cursor : typing.Optional[str] - Opaque cursor for pagination, obtained from the Zep-Next-Cursor - response header of the previous page. - - direction : typing.Optional[str] - Sort direction. One of "asc" or "desc". Defaults to "desc". + graph_uuid : str + Graph UUID 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. + Page size + + cursor : typing.Optional[str] + Opaque page cursor - 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. + filters : typing.Optional[typing.Dict[str, typing.Any]] - order_by : typing.Optional[str] - Field to sort by. One of "uuid" or "created_at". Defaults to "uuid". + idempotency_key : typing.Optional[str] request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - AsyncHttpResponse[typing.List[Episode]] - Episodes + AsyncPager[Episode, EpisodePage] + OK """ _response = await self._client_wrapper.httpx_client.request( - f"graph/episodes/user/{jsonable_encoder(user_id)}", + f"graphs/{jsonable_encoder(graph_uuid)}/episodes/list", method="POST", - json={ - "cursor": cursor, - "direction": direction, + params={ "limit": limit, - "mentioned_node_uuids": mentioned_node_uuids, - "order_by": order_by, + "cursor": cursor, + }, + json={ + "filters": filters, + }, + headers={ + "content-type": "application/json", + "Idempotency-Key": str(idempotency_key) if idempotency_key is not None else None, }, request_options=request_options, omit=OMIT, ) try: if 200 <= _response.status_code < 300: - _data = typing.cast( - typing.List[Episode], + _parsed_response = typing.cast( + EpisodePage, parse_obj_as( - type_=typing.List[Episode], # type: ignore + type_=EpisodePage, # type: ignore object_=_response.json(), ), ) - return AsyncHttpResponse(response=_response, data=_data) + _items = _parsed_response.items + _parsed_next = _parsed_response.next_cursor + _has_next = _parsed_next is not None and _parsed_next != "" + + async def _get_next(): + return await self.list( + graph_uuid, + limit=limit, + cursor=_parsed_next, + filters=filters, + idempotency_key=idempotency_key, + request_options=request_options, + ) + + return AsyncPager(has_next=_has_next, items=_items, get_next=_get_next, response=_parsed_response) if _response.status_code == 400: raise BadRequestError( headers=dict(_response.headers), body=typing.cast( - typing.Optional[typing.Any], + types_api_error_ApiError, parse_obj_as( - type_=typing.Optional[typing.Any], # type: ignore + type_=types_api_error_ApiError, # type: ignore object_=_response.json(), ), ), ) - if _response.status_code == 500: - raise InternalServerError( + if _response.status_code == 401: + raise UnauthorizedError( + 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, @@ -1029,19 +991,24 @@ async def list_by_user_id( raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response.text ) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response_json ) async def get( - self, uuid_: str, *, request_options: typing.Optional[RequestOptions] = None + self, graph_uuid: str, episode_uuid: str, *, request_options: typing.Optional[RequestOptions] = None ) -> AsyncHttpResponse[Episode]: """ - Returns episodes by UUID - Parameters ---------- - uuid_ : str + graph_uuid : str + Graph UUID + + episode_uuid : str Episode UUID request_options : typing.Optional[RequestOptions] @@ -1050,10 +1017,10 @@ async def get( Returns ------- AsyncHttpResponse[Episode] - Episode + OK """ _response = await self._client_wrapper.httpx_client.request( - f"graph/episodes/{jsonable_encoder(uuid_)}", + f"graphs/{jsonable_encoder(graph_uuid)}/episodes/{jsonable_encoder(episode_uuid)}", method="GET", request_options=request_options, ) @@ -1071,15 +1038,26 @@ async def get( raise BadRequestError( headers=dict(_response.headers), body=typing.cast( - typing.Optional[typing.Any], + types_api_error_ApiError, parse_obj_as( - type_=typing.Optional[typing.Any], # type: ignore + type_=types_api_error_ApiError, # type: ignore object_=_response.json(), ), ), ) - if _response.status_code == 500: - raise InternalServerError( + if _response.status_code == 401: + raise UnauthorizedError( + 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, @@ -1094,40 +1072,55 @@ async def get( raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response.text ) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response_json ) async def delete( - self, uuid_: str, *, request_options: typing.Optional[RequestOptions] = None - ) -> AsyncHttpResponse[SuccessResponse]: + self, + graph_uuid: str, + episode_uuid: str, + *, + idempotency_key: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[AsyncResult]: """ - Deletes an episode by its UUID. - Parameters ---------- - uuid_ : str + graph_uuid : str + Graph UUID + + episode_uuid : str Episode UUID + idempotency_key : typing.Optional[str] + request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - AsyncHttpResponse[SuccessResponse] - Episode deleted + AsyncHttpResponse[AsyncResult] + Accepted """ _response = await self._client_wrapper.httpx_client.request( - f"graph/episodes/{jsonable_encoder(uuid_)}", + f"graphs/{jsonable_encoder(graph_uuid)}/episodes/{jsonable_encoder(episode_uuid)}", method="DELETE", + headers={ + "Idempotency-Key": str(idempotency_key) if idempotency_key is not None else None, + }, request_options=request_options, ) try: if 200 <= _response.status_code < 300: _data = typing.cast( - SuccessResponse, + AsyncResult, parse_obj_as( - type_=SuccessResponse, # type: ignore + type_=AsyncResult, # type: ignore object_=_response.json(), ), ) @@ -1136,15 +1129,15 @@ async def delete( raise BadRequestError( headers=dict(_response.headers), body=typing.cast( - typing.Optional[typing.Any], + types_api_error_ApiError, parse_obj_as( - type_=typing.Optional[typing.Any], # type: ignore + type_=types_api_error_ApiError, # type: ignore object_=_response.json(), ), ), ) - if _response.status_code == 404: - raise NotFoundError( + if _response.status_code == 401: + raise UnauthorizedError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -1154,8 +1147,8 @@ async def delete( ), ), ) - if _response.status_code == 500: - raise InternalServerError( + if _response.status_code == 404: + raise NotFoundError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -1170,27 +1163,35 @@ async def delete( raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response.text ) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response_json ) async def update( self, - uuid_: str, + graph_uuid: str, + episode_uuid: str, *, - metadata: typing.Dict[str, typing.Optional[typing.Any]], + metadata: typing.Optional[typing.Dict[str, typing.Any]] = OMIT, + idempotency_key: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, ) -> AsyncHttpResponse[Episode]: """ - Update episode metadata with merge semantics. Supplied keys overwrite or add to existing metadata; keys set to null are removed. - Parameters ---------- - uuid_ : str + graph_uuid : str + Graph UUID + + episode_uuid : str Episode UUID - metadata : typing.Dict[str, typing.Optional[typing.Any]] - Updated metadata. Merged with existing metadata: supplied keys overwrite/add, keys set to null are removed. Maximum 10 keys. Values must be scalars (string, number, boolean, null) or arrays of scalars. + metadata : typing.Optional[typing.Dict[str, typing.Any]] + + idempotency_key : typing.Optional[str] request_options : typing.Optional[RequestOptions] Request-specific configuration. @@ -1198,16 +1199,17 @@ async def update( Returns ------- AsyncHttpResponse[Episode] - Updated episode + OK """ _response = await self._client_wrapper.httpx_client.request( - f"graph/episodes/{jsonable_encoder(uuid_)}", + f"graphs/{jsonable_encoder(graph_uuid)}/episodes/{jsonable_encoder(episode_uuid)}", method="PATCH", json={ "metadata": metadata, }, headers={ "content-type": "application/json", + "Idempotency-Key": str(idempotency_key) if idempotency_key is not None else None, }, request_options=request_options, omit=OMIT, @@ -1224,17 +1226,6 @@ async def update( 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 == 403: - raise ForbiddenError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -1244,8 +1235,8 @@ async def update( ), ), ) - if _response.status_code == 404: - raise NotFoundError( + if _response.status_code == 401: + raise UnauthorizedError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -1255,8 +1246,8 @@ async def update( ), ), ) - if _response.status_code == 500: - raise InternalServerError( + if _response.status_code == 404: + raise NotFoundError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -1271,70 +1262,9 @@ async def update( 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_nodes_and_edges( - self, uuid_: str, *, request_options: typing.Optional[RequestOptions] = None - ) -> AsyncHttpResponse[EpisodeMentions]: - """ - Deprecated. Use edge and node listing with `filters.episode_uuids` instead. Returns nodes and edges mentioned in an episode, subject to an internal cap; responses reduced by that cap set the Zep-Truncated header. - - Parameters - ---------- - uuid_ : str - Episode uuid - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - AsyncHttpResponse[EpisodeMentions] - Edges and nodes mentioned in an episode - """ - _response = await self._client_wrapper.httpx_client.request( - f"graph/episodes/{jsonable_encoder(uuid_)}/mentions", - method="GET", - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - _data = typing.cast( - EpisodeMentions, - parse_obj_as( - type_=EpisodeMentions, # 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 == 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 + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e ) raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response_json diff --git a/src/zep_cloud/graph/node/client.py b/src/zep_cloud/graph/node/client.py index 8fafd397..25b01601 100644 --- a/src/zep_cloud/graph/node/client.py +++ b/src/zep_cloud/graph/node/client.py @@ -3,13 +3,14 @@ import typing from ...core.client_wrapper import AsyncClientWrapper, SyncClientWrapper +from ...core.pagination import AsyncPager, SyncPager from ...core.request_options import RequestOptions -from ...types.entity_edge import EntityEdge -from ...types.entity_node import EntityNode -from ...types.episode_response import EpisodeResponse -from ...types.graph_node_neighbor import GraphNodeNeighbor -from ...types.search_filters import SearchFilters -from ...types.success_response import SuccessResponse +from ...types.add_nodes_result import AddNodesResult +from ...types.async_result import AsyncResult +from ...types.neighbor_entry import NeighborEntry +from ...types.neighbor_page import NeighborPage +from ...types.node import Node +from ...types.node_page import NodePage from .raw_client import AsyncRawNodeClient, RawNodeClient # this is used as the default value for optional parameters @@ -31,54 +32,31 @@ def with_raw_response(self) -> RawNodeClient: """ return self._raw_client - def get_by_graph_id( + def add( self, - graph_id: str, + graph_uuid: 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, + nodes: typing.Optional[typing.Sequence[typing.Dict[str, typing.Any]]] = OMIT, + idempotency_key: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, - ) -> typing.List[EntityNode]: + ) -> AddNodesResult: """ - Returns all nodes for a graph. - 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 + graph_uuid : str + Graph UUID - order_by : typing.Optional[str] - Field to sort by. One of "created_at", "valid_at", or "uuid" (default "uuid"). + nodes : typing.Optional[typing.Sequence[typing.Dict[str, typing.Any]]] - 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. + idempotency_key : typing.Optional[str] request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - typing.List[EntityNode] - Nodes + AddNodesResult + Accepted Examples -------- @@ -87,70 +65,48 @@ def get_by_graph_id( client = Zep( api_key="YOUR_API_KEY", ) - client.graph.node.get_by_graph_id( - graph_id="graph_id", + client.graph.node.add( + graph_uuid="graph_uuid", ) """ - _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, + _response = self._raw_client.add( + graph_uuid, nodes=nodes, idempotency_key=idempotency_key, request_options=request_options ) return _response.data - def get_by_user_id( + def list( self, - user_id: str, + graph_uuid: 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, + limit: typing.Optional[int] = None, + cursor: typing.Optional[str] = None, + filters: typing.Optional[typing.Dict[str, typing.Any]] = OMIT, + idempotency_key: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, - ) -> typing.List[EntityNode]: + ) -> SyncPager[Node, NodePage]: """ - Returns all nodes for a user - Parameters ---------- - user_id : str - User 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. + graph_uuid : str + Graph UUID limit : typing.Optional[int] - Maximum number of items to return + Page size - order_by : typing.Optional[str] - Field to sort by. One of "created_at", "valid_at", or "uuid" (default "uuid"). + cursor : typing.Optional[str] + Opaque page cursor - uuid_cursor : typing.Optional[str] - UUID based cursor, used for pagination. Should be the UUID of the last item in the previous page. + filters : typing.Optional[typing.Dict[str, typing.Any]] - Deprecated: prefer Cursor, the opaque cursor returned via the Zep-Next-Cursor response header. + idempotency_key : typing.Optional[str] request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - typing.List[EntityNode] - Nodes + SyncPager[Node, NodePage] + OK Examples -------- @@ -159,30 +115,33 @@ def get_by_user_id( client = Zep( api_key="YOUR_API_KEY", ) - client.graph.node.get_by_user_id( - user_id="user_id", + response = client.graph.node.list( + graph_uuid="graph_uuid", + limit=1, + cursor="cursor", ) + for item in response: + yield item + # alternatively, you can paginate page-by-page + for page in response.iter_pages(): + yield page """ - _response = self._raw_client.get_by_user_id( - user_id, + return self._raw_client.list( + graph_uuid, + limit=limit, cursor=cursor, - direction=direction, filters=filters, - limit=limit, - order_by=order_by, - uuid_cursor=uuid_cursor, + idempotency_key=idempotency_key, request_options=request_options, ) - return _response.data - def get_edges( - self, node_uuid: str, *, request_options: typing.Optional[RequestOptions] = None - ) -> typing.List[EntityEdge]: + def get(self, graph_uuid: str, node_uuid: str, *, request_options: typing.Optional[RequestOptions] = None) -> Node: """ - Deprecated. Use edge listing with `filters.connected_node_uuids`, or the neighbors endpoint (`POST /graph/node/{node_uuid}/neighbors`), instead. Returns all edges for a node, subject to an internal cap; responses reduced by that cap set the Zep-Truncated header. - Parameters ---------- + graph_uuid : str + Graph UUID + node_uuid : str Node UUID @@ -191,8 +150,8 @@ def get_edges( Returns ------- - typing.List[EntityEdge] - Edges + Node + OK Examples -------- @@ -201,31 +160,40 @@ def get_edges( client = Zep( api_key="YOUR_API_KEY", ) - client.graph.node.get_edges( + client.graph.node.get( + graph_uuid="graph_uuid", node_uuid="node_uuid", ) """ - _response = self._raw_client.get_edges(node_uuid, request_options=request_options) + _response = self._raw_client.get(graph_uuid, node_uuid, request_options=request_options) return _response.data - def get_episodes( - self, node_uuid: str, *, request_options: typing.Optional[RequestOptions] = None - ) -> EpisodeResponse: + def delete( + self, + graph_uuid: str, + node_uuid: str, + *, + idempotency_key: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncResult: """ - 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. - Parameters ---------- + graph_uuid : str + Graph UUID + node_uuid : str Node UUID + idempotency_key : typing.Optional[str] + request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - EpisodeResponse - Episodes + AsyncResult + Accepted Examples -------- @@ -234,68 +202,53 @@ def get_episodes( client = Zep( api_key="YOUR_API_KEY", ) - client.graph.node.get_episodes( + client.graph.node.delete( + graph_uuid="graph_uuid", node_uuid="node_uuid", ) """ - _response = self._raw_client.get_episodes(node_uuid, request_options=request_options) + _response = self._raw_client.delete( + graph_uuid, node_uuid, idempotency_key=idempotency_key, request_options=request_options + ) return _response.data - def get_neighbors( + def update( self, + graph_uuid: str, node_uuid: str, *, - cursor: typing.Optional[str] = OMIT, - direction: typing.Optional[str] = OMIT, - direction_sort: typing.Optional[str] = OMIT, - filters: typing.Optional[SearchFilters] = OMIT, - limit: typing.Optional[int] = OMIT, - order_by: typing.Optional[str] = OMIT, + attributes: typing.Optional[typing.Dict[str, typing.Any]] = OMIT, + name: typing.Optional[str] = OMIT, + summary: typing.Optional[str] = OMIT, + idempotency_key: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, - ) -> typing.List[GraphNodeNeighbor]: + ) -> Node: """ - Enumerates the distinct entity nodes directly connected to a node, together with the edges connecting each to it. - Parameters ---------- + graph_uuid : str + Graph UUID + node_uuid : str Node UUID - cursor : typing.Optional[str] - Opaque cursor for pagination, obtained from the Zep-Next-Cursor - response header of the previous page. - - direction : typing.Optional[str] - Orientation of the connecting edge relative to the anchor node: "out" - (anchor is the edge's source), "in" (anchor is the edge's target), or - "both" (either). Defaults to "both". - - direction_sort : typing.Optional[str] - Sort direction for order_by. One of "asc" or "desc". Defaults to - "desc". Named direction_sort to avoid clashing with the traversal - Direction field above. + attributes : typing.Optional[typing.Dict[str, typing.Any]] - 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_labels/exclude_node_labels). Reuses the graph.search filter - type. + name : typing.Optional[str] + Omit to leave unchanged, send JSON null to clear, or send a value to set. - limit : typing.Optional[int] - Maximum number of neighbor nodes to return. An explicit value is - clamped to 50; when omitted, the default page size (100) applies. + summary : typing.Optional[str] + Omit to leave unchanged, send JSON null to clear, or send a value to set. - order_by : typing.Optional[str] - Field to sort neighbor nodes by. One of "uuid" or "created_at". - Defaults to "uuid". + idempotency_key : typing.Optional[str] request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - typing.List[GraphNodeNeighbor] - Neighbors + Node + OK Examples -------- @@ -304,121 +257,62 @@ def get_neighbors( client = Zep( api_key="YOUR_API_KEY", ) - client.graph.node.get_neighbors( + client.graph.node.update( + graph_uuid="graph_uuid", node_uuid="node_uuid", ) """ - _response = self._raw_client.get_neighbors( + _response = self._raw_client.update( + graph_uuid, node_uuid, - cursor=cursor, - direction=direction, - direction_sort=direction_sort, - filters=filters, - limit=limit, - order_by=order_by, + attributes=attributes, + name=name, + summary=summary, + idempotency_key=idempotency_key, request_options=request_options, ) return _response.data - def get(self, uuid_: str, *, request_options: typing.Optional[RequestOptions] = None) -> EntityNode: - """ - Returns a specific node by its UUID. - - Parameters - ---------- - uuid_ : str - Node UUID - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - EntityNode - Node - - Examples - -------- - from zep_cloud import Zep - - client = Zep( - api_key="YOUR_API_KEY", - ) - client.graph.node.get( - uuid_="uuid", - ) - """ - _response = self._raw_client.get(uuid_, request_options=request_options) - return _response.data - - def delete(self, uuid_: str, *, request_options: typing.Optional[RequestOptions] = None) -> SuccessResponse: - """ - Deletes a node by UUID. - - Parameters - ---------- - uuid_ : str - Node UUID - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - SuccessResponse - Node deleted - - Examples - -------- - from zep_cloud import Zep - - client = Zep( - api_key="YOUR_API_KEY", - ) - client.graph.node.delete( - uuid_="uuid", - ) - """ - _response = self._raw_client.delete(uuid_, request_options=request_options) - return _response.data - - def update( + def list_neighbors( self, - uuid_: str, + graph_uuid: str, + node_uuid: str, *, - attributes: typing.Optional[typing.Dict[str, typing.Optional[typing.Any]]] = OMIT, - labels: typing.Optional[typing.Sequence[str]] = OMIT, - name: typing.Optional[str] = OMIT, - summary: typing.Optional[str] = OMIT, + limit: typing.Optional[int] = None, + cursor: typing.Optional[str] = None, + direction: typing.Optional[str] = OMIT, + filters: typing.Optional[typing.Dict[str, typing.Any]] = OMIT, + idempotency_key: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, - ) -> EntityNode: + ) -> SyncPager[NeighborEntry, NeighborPage]: """ - Updates an entity node by UUID. - Parameters ---------- - uuid_ : str + graph_uuid : str + Graph UUID + + node_uuid : str Node UUID - attributes : typing.Optional[typing.Dict[str, typing.Optional[typing.Any]]] - Updated attributes. Merged with existing attributes. Set a key to null to delete it. + limit : typing.Optional[int] + Page size - labels : typing.Optional[typing.Sequence[str]] - Updated labels for the node + cursor : typing.Optional[str] + Opaque page cursor - name : typing.Optional[str] - Updated name for the node + direction : typing.Optional[str] - summary : typing.Optional[str] - Updated summary for the node + filters : typing.Optional[typing.Dict[str, typing.Any]] + + idempotency_key : typing.Optional[str] request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - EntityNode - Updated node + SyncPager[NeighborEntry, NeighborPage] + OK Examples -------- @@ -427,14 +321,28 @@ def update( client = Zep( api_key="YOUR_API_KEY", ) - client.graph.node.update( - uuid_="uuid", + response = client.graph.node.list_neighbors( + graph_uuid="graph_uuid", + node_uuid="node_uuid", + limit=1, + cursor="cursor", ) + for item in response: + yield item + # alternatively, you can paginate page-by-page + for page in response.iter_pages(): + yield page """ - _response = self._raw_client.update( - uuid_, attributes=attributes, labels=labels, name=name, summary=summary, request_options=request_options + return self._raw_client.list_neighbors( + graph_uuid, + node_uuid, + limit=limit, + cursor=cursor, + direction=direction, + filters=filters, + idempotency_key=idempotency_key, + request_options=request_options, ) - return _response.data class AsyncNodeClient: @@ -452,54 +360,31 @@ def with_raw_response(self) -> AsyncRawNodeClient: """ return self._raw_client - async def get_by_graph_id( + async def add( self, - graph_id: str, + graph_uuid: 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, + nodes: typing.Optional[typing.Sequence[typing.Dict[str, typing.Any]]] = OMIT, + idempotency_key: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, - ) -> typing.List[EntityNode]: + ) -> AddNodesResult: """ - Returns all nodes for a graph. - 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 + graph_uuid : str + Graph UUID - order_by : typing.Optional[str] - Field to sort by. One of "created_at", "valid_at", or "uuid" (default "uuid"). + nodes : typing.Optional[typing.Sequence[typing.Dict[str, typing.Any]]] - 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. + idempotency_key : typing.Optional[str] request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - typing.List[EntityNode] - Nodes + AddNodesResult + Accepted Examples -------- @@ -513,73 +398,51 @@ async def get_by_graph_id( async def main() -> None: - await client.graph.node.get_by_graph_id( - graph_id="graph_id", + await client.graph.node.add( + graph_uuid="graph_uuid", ) 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, + _response = await self._raw_client.add( + graph_uuid, nodes=nodes, idempotency_key=idempotency_key, request_options=request_options ) return _response.data - async def get_by_user_id( + async def list( self, - user_id: str, + graph_uuid: 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, + limit: typing.Optional[int] = None, + cursor: typing.Optional[str] = None, + filters: typing.Optional[typing.Dict[str, typing.Any]] = OMIT, + idempotency_key: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, - ) -> typing.List[EntityNode]: + ) -> AsyncPager[Node, NodePage]: """ - Returns all nodes for a user - Parameters ---------- - user_id : str - User 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. + graph_uuid : str + Graph UUID limit : typing.Optional[int] - Maximum number of items to return + Page size - order_by : typing.Optional[str] - Field to sort by. One of "created_at", "valid_at", or "uuid" (default "uuid"). + cursor : typing.Optional[str] + Opaque page cursor - uuid_cursor : typing.Optional[str] - UUID based cursor, used for pagination. Should be the UUID of the last item in the previous page. + filters : typing.Optional[typing.Dict[str, typing.Any]] - Deprecated: prefer Cursor, the opaque cursor returned via the Zep-Next-Cursor response header. + idempotency_key : typing.Optional[str] request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - typing.List[EntityNode] - Nodes + AsyncPager[Node, NodePage] + OK Examples -------- @@ -593,33 +456,39 @@ async def get_by_user_id( async def main() -> None: - await client.graph.node.get_by_user_id( - user_id="user_id", + response = await client.graph.node.list( + graph_uuid="graph_uuid", + limit=1, + cursor="cursor", ) + async for item in response: + yield item + + # alternatively, you can paginate page-by-page + async for page in response.iter_pages(): + yield page asyncio.run(main()) """ - _response = await self._raw_client.get_by_user_id( - user_id, + return await self._raw_client.list( + graph_uuid, + limit=limit, cursor=cursor, - direction=direction, filters=filters, - limit=limit, - order_by=order_by, - uuid_cursor=uuid_cursor, + idempotency_key=idempotency_key, request_options=request_options, ) - return _response.data - async def get_edges( - self, node_uuid: str, *, request_options: typing.Optional[RequestOptions] = None - ) -> typing.List[EntityEdge]: + async def get( + self, graph_uuid: str, node_uuid: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> Node: """ - Deprecated. Use edge listing with `filters.connected_node_uuids`, or the neighbors endpoint (`POST /graph/node/{node_uuid}/neighbors`), instead. Returns all edges for a node, subject to an internal cap; responses reduced by that cap set the Zep-Truncated header. - Parameters ---------- + graph_uuid : str + Graph UUID + node_uuid : str Node UUID @@ -628,8 +497,8 @@ async def get_edges( Returns ------- - typing.List[EntityEdge] - Edges + Node + OK Examples -------- @@ -643,34 +512,43 @@ async def get_edges( async def main() -> None: - await client.graph.node.get_edges( + await client.graph.node.get( + graph_uuid="graph_uuid", node_uuid="node_uuid", ) asyncio.run(main()) """ - _response = await self._raw_client.get_edges(node_uuid, request_options=request_options) + _response = await self._raw_client.get(graph_uuid, node_uuid, request_options=request_options) return _response.data - async def get_episodes( - self, node_uuid: str, *, request_options: typing.Optional[RequestOptions] = None - ) -> EpisodeResponse: + async def delete( + self, + graph_uuid: str, + node_uuid: str, + *, + idempotency_key: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncResult: """ - 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. - Parameters ---------- + graph_uuid : str + Graph UUID + node_uuid : str Node UUID + idempotency_key : typing.Optional[str] + request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - EpisodeResponse - Episodes + AsyncResult + Accepted Examples -------- @@ -684,71 +562,56 @@ async def get_episodes( async def main() -> None: - await client.graph.node.get_episodes( + await client.graph.node.delete( + graph_uuid="graph_uuid", node_uuid="node_uuid", ) asyncio.run(main()) """ - _response = await self._raw_client.get_episodes(node_uuid, request_options=request_options) + _response = await self._raw_client.delete( + graph_uuid, node_uuid, idempotency_key=idempotency_key, request_options=request_options + ) return _response.data - async def get_neighbors( + async def update( self, + graph_uuid: str, node_uuid: str, *, - cursor: typing.Optional[str] = OMIT, - direction: typing.Optional[str] = OMIT, - direction_sort: typing.Optional[str] = OMIT, - filters: typing.Optional[SearchFilters] = OMIT, - limit: typing.Optional[int] = OMIT, - order_by: typing.Optional[str] = OMIT, + attributes: typing.Optional[typing.Dict[str, typing.Any]] = OMIT, + name: typing.Optional[str] = OMIT, + summary: typing.Optional[str] = OMIT, + idempotency_key: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, - ) -> typing.List[GraphNodeNeighbor]: + ) -> Node: """ - Enumerates the distinct entity nodes directly connected to a node, together with the edges connecting each to it. - Parameters ---------- + graph_uuid : str + Graph UUID + node_uuid : str Node UUID - cursor : typing.Optional[str] - Opaque cursor for pagination, obtained from the Zep-Next-Cursor - response header of the previous page. - - direction : typing.Optional[str] - Orientation of the connecting edge relative to the anchor node: "out" - (anchor is the edge's source), "in" (anchor is the edge's target), or - "both" (either). Defaults to "both". + attributes : typing.Optional[typing.Dict[str, typing.Any]] - direction_sort : typing.Optional[str] - Sort direction for order_by. One of "asc" or "desc". Defaults to - "desc". Named direction_sort to avoid clashing with the traversal - 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_labels/exclude_node_labels). Reuses the graph.search filter - type. + name : typing.Optional[str] + Omit to leave unchanged, send JSON null to clear, or send a value to set. - limit : typing.Optional[int] - Maximum number of neighbor nodes to return. An explicit value is - clamped to 50; when omitted, the default page size (100) applies. + summary : typing.Optional[str] + Omit to leave unchanged, send JSON null to clear, or send a value to set. - order_by : typing.Optional[str] - Field to sort neighbor nodes by. One of "uuid" or "created_at". - Defaults to "uuid". + idempotency_key : typing.Optional[str] request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - typing.List[GraphNodeNeighbor] - Neighbors + Node + OK Examples -------- @@ -762,140 +625,65 @@ async def get_neighbors( async def main() -> None: - await client.graph.node.get_neighbors( + await client.graph.node.update( + graph_uuid="graph_uuid", node_uuid="node_uuid", ) asyncio.run(main()) """ - _response = await self._raw_client.get_neighbors( + _response = await self._raw_client.update( + graph_uuid, node_uuid, - cursor=cursor, - direction=direction, - direction_sort=direction_sort, - filters=filters, - limit=limit, - order_by=order_by, + attributes=attributes, + name=name, + summary=summary, + idempotency_key=idempotency_key, request_options=request_options, ) return _response.data - async def get(self, uuid_: str, *, request_options: typing.Optional[RequestOptions] = None) -> EntityNode: - """ - Returns a specific node by its UUID. - - Parameters - ---------- - uuid_ : str - Node UUID - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - EntityNode - Node - - Examples - -------- - import asyncio - - from zep_cloud import AsyncZep - - client = AsyncZep( - api_key="YOUR_API_KEY", - ) - - - async def main() -> None: - await client.graph.node.get( - uuid_="uuid", - ) - - - asyncio.run(main()) - """ - _response = await self._raw_client.get(uuid_, request_options=request_options) - return _response.data - - async def delete(self, uuid_: str, *, request_options: typing.Optional[RequestOptions] = None) -> SuccessResponse: - """ - Deletes a node by UUID. - - Parameters - ---------- - uuid_ : str - Node UUID - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - SuccessResponse - Node deleted - - Examples - -------- - import asyncio - - from zep_cloud import AsyncZep - - client = AsyncZep( - api_key="YOUR_API_KEY", - ) - - - async def main() -> None: - await client.graph.node.delete( - uuid_="uuid", - ) - - - asyncio.run(main()) - """ - _response = await self._raw_client.delete(uuid_, request_options=request_options) - return _response.data - - async def update( + async def list_neighbors( self, - uuid_: str, + graph_uuid: str, + node_uuid: str, *, - attributes: typing.Optional[typing.Dict[str, typing.Optional[typing.Any]]] = OMIT, - labels: typing.Optional[typing.Sequence[str]] = OMIT, - name: typing.Optional[str] = OMIT, - summary: typing.Optional[str] = OMIT, + limit: typing.Optional[int] = None, + cursor: typing.Optional[str] = None, + direction: typing.Optional[str] = OMIT, + filters: typing.Optional[typing.Dict[str, typing.Any]] = OMIT, + idempotency_key: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, - ) -> EntityNode: + ) -> AsyncPager[NeighborEntry, NeighborPage]: """ - Updates an entity node by UUID. - Parameters ---------- - uuid_ : str + graph_uuid : str + Graph UUID + + node_uuid : str Node UUID - attributes : typing.Optional[typing.Dict[str, typing.Optional[typing.Any]]] - Updated attributes. Merged with existing attributes. Set a key to null to delete it. + limit : typing.Optional[int] + Page size - labels : typing.Optional[typing.Sequence[str]] - Updated labels for the node + cursor : typing.Optional[str] + Opaque page cursor - name : typing.Optional[str] - Updated name for the node + direction : typing.Optional[str] - summary : typing.Optional[str] - Updated summary for the node + filters : typing.Optional[typing.Dict[str, typing.Any]] + + idempotency_key : typing.Optional[str] request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - EntityNode - Updated node + AsyncPager[NeighborEntry, NeighborPage] + OK Examples -------- @@ -909,14 +697,29 @@ async def update( async def main() -> None: - await client.graph.node.update( - uuid_="uuid", + response = await client.graph.node.list_neighbors( + graph_uuid="graph_uuid", + node_uuid="node_uuid", + limit=1, + cursor="cursor", ) + async for item in response: + yield item + + # alternatively, you can paginate page-by-page + async for page in response.iter_pages(): + yield page asyncio.run(main()) """ - _response = await self._raw_client.update( - uuid_, attributes=attributes, labels=labels, name=name, summary=summary, request_options=request_options + return await self._raw_client.list_neighbors( + graph_uuid, + node_uuid, + limit=limit, + cursor=cursor, + direction=direction, + filters=filters, + idempotency_key=idempotency_key, + request_options=request_options, ) - return _response.data diff --git a/src/zep_cloud/graph/node/raw_client.py b/src/zep_cloud/graph/node/raw_client.py index 879bf2f6..7a6ad915 100644 --- a/src/zep_cloud/graph/node/raw_client.py +++ b/src/zep_cloud/graph/node/raw_client.py @@ -7,19 +7,21 @@ from ...core.client_wrapper import AsyncClientWrapper, SyncClientWrapper from ...core.http_response import AsyncHttpResponse, HttpResponse from ...core.jsonable_encoder import jsonable_encoder +from ...core.pagination import AsyncPager, SyncPager +from ...core.parse_error import ParsingError 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 ...errors.unauthorized_error import UnauthorizedError +from ...types.add_nodes_result import AddNodesResult from ...types.api_error import ApiError as types_api_error_ApiError -from ...types.entity_edge import EntityEdge -from ...types.entity_node import EntityNode -from ...types.episode_response import EpisodeResponse -from ...types.graph_node_neighbor import GraphNodeNeighbor -from ...types.search_filters import SearchFilters -from ...types.success_response import SuccessResponse +from ...types.async_result import AsyncResult +from ...types.neighbor_entry import NeighborEntry +from ...types.neighbor_page import NeighborPage +from ...types.node import Node +from ...types.node_page import NodePage +from pydantic import ValidationError # this is used as the default value for optional parameters OMIT = typing.cast(typing.Any, ...) @@ -29,67 +31,41 @@ class RawNodeClient: def __init__(self, *, client_wrapper: SyncClientWrapper): self._client_wrapper = client_wrapper - def get_by_graph_id( + def add( self, - graph_id: str, + graph_uuid: 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, + nodes: typing.Optional[typing.Sequence[typing.Dict[str, typing.Any]]] = OMIT, + idempotency_key: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, - ) -> HttpResponse[typing.List[EntityNode]]: + ) -> HttpResponse[AddNodesResult]: """ - Returns all nodes for a graph. - 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"). + graph_uuid : str + Graph UUID - uuid_cursor : typing.Optional[str] - UUID based cursor, used for pagination. Should be the UUID of the last item in the previous page. + nodes : typing.Optional[typing.Sequence[typing.Dict[str, typing.Any]]] - Deprecated: prefer Cursor, the opaque cursor returned via the Zep-Next-Cursor response header. + idempotency_key : typing.Optional[str] request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - HttpResponse[typing.List[EntityNode]] - Nodes + HttpResponse[AddNodesResult] + Accepted """ _response = self._client_wrapper.httpx_client.request( - f"graph/node/graph/{jsonable_encoder(graph_id)}", + f"graphs/{jsonable_encoder(graph_uuid)}/nodes", 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, + "nodes": nodes, + }, + headers={ + "content-type": "application/json", + "Idempotency-Key": str(idempotency_key) if idempotency_key is not None else None, }, request_options=request_options, omit=OMIT, @@ -97,9 +73,9 @@ def get_by_graph_id( try: if 200 <= _response.status_code < 300: _data = typing.cast( - typing.List[EntityNode], + AddNodesResult, parse_obj_as( - type_=typing.List[EntityNode], # type: ignore + type_=AddNodesResult, # type: ignore object_=_response.json(), ), ) @@ -108,15 +84,26 @@ def get_by_graph_id( raise BadRequestError( headers=dict(_response.headers), body=typing.cast( - typing.Optional[typing.Any], + types_api_error_ApiError, + parse_obj_as( + type_=types_api_error_ApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 401: + raise UnauthorizedError( + headers=dict(_response.headers), + body=typing.cast( + types_api_error_ApiError, parse_obj_as( - type_=typing.Optional[typing.Any], # type: ignore + type_=types_api_error_ApiError, # type: ignore object_=_response.json(), ), ), ) - if _response.status_code == 500: - raise InternalServerError( + if _response.status_code == 404: + raise NotFoundError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -131,98 +118,110 @@ def get_by_graph_id( raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response.text ) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response_json ) - def get_by_user_id( + def list( self, - user_id: str, + graph_uuid: 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, + limit: typing.Optional[int] = None, + cursor: typing.Optional[str] = None, + filters: typing.Optional[typing.Dict[str, typing.Any]] = OMIT, + idempotency_key: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, - ) -> HttpResponse[typing.List[EntityNode]]: + ) -> SyncPager[Node, NodePage]: """ - Returns all nodes for a user - Parameters ---------- - user_id : str - User 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. + graph_uuid : str + Graph UUID limit : typing.Optional[int] - Maximum number of items to return + Page size - order_by : typing.Optional[str] - Field to sort by. One of "created_at", "valid_at", or "uuid" (default "uuid"). + cursor : typing.Optional[str] + Opaque page cursor - uuid_cursor : typing.Optional[str] - UUID based cursor, used for pagination. Should be the UUID of the last item in the previous page. + filters : typing.Optional[typing.Dict[str, typing.Any]] - Deprecated: prefer Cursor, the opaque cursor returned via the Zep-Next-Cursor response header. + idempotency_key : typing.Optional[str] request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - HttpResponse[typing.List[EntityNode]] - Nodes + SyncPager[Node, NodePage] + OK """ _response = self._client_wrapper.httpx_client.request( - f"graph/node/user/{jsonable_encoder(user_id)}", + f"graphs/{jsonable_encoder(graph_uuid)}/nodes/list", method="POST", - json={ - "cursor": cursor, - "direction": direction, - "filters": convert_and_respect_annotation_metadata( - object_=filters, annotation=SearchFilters, direction="write" - ), + params={ "limit": limit, - "order_by": order_by, - "uuid_cursor": uuid_cursor, + "cursor": cursor, + }, + json={ + "filters": filters, + }, + headers={ + "content-type": "application/json", + "Idempotency-Key": str(idempotency_key) if idempotency_key is not None else None, }, request_options=request_options, omit=OMIT, ) try: if 200 <= _response.status_code < 300: - _data = typing.cast( - typing.List[EntityNode], + _parsed_response = typing.cast( + NodePage, parse_obj_as( - type_=typing.List[EntityNode], # type: ignore + type_=NodePage, # type: ignore object_=_response.json(), ), ) - return HttpResponse(response=_response, data=_data) + _items = _parsed_response.items + _parsed_next = _parsed_response.next_cursor + _has_next = _parsed_next is not None and _parsed_next != "" + _get_next = lambda: self.list( + graph_uuid, + limit=limit, + cursor=_parsed_next, + filters=filters, + idempotency_key=idempotency_key, + request_options=request_options, + ) + return SyncPager(has_next=_has_next, items=_items, get_next=_get_next, response=_parsed_response) if _response.status_code == 400: raise BadRequestError( headers=dict(_response.headers), body=typing.cast( - typing.Optional[typing.Any], + types_api_error_ApiError, + parse_obj_as( + type_=types_api_error_ApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 401: + raise UnauthorizedError( + headers=dict(_response.headers), + body=typing.cast( + types_api_error_ApiError, parse_obj_as( - type_=typing.Optional[typing.Any], # type: ignore + type_=types_api_error_ApiError, # type: ignore object_=_response.json(), ), ), ) - if _response.status_code == 500: - raise InternalServerError( + if _response.status_code == 404: + raise NotFoundError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -237,18 +236,23 @@ def get_by_user_id( raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response.text ) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response_json ) - def get_edges( - self, node_uuid: str, *, request_options: typing.Optional[RequestOptions] = None - ) -> HttpResponse[typing.List[EntityEdge]]: + def get( + self, graph_uuid: str, node_uuid: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[Node]: """ - Deprecated. Use edge listing with `filters.connected_node_uuids`, or the neighbors endpoint (`POST /graph/node/{node_uuid}/neighbors`), instead. Returns all edges for a node, subject to an internal cap; responses reduced by that cap set the Zep-Truncated header. - Parameters ---------- + graph_uuid : str + Graph UUID + node_uuid : str Node UUID @@ -257,20 +261,20 @@ def get_edges( Returns ------- - HttpResponse[typing.List[EntityEdge]] - Edges + HttpResponse[Node] + OK """ _response = self._client_wrapper.httpx_client.request( - f"graph/node/{jsonable_encoder(node_uuid)}/entity-edges", + f"graphs/{jsonable_encoder(graph_uuid)}/nodes/{jsonable_encoder(node_uuid)}", method="GET", request_options=request_options, ) try: if 200 <= _response.status_code < 300: _data = typing.cast( - typing.List[EntityEdge], + Node, parse_obj_as( - type_=typing.List[EntityEdge], # type: ignore + type_=Node, # type: ignore object_=_response.json(), ), ) @@ -279,15 +283,26 @@ def get_edges( raise BadRequestError( headers=dict(_response.headers), body=typing.cast( - typing.Optional[typing.Any], + types_api_error_ApiError, + parse_obj_as( + type_=types_api_error_ApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 401: + raise UnauthorizedError( + headers=dict(_response.headers), + body=typing.cast( + types_api_error_ApiError, parse_obj_as( - type_=typing.Optional[typing.Any], # type: ignore + type_=types_api_error_ApiError, # type: ignore object_=_response.json(), ), ), ) - if _response.status_code == 500: - raise InternalServerError( + if _response.status_code == 404: + raise NotFoundError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -302,40 +317,55 @@ def get_edges( raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response.text ) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response_json ) - def get_episodes( - self, node_uuid: str, *, request_options: typing.Optional[RequestOptions] = None - ) -> HttpResponse[EpisodeResponse]: + def delete( + self, + graph_uuid: str, + node_uuid: str, + *, + idempotency_key: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[AsyncResult]: """ - 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. - Parameters ---------- + graph_uuid : str + Graph UUID + node_uuid : str Node UUID + idempotency_key : typing.Optional[str] + request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - HttpResponse[EpisodeResponse] - Episodes + HttpResponse[AsyncResult] + Accepted """ _response = self._client_wrapper.httpx_client.request( - f"graph/node/{jsonable_encoder(node_uuid)}/episodes", - method="GET", + f"graphs/{jsonable_encoder(graph_uuid)}/nodes/{jsonable_encoder(node_uuid)}", + method="DELETE", + headers={ + "Idempotency-Key": str(idempotency_key) if idempotency_key is not None else None, + }, request_options=request_options, ) try: if 200 <= _response.status_code < 300: _data = typing.cast( - EpisodeResponse, + AsyncResult, parse_obj_as( - type_=EpisodeResponse, # type: ignore + type_=AsyncResult, # type: ignore object_=_response.json(), ), ) @@ -344,15 +374,26 @@ def get_episodes( raise BadRequestError( headers=dict(_response.headers), body=typing.cast( - typing.Optional[typing.Any], + types_api_error_ApiError, + parse_obj_as( + type_=types_api_error_ApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 401: + raise UnauthorizedError( + headers=dict(_response.headers), + body=typing.cast( + types_api_error_ApiError, parse_obj_as( - type_=typing.Optional[typing.Any], # type: ignore + type_=types_api_error_ApiError, # type: ignore object_=_response.json(), ), ), ) - if _response.status_code == 500: - raise InternalServerError( + if _response.status_code == 404: + raise NotFoundError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -367,81 +408,63 @@ def get_episodes( raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response.text ) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response_json ) - def get_neighbors( + def update( self, + graph_uuid: str, node_uuid: str, *, - cursor: typing.Optional[str] = OMIT, - direction: typing.Optional[str] = OMIT, - direction_sort: typing.Optional[str] = OMIT, - filters: typing.Optional[SearchFilters] = OMIT, - limit: typing.Optional[int] = OMIT, - order_by: typing.Optional[str] = OMIT, + attributes: typing.Optional[typing.Dict[str, typing.Any]] = OMIT, + name: typing.Optional[str] = OMIT, + summary: typing.Optional[str] = OMIT, + idempotency_key: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, - ) -> HttpResponse[typing.List[GraphNodeNeighbor]]: + ) -> HttpResponse[Node]: """ - Enumerates the distinct entity nodes directly connected to a node, together with the edges connecting each to it. - Parameters ---------- + graph_uuid : str + Graph UUID + node_uuid : str Node UUID - cursor : typing.Optional[str] - Opaque cursor for pagination, obtained from the Zep-Next-Cursor - response header of the previous page. - - direction : typing.Optional[str] - Orientation of the connecting edge relative to the anchor node: "out" - (anchor is the edge's source), "in" (anchor is the edge's target), or - "both" (either). Defaults to "both". - - direction_sort : typing.Optional[str] - Sort direction for order_by. One of "asc" or "desc". Defaults to - "desc". Named direction_sort to avoid clashing with the traversal - Direction field above. + attributes : typing.Optional[typing.Dict[str, typing.Any]] - 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_labels/exclude_node_labels). Reuses the graph.search filter - type. + name : typing.Optional[str] + Omit to leave unchanged, send JSON null to clear, or send a value to set. - limit : typing.Optional[int] - Maximum number of neighbor nodes to return. An explicit value is - clamped to 50; when omitted, the default page size (100) applies. + summary : typing.Optional[str] + Omit to leave unchanged, send JSON null to clear, or send a value to set. - order_by : typing.Optional[str] - Field to sort neighbor nodes by. One of "uuid" or "created_at". - Defaults to "uuid". + idempotency_key : typing.Optional[str] request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - HttpResponse[typing.List[GraphNodeNeighbor]] - Neighbors + HttpResponse[Node] + OK """ _response = self._client_wrapper.httpx_client.request( - f"graph/node/{jsonable_encoder(node_uuid)}/neighbors", - method="POST", + f"graphs/{jsonable_encoder(graph_uuid)}/nodes/{jsonable_encoder(node_uuid)}", + method="PATCH", json={ - "cursor": cursor, - "direction": direction, - "direction_sort": direction_sort, - "filters": convert_and_respect_annotation_metadata( - object_=filters, annotation=SearchFilters, direction="write" - ), - "limit": limit, - "order_by": order_by, + "attributes": attributes, + "name": name, + "summary": summary, }, headers={ "content-type": "application/json", + "Idempotency-Key": str(idempotency_key) if idempotency_key is not None else None, }, request_options=request_options, omit=OMIT, @@ -449,9 +472,9 @@ def get_neighbors( try: if 200 <= _response.status_code < 300: _data = typing.cast( - typing.List[GraphNodeNeighbor], + Node, parse_obj_as( - type_=typing.List[GraphNodeNeighbor], # type: ignore + type_=Node, # type: ignore object_=_response.json(), ), ) @@ -460,15 +483,15 @@ def get_neighbors( raise BadRequestError( headers=dict(_response.headers), body=typing.cast( - typing.Optional[typing.Any], + types_api_error_ApiError, parse_obj_as( - type_=typing.Optional[typing.Any], # type: ignore + type_=types_api_error_ApiError, # type: ignore object_=_response.json(), ), ), ) - if _response.status_code == 404: - raise NotFoundError( + if _response.status_code == 401: + raise UnauthorizedError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -478,8 +501,8 @@ def get_neighbors( ), ), ) - if _response.status_code == 500: - raise InternalServerError( + if _response.status_code == 404: + raise NotFoundError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -494,55 +517,109 @@ def get_neighbors( raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response.text ) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response_json ) - def get(self, uuid_: str, *, request_options: typing.Optional[RequestOptions] = None) -> HttpResponse[EntityNode]: + def list_neighbors( + self, + graph_uuid: str, + node_uuid: str, + *, + limit: typing.Optional[int] = None, + cursor: typing.Optional[str] = None, + direction: typing.Optional[str] = OMIT, + filters: typing.Optional[typing.Dict[str, typing.Any]] = OMIT, + idempotency_key: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> SyncPager[NeighborEntry, NeighborPage]: """ - Returns a specific node by its UUID. - Parameters ---------- - uuid_ : str + graph_uuid : str + Graph UUID + + node_uuid : str Node UUID + limit : typing.Optional[int] + Page size + + cursor : typing.Optional[str] + Opaque page cursor + + direction : typing.Optional[str] + + filters : typing.Optional[typing.Dict[str, typing.Any]] + + idempotency_key : typing.Optional[str] + request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - HttpResponse[EntityNode] - Node + SyncPager[NeighborEntry, NeighborPage] + OK """ _response = self._client_wrapper.httpx_client.request( - f"graph/node/{jsonable_encoder(uuid_)}", - method="GET", + f"graphs/{jsonable_encoder(graph_uuid)}/nodes/{jsonable_encoder(node_uuid)}/neighbors", + method="POST", + params={ + "limit": limit, + "cursor": cursor, + }, + json={ + "direction": direction, + "filters": filters, + }, + headers={ + "content-type": "application/json", + "Idempotency-Key": str(idempotency_key) if idempotency_key is not None else None, + }, request_options=request_options, + omit=OMIT, ) try: if 200 <= _response.status_code < 300: - _data = typing.cast( - EntityNode, + _parsed_response = typing.cast( + NeighborPage, parse_obj_as( - type_=EntityNode, # type: ignore + type_=NeighborPage, # type: ignore object_=_response.json(), ), ) - return HttpResponse(response=_response, data=_data) + _items = _parsed_response.items + _parsed_next = _parsed_response.next_cursor + _has_next = _parsed_next is not None and _parsed_next != "" + _get_next = lambda: self.list_neighbors( + graph_uuid, + node_uuid, + limit=limit, + cursor=_parsed_next, + direction=direction, + filters=filters, + idempotency_key=idempotency_key, + request_options=request_options, + ) + return SyncPager(has_next=_has_next, items=_items, get_next=_get_next, response=_parsed_response) if _response.status_code == 400: raise BadRequestError( headers=dict(_response.headers), body=typing.cast( - typing.Optional[typing.Any], + types_api_error_ApiError, parse_obj_as( - type_=typing.Optional[typing.Any], # type: ignore + type_=types_api_error_ApiError, # type: ignore object_=_response.json(), ), ), ) - if _response.status_code == 404: - raise NotFoundError( + if _response.status_code == 401: + raise UnauthorizedError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -552,8 +629,8 @@ def get(self, uuid_: str, *, request_options: typing.Optional[RequestOptions] = ), ), ) - if _response.status_code == 500: - raise InternalServerError( + if _response.status_code == 404: + raise NotFoundError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -568,57 +645,81 @@ def get(self, uuid_: str, *, request_options: typing.Optional[RequestOptions] = raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response.text ) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response_json ) - def delete( - self, uuid_: str, *, request_options: typing.Optional[RequestOptions] = None - ) -> HttpResponse[SuccessResponse]: - """ - Deletes a node by UUID. +class AsyncRawNodeClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._client_wrapper = client_wrapper + + async def add( + self, + graph_uuid: str, + *, + nodes: typing.Optional[typing.Sequence[typing.Dict[str, typing.Any]]] = OMIT, + idempotency_key: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[AddNodesResult]: + """ Parameters ---------- - uuid_ : str - Node UUID + graph_uuid : str + Graph UUID + + nodes : typing.Optional[typing.Sequence[typing.Dict[str, typing.Any]]] + + idempotency_key : typing.Optional[str] request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - HttpResponse[SuccessResponse] - Node deleted + AsyncHttpResponse[AddNodesResult] + Accepted """ - _response = self._client_wrapper.httpx_client.request( - f"graph/node/{jsonable_encoder(uuid_)}", - method="DELETE", + _response = await self._client_wrapper.httpx_client.request( + f"graphs/{jsonable_encoder(graph_uuid)}/nodes", + method="POST", + json={ + "nodes": nodes, + }, + headers={ + "content-type": "application/json", + "Idempotency-Key": str(idempotency_key) if idempotency_key is not None else None, + }, request_options=request_options, + omit=OMIT, ) try: if 200 <= _response.status_code < 300: _data = typing.cast( - SuccessResponse, + AddNodesResult, parse_obj_as( - type_=SuccessResponse, # type: ignore + type_=AddNodesResult, # type: ignore object_=_response.json(), ), ) - return HttpResponse(response=_response, data=_data) + return AsyncHttpResponse(response=_response, data=_data) if _response.status_code == 400: raise BadRequestError( headers=dict(_response.headers), body=typing.cast( - typing.Optional[typing.Any], + types_api_error_ApiError, parse_obj_as( - type_=typing.Optional[typing.Any], # type: ignore + type_=types_api_error_ApiError, # type: ignore object_=_response.json(), ), ), ) - if _response.status_code == 404: - raise NotFoundError( + if _response.status_code == 401: + raise UnauthorizedError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -628,8 +729,8 @@ def delete( ), ), ) - if _response.status_code == 500: - raise InternalServerError( + if _response.status_code == 404: + raise NotFoundError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -644,86 +745,102 @@ def delete( raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response.text ) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response_json ) - def update( + async def list( self, - uuid_: str, + graph_uuid: str, *, - attributes: typing.Optional[typing.Dict[str, typing.Optional[typing.Any]]] = OMIT, - labels: typing.Optional[typing.Sequence[str]] = OMIT, - name: typing.Optional[str] = OMIT, - summary: typing.Optional[str] = OMIT, + limit: typing.Optional[int] = None, + cursor: typing.Optional[str] = None, + filters: typing.Optional[typing.Dict[str, typing.Any]] = OMIT, + idempotency_key: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, - ) -> HttpResponse[EntityNode]: + ) -> AsyncPager[Node, NodePage]: """ - Updates an entity node by UUID. - Parameters ---------- - uuid_ : str - Node UUID + graph_uuid : str + Graph UUID - attributes : typing.Optional[typing.Dict[str, typing.Optional[typing.Any]]] - Updated attributes. Merged with existing attributes. Set a key to null to delete it. + limit : typing.Optional[int] + Page size - labels : typing.Optional[typing.Sequence[str]] - Updated labels for the node + cursor : typing.Optional[str] + Opaque page cursor - name : typing.Optional[str] - Updated name for the node + filters : typing.Optional[typing.Dict[str, typing.Any]] - summary : typing.Optional[str] - Updated summary for the node + idempotency_key : typing.Optional[str] request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - HttpResponse[EntityNode] - Updated node + AsyncPager[Node, NodePage] + OK """ - _response = self._client_wrapper.httpx_client.request( - f"graph/node/{jsonable_encoder(uuid_)}", - method="PATCH", + _response = await self._client_wrapper.httpx_client.request( + f"graphs/{jsonable_encoder(graph_uuid)}/nodes/list", + method="POST", + params={ + "limit": limit, + "cursor": cursor, + }, json={ - "attributes": attributes, - "labels": labels, - "name": name, - "summary": summary, + "filters": filters, }, headers={ "content-type": "application/json", + "Idempotency-Key": str(idempotency_key) if idempotency_key is not None else None, }, request_options=request_options, omit=OMIT, ) try: if 200 <= _response.status_code < 300: - _data = typing.cast( - EntityNode, + _parsed_response = typing.cast( + NodePage, parse_obj_as( - type_=EntityNode, # type: ignore + type_=NodePage, # type: ignore object_=_response.json(), ), ) - return HttpResponse(response=_response, data=_data) + _items = _parsed_response.items + _parsed_next = _parsed_response.next_cursor + _has_next = _parsed_next is not None and _parsed_next != "" + + async def _get_next(): + return await self.list( + graph_uuid, + limit=limit, + cursor=_parsed_next, + filters=filters, + idempotency_key=idempotency_key, + request_options=request_options, + ) + + return AsyncPager(has_next=_has_next, items=_items, get_next=_get_next, response=_parsed_response) if _response.status_code == 400: raise BadRequestError( headers=dict(_response.headers), body=typing.cast( - typing.Optional[typing.Any], + types_api_error_ApiError, parse_obj_as( - type_=typing.Optional[typing.Any], # type: ignore + type_=types_api_error_ApiError, # type: ignore object_=_response.json(), ), ), ) - if _response.status_code == 404: - raise NotFoundError( + if _response.status_code == 401: + raise UnauthorizedError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -733,8 +850,8 @@ def update( ), ), ) - if _response.status_code == 500: - raise InternalServerError( + if _response.status_code == 404: + raise NotFoundError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -749,103 +866,51 @@ def update( raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response.text ) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response_json ) - -class AsyncRawNodeClient: - 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[EntityNode]]: + async def get( + self, graph_uuid: str, node_uuid: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[Node]: """ - Returns all nodes for a graph. - 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. + graph_uuid : str + Graph UUID - 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. + node_uuid : str + Node UUID request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - AsyncHttpResponse[typing.List[EntityNode]] - Nodes + AsyncHttpResponse[Node] + OK """ _response = await self._client_wrapper.httpx_client.request( - f"graph/node/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, - }, + f"graphs/{jsonable_encoder(graph_uuid)}/nodes/{jsonable_encoder(node_uuid)}", + method="GET", request_options=request_options, - omit=OMIT, ) try: if 200 <= _response.status_code < 300: _data = typing.cast( - typing.List[EntityNode], + Node, parse_obj_as( - type_=typing.List[EntityNode], # type: ignore + type_=Node, # 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 == 500: - raise InternalServerError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -855,103 +920,19 @@ async def get_by_graph_id( ), ), ) - _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_by_user_id( - self, - user_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[EntityNode]]: - """ - Returns all nodes for a user - - Parameters - ---------- - user_id : str - User 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[EntityNode]] - Nodes - """ - _response = await self._client_wrapper.httpx_client.request( - f"graph/node/user/{jsonable_encoder(user_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, - }, - request_options=request_options, - omit=OMIT, - ) - try: - if 200 <= _response.status_code < 300: - _data = typing.cast( - typing.List[EntityNode], - parse_obj_as( - type_=typing.List[EntityNode], # type: ignore - object_=_response.json(), - ), - ) - return AsyncHttpResponse(response=_response, data=_data) - if _response.status_code == 400: - raise BadRequestError( + if _response.status_code == 401: + raise UnauthorizedError( headers=dict(_response.headers), body=typing.cast( - typing.Optional[typing.Any], + types_api_error_ApiError, parse_obj_as( - type_=typing.Optional[typing.Any], # type: ignore + type_=types_api_error_ApiError, # type: ignore object_=_response.json(), ), ), ) - if _response.status_code == 500: - raise InternalServerError( + if _response.status_code == 404: + raise NotFoundError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -966,57 +947,61 @@ async def get_by_user_id( raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response.text ) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response_json ) - async def get_edges( - self, node_uuid: str, *, request_options: typing.Optional[RequestOptions] = None - ) -> AsyncHttpResponse[typing.List[EntityEdge]]: + async def delete( + self, + graph_uuid: str, + node_uuid: str, + *, + idempotency_key: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[AsyncResult]: """ - Deprecated. Use edge listing with `filters.connected_node_uuids`, or the neighbors endpoint (`POST /graph/node/{node_uuid}/neighbors`), instead. Returns all edges for a node, subject to an internal cap; responses reduced by that cap set the Zep-Truncated header. - Parameters ---------- + graph_uuid : str + Graph UUID + node_uuid : str Node UUID + idempotency_key : typing.Optional[str] + request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - AsyncHttpResponse[typing.List[EntityEdge]] - Edges + AsyncHttpResponse[AsyncResult] + Accepted """ _response = await self._client_wrapper.httpx_client.request( - f"graph/node/{jsonable_encoder(node_uuid)}/entity-edges", - method="GET", + f"graphs/{jsonable_encoder(graph_uuid)}/nodes/{jsonable_encoder(node_uuid)}", + method="DELETE", + headers={ + "Idempotency-Key": str(idempotency_key) if idempotency_key is not None else None, + }, request_options=request_options, ) try: if 200 <= _response.status_code < 300: _data = typing.cast( - typing.List[EntityEdge], + AsyncResult, parse_obj_as( - type_=typing.List[EntityEdge], # type: ignore + type_=AsyncResult, # 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 == 500: - raise InternalServerError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -1026,62 +1011,19 @@ async def get_edges( ), ), ) - _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_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. - - Parameters - ---------- - node_uuid : str - Node UUID - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - AsyncHttpResponse[EpisodeResponse] - Episodes - """ - _response = await self._client_wrapper.httpx_client.request( - f"graph/node/{jsonable_encoder(node_uuid)}/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 == 400: - raise BadRequestError( + if _response.status_code == 401: + raise UnauthorizedError( headers=dict(_response.headers), body=typing.cast( - typing.Optional[typing.Any], + types_api_error_ApiError, parse_obj_as( - type_=typing.Optional[typing.Any], # type: ignore + type_=types_api_error_ApiError, # type: ignore object_=_response.json(), ), ), ) - if _response.status_code == 500: - raise InternalServerError( + if _response.status_code == 404: + raise NotFoundError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -1096,81 +1038,63 @@ async def get_episodes( raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response.text ) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response_json ) - async def get_neighbors( + async def update( self, + graph_uuid: str, node_uuid: str, *, - cursor: typing.Optional[str] = OMIT, - direction: typing.Optional[str] = OMIT, - direction_sort: typing.Optional[str] = OMIT, - filters: typing.Optional[SearchFilters] = OMIT, - limit: typing.Optional[int] = OMIT, - order_by: typing.Optional[str] = OMIT, + attributes: typing.Optional[typing.Dict[str, typing.Any]] = OMIT, + name: typing.Optional[str] = OMIT, + summary: typing.Optional[str] = OMIT, + idempotency_key: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, - ) -> AsyncHttpResponse[typing.List[GraphNodeNeighbor]]: + ) -> AsyncHttpResponse[Node]: """ - Enumerates the distinct entity nodes directly connected to a node, together with the edges connecting each to it. - Parameters ---------- + graph_uuid : str + Graph UUID + node_uuid : str Node UUID - cursor : typing.Optional[str] - Opaque cursor for pagination, obtained from the Zep-Next-Cursor - response header of the previous page. - - direction : typing.Optional[str] - Orientation of the connecting edge relative to the anchor node: "out" - (anchor is the edge's source), "in" (anchor is the edge's target), or - "both" (either). Defaults to "both". - - direction_sort : typing.Optional[str] - Sort direction for order_by. One of "asc" or "desc". Defaults to - "desc". Named direction_sort to avoid clashing with the traversal - Direction field above. + attributes : typing.Optional[typing.Dict[str, typing.Any]] - 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_labels/exclude_node_labels). Reuses the graph.search filter - type. + name : typing.Optional[str] + Omit to leave unchanged, send JSON null to clear, or send a value to set. - limit : typing.Optional[int] - Maximum number of neighbor nodes to return. An explicit value is - clamped to 50; when omitted, the default page size (100) applies. + summary : typing.Optional[str] + Omit to leave unchanged, send JSON null to clear, or send a value to set. - order_by : typing.Optional[str] - Field to sort neighbor nodes by. One of "uuid" or "created_at". - Defaults to "uuid". + idempotency_key : typing.Optional[str] request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - AsyncHttpResponse[typing.List[GraphNodeNeighbor]] - Neighbors + AsyncHttpResponse[Node] + OK """ _response = await self._client_wrapper.httpx_client.request( - f"graph/node/{jsonable_encoder(node_uuid)}/neighbors", - method="POST", + f"graphs/{jsonable_encoder(graph_uuid)}/nodes/{jsonable_encoder(node_uuid)}", + method="PATCH", json={ - "cursor": cursor, - "direction": direction, - "direction_sort": direction_sort, - "filters": convert_and_respect_annotation_metadata( - object_=filters, annotation=SearchFilters, direction="write" - ), - "limit": limit, - "order_by": order_by, + "attributes": attributes, + "name": name, + "summary": summary, }, headers={ "content-type": "application/json", + "Idempotency-Key": str(idempotency_key) if idempotency_key is not None else None, }, request_options=request_options, omit=OMIT, @@ -1178,26 +1102,15 @@ async def get_neighbors( try: if 200 <= _response.status_code < 300: _data = typing.cast( - typing.List[GraphNodeNeighbor], + Node, parse_obj_as( - type_=typing.List[GraphNodeNeighbor], # type: ignore + type_=Node, # 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, @@ -1207,8 +1120,8 @@ async def get_neighbors( ), ), ) - if _response.status_code == 500: - raise InternalServerError( + if _response.status_code == 401: + raise UnauthorizedError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -1218,60 +1131,6 @@ async def get_neighbors( ), ), ) - _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, uuid_: str, *, request_options: typing.Optional[RequestOptions] = None - ) -> AsyncHttpResponse[EntityNode]: - """ - Returns a specific node by its UUID. - - Parameters - ---------- - uuid_ : str - Node UUID - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - AsyncHttpResponse[EntityNode] - Node - """ - _response = await self._client_wrapper.httpx_client.request( - f"graph/node/{jsonable_encoder(uuid_)}", - method="GET", - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - _data = typing.cast( - EntityNode, - parse_obj_as( - type_=EntityNode, # 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), @@ -1283,178 +1142,117 @@ async def get( ), ), ) - 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 delete( - self, uuid_: str, *, request_options: typing.Optional[RequestOptions] = None - ) -> AsyncHttpResponse[SuccessResponse]: - """ - Deletes a node by UUID. - - Parameters - ---------- - uuid_ : str - Node UUID - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - AsyncHttpResponse[SuccessResponse] - Node deleted - """ - _response = await self._client_wrapper.httpx_client.request( - f"graph/node/{jsonable_encoder(uuid_)}", - method="DELETE", - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - _data = typing.cast( - SuccessResponse, - parse_obj_as( - type_=SuccessResponse, # 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 + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e ) raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response_json ) - async def update( + async def list_neighbors( self, - uuid_: str, + graph_uuid: str, + node_uuid: str, *, - attributes: typing.Optional[typing.Dict[str, typing.Optional[typing.Any]]] = OMIT, - labels: typing.Optional[typing.Sequence[str]] = OMIT, - name: typing.Optional[str] = OMIT, - summary: typing.Optional[str] = OMIT, + limit: typing.Optional[int] = None, + cursor: typing.Optional[str] = None, + direction: typing.Optional[str] = OMIT, + filters: typing.Optional[typing.Dict[str, typing.Any]] = OMIT, + idempotency_key: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, - ) -> AsyncHttpResponse[EntityNode]: + ) -> AsyncPager[NeighborEntry, NeighborPage]: """ - Updates an entity node by UUID. - Parameters ---------- - uuid_ : str + graph_uuid : str + Graph UUID + + node_uuid : str Node UUID - attributes : typing.Optional[typing.Dict[str, typing.Optional[typing.Any]]] - Updated attributes. Merged with existing attributes. Set a key to null to delete it. + limit : typing.Optional[int] + Page size - labels : typing.Optional[typing.Sequence[str]] - Updated labels for the node + cursor : typing.Optional[str] + Opaque page cursor - name : typing.Optional[str] - Updated name for the node + direction : typing.Optional[str] - summary : typing.Optional[str] - Updated summary for the node + filters : typing.Optional[typing.Dict[str, typing.Any]] + + idempotency_key : typing.Optional[str] request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - AsyncHttpResponse[EntityNode] - Updated node + AsyncPager[NeighborEntry, NeighborPage] + OK """ _response = await self._client_wrapper.httpx_client.request( - f"graph/node/{jsonable_encoder(uuid_)}", - method="PATCH", + f"graphs/{jsonable_encoder(graph_uuid)}/nodes/{jsonable_encoder(node_uuid)}/neighbors", + method="POST", + params={ + "limit": limit, + "cursor": cursor, + }, json={ - "attributes": attributes, - "labels": labels, - "name": name, - "summary": summary, + "direction": direction, + "filters": filters, }, headers={ "content-type": "application/json", + "Idempotency-Key": str(idempotency_key) if idempotency_key is not None else None, }, request_options=request_options, omit=OMIT, ) try: if 200 <= _response.status_code < 300: - _data = typing.cast( - EntityNode, + _parsed_response = typing.cast( + NeighborPage, parse_obj_as( - type_=EntityNode, # type: ignore + type_=NeighborPage, # type: ignore object_=_response.json(), ), ) - return AsyncHttpResponse(response=_response, data=_data) + _items = _parsed_response.items + _parsed_next = _parsed_response.next_cursor + _has_next = _parsed_next is not None and _parsed_next != "" + + async def _get_next(): + return await self.list_neighbors( + graph_uuid, + node_uuid, + limit=limit, + cursor=_parsed_next, + direction=direction, + filters=filters, + idempotency_key=idempotency_key, + request_options=request_options, + ) + + return AsyncPager(has_next=_has_next, items=_items, get_next=_get_next, response=_parsed_response) if _response.status_code == 400: raise BadRequestError( headers=dict(_response.headers), body=typing.cast( - typing.Optional[typing.Any], + types_api_error_ApiError, parse_obj_as( - type_=typing.Optional[typing.Any], # type: ignore + type_=types_api_error_ApiError, # type: ignore object_=_response.json(), ), ), ) - if _response.status_code == 404: - raise NotFoundError( + if _response.status_code == 401: + raise UnauthorizedError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -1464,8 +1262,8 @@ async def update( ), ), ) - if _response.status_code == 500: - raise InternalServerError( + if _response.status_code == 404: + raise NotFoundError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -1480,6 +1278,10 @@ async def update( raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response.text ) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response_json ) diff --git a/src/zep_cloud/graph/observation/client.py b/src/zep_cloud/graph/observation/client.py index cdd97fb5..7669ad8c 100644 --- a/src/zep_cloud/graph/observation/client.py +++ b/src/zep_cloud/graph/observation/client.py @@ -3,9 +3,10 @@ import typing from ...core.client_wrapper import AsyncClientWrapper, SyncClientWrapper +from ...core.pagination import AsyncPager, SyncPager from ...core.request_options import RequestOptions -from ...types.derived_node import DerivedNode -from ...types.search_filters import SearchFilters +from ...types.observation import Observation +from ...types.observation_page import ObservationPage from .raw_client import AsyncRawObservationClient, RawObservationClient # this is used as the default value for optional parameters @@ -27,126 +28,39 @@ def with_raw_response(self) -> RawObservationClient: """ return self._raw_client - def get_by_graph_id( + def list( self, - graph_id: str, + graph_uuid: 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, + limit: typing.Optional[int] = None, + cursor: typing.Optional[str] = None, + filters: typing.Optional[typing.Dict[str, typing.Any]] = OMIT, + idempotency_key: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, - ) -> typing.List[DerivedNode]: + ) -> SyncPager[Observation, ObservationPage]: """ - Returns read-only observation nodes for a graph. - 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. + graph_uuid : str + Graph UUID 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[DerivedNode] - Observations - - Examples - -------- - from zep_cloud import Zep - - client = Zep( - api_key="YOUR_API_KEY", - ) - client.graph.observation.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 - - def get_by_user_id( - self, - user_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[DerivedNode]: - """ - Returns read-only observation nodes for a user's graph. - - Parameters - ---------- - user_id : str - User ID + Page size 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. + Opaque page cursor - 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"). + filters : typing.Optional[typing.Dict[str, typing.Any]] - 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. + idempotency_key : typing.Optional[str] request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - typing.List[DerivedNode] - Observations + SyncPager[Observation, ObservationPage] + OK Examples -------- @@ -155,29 +69,36 @@ def get_by_user_id( client = Zep( api_key="YOUR_API_KEY", ) - client.graph.observation.get_by_user_id( - user_id="user_id", + response = client.graph.observation.list( + graph_uuid="graph_uuid", + limit=1, + cursor="cursor", ) - """ - _response = self._raw_client.get_by_user_id( - user_id, + for item in response: + yield item + # alternatively, you can paginate page-by-page + for page in response.iter_pages(): + yield page + """ + return self._raw_client.list( + graph_uuid, + limit=limit, cursor=cursor, - direction=direction, filters=filters, - limit=limit, - order_by=order_by, - uuid_cursor=uuid_cursor, + idempotency_key=idempotency_key, request_options=request_options, ) - return _response.data - def get(self, uuid_: str, *, request_options: typing.Optional[RequestOptions] = None) -> DerivedNode: + def get( + self, graph_uuid: str, observation_uuid: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> Observation: """ - Returns a specific observation node by UUID. Observation nodes are read-only. - Parameters ---------- - uuid_ : str + graph_uuid : str + Graph UUID + + observation_uuid : str Observation UUID request_options : typing.Optional[RequestOptions] @@ -185,8 +106,8 @@ def get(self, uuid_: str, *, request_options: typing.Optional[RequestOptions] = Returns ------- - DerivedNode - Observation + Observation + OK Examples -------- @@ -196,10 +117,11 @@ def get(self, uuid_: str, *, request_options: typing.Optional[RequestOptions] = api_key="YOUR_API_KEY", ) client.graph.observation.get( - uuid_="uuid", + graph_uuid="graph_uuid", + observation_uuid="observation_uuid", ) """ - _response = self._raw_client.get(uuid_, request_options=request_options) + _response = self._raw_client.get(graph_uuid, observation_uuid, request_options=request_options) return _response.data @@ -218,134 +140,39 @@ def with_raw_response(self) -> AsyncRawObservationClient: """ return self._raw_client - async def get_by_graph_id( + async def list( self, - graph_id: str, + graph_uuid: 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, + limit: typing.Optional[int] = None, + cursor: typing.Optional[str] = None, + filters: typing.Optional[typing.Dict[str, typing.Any]] = OMIT, + idempotency_key: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, - ) -> typing.List[DerivedNode]: + ) -> AsyncPager[Observation, ObservationPage]: """ - Returns read-only observation nodes for a graph. - 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. + graph_uuid : str + Graph UUID 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[DerivedNode] - Observations - - Examples - -------- - import asyncio - - from zep_cloud import AsyncZep - - client = AsyncZep( - api_key="YOUR_API_KEY", - ) - - - async def main() -> None: - await client.graph.observation.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 - - async def get_by_user_id( - self, - user_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[DerivedNode]: - """ - Returns read-only observation nodes for a user's graph. - - Parameters - ---------- - user_id : str - User ID + Page size 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"). + Opaque page cursor - uuid_cursor : typing.Optional[str] - UUID based cursor, used for pagination. Should be the UUID of the last item in the previous page. + filters : typing.Optional[typing.Dict[str, typing.Any]] - Deprecated: prefer Cursor, the opaque cursor returned via the Zep-Next-Cursor response header. + idempotency_key : typing.Optional[str] request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - typing.List[DerivedNode] - Observations + AsyncPager[Observation, ObservationPage] + OK Examples -------- @@ -359,32 +186,40 @@ async def get_by_user_id( async def main() -> None: - await client.graph.observation.get_by_user_id( - user_id="user_id", + response = await client.graph.observation.list( + graph_uuid="graph_uuid", + limit=1, + cursor="cursor", ) + async for item in response: + yield item + + # alternatively, you can paginate page-by-page + async for page in response.iter_pages(): + yield page asyncio.run(main()) """ - _response = await self._raw_client.get_by_user_id( - user_id, + return await self._raw_client.list( + graph_uuid, + limit=limit, cursor=cursor, - direction=direction, filters=filters, - limit=limit, - order_by=order_by, - uuid_cursor=uuid_cursor, + idempotency_key=idempotency_key, request_options=request_options, ) - return _response.data - async def get(self, uuid_: str, *, request_options: typing.Optional[RequestOptions] = None) -> DerivedNode: + async def get( + self, graph_uuid: str, observation_uuid: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> Observation: """ - Returns a specific observation node by UUID. Observation nodes are read-only. - Parameters ---------- - uuid_ : str + graph_uuid : str + Graph UUID + + observation_uuid : str Observation UUID request_options : typing.Optional[RequestOptions] @@ -392,8 +227,8 @@ async def get(self, uuid_: str, *, request_options: typing.Optional[RequestOptio Returns ------- - DerivedNode - Observation + Observation + OK Examples -------- @@ -408,11 +243,12 @@ async def get(self, uuid_: str, *, request_options: typing.Optional[RequestOptio async def main() -> None: await client.graph.observation.get( - uuid_="uuid", + graph_uuid="graph_uuid", + observation_uuid="observation_uuid", ) asyncio.run(main()) """ - _response = await self._raw_client.get(uuid_, request_options=request_options) + _response = await self._raw_client.get(graph_uuid, observation_uuid, request_options=request_options) return _response.data diff --git a/src/zep_cloud/graph/observation/raw_client.py b/src/zep_cloud/graph/observation/raw_client.py index b677dfa4..85ddd323 100644 --- a/src/zep_cloud/graph/observation/raw_client.py +++ b/src/zep_cloud/graph/observation/raw_client.py @@ -7,15 +7,17 @@ from ...core.client_wrapper import AsyncClientWrapper, SyncClientWrapper from ...core.http_response import AsyncHttpResponse, HttpResponse from ...core.jsonable_encoder import jsonable_encoder +from ...core.pagination import AsyncPager, SyncPager +from ...core.parse_error import ParsingError 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 ...errors.unauthorized_error import UnauthorizedError from ...types.api_error import ApiError as types_api_error_ApiError -from ...types.derived_node import DerivedNode -from ...types.search_filters import SearchFilters +from ...types.observation import Observation +from ...types.observation_page import ObservationPage +from pydantic import ValidationError # this is used as the default value for optional parameters OMIT = typing.cast(typing.Any, ...) @@ -25,94 +27,80 @@ class RawObservationClient: def __init__(self, *, client_wrapper: SyncClientWrapper): self._client_wrapper = client_wrapper - def get_by_graph_id( + def list( self, - graph_id: str, + graph_uuid: 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, + limit: typing.Optional[int] = None, + cursor: typing.Optional[str] = None, + filters: typing.Optional[typing.Dict[str, typing.Any]] = OMIT, + idempotency_key: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, - ) -> HttpResponse[typing.List[DerivedNode]]: + ) -> SyncPager[Observation, ObservationPage]: """ - Returns read-only observation nodes for a graph. - 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. + graph_uuid : str + Graph UUID limit : typing.Optional[int] - Maximum number of items to return + Page size - order_by : typing.Optional[str] - Field to sort by. One of "created_at", "valid_at", or "uuid" (default "uuid"). + cursor : typing.Optional[str] + Opaque page cursor - uuid_cursor : typing.Optional[str] - UUID based cursor, used for pagination. Should be the UUID of the last item in the previous page. + filters : typing.Optional[typing.Dict[str, typing.Any]] - Deprecated: prefer Cursor, the opaque cursor returned via the Zep-Next-Cursor response header. + idempotency_key : typing.Optional[str] request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - HttpResponse[typing.List[DerivedNode]] - Observations + SyncPager[Observation, ObservationPage] + OK """ _response = self._client_wrapper.httpx_client.request( - f"graph/observation/graph/{jsonable_encoder(graph_id)}", + f"graphs/{jsonable_encoder(graph_uuid)}/observations/list", method="POST", - json={ - "cursor": cursor, - "direction": direction, - "filters": convert_and_respect_annotation_metadata( - object_=filters, annotation=SearchFilters, direction="write" - ), + params={ "limit": limit, - "order_by": order_by, - "uuid_cursor": uuid_cursor, + "cursor": cursor, + }, + json={ + "filters": filters, + }, + headers={ + "content-type": "application/json", + "Idempotency-Key": str(idempotency_key) if idempotency_key is not None else None, }, request_options=request_options, omit=OMIT, ) try: if 200 <= _response.status_code < 300: - _data = typing.cast( - typing.List[DerivedNode], + _parsed_response = typing.cast( + ObservationPage, parse_obj_as( - type_=typing.List[DerivedNode], # type: ignore + type_=ObservationPage, # type: ignore object_=_response.json(), ), ) - return HttpResponse(response=_response, data=_data) + _items = _parsed_response.items + _parsed_next = _parsed_response.next_cursor + _has_next = _parsed_next is not None and _parsed_next != "" + _get_next = lambda: self.list( + graph_uuid, + limit=limit, + cursor=_parsed_next, + filters=filters, + idempotency_key=idempotency_key, + request_options=request_options, + ) + return SyncPager(has_next=_has_next, items=_items, get_next=_get_next, response=_parsed_response) 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, @@ -122,8 +110,8 @@ def get_by_graph_id( ), ), ) - if _response.status_code == 500: - raise InternalServerError( + if _response.status_code == 401: + raise UnauthorizedError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -133,101 +121,6 @@ def get_by_graph_id( ), ), ) - _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_by_user_id( - self, - user_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[DerivedNode]]: - """ - Returns read-only observation nodes for a user's graph. - - Parameters - ---------- - user_id : str - User 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[DerivedNode]] - Observations - """ - _response = self._client_wrapper.httpx_client.request( - f"graph/observation/user/{jsonable_encoder(user_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, - }, - request_options=request_options, - omit=OMIT, - ) - try: - if 200 <= _response.status_code < 300: - _data = typing.cast( - typing.List[DerivedNode], - parse_obj_as( - type_=typing.List[DerivedNode], # 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), @@ -239,33 +132,29 @@ def get_by_user_id( ), ), ) - 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 ) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response_json ) - def get(self, uuid_: str, *, request_options: typing.Optional[RequestOptions] = None) -> HttpResponse[DerivedNode]: + def get( + self, graph_uuid: str, observation_uuid: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[Observation]: """ - Returns a specific observation node by UUID. Observation nodes are read-only. - Parameters ---------- - uuid_ : str + graph_uuid : str + Graph UUID + + observation_uuid : str Observation UUID request_options : typing.Optional[RequestOptions] @@ -273,20 +162,20 @@ def get(self, uuid_: str, *, request_options: typing.Optional[RequestOptions] = Returns ------- - HttpResponse[DerivedNode] - Observation + HttpResponse[Observation] + OK """ _response = self._client_wrapper.httpx_client.request( - f"graph/observation/{jsonable_encoder(uuid_)}", + f"graphs/{jsonable_encoder(graph_uuid)}/observations/{jsonable_encoder(observation_uuid)}", method="GET", request_options=request_options, ) try: if 200 <= _response.status_code < 300: _data = typing.cast( - DerivedNode, + Observation, parse_obj_as( - type_=DerivedNode, # type: ignore + type_=Observation, # type: ignore object_=_response.json(), ), ) @@ -295,15 +184,15 @@ def get(self, uuid_: str, *, request_options: typing.Optional[RequestOptions] = raise BadRequestError( headers=dict(_response.headers), body=typing.cast( - typing.Optional[typing.Any], + types_api_error_ApiError, parse_obj_as( - type_=typing.Optional[typing.Any], # type: ignore + type_=types_api_error_ApiError, # type: ignore object_=_response.json(), ), ), ) - if _response.status_code == 404: - raise NotFoundError( + if _response.status_code == 401: + raise UnauthorizedError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -313,8 +202,8 @@ def get(self, uuid_: str, *, request_options: typing.Optional[RequestOptions] = ), ), ) - if _response.status_code == 500: - raise InternalServerError( + if _response.status_code == 404: + raise NotFoundError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -329,6 +218,10 @@ def get(self, uuid_: str, *, request_options: typing.Optional[RequestOptions] = raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response.text ) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response_json ) @@ -338,94 +231,83 @@ class AsyncRawObservationClient: def __init__(self, *, client_wrapper: AsyncClientWrapper): self._client_wrapper = client_wrapper - async def get_by_graph_id( + async def list( self, - graph_id: str, + graph_uuid: 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, + limit: typing.Optional[int] = None, + cursor: typing.Optional[str] = None, + filters: typing.Optional[typing.Dict[str, typing.Any]] = OMIT, + idempotency_key: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, - ) -> AsyncHttpResponse[typing.List[DerivedNode]]: + ) -> AsyncPager[Observation, ObservationPage]: """ - Returns read-only observation nodes for a graph. - 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. + graph_uuid : str + Graph UUID limit : typing.Optional[int] - Maximum number of items to return + Page size - order_by : typing.Optional[str] - Field to sort by. One of "created_at", "valid_at", or "uuid" (default "uuid"). + cursor : typing.Optional[str] + Opaque page cursor - uuid_cursor : typing.Optional[str] - UUID based cursor, used for pagination. Should be the UUID of the last item in the previous page. + filters : typing.Optional[typing.Dict[str, typing.Any]] - Deprecated: prefer Cursor, the opaque cursor returned via the Zep-Next-Cursor response header. + idempotency_key : typing.Optional[str] request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - AsyncHttpResponse[typing.List[DerivedNode]] - Observations + AsyncPager[Observation, ObservationPage] + OK """ _response = await self._client_wrapper.httpx_client.request( - f"graph/observation/graph/{jsonable_encoder(graph_id)}", + f"graphs/{jsonable_encoder(graph_uuid)}/observations/list", method="POST", - json={ - "cursor": cursor, - "direction": direction, - "filters": convert_and_respect_annotation_metadata( - object_=filters, annotation=SearchFilters, direction="write" - ), + params={ "limit": limit, - "order_by": order_by, - "uuid_cursor": uuid_cursor, + "cursor": cursor, + }, + json={ + "filters": filters, + }, + headers={ + "content-type": "application/json", + "Idempotency-Key": str(idempotency_key) if idempotency_key is not None else None, }, request_options=request_options, omit=OMIT, ) try: if 200 <= _response.status_code < 300: - _data = typing.cast( - typing.List[DerivedNode], + _parsed_response = typing.cast( + ObservationPage, parse_obj_as( - type_=typing.List[DerivedNode], # type: ignore + type_=ObservationPage, # type: ignore object_=_response.json(), ), ) - return AsyncHttpResponse(response=_response, data=_data) + _items = _parsed_response.items + _parsed_next = _parsed_response.next_cursor + _has_next = _parsed_next is not None and _parsed_next != "" + + async def _get_next(): + return await self.list( + graph_uuid, + limit=limit, + cursor=_parsed_next, + filters=filters, + idempotency_key=idempotency_key, + request_options=request_options, + ) + + return AsyncPager(has_next=_has_next, items=_items, get_next=_get_next, response=_parsed_response) 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, @@ -435,8 +317,8 @@ async def get_by_graph_id( ), ), ) - if _response.status_code == 500: - raise InternalServerError( + if _response.status_code == 401: + raise UnauthorizedError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -446,101 +328,6 @@ async def get_by_graph_id( ), ), ) - _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_by_user_id( - self, - user_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[DerivedNode]]: - """ - Returns read-only observation nodes for a user's graph. - - Parameters - ---------- - user_id : str - User 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[DerivedNode]] - Observations - """ - _response = await self._client_wrapper.httpx_client.request( - f"graph/observation/user/{jsonable_encoder(user_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, - }, - request_options=request_options, - omit=OMIT, - ) - try: - if 200 <= _response.status_code < 300: - _data = typing.cast( - typing.List[DerivedNode], - parse_obj_as( - type_=typing.List[DerivedNode], # 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), @@ -552,35 +339,29 @@ async def get_by_user_id( ), ), ) - 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 ) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response_json ) async def get( - self, uuid_: str, *, request_options: typing.Optional[RequestOptions] = None - ) -> AsyncHttpResponse[DerivedNode]: + self, graph_uuid: str, observation_uuid: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[Observation]: """ - Returns a specific observation node by UUID. Observation nodes are read-only. - Parameters ---------- - uuid_ : str + graph_uuid : str + Graph UUID + + observation_uuid : str Observation UUID request_options : typing.Optional[RequestOptions] @@ -588,20 +369,20 @@ async def get( Returns ------- - AsyncHttpResponse[DerivedNode] - Observation + AsyncHttpResponse[Observation] + OK """ _response = await self._client_wrapper.httpx_client.request( - f"graph/observation/{jsonable_encoder(uuid_)}", + f"graphs/{jsonable_encoder(graph_uuid)}/observations/{jsonable_encoder(observation_uuid)}", method="GET", request_options=request_options, ) try: if 200 <= _response.status_code < 300: _data = typing.cast( - DerivedNode, + Observation, parse_obj_as( - type_=DerivedNode, # type: ignore + type_=Observation, # type: ignore object_=_response.json(), ), ) @@ -610,15 +391,15 @@ async def get( raise BadRequestError( headers=dict(_response.headers), body=typing.cast( - typing.Optional[typing.Any], + types_api_error_ApiError, parse_obj_as( - type_=typing.Optional[typing.Any], # type: ignore + type_=types_api_error_ApiError, # type: ignore object_=_response.json(), ), ), ) - if _response.status_code == 404: - raise NotFoundError( + if _response.status_code == 401: + raise UnauthorizedError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -628,8 +409,8 @@ async def get( ), ), ) - if _response.status_code == 500: - raise InternalServerError( + if _response.status_code == 404: + raise NotFoundError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -644,6 +425,10 @@ async def get( raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response.text ) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response_json ) diff --git a/src/zep_cloud/graph/raw_client.py b/src/zep_cloud/graph/raw_client.py index 4527e28d..ff8e012a 100644 --- a/src/zep_cloud/graph/raw_client.py +++ b/src/zep_cloud/graph/raw_client.py @@ -7,38 +7,38 @@ from ..core.client_wrapper import AsyncClientWrapper, SyncClientWrapper from ..core.http_response import AsyncHttpResponse, HttpResponse from ..core.jsonable_encoder import jsonable_encoder +from ..core.pagination import AsyncPager, SyncPager +from ..core.parse_error import ParsingError 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 from ..errors.not_found_error import NotFoundError -from ..types.add_node_item import AddNodeItem -from ..types.add_nodes_response import AddNodesResponse -from ..types.add_triple_response import AddTripleResponse +from ..errors.unauthorized_error import UnauthorizedError from ..types.api_error import ApiError as types_api_error_ApiError -from ..types.clone_graph_response import CloneGraphResponse -from ..types.custom_instruction import CustomInstruction -from ..types.detect_config import DetectConfig -from ..types.detect_patterns_response import DetectPatternsResponse +from ..types.async_result import AsyncResult +from ..types.clone_graph_result import CloneGraphResult +from ..types.edge import Edge +from ..types.edge_page import EdgePage from ..types.edge_type import EdgeType from ..types.entity_type import EntityType -from ..types.entity_type_response import EntityTypeResponse from ..types.episode import Episode -from ..types.episode_data import EpisodeData +from ..types.episode_page import EpisodePage from ..types.graph import Graph -from ..types.graph_data_type import GraphDataType -from ..types.graph_list_response import GraphListResponse -from ..types.graph_search_results import GraphSearchResults -from ..types.graph_search_scope import GraphSearchScope -from ..types.graph_subgraph_response import GraphSubgraphResponse -from ..types.list_custom_instructions_response import ListCustomInstructionsResponse -from ..types.pattern_seeds import PatternSeeds -from ..types.recency_weight import RecencyWeight -from ..types.reranker import Reranker -from ..types.search_filters import SearchFilters -from ..types.success_response import SuccessResponse +from ..types.graph_context_response import GraphContextResponse +from ..types.graph_delete_result import GraphDeleteResult +from ..types.graph_page import GraphPage +from ..types.instructions import Instructions +from ..types.node import Node +from ..types.node_page import NodePage +from ..types.observation import Observation +from ..types.observation_page import ObservationPage +from ..types.observation_steering import ObservationSteering +from ..types.ontology import Ontology +from ..types.subgraph_response import SubgraphResponse +from ..types.thread_summary import ThreadSummary +from ..types.thread_summary_page import ThreadSummaryPage +from pydantic import ValidationError # this is used as the default value for optional parameters OMIT = typing.cast(typing.Any, ...) @@ -48,47 +48,59 @@ class RawGraphClient: def __init__(self, *, client_wrapper: SyncClientWrapper): self._client_wrapper = client_wrapper - def list_custom_instructions( + def create( self, *, - user_id: typing.Optional[str] = None, - graph_id: typing.Optional[str] = None, + description: typing.Optional[str] = OMIT, + graph_id: typing.Optional[str] = OMIT, + name: typing.Optional[str] = OMIT, + time_zone: typing.Optional[str] = OMIT, + idempotency_key: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, - ) -> HttpResponse[ListCustomInstructionsResponse]: + ) -> HttpResponse[Graph]: """ - Lists all custom instructions for a project, user, or graph. - Parameters ---------- - user_id : typing.Optional[str] - User ID to get user-specific instructions + description : typing.Optional[str] graph_id : typing.Optional[str] - Graph ID to get graph-specific instructions + + name : typing.Optional[str] + + time_zone : typing.Optional[str] + + idempotency_key : typing.Optional[str] request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - HttpResponse[ListCustomInstructionsResponse] - The list of instructions. + HttpResponse[Graph] + Created """ _response = self._client_wrapper.httpx_client.request( - "custom-instructions", - method="GET", - params={ - "user_id": user_id, + "graphs", + method="POST", + json={ + "description": description, "graph_id": graph_id, + "name": name, + "time_zone": time_zone, + }, + headers={ + "content-type": "application/json", + "Idempotency-Key": str(idempotency_key) if idempotency_key is not None else None, }, request_options=request_options, + omit=OMIT, ) try: if 200 <= _response.status_code < 300: _data = typing.cast( - ListCustomInstructionsResponse, + Graph, parse_obj_as( - type_=ListCustomInstructionsResponse, # type: ignore + type_=Graph, # type: ignore object_=_response.json(), ), ) @@ -97,15 +109,15 @@ def list_custom_instructions( raise BadRequestError( headers=dict(_response.headers), body=typing.cast( - typing.Optional[typing.Any], + types_api_error_ApiError, parse_obj_as( - type_=typing.Optional[typing.Any], # type: ignore + type_=types_api_error_ApiError, # type: ignore object_=_response.json(), ), ), ) - if _response.status_code == 404: - raise NotFoundError( + if _response.status_code == 401: + raise UnauthorizedError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -115,8 +127,8 @@ def list_custom_instructions( ), ), ) - if _response.status_code == 500: - raise InternalServerError( + if _response.status_code == 404: + raise NotFoundError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -131,79 +143,106 @@ def list_custom_instructions( raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response.text ) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response_json ) - def add_custom_instructions( + def list( self, *, - instructions: typing.Sequence[CustomInstruction], - graph_ids: typing.Optional[typing.Sequence[str]] = OMIT, - user_ids: typing.Optional[typing.Sequence[str]] = OMIT, + limit: typing.Optional[int] = None, + cursor: typing.Optional[str] = None, + order_by: typing.Optional[str] = None, + order: typing.Optional[str] = None, + search: typing.Optional[str] = OMIT, + idempotency_key: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, - ) -> HttpResponse[SuccessResponse]: + ) -> SyncPager[Graph, GraphPage]: """ - Adds new custom instructions for graphs without removing existing ones. If user_ids or graph_ids is empty, adds to project-wide default instructions. - Parameters ---------- - instructions : typing.Sequence[CustomInstruction] - Instructions to add to the graph. + limit : typing.Optional[int] + Page size + + cursor : typing.Optional[str] + Opaque page cursor - graph_ids : typing.Optional[typing.Sequence[str]] - Graph IDs to add the instructions to. If empty, the instructions are added to the project-wide default. + order_by : typing.Optional[str] + Sort field + + order : typing.Optional[str] + asc or desc + + search : typing.Optional[str] - user_ids : typing.Optional[typing.Sequence[str]] - User IDs to add the instructions to. If empty, the instructions are added to the project-wide default. + idempotency_key : typing.Optional[str] request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - HttpResponse[SuccessResponse] - Instructions added successfully + SyncPager[Graph, GraphPage] + OK """ _response = self._client_wrapper.httpx_client.request( - "custom-instructions", + "graphs/list", method="POST", + params={ + "limit": limit, + "cursor": cursor, + "order_by": order_by, + "order": order, + }, json={ - "graph_ids": graph_ids, - "instructions": convert_and_respect_annotation_metadata( - object_=instructions, annotation=typing.Sequence[CustomInstruction], direction="write" - ), - "user_ids": user_ids, + "search": search, }, headers={ "content-type": "application/json", + "Idempotency-Key": str(idempotency_key) if idempotency_key is not None else None, }, request_options=request_options, omit=OMIT, ) try: if 200 <= _response.status_code < 300: - _data = typing.cast( - SuccessResponse, + _parsed_response = typing.cast( + GraphPage, parse_obj_as( - type_=SuccessResponse, # type: ignore + type_=GraphPage, # type: ignore object_=_response.json(), ), ) - return HttpResponse(response=_response, data=_data) + _items = _parsed_response.items + _parsed_next = _parsed_response.next_cursor + _has_next = _parsed_next is not None and _parsed_next != "" + _get_next = lambda: self.list( + limit=limit, + cursor=_parsed_next, + order_by=order_by, + order=order, + search=search, + idempotency_key=idempotency_key, + request_options=request_options, + ) + return SyncPager(has_next=_has_next, items=_items, get_next=_get_next, response=_parsed_response) if _response.status_code == 400: raise BadRequestError( headers=dict(_response.headers), body=typing.cast( - typing.Optional[typing.Any], + types_api_error_ApiError, parse_obj_as( - type_=typing.Optional[typing.Any], # type: ignore + type_=types_api_error_ApiError, # type: ignore object_=_response.json(), ), ), ) - if _response.status_code == 404: - raise NotFoundError( + if _response.status_code == 401: + raise UnauthorizedError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -213,8 +252,8 @@ def add_custom_instructions( ), ), ) - if _response.status_code == 500: - raise InternalServerError( + if _response.status_code == 404: + raise NotFoundError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -229,50 +268,53 @@ def add_custom_instructions( raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response.text ) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response_json ) - def delete_custom_instructions( + def lookup( self, *, - graph_ids: typing.Optional[typing.Sequence[str]] = OMIT, - instruction_names: typing.Optional[typing.Sequence[str]] = OMIT, - user_ids: typing.Optional[typing.Sequence[str]] = OMIT, + graph_id: typing.Optional[str] = OMIT, + thread_id: typing.Optional[str] = OMIT, + user_id: typing.Optional[str] = OMIT, + idempotency_key: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, - ) -> HttpResponse[SuccessResponse]: + ) -> HttpResponse[Graph]: """ - Deletes custom instructions for graphs or project wide defaults. - Parameters ---------- - graph_ids : typing.Optional[typing.Sequence[str]] - Determines which group graphs will have their custom instructions deleted. If no graphs are provided, the project-wide custom instructions will be affected. + graph_id : typing.Optional[str] + + thread_id : typing.Optional[str] - instruction_names : typing.Optional[typing.Sequence[str]] - Unique identifier for the instructions to be deleted. If empty deletes all instructions. + user_id : typing.Optional[str] - user_ids : typing.Optional[typing.Sequence[str]] - Determines which user graphs will have their custom instructions deleted. If no users are provided, the project-wide custom instructions will be affected. + idempotency_key : typing.Optional[str] request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - HttpResponse[SuccessResponse] - Instructions deleted successfully + HttpResponse[Graph] + OK """ _response = self._client_wrapper.httpx_client.request( - "custom-instructions", - method="DELETE", + "graphs/lookup", + method="POST", json={ - "graph_ids": graph_ids, - "instruction_names": instruction_names, - "user_ids": user_ids, + "graph_id": graph_id, + "thread_id": thread_id, + "user_id": user_id, }, headers={ "content-type": "application/json", + "Idempotency-Key": str(idempotency_key) if idempotency_key is not None else None, }, request_options=request_options, omit=OMIT, @@ -280,9 +322,9 @@ def delete_custom_instructions( try: if 200 <= _response.status_code < 300: _data = typing.cast( - SuccessResponse, + Graph, parse_obj_as( - type_=SuccessResponse, # type: ignore + type_=Graph, # type: ignore object_=_response.json(), ), ) @@ -291,15 +333,15 @@ def delete_custom_instructions( raise BadRequestError( headers=dict(_response.headers), body=typing.cast( - typing.Optional[typing.Any], + types_api_error_ApiError, parse_obj_as( - type_=typing.Optional[typing.Any], # type: ignore + type_=types_api_error_ApiError, # type: ignore object_=_response.json(), ), ), ) - if _response.status_code == 404: - raise NotFoundError( + if _response.status_code == 401: + raise UnauthorizedError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -309,8 +351,8 @@ def delete_custom_instructions( ), ), ) - if _response.status_code == 500: - raise InternalServerError( + if _response.status_code == 404: + raise NotFoundError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -325,51 +367,40 @@ def delete_custom_instructions( raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response.text ) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response_json ) - def list_entity_types( - self, - *, - user_id: typing.Optional[str] = None, - graph_id: typing.Optional[str] = None, - request_options: typing.Optional[RequestOptions] = None, - ) -> HttpResponse[EntityTypeResponse]: + def get(self, graph_uuid: str, *, request_options: typing.Optional[RequestOptions] = None) -> HttpResponse[Graph]: """ - Returns all entity types for a project, user, or graph. - Parameters ---------- - user_id : typing.Optional[str] - User ID to get user-specific entity types - - graph_id : typing.Optional[str] - Graph ID to get graph-specific entity types + graph_uuid : str + Graph UUID request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - HttpResponse[EntityTypeResponse] - The list of entity types. + HttpResponse[Graph] + OK """ _response = self._client_wrapper.httpx_client.request( - "entity-types", + f"graphs/{jsonable_encoder(graph_uuid)}", method="GET", - params={ - "user_id": user_id, - "graph_id": graph_id, - }, request_options=request_options, ) try: if 200 <= _response.status_code < 300: _data = typing.cast( - EntityTypeResponse, + Graph, parse_obj_as( - type_=EntityTypeResponse, # type: ignore + type_=Graph, # type: ignore object_=_response.json(), ), ) @@ -378,15 +409,15 @@ def list_entity_types( raise BadRequestError( headers=dict(_response.headers), body=typing.cast( - typing.Optional[typing.Any], + types_api_error_ApiError, parse_obj_as( - type_=typing.Optional[typing.Any], # type: ignore + type_=types_api_error_ApiError, # type: ignore object_=_response.json(), ), ), ) - if _response.status_code == 404: - raise NotFoundError( + if _response.status_code == 401: + raise UnauthorizedError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -396,8 +427,8 @@ def list_entity_types( ), ), ) - if _response.status_code == 500: - raise InternalServerError( + if _response.status_code == 404: + raise NotFoundError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -412,65 +443,51 @@ def list_entity_types( raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response.text ) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response_json ) - def set_entity_types_internal( + def delete( self, + graph_uuid: str, *, - edge_types: typing.Optional[typing.Sequence[EdgeType]] = OMIT, - entity_types: typing.Optional[typing.Sequence[EntityType]] = OMIT, - graph_ids: typing.Optional[typing.Sequence[str]] = OMIT, - user_ids: typing.Optional[typing.Sequence[str]] = OMIT, + idempotency_key: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, - ) -> HttpResponse[SuccessResponse]: + ) -> HttpResponse[GraphDeleteResult]: """ - Sets the entity types for multiple users and graphs, replacing any existing ones. - Parameters ---------- - edge_types : typing.Optional[typing.Sequence[EdgeType]] - - entity_types : typing.Optional[typing.Sequence[EntityType]] - - graph_ids : typing.Optional[typing.Sequence[str]] + graph_uuid : str + Graph UUID - user_ids : typing.Optional[typing.Sequence[str]] + idempotency_key : typing.Optional[str] request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - HttpResponse[SuccessResponse] - Entity types set successfully + HttpResponse[GraphDeleteResult] + Accepted """ _response = self._client_wrapper.httpx_client.request( - "entity-types", - method="PUT", - json={ - "edge_types": convert_and_respect_annotation_metadata( - object_=edge_types, annotation=typing.Sequence[EdgeType], direction="write" - ), - "entity_types": convert_and_respect_annotation_metadata( - object_=entity_types, annotation=typing.Sequence[EntityType], direction="write" - ), - "graph_ids": graph_ids, - "user_ids": user_ids, - }, + f"graphs/{jsonable_encoder(graph_uuid)}", + method="DELETE", headers={ - "content-type": "application/json", + "Idempotency-Key": str(idempotency_key) if idempotency_key is not None else None, }, request_options=request_options, - omit=OMIT, ) try: if 200 <= _response.status_code < 300: _data = typing.cast( - SuccessResponse, + GraphDeleteResult, parse_obj_as( - type_=SuccessResponse, # type: ignore + type_=GraphDeleteResult, # type: ignore object_=_response.json(), ), ) @@ -479,15 +496,15 @@ def set_entity_types_internal( raise BadRequestError( headers=dict(_response.headers), body=typing.cast( - typing.Optional[typing.Any], + types_api_error_ApiError, parse_obj_as( - type_=typing.Optional[typing.Any], # type: ignore + type_=types_api_error_ApiError, # type: ignore object_=_response.json(), ), ), ) - if _response.status_code == 404: - raise NotFoundError( + if _response.status_code == 401: + raise UnauthorizedError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -497,8 +514,8 @@ def set_entity_types_internal( ), ), ) - if _response.status_code == 500: - raise InternalServerError( + if _response.status_code == 404: + raise NotFoundError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -513,71 +530,60 @@ def set_entity_types_internal( raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response.text ) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response_json ) - def add( + def update( self, + graph_uuid: str, *, - data: str, - type: GraphDataType, - created_at: 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, - strict_ontology: typing.Optional[bool] = OMIT, - user_id: typing.Optional[str] = OMIT, + description: typing.Optional[str] = OMIT, + name: typing.Optional[str] = OMIT, + time_zone: typing.Optional[str] = OMIT, + idempotency_key: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, - ) -> HttpResponse[Episode]: + ) -> HttpResponse[Graph]: """ - Add data to the graph. - Parameters ---------- - data : str - - type : GraphDataType - - created_at : typing.Optional[str] + graph_uuid : str + Graph UUID - 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. - - metadata : typing.Optional[typing.Dict[str, typing.Optional[typing.Any]]] - Optional metadata key-value pairs. Max 10 keys. Values must be strings, numbers, booleans, or arrays of scalars. + description : typing.Optional[str] + Omit to leave unchanged, send JSON null to clear, or send a value to set. - source_description : typing.Optional[str] + name : typing.Optional[str] + Omit to leave unchanged, send JSON null to clear, or send a value to set. - strict_ontology : typing.Optional[bool] - When true, prevents extraction of generic Entity nodes that do not match the configured ontology. + time_zone : typing.Optional[str] + Omit to leave unchanged, send JSON null to clear, or send a value to set. - user_id : typing.Optional[str] - User ID is the ID of the user to which the data will be added. If not adding to a user graph, please use graph_id field instead. + idempotency_key : typing.Optional[str] request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - HttpResponse[Episode] - Added episode + HttpResponse[Graph] + OK """ _response = self._client_wrapper.httpx_client.request( - "graph", - method="POST", + f"graphs/{jsonable_encoder(graph_uuid)}", + method="PATCH", json={ - "created_at": created_at, - "data": data, - "graph_id": graph_id, - "metadata": metadata, - "source_description": source_description, - "strict_ontology": strict_ontology, - "type": type, - "user_id": user_id, + "description": description, + "name": name, + "time_zone": time_zone, }, headers={ "content-type": "application/json", + "Idempotency-Key": str(idempotency_key) if idempotency_key is not None else None, }, request_options=request_options, omit=OMIT, @@ -585,9 +591,9 @@ def add( try: if 200 <= _response.status_code < 300: _data = typing.cast( - Episode, + Graph, parse_obj_as( - type_=Episode, # type: ignore + type_=Graph, # type: ignore object_=_response.json(), ), ) @@ -596,15 +602,26 @@ def add( raise BadRequestError( headers=dict(_response.headers), body=typing.cast( - typing.Optional[typing.Any], + types_api_error_ApiError, + parse_obj_as( + type_=types_api_error_ApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 401: + raise UnauthorizedError( + headers=dict(_response.headers), + body=typing.cast( + types_api_error_ApiError, parse_obj_as( - type_=typing.Optional[typing.Any], # type: ignore + type_=types_api_error_ApiError, # type: ignore object_=_response.json(), ), ), ) - if _response.status_code == 500: - raise InternalServerError( + if _response.status_code == 404: + raise NotFoundError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -619,58 +636,49 @@ def add( raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response.text ) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response_json ) - def add_batch( + def clone( self, + graph_uuid: str, *, - episodes: typing.Sequence[EpisodeData], - graph_id: typing.Optional[str] = OMIT, - strict_ontology: typing.Optional[bool] = OMIT, - user_id: typing.Optional[str] = OMIT, + target_graph_id: typing.Optional[str] = OMIT, + idempotency_key: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, - ) -> HttpResponse[typing.List[Episode]]: + ) -> HttpResponse[CloneGraphResult]: """ - Deprecated. Use the [Batch API](/adding-batch-data) (`client.batch.*`) instead. - - Adds data to the graph in batch mode, processing episodes concurrently. - Parameters ---------- - episodes : typing.Sequence[EpisodeData] + graph_uuid : str + Graph UUID - 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. - - strict_ontology : typing.Optional[bool] - When true, prevents extraction of generic Entity nodes that do not match the configured ontology. + target_graph_id : typing.Optional[str] - user_id : typing.Optional[str] - User ID is the ID of the user to which the data will be added. If not adding to a user graph, please use graph_id field instead. + idempotency_key : typing.Optional[str] request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - HttpResponse[typing.List[Episode]] - Added episodes + HttpResponse[CloneGraphResult] + Accepted """ _response = self._client_wrapper.httpx_client.request( - "graph-batch", + f"graphs/{jsonable_encoder(graph_uuid)}/clone", method="POST", json={ - "episodes": convert_and_respect_annotation_metadata( - object_=episodes, annotation=typing.Sequence[EpisodeData], direction="write" - ), - "graph_id": graph_id, - "strict_ontology": strict_ontology, - "user_id": user_id, + "target_graph_id": target_graph_id, }, headers={ "content-type": "application/json", + "Idempotency-Key": str(idempotency_key) if idempotency_key is not None else None, }, request_options=request_options, omit=OMIT, @@ -678,9 +686,9 @@ def add_batch( try: if 200 <= _response.status_code < 300: _data = typing.cast( - typing.List[Episode], + CloneGraphResult, parse_obj_as( - type_=typing.List[Episode], # type: ignore + type_=CloneGraphResult, # type: ignore object_=_response.json(), ), ) @@ -689,15 +697,26 @@ def add_batch( raise BadRequestError( headers=dict(_response.headers), body=typing.cast( - typing.Optional[typing.Any], + types_api_error_ApiError, + parse_obj_as( + type_=types_api_error_ApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 401: + raise UnauthorizedError( + headers=dict(_response.headers), + body=typing.cast( + types_api_error_ApiError, parse_obj_as( - type_=typing.Optional[typing.Any], # type: ignore + type_=types_api_error_ApiError, # type: ignore object_=_response.json(), ), ), ) - if _response.status_code == 500: - raise InternalServerError( + if _response.status_code == 404: + raise NotFoundError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -712,145 +731,69 @@ def add_batch( raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response.text ) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response_json ) - def add_fact_triple( + def get_context( self, + graph_uuid: str, *, - fact: str, - fact_name: str, - created_at: typing.Optional[str] = OMIT, - edge_attributes: typing.Optional[typing.Dict[str, typing.Optional[typing.Any]]] = OMIT, - expired_at: typing.Optional[str] = OMIT, - graph_id: typing.Optional[str] = OMIT, - invalid_at: typing.Optional[str] = OMIT, - metadata: typing.Optional[typing.Dict[str, typing.Optional[typing.Any]]] = OMIT, - source_node_attributes: typing.Optional[typing.Dict[str, typing.Optional[typing.Any]]] = OMIT, - source_node_labels: typing.Optional[typing.Sequence[str]] = OMIT, - source_node_name: typing.Optional[str] = OMIT, - source_node_summary: typing.Optional[str] = OMIT, - source_node_uuid: typing.Optional[str] = OMIT, - target_node_attributes: typing.Optional[typing.Dict[str, typing.Optional[typing.Any]]] = OMIT, - target_node_labels: typing.Optional[typing.Sequence[str]] = OMIT, - target_node_name: typing.Optional[str] = OMIT, - target_node_summary: typing.Optional[str] = OMIT, - target_node_uuid: typing.Optional[str] = OMIT, - user_id: typing.Optional[str] = OMIT, - valid_at: typing.Optional[str] = OMIT, + filters: typing.Optional[typing.Dict[str, typing.Any]] = OMIT, + include_results: typing.Optional[bool] = OMIT, + max_characters: typing.Optional[int] = OMIT, + query: typing.Optional[str] = OMIT, + recency_bias: typing.Optional[str] = OMIT, + template_uuid: typing.Optional[str] = OMIT, + idempotency_key: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, - ) -> HttpResponse[AddTripleResponse]: + ) -> HttpResponse[GraphContextResponse]: """ - Add a fact triple for a user or group - Parameters ---------- - fact : str - The fact relating the two nodes that this edge represents - - fact_name : str - The name of the edge to add. Should be all caps using snake case (eg RELATES_TO) - - created_at : typing.Optional[str] - The timestamp of the message - - edge_attributes : typing.Optional[typing.Dict[str, typing.Optional[typing.Any]]] - Additional attributes of the edge. Values must be scalar types (string, number, boolean, or null). - Nested objects and arrays are not allowed. - - expired_at : typing.Optional[str] - The time (if any) at which the edge expires + graph_uuid : str + Graph UUID - graph_id : typing.Optional[str] - - invalid_at : typing.Optional[str] - The time (if any) at which the fact stops being true - - metadata : typing.Optional[typing.Dict[str, typing.Optional[typing.Any]]] - Optional metadata key-value pairs for the shadow episode created for this fact triple. - Max 10 keys. Values must be strings, numbers, or booleans. - - source_node_attributes : typing.Optional[typing.Dict[str, typing.Optional[typing.Any]]] - Additional attributes of the source node. Values must be scalar types (string, number, boolean, or null). - Nested objects and arrays are not allowed. - - source_node_labels : typing.Optional[typing.Sequence[str]] - The labels for the source node. At most one entity-type label may be - provided so that manually-added triples remain consistent with automatic - episode extraction, which assigns one best-match entity type per node. - The base "Entity" label is added implicitly by the graph layer on save - and does not need to be supplied here. - - source_node_name : typing.Optional[str] - The name of the source node to add - - source_node_summary : typing.Optional[str] - The summary of the source node to add - - source_node_uuid : typing.Optional[str] - The source node uuid + filters : typing.Optional[typing.Dict[str, typing.Any]] - target_node_attributes : typing.Optional[typing.Dict[str, typing.Optional[typing.Any]]] - Additional attributes of the target node. Values must be scalar types (string, number, boolean, or null). - Nested objects and arrays are not allowed. + include_results : typing.Optional[bool] - target_node_labels : typing.Optional[typing.Sequence[str]] - The labels for the target node. At most one entity-type label may be - provided so that manually-added triples remain consistent with automatic - episode extraction, which assigns one best-match entity type per node. - The base "Entity" label is added implicitly by the graph layer on save - and does not need to be supplied here. - - target_node_name : typing.Optional[str] - The name of the target node to add + max_characters : typing.Optional[int] - target_node_summary : typing.Optional[str] - The summary of the target node to add + query : typing.Optional[str] - target_node_uuid : typing.Optional[str] - The target node uuid + recency_bias : typing.Optional[str] - user_id : typing.Optional[str] + template_uuid : typing.Optional[str] - valid_at : typing.Optional[str] - The time at which the fact becomes true + idempotency_key : typing.Optional[str] request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - HttpResponse[AddTripleResponse] - Resulting triple + HttpResponse[GraphContextResponse] + OK """ _response = self._client_wrapper.httpx_client.request( - "graph/add-fact-triple", + f"graphs/{jsonable_encoder(graph_uuid)}/context", method="POST", json={ - "created_at": created_at, - "edge_attributes": edge_attributes, - "expired_at": expired_at, - "fact": fact, - "fact_name": fact_name, - "graph_id": graph_id, - "invalid_at": invalid_at, - "metadata": metadata, - "source_node_attributes": source_node_attributes, - "source_node_labels": source_node_labels, - "source_node_name": source_node_name, - "source_node_summary": source_node_summary, - "source_node_uuid": source_node_uuid, - "target_node_attributes": target_node_attributes, - "target_node_labels": target_node_labels, - "target_node_name": target_node_name, - "target_node_summary": target_node_summary, - "target_node_uuid": target_node_uuid, - "user_id": user_id, - "valid_at": valid_at, + "filters": filters, + "include_results": include_results, + "max_characters": max_characters, + "query": query, + "recency_bias": recency_bias, + "template_uuid": template_uuid, }, headers={ "content-type": "application/json", + "Idempotency-Key": str(idempotency_key) if idempotency_key is not None else None, }, request_options=request_options, omit=OMIT, @@ -858,9 +801,9 @@ def add_fact_triple( try: if 200 <= _response.status_code < 300: _data = typing.cast( - AddTripleResponse, + GraphContextResponse, parse_obj_as( - type_=AddTripleResponse, # type: ignore + type_=GraphContextResponse, # type: ignore object_=_response.json(), ), ) @@ -869,15 +812,26 @@ def add_fact_triple( raise BadRequestError( headers=dict(_response.headers), body=typing.cast( - typing.Optional[typing.Any], + types_api_error_ApiError, + parse_obj_as( + type_=types_api_error_ApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 401: + raise UnauthorizedError( + headers=dict(_response.headers), + body=typing.cast( + types_api_error_ApiError, parse_obj_as( - type_=typing.Optional[typing.Any], # type: ignore + type_=types_api_error_ApiError, # type: ignore object_=_response.json(), ), ), ) - if _response.status_code == 500: - raise InternalServerError( + if _response.status_code == 404: + raise NotFoundError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -892,65 +846,42 @@ def add_fact_triple( raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response.text ) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response_json ) - def clone( - self, - *, - source_graph_id: typing.Optional[str] = OMIT, - source_user_id: typing.Optional[str] = OMIT, - target_graph_id: typing.Optional[str] = OMIT, - target_user_id: typing.Optional[str] = OMIT, - request_options: typing.Optional[RequestOptions] = None, - ) -> HttpResponse[CloneGraphResponse]: + def get_instructions( + self, graph_uuid: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[Instructions]: """ - Clone a user or group graph. - Parameters ---------- - source_graph_id : typing.Optional[str] - source_graph_id is the ID of the graph to be cloned. Required if source_user_id is not provided - - source_user_id : typing.Optional[str] - user_id of the user whose graph is being cloned. Required if source_graph_id is not provided - - target_graph_id : typing.Optional[str] - target_graph_id is the ID to be set on the cloned graph. Must not point to an existing graph. Required if target_user_id is not provided. - - target_user_id : typing.Optional[str] - user_id to be set on the cloned user. Must not point to an existing user. Required if target_graph_id is not provided. + graph_uuid : str + Graph UUID request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - HttpResponse[CloneGraphResponse] - Response object containing graph_id or user_id pointing to the new graph + HttpResponse[Instructions] + OK """ _response = self._client_wrapper.httpx_client.request( - "graph/clone", - method="POST", - json={ - "source_graph_id": source_graph_id, - "source_user_id": source_user_id, - "target_graph_id": target_graph_id, - "target_user_id": target_user_id, - }, - headers={ - "content-type": "application/json", - }, + f"graphs/{jsonable_encoder(graph_uuid)}/instructions", + method="GET", request_options=request_options, - omit=OMIT, ) try: if 200 <= _response.status_code < 300: _data = typing.cast( - CloneGraphResponse, + Instructions, parse_obj_as( - type_=CloneGraphResponse, # type: ignore + type_=Instructions, # type: ignore object_=_response.json(), ), ) @@ -959,15 +890,26 @@ def clone( raise BadRequestError( headers=dict(_response.headers), body=typing.cast( - typing.Optional[typing.Any], + types_api_error_ApiError, + parse_obj_as( + type_=types_api_error_ApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 401: + raise UnauthorizedError( + headers=dict(_response.headers), + body=typing.cast( + types_api_error_ApiError, parse_obj_as( - type_=typing.Optional[typing.Any], # type: ignore + type_=types_api_error_ApiError, # type: ignore object_=_response.json(), ), ), ) - if _response.status_code == 500: - raise InternalServerError( + if _response.status_code == 404: + raise NotFoundError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -982,52 +924,53 @@ def clone( raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response.text ) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response_json ) - def create( + def set_instructions( self, + graph_uuid: str, *, - graph_id: str, - description: typing.Optional[str] = OMIT, - name: typing.Optional[str] = OMIT, - time_zone: typing.Optional[str] = OMIT, + inherited: typing.Optional[bool] = OMIT, + instructions: typing.Optional[typing.Sequence[typing.Dict[str, typing.Any]]] = OMIT, + idempotency_key: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, - ) -> HttpResponse[Graph]: + ) -> HttpResponse[Instructions]: """ - Creates a new graph. - Parameters ---------- - graph_id : str + graph_uuid : str + Graph UUID - description : typing.Optional[str] + inherited : typing.Optional[bool] - name : typing.Optional[str] + instructions : typing.Optional[typing.Sequence[typing.Dict[str, typing.Any]]] - time_zone : typing.Optional[str] - The graph's IANA time zone. Stored on its group-backed subject. + idempotency_key : typing.Optional[str] request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - HttpResponse[Graph] - The added graph + HttpResponse[Instructions] + OK """ _response = self._client_wrapper.httpx_client.request( - "graph/create", - method="POST", + f"graphs/{jsonable_encoder(graph_uuid)}/instructions", + method="PUT", json={ - "description": description, - "graph_id": graph_id, - "name": name, - "time_zone": time_zone, + "inherited": inherited, + "instructions": instructions, }, headers={ "content-type": "application/json", + "Idempotency-Key": str(idempotency_key) if idempotency_key is not None else None, }, request_options=request_options, omit=OMIT, @@ -1035,9 +978,9 @@ def create( try: if 200 <= _response.status_code < 300: _data = typing.cast( - Graph, + Instructions, parse_obj_as( - type_=Graph, # type: ignore + type_=Instructions, # type: ignore object_=_response.json(), ), ) @@ -1046,15 +989,26 @@ def create( raise BadRequestError( headers=dict(_response.headers), body=typing.cast( - typing.Optional[typing.Any], + types_api_error_ApiError, + parse_obj_as( + type_=types_api_error_ApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 401: + raise UnauthorizedError( + headers=dict(_response.headers), + body=typing.cast( + types_api_error_ApiError, parse_obj_as( - type_=typing.Optional[typing.Any], # type: ignore + type_=types_api_error_ApiError, # type: ignore object_=_response.json(), ), ), ) - if _response.status_code == 500: - raise InternalServerError( + if _response.status_code == 404: + raise NotFoundError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -1069,66 +1023,42 @@ def create( raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response.text ) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response_json ) - def list_all( - self, - *, - page_number: typing.Optional[int] = None, - page_size: typing.Optional[int] = None, - search: typing.Optional[str] = None, - order_by: typing.Optional[str] = None, - asc: typing.Optional[bool] = None, - request_options: typing.Optional[RequestOptions] = None, - ) -> HttpResponse[GraphListResponse]: + def get_observation_steering( + self, graph_uuid: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[ObservationSteering]: """ - Returns all graphs. In order to list users, use user.list_ordered instead - Parameters ---------- - page_number : typing.Optional[int] - Page number for pagination, starting from 1. - - page_size : typing.Optional[int] - Number of graphs to retrieve per page (default 50, range 1-100; explicit 0 is invalid). - - search : typing.Optional[str] - Search term for filtering graphs by graph_id, name, or description. Queries longer than 200 Unicode code points after whitespace normalization are invalid. - - order_by : typing.Optional[str] - Column to sort by (created_at, graph_id, name). - - asc : typing.Optional[bool] - Sort in ascending order. + graph_uuid : str + Graph UUID request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - HttpResponse[GraphListResponse] - Successfully retrieved list of graphs. + HttpResponse[ObservationSteering] + OK """ _response = self._client_wrapper.httpx_client.request( - "graph/list-all", + f"graphs/{jsonable_encoder(graph_uuid)}/observation-steering", method="GET", - params={ - "pageNumber": page_number, - "pageSize": page_size, - "search": search, - "order_by": order_by, - "asc": asc, - }, request_options=request_options, ) try: if 200 <= _response.status_code < 300: _data = typing.cast( - GraphListResponse, + ObservationSteering, parse_obj_as( - type_=GraphListResponse, # type: ignore + type_=ObservationSteering, # type: ignore object_=_response.json(), ), ) @@ -1137,15 +1067,26 @@ def list_all( raise BadRequestError( headers=dict(_response.headers), body=typing.cast( - typing.Optional[typing.Any], + types_api_error_ApiError, + parse_obj_as( + type_=types_api_error_ApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 401: + raise UnauthorizedError( + headers=dict(_response.headers), + body=typing.cast( + types_api_error_ApiError, parse_obj_as( - type_=typing.Optional[typing.Any], # type: ignore + type_=types_api_error_ApiError, # type: ignore object_=_response.json(), ), ), ) - if _response.status_code == 500: - raise InternalServerError( + if _response.status_code == 404: + raise NotFoundError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -1160,50 +1101,57 @@ def list_all( raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response.text ) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response_json ) - def add_nodes( + def set_observation_steering( self, + graph_uuid: str, *, - nodes: typing.Sequence[AddNodeItem], - graph_id: typing.Optional[str] = OMIT, - user_id: typing.Optional[str] = OMIT, + inherited: typing.Optional[bool] = OMIT, + instruction: typing.Optional[str] = OMIT, + types: typing.Optional[typing.Sequence[typing.Dict[str, typing.Any]]] = OMIT, + idempotency_key: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, - ) -> HttpResponse[AddNodesResponse]: + ) -> HttpResponse[ObservationSteering]: """ - Add entity nodes to a user or graph directly, without episode ingestion. Up to 100 nodes per request. - Parameters ---------- - nodes : typing.Sequence[AddNodeItem] - The nodes to add. 1 to 100 items. + graph_uuid : str + Graph UUID - graph_id : typing.Optional[str] + inherited : typing.Optional[bool] - user_id : typing.Optional[str] + instruction : typing.Optional[str] + + types : typing.Optional[typing.Sequence[typing.Dict[str, typing.Any]]] + + idempotency_key : typing.Optional[str] request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - HttpResponse[AddNodesResponse] - Accepted + HttpResponse[ObservationSteering] + OK """ _response = self._client_wrapper.httpx_client.request( - "graph/nodes", - method="POST", + f"graphs/{jsonable_encoder(graph_uuid)}/observation-steering", + method="PUT", json={ - "graph_id": graph_id, - "nodes": convert_and_respect_annotation_metadata( - object_=nodes, annotation=typing.Sequence[AddNodeItem], direction="write" - ), - "user_id": user_id, + "inherited": inherited, + "instruction": instruction, + "types": types, }, headers={ "content-type": "application/json", + "Idempotency-Key": str(idempotency_key) if idempotency_key is not None else None, }, request_options=request_options, omit=OMIT, @@ -1211,9 +1159,9 @@ def add_nodes( try: if 200 <= _response.status_code < 300: _data = typing.cast( - AddNodesResponse, + ObservationSteering, parse_obj_as( - type_=AddNodesResponse, # type: ignore + type_=ObservationSteering, # type: ignore object_=_response.json(), ), ) @@ -1222,15 +1170,26 @@ def add_nodes( raise BadRequestError( headers=dict(_response.headers), body=typing.cast( - typing.Optional[typing.Any], + types_api_error_ApiError, + parse_obj_as( + type_=types_api_error_ApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 401: + raise UnauthorizedError( + headers=dict(_response.headers), + body=typing.cast( + types_api_error_ApiError, parse_obj_as( - type_=typing.Optional[typing.Any], # type: ignore + type_=types_api_error_ApiError, # type: ignore object_=_response.json(), ), ), ) - if _response.status_code == 500: - raise InternalServerError( + if _response.status_code == 404: + raise NotFoundError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -1245,131 +1204,48 @@ def add_nodes( raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response.text ) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response_json ) - def detect_patterns( - self, - *, - detect: typing.Optional[DetectConfig] = OMIT, - edge_limit: typing.Optional[int] = OMIT, - graph_id: typing.Optional[str] = OMIT, - limit: typing.Optional[int] = OMIT, - min_occurrences: typing.Optional[int] = OMIT, - query: typing.Optional[str] = OMIT, - query_limit: typing.Optional[int] = OMIT, - recency_weight: typing.Optional[RecencyWeight] = OMIT, - search_filters: typing.Optional[SearchFilters] = OMIT, - seeds: typing.Optional[PatternSeeds] = OMIT, - user_id: typing.Optional[str] = OMIT, - request_options: typing.Optional[RequestOptions] = None, - ) -> HttpResponse[DetectPatternsResponse]: + def get_ontology( + self, graph_uuid: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[Ontology]: """ - 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, - detects triple-frequency patterns, and returns resolved edges ranked by relevance. - Parameters ---------- - detect : typing.Optional[DetectConfig] - Which pattern types to detect with type-specific configuration. - Omit to detect all types with defaults. Ignored when query is set. - - edge_limit : typing.Optional[int] - Max resolved edges per pattern. Default: 10, Max: 100. Only used with query. - - graph_id : typing.Optional[str] - Graph ID when detecting patterns on a named graph - - limit : typing.Optional[int] - Max patterns to return. Default: 50, Max: 200 - - min_occurrences : typing.Optional[int] - Minimum occurrence count to report a pattern. Default: 2 - - query : typing.Optional[str] - Search query for discovering seed nodes via hybrid search. - When set, forces triple-frequency detection only and enables edge resolution - with cross-encoder reranking. Mutually exclusive with seeds. - - query_limit : typing.Optional[int] - Max seed nodes from search. Default: 10, Max: 50. Only used with query. - - recency_weight : typing.Optional[RecencyWeight] - Exponential half-life decay applied to edge created_at timestamps. - Valid values: none, 7_days, 30_days, 90_days. Default: none - - search_filters : typing.Optional[SearchFilters] - Filters which edges/nodes participate in pattern detection. - Reuses the same filter format as /graph/search. - - seeds : typing.Optional[PatternSeeds] - Seed selection. If omitted, analyzes the entire graph. Mutually exclusive with query. - - user_id : typing.Optional[str] - User ID when detecting patterns on a user graph + graph_uuid : str + Graph UUID request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - HttpResponse[DetectPatternsResponse] - Detected patterns + HttpResponse[Ontology] + OK """ _response = self._client_wrapper.httpx_client.request( - "graph/patterns", - method="POST", - json={ - "detect": convert_and_respect_annotation_metadata( - object_=detect, annotation=DetectConfig, direction="write" - ), - "edge_limit": edge_limit, - "graph_id": graph_id, - "limit": limit, - "min_occurrences": min_occurrences, - "query": query, - "query_limit": query_limit, - "recency_weight": recency_weight, - "search_filters": convert_and_respect_annotation_metadata( - object_=search_filters, annotation=SearchFilters, direction="write" - ), - "seeds": convert_and_respect_annotation_metadata( - object_=seeds, annotation=PatternSeeds, direction="write" - ), - "user_id": user_id, - }, - headers={ - "content-type": "application/json", - }, + f"graphs/{jsonable_encoder(graph_uuid)}/ontology", + method="GET", request_options=request_options, - omit=OMIT, ) try: if 200 <= _response.status_code < 300: _data = typing.cast( - DetectPatternsResponse, + Ontology, parse_obj_as( - type_=DetectPatternsResponse, # type: ignore + type_=Ontology, # 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 == 403: - raise ForbiddenError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -1379,8 +1255,8 @@ def detect_patterns( ), ), ) - if _response.status_code == 404: - raise NotFoundError( + if _response.status_code == 401: + raise UnauthorizedError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -1390,8 +1266,8 @@ def detect_patterns( ), ), ) - if _response.status_code == 500: - raise InternalServerError( + if _response.status_code == 404: + raise NotFoundError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -1406,103 +1282,61 @@ def detect_patterns( raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response.text ) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response_json ) - def search( + def set_ontology( self, + graph_uuid: str, *, - query: str, - bfs_origin_node_uuids: typing.Optional[typing.Sequence[str]] = OMIT, - center_node_uuid: typing.Optional[str] = OMIT, - graph_id: typing.Optional[str] = OMIT, - limit: typing.Optional[int] = OMIT, - max_characters: typing.Optional[int] = OMIT, - mmr_lambda: typing.Optional[float] = OMIT, - reranker: typing.Optional[Reranker] = OMIT, - return_raw_results: typing.Optional[bool] = OMIT, - scope: typing.Optional[GraphSearchScope] = OMIT, - search_filters: typing.Optional[SearchFilters] = OMIT, - user_id: typing.Optional[str] = OMIT, + edge_types: typing.Optional[typing.Sequence[EdgeType]] = OMIT, + entity_types: typing.Optional[typing.Sequence[EntityType]] = OMIT, + inherited: typing.Optional[bool] = OMIT, + idempotency_key: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, - ) -> HttpResponse[GraphSearchResults]: + ) -> HttpResponse[Ontology]: """ - Perform a graph search query. - Parameters ---------- - query : str - The string to search for (required) + graph_uuid : str + Graph UUID - bfs_origin_node_uuids : typing.Optional[typing.Sequence[str]] - Nodes that are the origins of the BFS searches - - center_node_uuid : typing.Optional[str] - Node to rerank around for node distance reranking - - graph_id : typing.Optional[str] - The graph_id to search in. When searching user graph, please use user_id instead. - - limit : typing.Optional[int] - The maximum number of facts to retrieve for non-auto scopes. Defaults to 10. Limited to 50. Ignored when scope=auto. - - max_characters : typing.Optional[int] - Maximum total characters across all selected results when scope=auto. Defaults to 2500. Limited to 50000. - - mmr_lambda : typing.Optional[float] - weighting for maximal marginal relevance - - reranker : typing.Optional[Reranker] - Defaults to RRF. Ignored when scope=auto except node_distance and episode_mentions are rejected; - auto search always uses RRF retrieval and applies its own internal rerank after retrieval. - episode_mentions ranks edge candidates by how many of the episodes listed - in search_filters.episode_uuids mention them; without episode_uuids it has - no effect and results are ranked as if no reranker were specified. - - return_raw_results : typing.Optional[bool] - When scope=auto, include the selected raw graph results alongside the materialized context block. - For graph-service-backed auto mode, selected raw results may include episodes, - edges, nodes, observations, and thread_summaries. + edge_types : typing.Optional[typing.Sequence[EdgeType]] - scope : typing.Optional[GraphSearchScope] - Defaults to Edges. + entity_types : typing.Optional[typing.Sequence[EntityType]] - search_filters : typing.Optional[SearchFilters] - Search filters to apply to the search + inherited : typing.Optional[bool] - user_id : typing.Optional[str] - The user_id when searching user graph. If not searching user graph, please use graph_id instead. + idempotency_key : typing.Optional[str] request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - HttpResponse[GraphSearchResults] - Graph search results or auto-context block + HttpResponse[Ontology] + OK """ _response = self._client_wrapper.httpx_client.request( - "graph/search", - method="POST", + f"graphs/{jsonable_encoder(graph_uuid)}/ontology", + method="PUT", json={ - "bfs_origin_node_uuids": bfs_origin_node_uuids, - "center_node_uuid": center_node_uuid, - "graph_id": graph_id, - "limit": limit, - "max_characters": max_characters, - "mmr_lambda": mmr_lambda, - "query": query, - "reranker": reranker, - "return_raw_results": return_raw_results, - "scope": scope, - "search_filters": convert_and_respect_annotation_metadata( - object_=search_filters, annotation=SearchFilters, direction="write" + "edge_types": convert_and_respect_annotation_metadata( + object_=edge_types, annotation=typing.Sequence[EdgeType], direction="write" ), - "user_id": user_id, + "entity_types": convert_and_respect_annotation_metadata( + object_=entity_types, annotation=typing.Sequence[EntityType], direction="write" + ), + "inherited": inherited, }, headers={ "content-type": "application/json", + "Idempotency-Key": str(idempotency_key) if idempotency_key is not None else None, }, request_options=request_options, omit=OMIT, @@ -1510,9 +1344,9 @@ def search( try: if 200 <= _response.status_code < 300: _data = typing.cast( - GraphSearchResults, + Ontology, parse_obj_as( - type_=GraphSearchResults, # type: ignore + type_=Ontology, # type: ignore object_=_response.json(), ), ) @@ -1521,15 +1355,26 @@ def search( raise BadRequestError( headers=dict(_response.headers), body=typing.cast( - typing.Optional[typing.Any], + types_api_error_ApiError, + parse_obj_as( + type_=types_api_error_ApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 401: + raise UnauthorizedError( + headers=dict(_response.headers), + body=typing.cast( + types_api_error_ApiError, parse_obj_as( - type_=typing.Optional[typing.Any], # type: ignore + type_=types_api_error_ApiError, # type: ignore object_=_response.json(), ), ), ) - if _response.status_code == 500: - raise InternalServerError( + if _response.status_code == 404: + raise NotFoundError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -1544,113 +1389,124 @@ def search( raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response.text ) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response_json ) - def get_subgraph( + def search_edges( self, + graph_uuid: str, *, - seed_node_uuids: typing.Sequence[str], - depth: typing.Optional[int] = OMIT, - direction: typing.Optional[str] = OMIT, - graph_id: typing.Optional[str] = OMIT, - max_edges: typing.Optional[int] = OMIT, - max_nodes: typing.Optional[int] = OMIT, - search_filters: typing.Optional[SearchFilters] = OMIT, - user_id: typing.Optional[str] = OMIT, + limit: typing.Optional[int] = None, + cursor: typing.Optional[str] = None, + bfs_origin_node_uuids: typing.Optional[typing.Sequence[str]] = OMIT, + center_node_uuid: typing.Optional[str] = OMIT, + filters: typing.Optional[typing.Dict[str, typing.Any]] = OMIT, + mmr_lambda: typing.Optional[float] = OMIT, + query: typing.Optional[str] = OMIT, + reranker: typing.Optional[str] = OMIT, + idempotency_key: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, - ) -> HttpResponse[GraphSubgraphResponse]: + ) -> SyncPager[Edge, EdgePage]: """ - Returns the bounded neighborhood of a set of seed nodes as a single {nodes, edges} payload: breadth-first expansion up to a caller-specified depth, subject to explicit budgets, with explicit truncation reporting. - Parameters ---------- - seed_node_uuids : typing.Sequence[str] - Seed node UUIDs to expand from, in traversal-priority order: seeds are - admitted before any expansion, in this order, and count toward - max_nodes first. 1-20 entries, required. Seeds that do not exist in - the target graph are ignored, not an error. + graph_uuid : str + Graph UUID - depth : typing.Optional[int] - Maximum traversal depth from the seeds. 1-3. Defaults to 1. + limit : typing.Optional[int] + Page size - direction : typing.Optional[str] - Edge orientation followed during expansion, relative to each frontier - node: "in" | "out" | "both". Defaults to "both". + cursor : typing.Optional[str] + Opaque page cursor - graph_id : typing.Optional[str] - graph_id identifies the target named graph. Exactly one of user_id or - graph_id is required. + bfs_origin_node_uuids : typing.Optional[typing.Sequence[str]] - max_edges : typing.Optional[int] - Maximum number of edges in the response. 1-1000. Defaults to 200. + center_node_uuid : typing.Optional[str] - max_nodes : typing.Optional[int] - Maximum number of nodes in the response, including admitted seeds. - 1-500. Defaults to 100. + filters : typing.Optional[typing.Dict[str, typing.Any]] - 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). + mmr_lambda : typing.Optional[float] - user_id : typing.Optional[str] - user_id identifies the target user graph. Exactly one of user_id or - graph_id is required. + query : typing.Optional[str] + + reranker : typing.Optional[str] + + idempotency_key : typing.Optional[str] request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - HttpResponse[GraphSubgraphResponse] - Subgraph + SyncPager[Edge, EdgePage] + OK """ _response = self._client_wrapper.httpx_client.request( - "graph/subgraph", + f"graphs/{jsonable_encoder(graph_uuid)}/search/edges", method="POST", + params={ + "limit": limit, + "cursor": cursor, + }, json={ - "depth": depth, - "direction": direction, - "graph_id": graph_id, - "max_edges": max_edges, - "max_nodes": max_nodes, - "search_filters": convert_and_respect_annotation_metadata( - object_=search_filters, annotation=SearchFilters, direction="write" - ), - "seed_node_uuids": seed_node_uuids, - "user_id": user_id, + "bfs_origin_node_uuids": bfs_origin_node_uuids, + "center_node_uuid": center_node_uuid, + "filters": filters, + "mmr_lambda": mmr_lambda, + "query": query, + "reranker": reranker, }, headers={ "content-type": "application/json", + "Idempotency-Key": str(idempotency_key) if idempotency_key is not None else None, }, request_options=request_options, omit=OMIT, ) try: if 200 <= _response.status_code < 300: - _data = typing.cast( - GraphSubgraphResponse, + _parsed_response = typing.cast( + EdgePage, parse_obj_as( - type_=GraphSubgraphResponse, # type: ignore + type_=EdgePage, # type: ignore object_=_response.json(), ), ) - return HttpResponse(response=_response, data=_data) + _items = _parsed_response.items + _parsed_next = _parsed_response.next_cursor + _has_next = _parsed_next is not None and _parsed_next != "" + _get_next = lambda: self.search_edges( + graph_uuid, + limit=limit, + cursor=_parsed_next, + bfs_origin_node_uuids=bfs_origin_node_uuids, + center_node_uuid=center_node_uuid, + filters=filters, + mmr_lambda=mmr_lambda, + query=query, + reranker=reranker, + idempotency_key=idempotency_key, + request_options=request_options, + ) + return SyncPager(has_next=_has_next, items=_items, get_next=_get_next, response=_parsed_response) if _response.status_code == 400: raise BadRequestError( headers=dict(_response.headers), body=typing.cast( - typing.Optional[typing.Any], + types_api_error_ApiError, parse_obj_as( - type_=typing.Optional[typing.Any], # type: ignore + type_=types_api_error_ApiError, # type: ignore object_=_response.json(), ), ), ) - if _response.status_code == 403: - raise ForbiddenError( + if _response.status_code == 401: + raise UnauthorizedError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -1671,13 +1527,145 @@ def get_subgraph( ), ), ) - 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 + _response_json = _response.json() + except JSONDecodeError: + raise core_api_error_ApiError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.text + ) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) + raise core_api_error_ApiError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response_json + ) + + def search_episodes( + self, + graph_uuid: str, + *, + limit: typing.Optional[int] = None, + cursor: typing.Optional[str] = None, + bfs_origin_node_uuids: typing.Optional[typing.Sequence[str]] = OMIT, + center_node_uuid: typing.Optional[str] = OMIT, + filters: typing.Optional[typing.Dict[str, typing.Any]] = OMIT, + mmr_lambda: typing.Optional[float] = OMIT, + query: typing.Optional[str] = OMIT, + reranker: typing.Optional[str] = OMIT, + idempotency_key: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> SyncPager[Episode, EpisodePage]: + """ + Parameters + ---------- + graph_uuid : str + Graph UUID + + limit : typing.Optional[int] + Page size + + cursor : typing.Optional[str] + Opaque page cursor + + bfs_origin_node_uuids : typing.Optional[typing.Sequence[str]] + + center_node_uuid : typing.Optional[str] + + filters : typing.Optional[typing.Dict[str, typing.Any]] + + mmr_lambda : typing.Optional[float] + + query : typing.Optional[str] + + reranker : typing.Optional[str] + + idempotency_key : typing.Optional[str] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + SyncPager[Episode, EpisodePage] + OK + """ + _response = self._client_wrapper.httpx_client.request( + f"graphs/{jsonable_encoder(graph_uuid)}/search/episodes", + method="POST", + params={ + "limit": limit, + "cursor": cursor, + }, + json={ + "bfs_origin_node_uuids": bfs_origin_node_uuids, + "center_node_uuid": center_node_uuid, + "filters": filters, + "mmr_lambda": mmr_lambda, + "query": query, + "reranker": reranker, + }, + headers={ + "content-type": "application/json", + "Idempotency-Key": str(idempotency_key) if idempotency_key is not None else None, + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _parsed_response = typing.cast( + EpisodePage, + parse_obj_as( + type_=EpisodePage, # type: ignore + object_=_response.json(), + ), + ) + _items = _parsed_response.items + _parsed_next = _parsed_response.next_cursor + _has_next = _parsed_next is not None and _parsed_next != "" + _get_next = lambda: self.search_episodes( + graph_uuid, + limit=limit, + cursor=_parsed_next, + bfs_origin_node_uuids=bfs_origin_node_uuids, + center_node_uuid=center_node_uuid, + filters=filters, + mmr_lambda=mmr_lambda, + query=query, + reranker=reranker, + idempotency_key=idempotency_key, + request_options=request_options, + ) + return SyncPager(has_next=_has_next, items=_items, get_next=_get_next, response=_parsed_response) + if _response.status_code == 400: + raise BadRequestError( + 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 == 401: + raise UnauthorizedError( + 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(), ), ), @@ -1687,42 +1675,133 @@ def get_subgraph( raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response.text ) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response_json ) - def get(self, graph_id: str, *, request_options: typing.Optional[RequestOptions] = None) -> HttpResponse[Graph]: + def search_nodes( + self, + graph_uuid: str, + *, + limit: typing.Optional[int] = None, + cursor: typing.Optional[str] = None, + bfs_origin_node_uuids: typing.Optional[typing.Sequence[str]] = OMIT, + center_node_uuid: typing.Optional[str] = OMIT, + filters: typing.Optional[typing.Dict[str, typing.Any]] = OMIT, + mmr_lambda: typing.Optional[float] = OMIT, + query: typing.Optional[str] = OMIT, + reranker: typing.Optional[str] = OMIT, + idempotency_key: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> SyncPager[Node, NodePage]: """ - Returns a graph. - Parameters ---------- - graph_id : str - The graph_id of the graph to get. + graph_uuid : str + Graph UUID + + limit : typing.Optional[int] + Page size + + cursor : typing.Optional[str] + Opaque page cursor + + bfs_origin_node_uuids : typing.Optional[typing.Sequence[str]] + + center_node_uuid : typing.Optional[str] + + filters : typing.Optional[typing.Dict[str, typing.Any]] + + mmr_lambda : typing.Optional[float] + + query : typing.Optional[str] + + reranker : typing.Optional[str] + + idempotency_key : typing.Optional[str] request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - HttpResponse[Graph] - The graph that was retrieved. + SyncPager[Node, NodePage] + OK """ _response = self._client_wrapper.httpx_client.request( - f"graph/{jsonable_encoder(graph_id)}", - method="GET", + f"graphs/{jsonable_encoder(graph_uuid)}/search/nodes", + method="POST", + params={ + "limit": limit, + "cursor": cursor, + }, + json={ + "bfs_origin_node_uuids": bfs_origin_node_uuids, + "center_node_uuid": center_node_uuid, + "filters": filters, + "mmr_lambda": mmr_lambda, + "query": query, + "reranker": reranker, + }, + headers={ + "content-type": "application/json", + "Idempotency-Key": str(idempotency_key) if idempotency_key is not None else None, + }, request_options=request_options, + omit=OMIT, ) try: if 200 <= _response.status_code < 300: - _data = typing.cast( - Graph, + _parsed_response = typing.cast( + NodePage, parse_obj_as( - type_=Graph, # type: ignore + type_=NodePage, # type: ignore object_=_response.json(), ), ) - return HttpResponse(response=_response, data=_data) + _items = _parsed_response.items + _parsed_next = _parsed_response.next_cursor + _has_next = _parsed_next is not None and _parsed_next != "" + _get_next = lambda: self.search_nodes( + graph_uuid, + limit=limit, + cursor=_parsed_next, + bfs_origin_node_uuids=bfs_origin_node_uuids, + center_node_uuid=center_node_uuid, + filters=filters, + mmr_lambda=mmr_lambda, + query=query, + reranker=reranker, + idempotency_key=idempotency_key, + request_options=request_options, + ) + return SyncPager(has_next=_has_next, items=_items, get_next=_get_next, response=_parsed_response) + if _response.status_code == 400: + raise BadRequestError( + 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 == 401: + raise UnauthorizedError( + 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), @@ -1734,8 +1813,140 @@ def get(self, graph_id: str, *, request_options: typing.Optional[RequestOptions] ), ), ) - if _response.status_code == 500: - raise InternalServerError( + _response_json = _response.json() + except JSONDecodeError: + raise core_api_error_ApiError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.text + ) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) + raise core_api_error_ApiError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response_json + ) + + def search_observations( + self, + graph_uuid: str, + *, + limit: typing.Optional[int] = None, + cursor: typing.Optional[str] = None, + bfs_origin_node_uuids: typing.Optional[typing.Sequence[str]] = OMIT, + center_node_uuid: typing.Optional[str] = OMIT, + filters: typing.Optional[typing.Dict[str, typing.Any]] = OMIT, + mmr_lambda: typing.Optional[float] = OMIT, + query: typing.Optional[str] = OMIT, + reranker: typing.Optional[str] = OMIT, + idempotency_key: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> SyncPager[Observation, ObservationPage]: + """ + Parameters + ---------- + graph_uuid : str + Graph UUID + + limit : typing.Optional[int] + Page size + + cursor : typing.Optional[str] + Opaque page cursor + + bfs_origin_node_uuids : typing.Optional[typing.Sequence[str]] + + center_node_uuid : typing.Optional[str] + + filters : typing.Optional[typing.Dict[str, typing.Any]] + + mmr_lambda : typing.Optional[float] + + query : typing.Optional[str] + + reranker : typing.Optional[str] + + idempotency_key : typing.Optional[str] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + SyncPager[Observation, ObservationPage] + OK + """ + _response = self._client_wrapper.httpx_client.request( + f"graphs/{jsonable_encoder(graph_uuid)}/search/observations", + method="POST", + params={ + "limit": limit, + "cursor": cursor, + }, + json={ + "bfs_origin_node_uuids": bfs_origin_node_uuids, + "center_node_uuid": center_node_uuid, + "filters": filters, + "mmr_lambda": mmr_lambda, + "query": query, + "reranker": reranker, + }, + headers={ + "content-type": "application/json", + "Idempotency-Key": str(idempotency_key) if idempotency_key is not None else None, + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _parsed_response = typing.cast( + ObservationPage, + parse_obj_as( + type_=ObservationPage, # type: ignore + object_=_response.json(), + ), + ) + _items = _parsed_response.items + _parsed_next = _parsed_response.next_cursor + _has_next = _parsed_next is not None and _parsed_next != "" + _get_next = lambda: self.search_observations( + graph_uuid, + limit=limit, + cursor=_parsed_next, + bfs_origin_node_uuids=bfs_origin_node_uuids, + center_node_uuid=center_node_uuid, + filters=filters, + mmr_lambda=mmr_lambda, + query=query, + reranker=reranker, + idempotency_key=idempotency_key, + request_options=request_options, + ) + return SyncPager(has_next=_has_next, items=_items, get_next=_get_next, response=_parsed_response) + if _response.status_code == 400: + raise BadRequestError( + 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 == 401: + raise UnauthorizedError( + 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, @@ -1750,40 +1961,222 @@ def get(self, graph_id: str, *, request_options: typing.Optional[RequestOptions] raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response.text ) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response_json ) - def delete( - self, graph_id: str, *, request_options: typing.Optional[RequestOptions] = None - ) -> HttpResponse[SuccessResponse]: + def search_thread_summaries( + self, + graph_uuid: str, + *, + limit: typing.Optional[int] = None, + cursor: typing.Optional[str] = None, + bfs_origin_node_uuids: typing.Optional[typing.Sequence[str]] = OMIT, + center_node_uuid: typing.Optional[str] = OMIT, + filters: typing.Optional[typing.Dict[str, typing.Any]] = OMIT, + mmr_lambda: typing.Optional[float] = OMIT, + query: typing.Optional[str] = OMIT, + reranker: typing.Optional[str] = OMIT, + idempotency_key: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> SyncPager[ThreadSummary, ThreadSummaryPage]: + """ + Parameters + ---------- + graph_uuid : str + Graph UUID + + limit : typing.Optional[int] + Page size + + cursor : typing.Optional[str] + Opaque page cursor + + bfs_origin_node_uuids : typing.Optional[typing.Sequence[str]] + + center_node_uuid : typing.Optional[str] + + filters : typing.Optional[typing.Dict[str, typing.Any]] + + mmr_lambda : typing.Optional[float] + + query : typing.Optional[str] + + reranker : typing.Optional[str] + + idempotency_key : typing.Optional[str] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + SyncPager[ThreadSummary, ThreadSummaryPage] + OK """ - Deletes a graph. If you would like to delete a user graph, make sure to use user.delete instead. + _response = self._client_wrapper.httpx_client.request( + f"graphs/{jsonable_encoder(graph_uuid)}/search/thread-summaries", + method="POST", + params={ + "limit": limit, + "cursor": cursor, + }, + json={ + "bfs_origin_node_uuids": bfs_origin_node_uuids, + "center_node_uuid": center_node_uuid, + "filters": filters, + "mmr_lambda": mmr_lambda, + "query": query, + "reranker": reranker, + }, + headers={ + "content-type": "application/json", + "Idempotency-Key": str(idempotency_key) if idempotency_key is not None else None, + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _parsed_response = typing.cast( + ThreadSummaryPage, + parse_obj_as( + type_=ThreadSummaryPage, # type: ignore + object_=_response.json(), + ), + ) + _items = _parsed_response.items + _parsed_next = _parsed_response.next_cursor + _has_next = _parsed_next is not None and _parsed_next != "" + _get_next = lambda: self.search_thread_summaries( + graph_uuid, + limit=limit, + cursor=_parsed_next, + bfs_origin_node_uuids=bfs_origin_node_uuids, + center_node_uuid=center_node_uuid, + filters=filters, + mmr_lambda=mmr_lambda, + query=query, + reranker=reranker, + idempotency_key=idempotency_key, + request_options=request_options, + ) + return SyncPager(has_next=_has_next, items=_items, get_next=_get_next, response=_parsed_response) + if _response.status_code == 400: + raise BadRequestError( + 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 == 401: + raise UnauthorizedError( + 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(), + ), + ), + ) + _response_json = _response.json() + except JSONDecodeError: + raise core_api_error_ApiError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.text + ) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) + raise core_api_error_ApiError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response_json + ) + def get_subgraph( + self, + graph_uuid: str, + *, + depth: typing.Optional[int] = OMIT, + direction: typing.Optional[str] = OMIT, + filters: typing.Optional[typing.Dict[str, typing.Any]] = OMIT, + max_edges: typing.Optional[int] = OMIT, + max_nodes: typing.Optional[int] = OMIT, + seed_node_uuids: typing.Optional[typing.Sequence[str]] = OMIT, + idempotency_key: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[SubgraphResponse]: + """ Parameters ---------- - graph_id : str - Graph ID + graph_uuid : str + Graph UUID + + depth : typing.Optional[int] + + direction : typing.Optional[str] + + filters : typing.Optional[typing.Dict[str, typing.Any]] + + max_edges : typing.Optional[int] + + max_nodes : typing.Optional[int] + + seed_node_uuids : typing.Optional[typing.Sequence[str]] + + idempotency_key : typing.Optional[str] request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - HttpResponse[SuccessResponse] - Deleted + HttpResponse[SubgraphResponse] + OK """ _response = self._client_wrapper.httpx_client.request( - f"graph/{jsonable_encoder(graph_id)}", - method="DELETE", + f"graphs/{jsonable_encoder(graph_uuid)}/subgraph", + method="POST", + json={ + "depth": depth, + "direction": direction, + "filters": filters, + "max_edges": max_edges, + "max_nodes": max_nodes, + "seed_node_uuids": seed_node_uuids, + }, + headers={ + "content-type": "application/json", + "Idempotency-Key": str(idempotency_key) if idempotency_key is not None else None, + }, request_options=request_options, + omit=OMIT, ) try: if 200 <= _response.status_code < 300: _data = typing.cast( - SuccessResponse, + SubgraphResponse, parse_obj_as( - type_=SuccessResponse, # type: ignore + type_=SubgraphResponse, # type: ignore object_=_response.json(), ), ) @@ -1792,9 +2185,20 @@ def delete( raise BadRequestError( headers=dict(_response.headers), body=typing.cast( - typing.Optional[typing.Any], + types_api_error_ApiError, + parse_obj_as( + type_=types_api_error_ApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 401: + raise UnauthorizedError( + headers=dict(_response.headers), + body=typing.cast( + types_api_error_ApiError, parse_obj_as( - type_=typing.Optional[typing.Any], # type: ignore + type_=types_api_error_ApiError, # type: ignore object_=_response.json(), ), ), @@ -1810,8 +2214,84 @@ def delete( ), ), ) - if _response.status_code == 500: - raise InternalServerError( + _response_json = _response.json() + except JSONDecodeError: + raise core_api_error_ApiError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.text + ) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) + raise core_api_error_ApiError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response_json + ) + + def warm( + self, + graph_uuid: str, + *, + idempotency_key: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[AsyncResult]: + """ + Parameters + ---------- + graph_uuid : str + Graph UUID + + idempotency_key : typing.Optional[str] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[AsyncResult] + Accepted + """ + _response = self._client_wrapper.httpx_client.request( + f"graphs/{jsonable_encoder(graph_uuid)}/warm", + method="POST", + headers={ + "Idempotency-Key": str(idempotency_key) if idempotency_key is not None else None, + }, + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + AsyncResult, + parse_obj_as( + type_=AsyncResult, # 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( + types_api_error_ApiError, + parse_obj_as( + type_=types_api_error_ApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 401: + raise UnauthorizedError( + 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, @@ -1826,52 +2306,62 @@ def delete( raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response.text ) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response_json ) - def update( + +class AsyncRawGraphClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._client_wrapper = client_wrapper + + async def create( self, - graph_id: str, *, description: typing.Optional[str] = OMIT, + graph_id: typing.Optional[str] = OMIT, name: typing.Optional[str] = OMIT, time_zone: typing.Optional[str] = OMIT, + idempotency_key: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, - ) -> HttpResponse[Graph]: + ) -> AsyncHttpResponse[Graph]: """ - Updates information about a graph. - Parameters ---------- - graph_id : str - Graph ID - description : typing.Optional[str] + graph_id : typing.Optional[str] + name : typing.Optional[str] time_zone : typing.Optional[str] - The graph's IANA time zone. Stored on its group-backed subject. + + idempotency_key : typing.Optional[str] request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - HttpResponse[Graph] - The updated graph object + AsyncHttpResponse[Graph] + Created """ - _response = self._client_wrapper.httpx_client.request( - f"graph/{jsonable_encoder(graph_id)}", - method="PATCH", + _response = await self._client_wrapper.httpx_client.request( + "graphs", + method="POST", json={ "description": description, + "graph_id": graph_id, "name": name, "time_zone": time_zone, }, headers={ "content-type": "application/json", + "Idempotency-Key": str(idempotency_key) if idempotency_key is not None else None, }, request_options=request_options, omit=OMIT, @@ -1885,20 +2375,20 @@ def update( object_=_response.json(), ), ) - return HttpResponse(response=_response, data=_data) + return AsyncHttpResponse(response=_response, data=_data) if _response.status_code == 400: raise BadRequestError( headers=dict(_response.headers), body=typing.cast( - typing.Optional[typing.Any], + types_api_error_ApiError, parse_obj_as( - type_=typing.Optional[typing.Any], # type: ignore + type_=types_api_error_ApiError, # type: ignore object_=_response.json(), ), ), ) - if _response.status_code == 404: - raise NotFoundError( + if _response.status_code == 401: + raise UnauthorizedError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -1908,8 +2398,8 @@ def update( ), ), ) - if _response.status_code == 500: - raise InternalServerError( + if _response.status_code == 404: + raise NotFoundError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -1924,46 +2414,109 @@ def update( raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response.text ) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response_json ) - def warm( - self, graph_id: str, *, request_options: typing.Optional[RequestOptions] = None - ) -> HttpResponse[SuccessResponse]: + async def list( + self, + *, + limit: typing.Optional[int] = None, + cursor: typing.Optional[str] = None, + order_by: typing.Optional[str] = None, + order: typing.Optional[str] = None, + search: typing.Optional[str] = OMIT, + idempotency_key: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncPager[Graph, GraphPage]: """ - Hints Zep to warm a graph for low-latency search - Parameters ---------- - graph_id : str - The graph_id of the graph to warm. + limit : typing.Optional[int] + Page size + + cursor : typing.Optional[str] + Opaque page cursor + + order_by : typing.Optional[str] + Sort field + + order : typing.Optional[str] + asc or desc + + search : typing.Optional[str] + + idempotency_key : typing.Optional[str] request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - HttpResponse[SuccessResponse] - Warm hint accepted + AsyncPager[Graph, GraphPage] + OK """ - _response = self._client_wrapper.httpx_client.request( - f"graph/{jsonable_encoder(graph_id)}/warm", - method="GET", + _response = await self._client_wrapper.httpx_client.request( + "graphs/list", + method="POST", + params={ + "limit": limit, + "cursor": cursor, + "order_by": order_by, + "order": order, + }, + json={ + "search": search, + }, + headers={ + "content-type": "application/json", + "Idempotency-Key": str(idempotency_key) if idempotency_key is not None else None, + }, request_options=request_options, + omit=OMIT, ) try: if 200 <= _response.status_code < 300: - _data = typing.cast( - SuccessResponse, + _parsed_response = typing.cast( + GraphPage, parse_obj_as( - type_=SuccessResponse, # type: ignore + type_=GraphPage, # type: ignore object_=_response.json(), ), ) - return HttpResponse(response=_response, data=_data) - if _response.status_code == 404: - raise NotFoundError( + _items = _parsed_response.items + _parsed_next = _parsed_response.next_cursor + _has_next = _parsed_next is not None and _parsed_next != "" + + async def _get_next(): + return await self.list( + limit=limit, + cursor=_parsed_next, + order_by=order_by, + order=order, + search=search, + idempotency_key=idempotency_key, + request_options=request_options, + ) + + return AsyncPager(has_next=_has_next, items=_items, get_next=_get_next, response=_parsed_response) + if _response.status_code == 400: + raise BadRequestError( + 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 == 401: + raise UnauthorizedError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -1973,8 +2526,8 @@ def warm( ), ), ) - if _response.status_code == 500: - raise InternalServerError( + if _response.status_code == 404: + raise NotFoundError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -1989,56 +2542,63 @@ def warm( raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response.text ) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response_json ) - -class AsyncRawGraphClient: - def __init__(self, *, client_wrapper: AsyncClientWrapper): - self._client_wrapper = client_wrapper - - async def list_custom_instructions( + async def lookup( self, *, - user_id: typing.Optional[str] = None, - graph_id: typing.Optional[str] = None, + graph_id: typing.Optional[str] = OMIT, + thread_id: typing.Optional[str] = OMIT, + user_id: typing.Optional[str] = OMIT, + idempotency_key: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, - ) -> AsyncHttpResponse[ListCustomInstructionsResponse]: + ) -> AsyncHttpResponse[Graph]: """ - Lists all custom instructions for a project, user, or graph. - Parameters ---------- + graph_id : typing.Optional[str] + + thread_id : typing.Optional[str] + user_id : typing.Optional[str] - User ID to get user-specific instructions - graph_id : typing.Optional[str] - Graph ID to get graph-specific instructions + idempotency_key : typing.Optional[str] request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - AsyncHttpResponse[ListCustomInstructionsResponse] - The list of instructions. + AsyncHttpResponse[Graph] + OK """ _response = await self._client_wrapper.httpx_client.request( - "custom-instructions", - method="GET", - params={ - "user_id": user_id, + "graphs/lookup", + method="POST", + json={ "graph_id": graph_id, + "thread_id": thread_id, + "user_id": user_id, + }, + headers={ + "content-type": "application/json", + "Idempotency-Key": str(idempotency_key) if idempotency_key is not None else None, }, request_options=request_options, + omit=OMIT, ) try: if 200 <= _response.status_code < 300: _data = typing.cast( - ListCustomInstructionsResponse, + Graph, parse_obj_as( - type_=ListCustomInstructionsResponse, # type: ignore + type_=Graph, # type: ignore object_=_response.json(), ), ) @@ -2047,15 +2607,15 @@ async def list_custom_instructions( raise BadRequestError( headers=dict(_response.headers), body=typing.cast( - typing.Optional[typing.Any], + types_api_error_ApiError, parse_obj_as( - type_=typing.Optional[typing.Any], # type: ignore + type_=types_api_error_ApiError, # type: ignore object_=_response.json(), ), ), ) - if _response.status_code == 404: - raise NotFoundError( + if _response.status_code == 401: + raise UnauthorizedError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -2065,8 +2625,8 @@ async def list_custom_instructions( ), ), ) - if _response.status_code == 500: - raise InternalServerError( + if _response.status_code == 404: + raise NotFoundError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -2081,62 +2641,42 @@ async def list_custom_instructions( raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response.text ) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response_json ) - async def add_custom_instructions( - self, - *, - instructions: typing.Sequence[CustomInstruction], - graph_ids: typing.Optional[typing.Sequence[str]] = OMIT, - user_ids: typing.Optional[typing.Sequence[str]] = OMIT, - request_options: typing.Optional[RequestOptions] = None, - ) -> AsyncHttpResponse[SuccessResponse]: + async def get( + self, graph_uuid: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[Graph]: """ - Adds new custom instructions for graphs without removing existing ones. If user_ids or graph_ids is empty, adds to project-wide default instructions. - Parameters ---------- - instructions : typing.Sequence[CustomInstruction] - Instructions to add to the graph. - - graph_ids : typing.Optional[typing.Sequence[str]] - Graph IDs to add the instructions to. If empty, the instructions are added to the project-wide default. - - user_ids : typing.Optional[typing.Sequence[str]] - User IDs to add the instructions to. If empty, the instructions are added to the project-wide default. + graph_uuid : str + Graph UUID request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - AsyncHttpResponse[SuccessResponse] - Instructions added successfully + AsyncHttpResponse[Graph] + OK """ _response = await self._client_wrapper.httpx_client.request( - "custom-instructions", - method="POST", - json={ - "graph_ids": graph_ids, - "instructions": convert_and_respect_annotation_metadata( - object_=instructions, annotation=typing.Sequence[CustomInstruction], direction="write" - ), - "user_ids": user_ids, - }, - headers={ - "content-type": "application/json", - }, + f"graphs/{jsonable_encoder(graph_uuid)}", + method="GET", request_options=request_options, - omit=OMIT, ) try: if 200 <= _response.status_code < 300: _data = typing.cast( - SuccessResponse, + Graph, parse_obj_as( - type_=SuccessResponse, # type: ignore + type_=Graph, # type: ignore object_=_response.json(), ), ) @@ -2145,15 +2685,15 @@ async def add_custom_instructions( raise BadRequestError( headers=dict(_response.headers), body=typing.cast( - typing.Optional[typing.Any], + types_api_error_ApiError, parse_obj_as( - type_=typing.Optional[typing.Any], # type: ignore + type_=types_api_error_ApiError, # type: ignore object_=_response.json(), ), ), ) - if _response.status_code == 404: - raise NotFoundError( + if _response.status_code == 401: + raise UnauthorizedError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -2163,8 +2703,8 @@ async def add_custom_instructions( ), ), ) - if _response.status_code == 500: - raise InternalServerError( + if _response.status_code == 404: + raise NotFoundError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -2179,60 +2719,51 @@ async def add_custom_instructions( raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response.text ) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response_json ) - async def delete_custom_instructions( + async def delete( self, + graph_uuid: str, *, - graph_ids: typing.Optional[typing.Sequence[str]] = OMIT, - instruction_names: typing.Optional[typing.Sequence[str]] = OMIT, - user_ids: typing.Optional[typing.Sequence[str]] = OMIT, + idempotency_key: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, - ) -> AsyncHttpResponse[SuccessResponse]: + ) -> AsyncHttpResponse[GraphDeleteResult]: """ - Deletes custom instructions for graphs or project wide defaults. - Parameters ---------- - graph_ids : typing.Optional[typing.Sequence[str]] - Determines which group graphs will have their custom instructions deleted. If no graphs are provided, the project-wide custom instructions will be affected. - - instruction_names : typing.Optional[typing.Sequence[str]] - Unique identifier for the instructions to be deleted. If empty deletes all instructions. + graph_uuid : str + Graph UUID - user_ids : typing.Optional[typing.Sequence[str]] - Determines which user graphs will have their custom instructions deleted. If no users are provided, the project-wide custom instructions will be affected. + idempotency_key : typing.Optional[str] request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - AsyncHttpResponse[SuccessResponse] - Instructions deleted successfully + AsyncHttpResponse[GraphDeleteResult] + Accepted """ _response = await self._client_wrapper.httpx_client.request( - "custom-instructions", + f"graphs/{jsonable_encoder(graph_uuid)}", method="DELETE", - json={ - "graph_ids": graph_ids, - "instruction_names": instruction_names, - "user_ids": user_ids, - }, headers={ - "content-type": "application/json", + "Idempotency-Key": str(idempotency_key) if idempotency_key is not None else None, }, request_options=request_options, - omit=OMIT, ) try: if 200 <= _response.status_code < 300: _data = typing.cast( - SuccessResponse, + GraphDeleteResult, parse_obj_as( - type_=SuccessResponse, # type: ignore + type_=GraphDeleteResult, # type: ignore object_=_response.json(), ), ) @@ -2241,15 +2772,15 @@ async def delete_custom_instructions( raise BadRequestError( headers=dict(_response.headers), body=typing.cast( - typing.Optional[typing.Any], + types_api_error_ApiError, parse_obj_as( - type_=typing.Optional[typing.Any], # type: ignore + type_=types_api_error_ApiError, # type: ignore object_=_response.json(), ), ), ) - if _response.status_code == 404: - raise NotFoundError( + if _response.status_code == 401: + raise UnauthorizedError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -2259,8 +2790,8 @@ async def delete_custom_instructions( ), ), ) - if _response.status_code == 500: - raise InternalServerError( + if _response.status_code == 404: + raise NotFoundError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -2275,51 +2806,70 @@ async def delete_custom_instructions( raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response.text ) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response_json ) - async def list_entity_types( + async def update( self, + graph_uuid: str, *, - user_id: typing.Optional[str] = None, - graph_id: typing.Optional[str] = None, + description: typing.Optional[str] = OMIT, + name: typing.Optional[str] = OMIT, + time_zone: typing.Optional[str] = OMIT, + idempotency_key: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, - ) -> AsyncHttpResponse[EntityTypeResponse]: + ) -> AsyncHttpResponse[Graph]: """ - Returns all entity types for a project, user, or graph. - Parameters ---------- - user_id : typing.Optional[str] - User ID to get user-specific entity types + graph_uuid : str + Graph UUID - graph_id : typing.Optional[str] - Graph ID to get graph-specific entity types + description : typing.Optional[str] + Omit to leave unchanged, send JSON null to clear, or send a value to set. + + name : typing.Optional[str] + Omit to leave unchanged, send JSON null to clear, or send a value to set. + + time_zone : typing.Optional[str] + Omit to leave unchanged, send JSON null to clear, or send a value to set. + + idempotency_key : typing.Optional[str] request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - AsyncHttpResponse[EntityTypeResponse] - The list of entity types. + AsyncHttpResponse[Graph] + OK """ _response = await self._client_wrapper.httpx_client.request( - "entity-types", - method="GET", - params={ - "user_id": user_id, - "graph_id": graph_id, + f"graphs/{jsonable_encoder(graph_uuid)}", + method="PATCH", + json={ + "description": description, + "name": name, + "time_zone": time_zone, + }, + headers={ + "content-type": "application/json", + "Idempotency-Key": str(idempotency_key) if idempotency_key is not None else None, }, request_options=request_options, + omit=OMIT, ) try: if 200 <= _response.status_code < 300: _data = typing.cast( - EntityTypeResponse, + Graph, parse_obj_as( - type_=EntityTypeResponse, # type: ignore + type_=Graph, # type: ignore object_=_response.json(), ), ) @@ -2328,15 +2878,15 @@ async def list_entity_types( raise BadRequestError( headers=dict(_response.headers), body=typing.cast( - typing.Optional[typing.Any], + types_api_error_ApiError, parse_obj_as( - type_=typing.Optional[typing.Any], # type: ignore + type_=types_api_error_ApiError, # type: ignore object_=_response.json(), ), ), ) - if _response.status_code == 404: - raise NotFoundError( + if _response.status_code == 401: + raise UnauthorizedError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -2346,8 +2896,8 @@ async def list_entity_types( ), ), ) - if _response.status_code == 500: - raise InternalServerError( + if _response.status_code == 404: + raise NotFoundError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -2362,55 +2912,49 @@ async def list_entity_types( raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response.text ) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response_json ) - async def set_entity_types_internal( + async def clone( self, + graph_uuid: str, *, - edge_types: typing.Optional[typing.Sequence[EdgeType]] = OMIT, - entity_types: typing.Optional[typing.Sequence[EntityType]] = OMIT, - graph_ids: typing.Optional[typing.Sequence[str]] = OMIT, - user_ids: typing.Optional[typing.Sequence[str]] = OMIT, + target_graph_id: typing.Optional[str] = OMIT, + idempotency_key: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, - ) -> AsyncHttpResponse[SuccessResponse]: + ) -> AsyncHttpResponse[CloneGraphResult]: """ - Sets the entity types for multiple users and graphs, replacing any existing ones. - Parameters ---------- - edge_types : typing.Optional[typing.Sequence[EdgeType]] - - entity_types : typing.Optional[typing.Sequence[EntityType]] + graph_uuid : str + Graph UUID - graph_ids : typing.Optional[typing.Sequence[str]] + target_graph_id : typing.Optional[str] - user_ids : typing.Optional[typing.Sequence[str]] + idempotency_key : typing.Optional[str] request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - AsyncHttpResponse[SuccessResponse] - Entity types set successfully + AsyncHttpResponse[CloneGraphResult] + Accepted """ _response = await self._client_wrapper.httpx_client.request( - "entity-types", - method="PUT", + f"graphs/{jsonable_encoder(graph_uuid)}/clone", + method="POST", json={ - "edge_types": convert_and_respect_annotation_metadata( - object_=edge_types, annotation=typing.Sequence[EdgeType], direction="write" - ), - "entity_types": convert_and_respect_annotation_metadata( - object_=entity_types, annotation=typing.Sequence[EntityType], direction="write" - ), - "graph_ids": graph_ids, - "user_ids": user_ids, + "target_graph_id": target_graph_id, }, headers={ "content-type": "application/json", + "Idempotency-Key": str(idempotency_key) if idempotency_key is not None else None, }, request_options=request_options, omit=OMIT, @@ -2418,9 +2962,9 @@ async def set_entity_types_internal( try: if 200 <= _response.status_code < 300: _data = typing.cast( - SuccessResponse, + CloneGraphResult, parse_obj_as( - type_=SuccessResponse, # type: ignore + type_=CloneGraphResult, # type: ignore object_=_response.json(), ), ) @@ -2429,15 +2973,15 @@ async def set_entity_types_internal( raise BadRequestError( headers=dict(_response.headers), body=typing.cast( - typing.Optional[typing.Any], + types_api_error_ApiError, parse_obj_as( - type_=typing.Optional[typing.Any], # type: ignore + type_=types_api_error_ApiError, # type: ignore object_=_response.json(), ), ), ) - if _response.status_code == 404: - raise NotFoundError( + if _response.status_code == 401: + raise UnauthorizedError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -2447,8 +2991,8 @@ async def set_entity_types_internal( ), ), ) - if _response.status_code == 500: - raise InternalServerError( + if _response.status_code == 404: + raise NotFoundError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -2463,71 +3007,69 @@ async def set_entity_types_internal( raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response.text ) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response_json ) - async def add( + async def get_context( self, + graph_uuid: str, *, - data: str, - type: GraphDataType, - created_at: 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, - strict_ontology: typing.Optional[bool] = OMIT, - user_id: typing.Optional[str] = OMIT, + filters: typing.Optional[typing.Dict[str, typing.Any]] = OMIT, + include_results: typing.Optional[bool] = OMIT, + max_characters: typing.Optional[int] = OMIT, + query: typing.Optional[str] = OMIT, + recency_bias: typing.Optional[str] = OMIT, + template_uuid: typing.Optional[str] = OMIT, + idempotency_key: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, - ) -> AsyncHttpResponse[Episode]: + ) -> AsyncHttpResponse[GraphContextResponse]: """ - Add data to the graph. - Parameters ---------- - data : str + graph_uuid : str + Graph UUID - type : GraphDataType + filters : typing.Optional[typing.Dict[str, typing.Any]] - created_at : typing.Optional[str] + include_results : typing.Optional[bool] - 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. + max_characters : typing.Optional[int] - metadata : typing.Optional[typing.Dict[str, typing.Optional[typing.Any]]] - Optional metadata key-value pairs. Max 10 keys. Values must be strings, numbers, booleans, or arrays of scalars. + query : typing.Optional[str] - source_description : typing.Optional[str] + recency_bias : typing.Optional[str] - strict_ontology : typing.Optional[bool] - When true, prevents extraction of generic Entity nodes that do not match the configured ontology. + template_uuid : typing.Optional[str] - user_id : typing.Optional[str] - User ID is the ID of the user to which the data will be added. If not adding to a user graph, please use graph_id field instead. + idempotency_key : typing.Optional[str] request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - AsyncHttpResponse[Episode] - Added episode + AsyncHttpResponse[GraphContextResponse] + OK """ _response = await self._client_wrapper.httpx_client.request( - "graph", + f"graphs/{jsonable_encoder(graph_uuid)}/context", method="POST", json={ - "created_at": created_at, - "data": data, - "graph_id": graph_id, - "metadata": metadata, - "source_description": source_description, - "strict_ontology": strict_ontology, - "type": type, - "user_id": user_id, + "filters": filters, + "include_results": include_results, + "max_characters": max_characters, + "query": query, + "recency_bias": recency_bias, + "template_uuid": template_uuid, }, headers={ "content-type": "application/json", + "Idempotency-Key": str(idempotency_key) if idempotency_key is not None else None, }, request_options=request_options, omit=OMIT, @@ -2535,9 +3077,9 @@ async def add( try: if 200 <= _response.status_code < 300: _data = typing.cast( - Episode, + GraphContextResponse, parse_obj_as( - type_=Episode, # type: ignore + type_=GraphContextResponse, # type: ignore object_=_response.json(), ), ) @@ -2546,15 +3088,26 @@ async def add( raise BadRequestError( headers=dict(_response.headers), body=typing.cast( - typing.Optional[typing.Any], + types_api_error_ApiError, + parse_obj_as( + type_=types_api_error_ApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 401: + raise UnauthorizedError( + headers=dict(_response.headers), + body=typing.cast( + types_api_error_ApiError, parse_obj_as( - type_=typing.Optional[typing.Any], # type: ignore + type_=types_api_error_ApiError, # type: ignore object_=_response.json(), ), ), ) - if _response.status_code == 500: - raise InternalServerError( + if _response.status_code == 404: + raise NotFoundError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -2569,68 +3122,42 @@ async def add( raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response.text ) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response_json ) - async def add_batch( - self, - *, - episodes: typing.Sequence[EpisodeData], - graph_id: typing.Optional[str] = OMIT, - strict_ontology: typing.Optional[bool] = OMIT, - user_id: typing.Optional[str] = OMIT, - request_options: typing.Optional[RequestOptions] = None, - ) -> AsyncHttpResponse[typing.List[Episode]]: + async def get_instructions( + self, graph_uuid: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[Instructions]: """ - Deprecated. Use the [Batch API](/adding-batch-data) (`client.batch.*`) instead. - - Adds data to the graph in batch mode, processing episodes concurrently. - - Parameters - ---------- - episodes : typing.Sequence[EpisodeData] - - 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. - - strict_ontology : typing.Optional[bool] - When true, prevents extraction of generic Entity nodes that do not match the configured ontology. - - user_id : typing.Optional[str] - User ID is the ID of the user to which the data will be added. If not adding to a user graph, please use graph_id field instead. + Parameters + ---------- + graph_uuid : str + Graph UUID request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - AsyncHttpResponse[typing.List[Episode]] - Added episodes + AsyncHttpResponse[Instructions] + OK """ _response = await self._client_wrapper.httpx_client.request( - "graph-batch", - method="POST", - json={ - "episodes": convert_and_respect_annotation_metadata( - object_=episodes, annotation=typing.Sequence[EpisodeData], direction="write" - ), - "graph_id": graph_id, - "strict_ontology": strict_ontology, - "user_id": user_id, - }, - headers={ - "content-type": "application/json", - }, + f"graphs/{jsonable_encoder(graph_uuid)}/instructions", + method="GET", request_options=request_options, - omit=OMIT, ) try: if 200 <= _response.status_code < 300: _data = typing.cast( - typing.List[Episode], + Instructions, parse_obj_as( - type_=typing.List[Episode], # type: ignore + type_=Instructions, # type: ignore object_=_response.json(), ), ) @@ -2639,15 +3166,26 @@ async def add_batch( raise BadRequestError( headers=dict(_response.headers), body=typing.cast( - typing.Optional[typing.Any], + types_api_error_ApiError, + parse_obj_as( + type_=types_api_error_ApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 401: + raise UnauthorizedError( + headers=dict(_response.headers), + body=typing.cast( + types_api_error_ApiError, parse_obj_as( - type_=typing.Optional[typing.Any], # type: ignore + type_=types_api_error_ApiError, # type: ignore object_=_response.json(), ), ), ) - if _response.status_code == 500: - raise InternalServerError( + if _response.status_code == 404: + raise NotFoundError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -2662,145 +3200,53 @@ async def add_batch( raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response.text ) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response_json ) - async def add_fact_triple( + async def set_instructions( self, + graph_uuid: str, *, - fact: str, - fact_name: str, - created_at: typing.Optional[str] = OMIT, - edge_attributes: typing.Optional[typing.Dict[str, typing.Optional[typing.Any]]] = OMIT, - expired_at: typing.Optional[str] = OMIT, - graph_id: typing.Optional[str] = OMIT, - invalid_at: typing.Optional[str] = OMIT, - metadata: typing.Optional[typing.Dict[str, typing.Optional[typing.Any]]] = OMIT, - source_node_attributes: typing.Optional[typing.Dict[str, typing.Optional[typing.Any]]] = OMIT, - source_node_labels: typing.Optional[typing.Sequence[str]] = OMIT, - source_node_name: typing.Optional[str] = OMIT, - source_node_summary: typing.Optional[str] = OMIT, - source_node_uuid: typing.Optional[str] = OMIT, - target_node_attributes: typing.Optional[typing.Dict[str, typing.Optional[typing.Any]]] = OMIT, - target_node_labels: typing.Optional[typing.Sequence[str]] = OMIT, - target_node_name: typing.Optional[str] = OMIT, - target_node_summary: typing.Optional[str] = OMIT, - target_node_uuid: typing.Optional[str] = OMIT, - user_id: typing.Optional[str] = OMIT, - valid_at: typing.Optional[str] = OMIT, + inherited: typing.Optional[bool] = OMIT, + instructions: typing.Optional[typing.Sequence[typing.Dict[str, typing.Any]]] = OMIT, + idempotency_key: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, - ) -> AsyncHttpResponse[AddTripleResponse]: + ) -> AsyncHttpResponse[Instructions]: """ - Add a fact triple for a user or group - Parameters ---------- - fact : str - The fact relating the two nodes that this edge represents - - fact_name : str - The name of the edge to add. Should be all caps using snake case (eg RELATES_TO) - - created_at : typing.Optional[str] - The timestamp of the message + graph_uuid : str + Graph UUID - edge_attributes : typing.Optional[typing.Dict[str, typing.Optional[typing.Any]]] - Additional attributes of the edge. Values must be scalar types (string, number, boolean, or null). - Nested objects and arrays are not allowed. + inherited : typing.Optional[bool] - expired_at : typing.Optional[str] - The time (if any) at which the edge expires + instructions : typing.Optional[typing.Sequence[typing.Dict[str, typing.Any]]] - graph_id : typing.Optional[str] - - invalid_at : typing.Optional[str] - The time (if any) at which the fact stops being true - - metadata : typing.Optional[typing.Dict[str, typing.Optional[typing.Any]]] - Optional metadata key-value pairs for the shadow episode created for this fact triple. - Max 10 keys. Values must be strings, numbers, or booleans. - - source_node_attributes : typing.Optional[typing.Dict[str, typing.Optional[typing.Any]]] - Additional attributes of the source node. Values must be scalar types (string, number, boolean, or null). - Nested objects and arrays are not allowed. - - source_node_labels : typing.Optional[typing.Sequence[str]] - The labels for the source node. At most one entity-type label may be - provided so that manually-added triples remain consistent with automatic - episode extraction, which assigns one best-match entity type per node. - The base "Entity" label is added implicitly by the graph layer on save - and does not need to be supplied here. - - source_node_name : typing.Optional[str] - The name of the source node to add - - source_node_summary : typing.Optional[str] - The summary of the source node to add - - source_node_uuid : typing.Optional[str] - The source node uuid - - target_node_attributes : typing.Optional[typing.Dict[str, typing.Optional[typing.Any]]] - Additional attributes of the target node. Values must be scalar types (string, number, boolean, or null). - Nested objects and arrays are not allowed. - - target_node_labels : typing.Optional[typing.Sequence[str]] - The labels for the target node. At most one entity-type label may be - provided so that manually-added triples remain consistent with automatic - episode extraction, which assigns one best-match entity type per node. - The base "Entity" label is added implicitly by the graph layer on save - and does not need to be supplied here. - - target_node_name : typing.Optional[str] - The name of the target node to add - - target_node_summary : typing.Optional[str] - The summary of the target node to add - - target_node_uuid : typing.Optional[str] - The target node uuid - - user_id : typing.Optional[str] - - valid_at : typing.Optional[str] - The time at which the fact becomes true + idempotency_key : typing.Optional[str] request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - AsyncHttpResponse[AddTripleResponse] - Resulting triple + AsyncHttpResponse[Instructions] + OK """ _response = await self._client_wrapper.httpx_client.request( - "graph/add-fact-triple", - method="POST", + f"graphs/{jsonable_encoder(graph_uuid)}/instructions", + method="PUT", json={ - "created_at": created_at, - "edge_attributes": edge_attributes, - "expired_at": expired_at, - "fact": fact, - "fact_name": fact_name, - "graph_id": graph_id, - "invalid_at": invalid_at, - "metadata": metadata, - "source_node_attributes": source_node_attributes, - "source_node_labels": source_node_labels, - "source_node_name": source_node_name, - "source_node_summary": source_node_summary, - "source_node_uuid": source_node_uuid, - "target_node_attributes": target_node_attributes, - "target_node_labels": target_node_labels, - "target_node_name": target_node_name, - "target_node_summary": target_node_summary, - "target_node_uuid": target_node_uuid, - "user_id": user_id, - "valid_at": valid_at, + "inherited": inherited, + "instructions": instructions, }, headers={ "content-type": "application/json", + "Idempotency-Key": str(idempotency_key) if idempotency_key is not None else None, }, request_options=request_options, omit=OMIT, @@ -2808,9 +3254,9 @@ async def add_fact_triple( try: if 200 <= _response.status_code < 300: _data = typing.cast( - AddTripleResponse, + Instructions, parse_obj_as( - type_=AddTripleResponse, # type: ignore + type_=Instructions, # type: ignore object_=_response.json(), ), ) @@ -2819,15 +3265,26 @@ async def add_fact_triple( raise BadRequestError( headers=dict(_response.headers), body=typing.cast( - typing.Optional[typing.Any], + types_api_error_ApiError, + parse_obj_as( + type_=types_api_error_ApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 401: + raise UnauthorizedError( + headers=dict(_response.headers), + body=typing.cast( + types_api_error_ApiError, parse_obj_as( - type_=typing.Optional[typing.Any], # type: ignore + type_=types_api_error_ApiError, # type: ignore object_=_response.json(), ), ), ) - if _response.status_code == 500: - raise InternalServerError( + if _response.status_code == 404: + raise NotFoundError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -2842,65 +3299,42 @@ async def add_fact_triple( raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response.text ) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response_json ) - async def clone( - self, - *, - source_graph_id: typing.Optional[str] = OMIT, - source_user_id: typing.Optional[str] = OMIT, - target_graph_id: typing.Optional[str] = OMIT, - target_user_id: typing.Optional[str] = OMIT, - request_options: typing.Optional[RequestOptions] = None, - ) -> AsyncHttpResponse[CloneGraphResponse]: + async def get_observation_steering( + self, graph_uuid: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[ObservationSteering]: """ - Clone a user or group graph. - Parameters ---------- - source_graph_id : typing.Optional[str] - source_graph_id is the ID of the graph to be cloned. Required if source_user_id is not provided - - source_user_id : typing.Optional[str] - user_id of the user whose graph is being cloned. Required if source_graph_id is not provided - - target_graph_id : typing.Optional[str] - target_graph_id is the ID to be set on the cloned graph. Must not point to an existing graph. Required if target_user_id is not provided. - - target_user_id : typing.Optional[str] - user_id to be set on the cloned user. Must not point to an existing user. Required if target_graph_id is not provided. + graph_uuid : str + Graph UUID request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - AsyncHttpResponse[CloneGraphResponse] - Response object containing graph_id or user_id pointing to the new graph + AsyncHttpResponse[ObservationSteering] + OK """ _response = await self._client_wrapper.httpx_client.request( - "graph/clone", - method="POST", - json={ - "source_graph_id": source_graph_id, - "source_user_id": source_user_id, - "target_graph_id": target_graph_id, - "target_user_id": target_user_id, - }, - headers={ - "content-type": "application/json", - }, + f"graphs/{jsonable_encoder(graph_uuid)}/observation-steering", + method="GET", request_options=request_options, - omit=OMIT, ) try: if 200 <= _response.status_code < 300: _data = typing.cast( - CloneGraphResponse, + ObservationSteering, parse_obj_as( - type_=CloneGraphResponse, # type: ignore + type_=ObservationSteering, # type: ignore object_=_response.json(), ), ) @@ -2909,15 +3343,26 @@ async def clone( raise BadRequestError( headers=dict(_response.headers), body=typing.cast( - typing.Optional[typing.Any], + types_api_error_ApiError, + parse_obj_as( + type_=types_api_error_ApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 401: + raise UnauthorizedError( + headers=dict(_response.headers), + body=typing.cast( + types_api_error_ApiError, parse_obj_as( - type_=typing.Optional[typing.Any], # type: ignore + type_=types_api_error_ApiError, # type: ignore object_=_response.json(), ), ), ) - if _response.status_code == 500: - raise InternalServerError( + if _response.status_code == 404: + raise NotFoundError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -2932,52 +3377,57 @@ async def clone( raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response.text ) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response_json ) - async def create( + async def set_observation_steering( self, + graph_uuid: str, *, - graph_id: str, - description: typing.Optional[str] = OMIT, - name: typing.Optional[str] = OMIT, - time_zone: typing.Optional[str] = OMIT, + inherited: typing.Optional[bool] = OMIT, + instruction: typing.Optional[str] = OMIT, + types: typing.Optional[typing.Sequence[typing.Dict[str, typing.Any]]] = OMIT, + idempotency_key: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, - ) -> AsyncHttpResponse[Graph]: + ) -> AsyncHttpResponse[ObservationSteering]: """ - Creates a new graph. - Parameters ---------- - graph_id : str + graph_uuid : str + Graph UUID - description : typing.Optional[str] + inherited : typing.Optional[bool] - name : typing.Optional[str] + instruction : typing.Optional[str] - time_zone : typing.Optional[str] - The graph's IANA time zone. Stored on its group-backed subject. + types : typing.Optional[typing.Sequence[typing.Dict[str, typing.Any]]] + + idempotency_key : typing.Optional[str] request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - AsyncHttpResponse[Graph] - The added graph + AsyncHttpResponse[ObservationSteering] + OK """ _response = await self._client_wrapper.httpx_client.request( - "graph/create", - method="POST", + f"graphs/{jsonable_encoder(graph_uuid)}/observation-steering", + method="PUT", json={ - "description": description, - "graph_id": graph_id, - "name": name, - "time_zone": time_zone, + "inherited": inherited, + "instruction": instruction, + "types": types, }, headers={ "content-type": "application/json", + "Idempotency-Key": str(idempotency_key) if idempotency_key is not None else None, }, request_options=request_options, omit=OMIT, @@ -2985,9 +3435,9 @@ async def create( try: if 200 <= _response.status_code < 300: _data = typing.cast( - Graph, + ObservationSteering, parse_obj_as( - type_=Graph, # type: ignore + type_=ObservationSteering, # type: ignore object_=_response.json(), ), ) @@ -2996,15 +3446,26 @@ async def create( raise BadRequestError( headers=dict(_response.headers), body=typing.cast( - typing.Optional[typing.Any], + types_api_error_ApiError, + parse_obj_as( + type_=types_api_error_ApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 401: + raise UnauthorizedError( + headers=dict(_response.headers), + body=typing.cast( + types_api_error_ApiError, parse_obj_as( - type_=typing.Optional[typing.Any], # type: ignore + type_=types_api_error_ApiError, # type: ignore object_=_response.json(), ), ), ) - if _response.status_code == 500: - raise InternalServerError( + if _response.status_code == 404: + raise NotFoundError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -3019,66 +3480,42 @@ async def create( raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response.text ) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response_json ) - async def list_all( - self, - *, - page_number: typing.Optional[int] = None, - page_size: typing.Optional[int] = None, - search: typing.Optional[str] = None, - order_by: typing.Optional[str] = None, - asc: typing.Optional[bool] = None, - request_options: typing.Optional[RequestOptions] = None, - ) -> AsyncHttpResponse[GraphListResponse]: + async def get_ontology( + self, graph_uuid: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[Ontology]: """ - Returns all graphs. In order to list users, use user.list_ordered instead - Parameters ---------- - page_number : typing.Optional[int] - Page number for pagination, starting from 1. - - page_size : typing.Optional[int] - Number of graphs to retrieve per page (default 50, range 1-100; explicit 0 is invalid). - - search : typing.Optional[str] - Search term for filtering graphs by graph_id, name, or description. Queries longer than 200 Unicode code points after whitespace normalization are invalid. - - order_by : typing.Optional[str] - Column to sort by (created_at, graph_id, name). - - asc : typing.Optional[bool] - Sort in ascending order. + graph_uuid : str + Graph UUID request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - AsyncHttpResponse[GraphListResponse] - Successfully retrieved list of graphs. + AsyncHttpResponse[Ontology] + OK """ _response = await self._client_wrapper.httpx_client.request( - "graph/list-all", + f"graphs/{jsonable_encoder(graph_uuid)}/ontology", method="GET", - params={ - "pageNumber": page_number, - "pageSize": page_size, - "search": search, - "order_by": order_by, - "asc": asc, - }, request_options=request_options, ) try: if 200 <= _response.status_code < 300: _data = typing.cast( - GraphListResponse, + Ontology, parse_obj_as( - type_=GraphListResponse, # type: ignore + type_=Ontology, # type: ignore object_=_response.json(), ), ) @@ -3087,15 +3524,26 @@ async def list_all( raise BadRequestError( headers=dict(_response.headers), body=typing.cast( - typing.Optional[typing.Any], + types_api_error_ApiError, + parse_obj_as( + type_=types_api_error_ApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 401: + raise UnauthorizedError( + headers=dict(_response.headers), + body=typing.cast( + types_api_error_ApiError, parse_obj_as( - type_=typing.Optional[typing.Any], # type: ignore + type_=types_api_error_ApiError, # type: ignore object_=_response.json(), ), ), ) - if _response.status_code == 500: - raise InternalServerError( + if _response.status_code == 404: + raise NotFoundError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -3110,50 +3558,61 @@ async def list_all( raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response.text ) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response_json ) - async def add_nodes( + async def set_ontology( self, + graph_uuid: str, *, - nodes: typing.Sequence[AddNodeItem], - graph_id: typing.Optional[str] = OMIT, - user_id: typing.Optional[str] = OMIT, + edge_types: typing.Optional[typing.Sequence[EdgeType]] = OMIT, + entity_types: typing.Optional[typing.Sequence[EntityType]] = OMIT, + inherited: typing.Optional[bool] = OMIT, + idempotency_key: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, - ) -> AsyncHttpResponse[AddNodesResponse]: + ) -> AsyncHttpResponse[Ontology]: """ - Add entity nodes to a user or graph directly, without episode ingestion. Up to 100 nodes per request. - Parameters ---------- - nodes : typing.Sequence[AddNodeItem] - The nodes to add. 1 to 100 items. + graph_uuid : str + Graph UUID - graph_id : typing.Optional[str] + edge_types : typing.Optional[typing.Sequence[EdgeType]] - user_id : typing.Optional[str] + entity_types : typing.Optional[typing.Sequence[EntityType]] + + inherited : typing.Optional[bool] + + idempotency_key : typing.Optional[str] request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - AsyncHttpResponse[AddNodesResponse] - Accepted + AsyncHttpResponse[Ontology] + OK """ _response = await self._client_wrapper.httpx_client.request( - "graph/nodes", - method="POST", + f"graphs/{jsonable_encoder(graph_uuid)}/ontology", + method="PUT", json={ - "graph_id": graph_id, - "nodes": convert_and_respect_annotation_metadata( - object_=nodes, annotation=typing.Sequence[AddNodeItem], direction="write" + "edge_types": convert_and_respect_annotation_metadata( + object_=edge_types, annotation=typing.Sequence[EdgeType], direction="write" ), - "user_id": user_id, + "entity_types": convert_and_respect_annotation_metadata( + object_=entity_types, annotation=typing.Sequence[EntityType], direction="write" + ), + "inherited": inherited, }, headers={ "content-type": "application/json", + "Idempotency-Key": str(idempotency_key) if idempotency_key is not None else None, }, request_options=request_options, omit=OMIT, @@ -3161,9 +3620,9 @@ async def add_nodes( try: if 200 <= _response.status_code < 300: _data = typing.cast( - AddNodesResponse, + Ontology, parse_obj_as( - type_=AddNodesResponse, # type: ignore + type_=Ontology, # type: ignore object_=_response.json(), ), ) @@ -3172,15 +3631,26 @@ async def add_nodes( raise BadRequestError( headers=dict(_response.headers), body=typing.cast( - typing.Optional[typing.Any], + types_api_error_ApiError, + parse_obj_as( + type_=types_api_error_ApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 401: + raise UnauthorizedError( + headers=dict(_response.headers), + body=typing.cast( + types_api_error_ApiError, parse_obj_as( - type_=typing.Optional[typing.Any], # type: ignore + type_=types_api_error_ApiError, # type: ignore object_=_response.json(), ), ), ) - if _response.status_code == 500: - raise InternalServerError( + if _response.status_code == 404: + raise NotFoundError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -3195,131 +3665,116 @@ async def add_nodes( raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response.text ) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response_json ) - async def detect_patterns( + async def search_edges( self, + graph_uuid: str, *, - detect: typing.Optional[DetectConfig] = OMIT, - edge_limit: typing.Optional[int] = OMIT, - graph_id: typing.Optional[str] = OMIT, - limit: typing.Optional[int] = OMIT, - min_occurrences: typing.Optional[int] = OMIT, + limit: typing.Optional[int] = None, + cursor: typing.Optional[str] = None, + bfs_origin_node_uuids: typing.Optional[typing.Sequence[str]] = OMIT, + center_node_uuid: typing.Optional[str] = OMIT, + filters: typing.Optional[typing.Dict[str, typing.Any]] = OMIT, + mmr_lambda: typing.Optional[float] = OMIT, query: typing.Optional[str] = OMIT, - query_limit: typing.Optional[int] = OMIT, - recency_weight: typing.Optional[RecencyWeight] = OMIT, - search_filters: typing.Optional[SearchFilters] = OMIT, - seeds: typing.Optional[PatternSeeds] = OMIT, - user_id: typing.Optional[str] = OMIT, + reranker: typing.Optional[str] = OMIT, + idempotency_key: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, - ) -> AsyncHttpResponse[DetectPatternsResponse]: + ) -> AsyncPager[Edge, EdgePage]: """ - 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, - detects triple-frequency patterns, and returns resolved edges ranked by relevance. - Parameters ---------- - detect : typing.Optional[DetectConfig] - Which pattern types to detect with type-specific configuration. - Omit to detect all types with defaults. Ignored when query is set. - - edge_limit : typing.Optional[int] - Max resolved edges per pattern. Default: 10, Max: 100. Only used with query. - - graph_id : typing.Optional[str] - Graph ID when detecting patterns on a named graph + graph_uuid : str + Graph UUID limit : typing.Optional[int] - Max patterns to return. Default: 50, Max: 200 + Page size - min_occurrences : typing.Optional[int] - Minimum occurrence count to report a pattern. Default: 2 + cursor : typing.Optional[str] + Opaque page cursor - query : typing.Optional[str] - Search query for discovering seed nodes via hybrid search. - When set, forces triple-frequency detection only and enables edge resolution - with cross-encoder reranking. Mutually exclusive with seeds. + bfs_origin_node_uuids : typing.Optional[typing.Sequence[str]] + + center_node_uuid : typing.Optional[str] - query_limit : typing.Optional[int] - Max seed nodes from search. Default: 10, Max: 50. Only used with query. + filters : typing.Optional[typing.Dict[str, typing.Any]] - recency_weight : typing.Optional[RecencyWeight] - Exponential half-life decay applied to edge created_at timestamps. - Valid values: none, 7_days, 30_days, 90_days. Default: none + mmr_lambda : typing.Optional[float] - search_filters : typing.Optional[SearchFilters] - Filters which edges/nodes participate in pattern detection. - Reuses the same filter format as /graph/search. + query : typing.Optional[str] - seeds : typing.Optional[PatternSeeds] - Seed selection. If omitted, analyzes the entire graph. Mutually exclusive with query. + reranker : typing.Optional[str] - user_id : typing.Optional[str] - User ID when detecting patterns on a user graph + idempotency_key : typing.Optional[str] request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - AsyncHttpResponse[DetectPatternsResponse] - Detected patterns + AsyncPager[Edge, EdgePage] + OK """ _response = await self._client_wrapper.httpx_client.request( - "graph/patterns", + f"graphs/{jsonable_encoder(graph_uuid)}/search/edges", method="POST", - json={ - "detect": convert_and_respect_annotation_metadata( - object_=detect, annotation=DetectConfig, direction="write" - ), - "edge_limit": edge_limit, - "graph_id": graph_id, + params={ "limit": limit, - "min_occurrences": min_occurrences, + "cursor": cursor, + }, + json={ + "bfs_origin_node_uuids": bfs_origin_node_uuids, + "center_node_uuid": center_node_uuid, + "filters": filters, + "mmr_lambda": mmr_lambda, "query": query, - "query_limit": query_limit, - "recency_weight": recency_weight, - "search_filters": convert_and_respect_annotation_metadata( - object_=search_filters, annotation=SearchFilters, direction="write" - ), - "seeds": convert_and_respect_annotation_metadata( - object_=seeds, annotation=PatternSeeds, direction="write" - ), - "user_id": user_id, + "reranker": reranker, }, headers={ "content-type": "application/json", + "Idempotency-Key": str(idempotency_key) if idempotency_key is not None else None, }, request_options=request_options, omit=OMIT, ) try: if 200 <= _response.status_code < 300: - _data = typing.cast( - DetectPatternsResponse, + _parsed_response = typing.cast( + EdgePage, parse_obj_as( - type_=DetectPatternsResponse, # type: ignore + type_=EdgePage, # type: ignore object_=_response.json(), ), ) - return AsyncHttpResponse(response=_response, data=_data) + _items = _parsed_response.items + _parsed_next = _parsed_response.next_cursor + _has_next = _parsed_next is not None and _parsed_next != "" + + async def _get_next(): + return await self.search_edges( + graph_uuid, + limit=limit, + cursor=_parsed_next, + bfs_origin_node_uuids=bfs_origin_node_uuids, + center_node_uuid=center_node_uuid, + filters=filters, + mmr_lambda=mmr_lambda, + query=query, + reranker=reranker, + idempotency_key=idempotency_key, + request_options=request_options, + ) + + return AsyncPager(has_next=_has_next, items=_items, get_next=_get_next, response=_parsed_response) 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 == 403: - raise ForbiddenError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -3329,8 +3784,8 @@ async def detect_patterns( ), ), ) - if _response.status_code == 404: - raise NotFoundError( + if _response.status_code == 401: + raise UnauthorizedError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -3340,8 +3795,8 @@ async def detect_patterns( ), ), ) - if _response.status_code == 500: - raise InternalServerError( + if _response.status_code == 404: + raise NotFoundError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -3356,130 +3811,138 @@ async def detect_patterns( raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response.text ) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response_json ) - async def search( + async def search_episodes( self, + graph_uuid: str, *, - query: str, + limit: typing.Optional[int] = None, + cursor: typing.Optional[str] = None, bfs_origin_node_uuids: typing.Optional[typing.Sequence[str]] = OMIT, center_node_uuid: typing.Optional[str] = OMIT, - graph_id: typing.Optional[str] = OMIT, - limit: typing.Optional[int] = OMIT, - max_characters: typing.Optional[int] = OMIT, + filters: typing.Optional[typing.Dict[str, typing.Any]] = OMIT, mmr_lambda: typing.Optional[float] = OMIT, - reranker: typing.Optional[Reranker] = OMIT, - return_raw_results: typing.Optional[bool] = OMIT, - scope: typing.Optional[GraphSearchScope] = OMIT, - search_filters: typing.Optional[SearchFilters] = OMIT, - user_id: typing.Optional[str] = OMIT, + query: typing.Optional[str] = OMIT, + reranker: typing.Optional[str] = OMIT, + idempotency_key: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, - ) -> AsyncHttpResponse[GraphSearchResults]: + ) -> AsyncPager[Episode, EpisodePage]: """ - Perform a graph search query. - Parameters ---------- - query : str - The string to search for (required) + graph_uuid : str + Graph UUID - bfs_origin_node_uuids : typing.Optional[typing.Sequence[str]] - Nodes that are the origins of the BFS searches + limit : typing.Optional[int] + Page size - center_node_uuid : typing.Optional[str] - Node to rerank around for node distance reranking + cursor : typing.Optional[str] + Opaque page cursor - graph_id : typing.Optional[str] - The graph_id to search in. When searching user graph, please use user_id instead. + bfs_origin_node_uuids : typing.Optional[typing.Sequence[str]] - limit : typing.Optional[int] - The maximum number of facts to retrieve for non-auto scopes. Defaults to 10. Limited to 50. Ignored when scope=auto. + center_node_uuid : typing.Optional[str] - max_characters : typing.Optional[int] - Maximum total characters across all selected results when scope=auto. Defaults to 2500. Limited to 50000. + filters : typing.Optional[typing.Dict[str, typing.Any]] mmr_lambda : typing.Optional[float] - weighting for maximal marginal relevance - reranker : typing.Optional[Reranker] - Defaults to RRF. Ignored when scope=auto except node_distance and episode_mentions are rejected; - auto search always uses RRF retrieval and applies its own internal rerank after retrieval. - episode_mentions ranks edge candidates by how many of the episodes listed - in search_filters.episode_uuids mention them; without episode_uuids it has - no effect and results are ranked as if no reranker were specified. - - return_raw_results : typing.Optional[bool] - When scope=auto, include the selected raw graph results alongside the materialized context block. - For graph-service-backed auto mode, selected raw results may include episodes, - edges, nodes, observations, and thread_summaries. - - scope : typing.Optional[GraphSearchScope] - Defaults to Edges. + query : typing.Optional[str] - search_filters : typing.Optional[SearchFilters] - Search filters to apply to the search + reranker : typing.Optional[str] - user_id : typing.Optional[str] - The user_id when searching user graph. If not searching user graph, please use graph_id instead. + idempotency_key : typing.Optional[str] request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - AsyncHttpResponse[GraphSearchResults] - Graph search results or auto-context block + AsyncPager[Episode, EpisodePage] + OK """ _response = await self._client_wrapper.httpx_client.request( - "graph/search", + f"graphs/{jsonable_encoder(graph_uuid)}/search/episodes", method="POST", + params={ + "limit": limit, + "cursor": cursor, + }, json={ "bfs_origin_node_uuids": bfs_origin_node_uuids, "center_node_uuid": center_node_uuid, - "graph_id": graph_id, - "limit": limit, - "max_characters": max_characters, + "filters": filters, "mmr_lambda": mmr_lambda, "query": query, "reranker": reranker, - "return_raw_results": return_raw_results, - "scope": scope, - "search_filters": convert_and_respect_annotation_metadata( - object_=search_filters, annotation=SearchFilters, direction="write" - ), - "user_id": user_id, }, headers={ "content-type": "application/json", + "Idempotency-Key": str(idempotency_key) if idempotency_key is not None else None, }, request_options=request_options, omit=OMIT, ) try: if 200 <= _response.status_code < 300: - _data = typing.cast( - GraphSearchResults, + _parsed_response = typing.cast( + EpisodePage, parse_obj_as( - type_=GraphSearchResults, # type: ignore + type_=EpisodePage, # type: ignore object_=_response.json(), ), ) - return AsyncHttpResponse(response=_response, data=_data) + _items = _parsed_response.items + _parsed_next = _parsed_response.next_cursor + _has_next = _parsed_next is not None and _parsed_next != "" + + async def _get_next(): + return await self.search_episodes( + graph_uuid, + limit=limit, + cursor=_parsed_next, + bfs_origin_node_uuids=bfs_origin_node_uuids, + center_node_uuid=center_node_uuid, + filters=filters, + mmr_lambda=mmr_lambda, + query=query, + reranker=reranker, + idempotency_key=idempotency_key, + request_options=request_options, + ) + + return AsyncPager(has_next=_has_next, items=_items, get_next=_get_next, response=_parsed_response) if _response.status_code == 400: raise BadRequestError( headers=dict(_response.headers), body=typing.cast( - typing.Optional[typing.Any], + types_api_error_ApiError, + parse_obj_as( + type_=types_api_error_ApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 401: + raise UnauthorizedError( + headers=dict(_response.headers), + body=typing.cast( + types_api_error_ApiError, parse_obj_as( - type_=typing.Optional[typing.Any], # type: ignore + type_=types_api_error_ApiError, # type: ignore object_=_response.json(), ), ), ) - if _response.status_code == 500: - raise InternalServerError( + if _response.status_code == 404: + raise NotFoundError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -3494,113 +3957,116 @@ async def search( raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response.text ) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response_json ) - async def get_subgraph( + async def search_nodes( self, + graph_uuid: str, *, - seed_node_uuids: typing.Sequence[str], - depth: typing.Optional[int] = OMIT, - direction: typing.Optional[str] = OMIT, - graph_id: typing.Optional[str] = OMIT, - max_edges: typing.Optional[int] = OMIT, - max_nodes: typing.Optional[int] = OMIT, - search_filters: typing.Optional[SearchFilters] = OMIT, - user_id: typing.Optional[str] = OMIT, + limit: typing.Optional[int] = None, + cursor: typing.Optional[str] = None, + bfs_origin_node_uuids: typing.Optional[typing.Sequence[str]] = OMIT, + center_node_uuid: typing.Optional[str] = OMIT, + filters: typing.Optional[typing.Dict[str, typing.Any]] = OMIT, + mmr_lambda: typing.Optional[float] = OMIT, + query: typing.Optional[str] = OMIT, + reranker: typing.Optional[str] = OMIT, + idempotency_key: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, - ) -> AsyncHttpResponse[GraphSubgraphResponse]: + ) -> AsyncPager[Node, NodePage]: """ - Returns the bounded neighborhood of a set of seed nodes as a single {nodes, edges} payload: breadth-first expansion up to a caller-specified depth, subject to explicit budgets, with explicit truncation reporting. - Parameters ---------- - seed_node_uuids : typing.Sequence[str] - Seed node UUIDs to expand from, in traversal-priority order: seeds are - admitted before any expansion, in this order, and count toward - max_nodes first. 1-20 entries, required. Seeds that do not exist in - the target graph are ignored, not an error. + graph_uuid : str + Graph UUID - depth : typing.Optional[int] - Maximum traversal depth from the seeds. 1-3. Defaults to 1. + limit : typing.Optional[int] + Page size - direction : typing.Optional[str] - Edge orientation followed during expansion, relative to each frontier - node: "in" | "out" | "both". Defaults to "both". + cursor : typing.Optional[str] + Opaque page cursor - graph_id : typing.Optional[str] - graph_id identifies the target named graph. Exactly one of user_id or - graph_id is required. + bfs_origin_node_uuids : typing.Optional[typing.Sequence[str]] - max_edges : typing.Optional[int] - Maximum number of edges in the response. 1-1000. Defaults to 200. + center_node_uuid : typing.Optional[str] - max_nodes : typing.Optional[int] - Maximum number of nodes in the response, including admitted seeds. - 1-500. Defaults to 100. + filters : typing.Optional[typing.Dict[str, typing.Any]] - 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). + mmr_lambda : typing.Optional[float] - user_id : typing.Optional[str] - user_id identifies the target user graph. Exactly one of user_id or - graph_id is required. + query : typing.Optional[str] + + reranker : typing.Optional[str] + + idempotency_key : typing.Optional[str] request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - AsyncHttpResponse[GraphSubgraphResponse] - Subgraph + AsyncPager[Node, NodePage] + OK """ _response = await self._client_wrapper.httpx_client.request( - "graph/subgraph", + f"graphs/{jsonable_encoder(graph_uuid)}/search/nodes", method="POST", + params={ + "limit": limit, + "cursor": cursor, + }, json={ - "depth": depth, - "direction": direction, - "graph_id": graph_id, - "max_edges": max_edges, - "max_nodes": max_nodes, - "search_filters": convert_and_respect_annotation_metadata( - object_=search_filters, annotation=SearchFilters, direction="write" - ), - "seed_node_uuids": seed_node_uuids, - "user_id": user_id, + "bfs_origin_node_uuids": bfs_origin_node_uuids, + "center_node_uuid": center_node_uuid, + "filters": filters, + "mmr_lambda": mmr_lambda, + "query": query, + "reranker": reranker, }, headers={ "content-type": "application/json", + "Idempotency-Key": str(idempotency_key) if idempotency_key is not None else None, }, request_options=request_options, omit=OMIT, ) try: if 200 <= _response.status_code < 300: - _data = typing.cast( - GraphSubgraphResponse, + _parsed_response = typing.cast( + NodePage, parse_obj_as( - type_=GraphSubgraphResponse, # type: ignore + type_=NodePage, # type: ignore object_=_response.json(), ), ) - return AsyncHttpResponse(response=_response, data=_data) + _items = _parsed_response.items + _parsed_next = _parsed_response.next_cursor + _has_next = _parsed_next is not None and _parsed_next != "" + + async def _get_next(): + return await self.search_nodes( + graph_uuid, + limit=limit, + cursor=_parsed_next, + bfs_origin_node_uuids=bfs_origin_node_uuids, + center_node_uuid=center_node_uuid, + filters=filters, + mmr_lambda=mmr_lambda, + query=query, + reranker=reranker, + idempotency_key=idempotency_key, + request_options=request_options, + ) + + return AsyncPager(has_next=_has_next, items=_items, get_next=_get_next, response=_parsed_response) 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 == 403: - raise ForbiddenError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -3610,8 +4076,8 @@ async def get_subgraph( ), ), ) - if _response.status_code == 404: - raise NotFoundError( + if _response.status_code == 401: + raise UnauthorizedError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -3621,8 +4087,8 @@ async def get_subgraph( ), ), ) - if _response.status_code == 500: - raise InternalServerError( + if _response.status_code == 404: + raise NotFoundError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -3637,46 +4103,116 @@ async def get_subgraph( raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response.text ) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response_json ) - async def get( - self, graph_id: str, *, request_options: typing.Optional[RequestOptions] = None - ) -> AsyncHttpResponse[Graph]: + async def search_observations( + self, + graph_uuid: str, + *, + limit: typing.Optional[int] = None, + cursor: typing.Optional[str] = None, + bfs_origin_node_uuids: typing.Optional[typing.Sequence[str]] = OMIT, + center_node_uuid: typing.Optional[str] = OMIT, + filters: typing.Optional[typing.Dict[str, typing.Any]] = OMIT, + mmr_lambda: typing.Optional[float] = OMIT, + query: typing.Optional[str] = OMIT, + reranker: typing.Optional[str] = OMIT, + idempotency_key: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncPager[Observation, ObservationPage]: """ - Returns a graph. - Parameters ---------- - graph_id : str - The graph_id of the graph to get. + graph_uuid : str + Graph UUID + + limit : typing.Optional[int] + Page size + + cursor : typing.Optional[str] + Opaque page cursor + + bfs_origin_node_uuids : typing.Optional[typing.Sequence[str]] + + center_node_uuid : typing.Optional[str] + + filters : typing.Optional[typing.Dict[str, typing.Any]] + + mmr_lambda : typing.Optional[float] + + query : typing.Optional[str] + + reranker : typing.Optional[str] + + idempotency_key : typing.Optional[str] request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - AsyncHttpResponse[Graph] - The graph that was retrieved. + AsyncPager[Observation, ObservationPage] + OK """ _response = await self._client_wrapper.httpx_client.request( - f"graph/{jsonable_encoder(graph_id)}", - method="GET", + f"graphs/{jsonable_encoder(graph_uuid)}/search/observations", + method="POST", + params={ + "limit": limit, + "cursor": cursor, + }, + json={ + "bfs_origin_node_uuids": bfs_origin_node_uuids, + "center_node_uuid": center_node_uuid, + "filters": filters, + "mmr_lambda": mmr_lambda, + "query": query, + "reranker": reranker, + }, + headers={ + "content-type": "application/json", + "Idempotency-Key": str(idempotency_key) if idempotency_key is not None else None, + }, request_options=request_options, + omit=OMIT, ) try: if 200 <= _response.status_code < 300: - _data = typing.cast( - Graph, + _parsed_response = typing.cast( + ObservationPage, parse_obj_as( - type_=Graph, # type: ignore + type_=ObservationPage, # type: ignore object_=_response.json(), ), ) - return AsyncHttpResponse(response=_response, data=_data) - if _response.status_code == 404: - raise NotFoundError( + _items = _parsed_response.items + _parsed_next = _parsed_response.next_cursor + _has_next = _parsed_next is not None and _parsed_next != "" + + async def _get_next(): + return await self.search_observations( + graph_uuid, + limit=limit, + cursor=_parsed_next, + bfs_origin_node_uuids=bfs_origin_node_uuids, + center_node_uuid=center_node_uuid, + filters=filters, + mmr_lambda=mmr_lambda, + query=query, + reranker=reranker, + idempotency_key=idempotency_key, + request_options=request_options, + ) + + return AsyncPager(has_next=_has_next, items=_items, get_next=_get_next, response=_parsed_response) + if _response.status_code == 400: + raise BadRequestError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -3686,8 +4222,19 @@ async def get( ), ), ) - if _response.status_code == 500: - raise InternalServerError( + if _response.status_code == 401: + raise UnauthorizedError( + 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, @@ -3702,57 +4249,127 @@ async def get( raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response.text ) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response_json ) - async def delete( - self, graph_id: str, *, request_options: typing.Optional[RequestOptions] = None - ) -> AsyncHttpResponse[SuccessResponse]: + async def search_thread_summaries( + self, + graph_uuid: str, + *, + limit: typing.Optional[int] = None, + cursor: typing.Optional[str] = None, + bfs_origin_node_uuids: typing.Optional[typing.Sequence[str]] = OMIT, + center_node_uuid: typing.Optional[str] = OMIT, + filters: typing.Optional[typing.Dict[str, typing.Any]] = OMIT, + mmr_lambda: typing.Optional[float] = OMIT, + query: typing.Optional[str] = OMIT, + reranker: typing.Optional[str] = OMIT, + idempotency_key: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncPager[ThreadSummary, ThreadSummaryPage]: """ - Deletes a graph. If you would like to delete a user graph, make sure to use user.delete instead. - Parameters ---------- - graph_id : str - Graph ID + graph_uuid : str + Graph UUID + + limit : typing.Optional[int] + Page size + + cursor : typing.Optional[str] + Opaque page cursor + + bfs_origin_node_uuids : typing.Optional[typing.Sequence[str]] + + center_node_uuid : typing.Optional[str] + + filters : typing.Optional[typing.Dict[str, typing.Any]] + + mmr_lambda : typing.Optional[float] + + query : typing.Optional[str] + + reranker : typing.Optional[str] + + idempotency_key : typing.Optional[str] request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - AsyncHttpResponse[SuccessResponse] - Deleted + AsyncPager[ThreadSummary, ThreadSummaryPage] + OK """ _response = await self._client_wrapper.httpx_client.request( - f"graph/{jsonable_encoder(graph_id)}", - method="DELETE", + f"graphs/{jsonable_encoder(graph_uuid)}/search/thread-summaries", + method="POST", + params={ + "limit": limit, + "cursor": cursor, + }, + json={ + "bfs_origin_node_uuids": bfs_origin_node_uuids, + "center_node_uuid": center_node_uuid, + "filters": filters, + "mmr_lambda": mmr_lambda, + "query": query, + "reranker": reranker, + }, + headers={ + "content-type": "application/json", + "Idempotency-Key": str(idempotency_key) if idempotency_key is not None else None, + }, request_options=request_options, + omit=OMIT, ) try: if 200 <= _response.status_code < 300: - _data = typing.cast( - SuccessResponse, + _parsed_response = typing.cast( + ThreadSummaryPage, parse_obj_as( - type_=SuccessResponse, # type: ignore + type_=ThreadSummaryPage, # type: ignore object_=_response.json(), ), ) - return AsyncHttpResponse(response=_response, data=_data) + _items = _parsed_response.items + _parsed_next = _parsed_response.next_cursor + _has_next = _parsed_next is not None and _parsed_next != "" + + async def _get_next(): + return await self.search_thread_summaries( + graph_uuid, + limit=limit, + cursor=_parsed_next, + bfs_origin_node_uuids=bfs_origin_node_uuids, + center_node_uuid=center_node_uuid, + filters=filters, + mmr_lambda=mmr_lambda, + query=query, + reranker=reranker, + idempotency_key=idempotency_key, + request_options=request_options, + ) + + return AsyncPager(has_next=_has_next, items=_items, get_next=_get_next, response=_parsed_response) if _response.status_code == 400: raise BadRequestError( headers=dict(_response.headers), body=typing.cast( - typing.Optional[typing.Any], + types_api_error_ApiError, parse_obj_as( - type_=typing.Optional[typing.Any], # type: ignore + type_=types_api_error_ApiError, # type: ignore object_=_response.json(), ), ), ) - if _response.status_code == 404: - raise NotFoundError( + if _response.status_code == 401: + raise UnauthorizedError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -3762,8 +4379,8 @@ async def delete( ), ), ) - if _response.status_code == 500: - raise InternalServerError( + if _response.status_code == 404: + raise NotFoundError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -3778,52 +4395,69 @@ async def delete( raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response.text ) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response_json ) - async def update( + async def get_subgraph( self, - graph_id: str, + graph_uuid: str, *, - description: typing.Optional[str] = OMIT, - name: typing.Optional[str] = OMIT, - time_zone: typing.Optional[str] = OMIT, + depth: typing.Optional[int] = OMIT, + direction: typing.Optional[str] = OMIT, + filters: typing.Optional[typing.Dict[str, typing.Any]] = OMIT, + max_edges: typing.Optional[int] = OMIT, + max_nodes: typing.Optional[int] = OMIT, + seed_node_uuids: typing.Optional[typing.Sequence[str]] = OMIT, + idempotency_key: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, - ) -> AsyncHttpResponse[Graph]: + ) -> AsyncHttpResponse[SubgraphResponse]: """ - Updates information about a graph. - Parameters ---------- - graph_id : str - Graph ID + graph_uuid : str + Graph UUID - description : typing.Optional[str] + depth : typing.Optional[int] - name : typing.Optional[str] + direction : typing.Optional[str] - time_zone : typing.Optional[str] - The graph's IANA time zone. Stored on its group-backed subject. + filters : typing.Optional[typing.Dict[str, typing.Any]] + + max_edges : typing.Optional[int] + + max_nodes : typing.Optional[int] + + seed_node_uuids : typing.Optional[typing.Sequence[str]] + + idempotency_key : typing.Optional[str] request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - AsyncHttpResponse[Graph] - The updated graph object + AsyncHttpResponse[SubgraphResponse] + OK """ _response = await self._client_wrapper.httpx_client.request( - f"graph/{jsonable_encoder(graph_id)}", - method="PATCH", + f"graphs/{jsonable_encoder(graph_uuid)}/subgraph", + method="POST", json={ - "description": description, - "name": name, - "time_zone": time_zone, + "depth": depth, + "direction": direction, + "filters": filters, + "max_edges": max_edges, + "max_nodes": max_nodes, + "seed_node_uuids": seed_node_uuids, }, headers={ "content-type": "application/json", + "Idempotency-Key": str(idempotency_key) if idempotency_key is not None else None, }, request_options=request_options, omit=OMIT, @@ -3831,9 +4465,9 @@ async def update( try: if 200 <= _response.status_code < 300: _data = typing.cast( - Graph, + SubgraphResponse, parse_obj_as( - type_=Graph, # type: ignore + type_=SubgraphResponse, # type: ignore object_=_response.json(), ), ) @@ -3842,15 +4476,15 @@ async def update( raise BadRequestError( headers=dict(_response.headers), body=typing.cast( - typing.Optional[typing.Any], + types_api_error_ApiError, parse_obj_as( - type_=typing.Optional[typing.Any], # type: ignore + type_=types_api_error_ApiError, # type: ignore object_=_response.json(), ), ), ) - if _response.status_code == 404: - raise NotFoundError( + if _response.status_code == 401: + raise UnauthorizedError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -3860,8 +4494,8 @@ async def update( ), ), ) - if _response.status_code == 500: - raise InternalServerError( + if _response.status_code == 404: + raise NotFoundError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -3876,46 +4510,68 @@ async def update( raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response.text ) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response_json ) async def warm( - self, graph_id: str, *, request_options: typing.Optional[RequestOptions] = None - ) -> AsyncHttpResponse[SuccessResponse]: + self, + graph_uuid: str, + *, + idempotency_key: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[AsyncResult]: """ - Hints Zep to warm a graph for low-latency search - Parameters ---------- - graph_id : str - The graph_id of the graph to warm. + graph_uuid : str + Graph UUID + + idempotency_key : typing.Optional[str] request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - AsyncHttpResponse[SuccessResponse] - Warm hint accepted + AsyncHttpResponse[AsyncResult] + Accepted """ _response = await self._client_wrapper.httpx_client.request( - f"graph/{jsonable_encoder(graph_id)}/warm", - method="GET", + f"graphs/{jsonable_encoder(graph_uuid)}/warm", + method="POST", + headers={ + "Idempotency-Key": str(idempotency_key) if idempotency_key is not None else None, + }, request_options=request_options, ) try: if 200 <= _response.status_code < 300: _data = typing.cast( - SuccessResponse, + AsyncResult, parse_obj_as( - type_=SuccessResponse, # type: ignore + type_=AsyncResult, # type: ignore object_=_response.json(), ), ) return AsyncHttpResponse(response=_response, data=_data) - if _response.status_code == 404: - raise NotFoundError( + if _response.status_code == 400: + raise BadRequestError( + 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 == 401: + raise UnauthorizedError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -3925,8 +4581,8 @@ async def warm( ), ), ) - if _response.status_code == 500: - raise InternalServerError( + if _response.status_code == 404: + raise NotFoundError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -3941,6 +4597,10 @@ async def warm( raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response.text ) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response_json ) diff --git a/src/zep_cloud/graph/thread_summary/client.py b/src/zep_cloud/graph/thread_summary/client.py index 02b8db7f..779cf028 100644 --- a/src/zep_cloud/graph/thread_summary/client.py +++ b/src/zep_cloud/graph/thread_summary/client.py @@ -3,9 +3,10 @@ import typing from ...core.client_wrapper import AsyncClientWrapper, SyncClientWrapper +from ...core.pagination import AsyncPager, SyncPager from ...core.request_options import RequestOptions -from ...types.search_filters import SearchFilters from ...types.thread_summary import ThreadSummary +from ...types.thread_summary_page import ThreadSummaryPage from .raw_client import AsyncRawThreadSummaryClient, RawThreadSummaryClient # this is used as the default value for optional parameters @@ -27,126 +28,39 @@ def with_raw_response(self) -> RawThreadSummaryClient: """ return self._raw_client - def get_by_graph_id( + def list( self, - graph_id: str, + graph_uuid: 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, + limit: typing.Optional[int] = None, + cursor: typing.Optional[str] = None, + filters: typing.Optional[typing.Dict[str, typing.Any]] = OMIT, + idempotency_key: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, - ) -> typing.List[ThreadSummary]: + ) -> SyncPager[ThreadSummary, ThreadSummaryPage]: """ - Returns incremental thread summaries associated with the graph. - 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. + graph_uuid : str + Graph UUID 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[ThreadSummary] - Thread summaries - - Examples - -------- - from zep_cloud import Zep - - client = Zep( - api_key="YOUR_API_KEY", - ) - client.graph.thread_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 - - def get_by_user_id( - self, - user_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[ThreadSummary]: - """ - Returns incremental thread summaries generated from messages in each thread associated with the user's graph. - - Parameters - ---------- - user_id : str - User ID + Page size 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. + Opaque page cursor - direction : typing.Optional[str] - Sort direction. One of "asc" or "desc" (default "desc"). + filters : typing.Optional[typing.Dict[str, typing.Any]] - 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. + idempotency_key : typing.Optional[str] request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - typing.List[ThreadSummary] - Thread summaries + SyncPager[ThreadSummary, ThreadSummaryPage] + OK Examples -------- @@ -155,21 +69,25 @@ def get_by_user_id( client = Zep( api_key="YOUR_API_KEY", ) - client.graph.thread_summary.get_by_user_id( - user_id="user_id", + response = client.graph.thread_summary.list( + graph_uuid="graph_uuid", + limit=1, + cursor="cursor", ) + for item in response: + yield item + # alternatively, you can paginate page-by-page + for page in response.iter_pages(): + yield page """ - _response = self._raw_client.get_by_user_id( - user_id, + return self._raw_client.list( + graph_uuid, + limit=limit, cursor=cursor, - direction=direction, filters=filters, - limit=limit, - order_by=order_by, - uuid_cursor=uuid_cursor, + idempotency_key=idempotency_key, request_options=request_options, ) - return _response.data class AsyncThreadSummaryClient: @@ -187,134 +105,39 @@ def with_raw_response(self) -> AsyncRawThreadSummaryClient: """ return self._raw_client - async def get_by_graph_id( + async def list( self, - graph_id: str, + graph_uuid: 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, + limit: typing.Optional[int] = None, + cursor: typing.Optional[str] = None, + filters: typing.Optional[typing.Dict[str, typing.Any]] = OMIT, + idempotency_key: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, - ) -> typing.List[ThreadSummary]: + ) -> AsyncPager[ThreadSummary, ThreadSummaryPage]: """ - Returns incremental thread summaries associated with the graph. - 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. + graph_uuid : str + Graph UUID 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[ThreadSummary] - Thread summaries - - Examples - -------- - import asyncio - - from zep_cloud import AsyncZep - - client = AsyncZep( - api_key="YOUR_API_KEY", - ) - - - async def main() -> None: - await client.graph.thread_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 - - async def get_by_user_id( - self, - user_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[ThreadSummary]: - """ - Returns incremental thread summaries generated from messages in each thread associated with the user's graph. - - Parameters - ---------- - user_id : str - User ID + Page size 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. + Opaque page cursor - 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. + filters : typing.Optional[typing.Dict[str, typing.Any]] - Deprecated: prefer Cursor, the opaque cursor returned via the Zep-Next-Cursor response header. + idempotency_key : typing.Optional[str] request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - typing.List[ThreadSummary] - Thread summaries + AsyncPager[ThreadSummary, ThreadSummaryPage] + OK Examples -------- @@ -328,21 +151,26 @@ async def get_by_user_id( async def main() -> None: - await client.graph.thread_summary.get_by_user_id( - user_id="user_id", + response = await client.graph.thread_summary.list( + graph_uuid="graph_uuid", + limit=1, + cursor="cursor", ) + async for item in response: + yield item + + # alternatively, you can paginate page-by-page + async for page in response.iter_pages(): + yield page asyncio.run(main()) """ - _response = await self._raw_client.get_by_user_id( - user_id, + return await self._raw_client.list( + graph_uuid, + limit=limit, cursor=cursor, - direction=direction, filters=filters, - limit=limit, - order_by=order_by, - uuid_cursor=uuid_cursor, + idempotency_key=idempotency_key, request_options=request_options, ) - return _response.data diff --git a/src/zep_cloud/graph/thread_summary/raw_client.py b/src/zep_cloud/graph/thread_summary/raw_client.py index 063a02f1..274635b9 100644 --- a/src/zep_cloud/graph/thread_summary/raw_client.py +++ b/src/zep_cloud/graph/thread_summary/raw_client.py @@ -5,17 +5,18 @@ 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.pagination import AsyncPager, SyncPager +from ...core.parse_error import ParsingError 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 ...errors.unauthorized_error import UnauthorizedError from ...types.api_error import ApiError as types_api_error_ApiError -from ...types.search_filters import SearchFilters from ...types.thread_summary import ThreadSummary +from ...types.thread_summary_page import ThreadSummaryPage +from pydantic import ValidationError # this is used as the default value for optional parameters OMIT = typing.cast(typing.Any, ...) @@ -25,94 +26,80 @@ class RawThreadSummaryClient: def __init__(self, *, client_wrapper: SyncClientWrapper): self._client_wrapper = client_wrapper - def get_by_graph_id( + def list( self, - graph_id: str, + graph_uuid: 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, + limit: typing.Optional[int] = None, + cursor: typing.Optional[str] = None, + filters: typing.Optional[typing.Dict[str, typing.Any]] = OMIT, + idempotency_key: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, - ) -> HttpResponse[typing.List[ThreadSummary]]: + ) -> SyncPager[ThreadSummary, ThreadSummaryPage]: """ - Returns incremental thread summaries associated with the graph. - 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. + graph_uuid : str + Graph UUID limit : typing.Optional[int] - Maximum number of items to return + Page size - order_by : typing.Optional[str] - Field to sort by. One of "created_at", "valid_at", or "uuid" (default "uuid"). + cursor : typing.Optional[str] + Opaque page cursor - uuid_cursor : typing.Optional[str] - UUID based cursor, used for pagination. Should be the UUID of the last item in the previous page. + filters : typing.Optional[typing.Dict[str, typing.Any]] - Deprecated: prefer Cursor, the opaque cursor returned via the Zep-Next-Cursor response header. + idempotency_key : typing.Optional[str] request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - HttpResponse[typing.List[ThreadSummary]] - Thread summaries + SyncPager[ThreadSummary, ThreadSummaryPage] + OK """ _response = self._client_wrapper.httpx_client.request( - f"graph/thread-summary/graph/{jsonable_encoder(graph_id)}", + f"graphs/{jsonable_encoder(graph_uuid)}/thread-summaries/list", method="POST", - json={ - "cursor": cursor, - "direction": direction, - "filters": convert_and_respect_annotation_metadata( - object_=filters, annotation=SearchFilters, direction="write" - ), + params={ "limit": limit, - "order_by": order_by, - "uuid_cursor": uuid_cursor, + "cursor": cursor, + }, + json={ + "filters": filters, + }, + headers={ + "content-type": "application/json", + "Idempotency-Key": str(idempotency_key) if idempotency_key is not None else None, }, request_options=request_options, omit=OMIT, ) try: if 200 <= _response.status_code < 300: - _data = typing.cast( - typing.List[ThreadSummary], + _parsed_response = typing.cast( + ThreadSummaryPage, parse_obj_as( - type_=typing.List[ThreadSummary], # type: ignore + type_=ThreadSummaryPage, # type: ignore object_=_response.json(), ), ) - return HttpResponse(response=_response, data=_data) + _items = _parsed_response.items + _parsed_next = _parsed_response.next_cursor + _has_next = _parsed_next is not None and _parsed_next != "" + _get_next = lambda: self.list( + graph_uuid, + limit=limit, + cursor=_parsed_next, + filters=filters, + idempotency_key=idempotency_key, + request_options=request_options, + ) + return SyncPager(has_next=_has_next, items=_items, get_next=_get_next, response=_parsed_response) 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, @@ -122,8 +109,8 @@ def get_by_graph_id( ), ), ) - if _response.status_code == 500: - raise InternalServerError( + if _response.status_code == 401: + raise UnauthorizedError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -133,101 +120,6 @@ def get_by_graph_id( ), ), ) - _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_by_user_id( - self, - user_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[ThreadSummary]]: - """ - Returns incremental thread summaries generated from messages in each thread associated with the user's graph. - - Parameters - ---------- - user_id : str - User 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[ThreadSummary]] - Thread summaries - """ - _response = self._client_wrapper.httpx_client.request( - f"graph/thread-summary/user/{jsonable_encoder(user_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, - }, - request_options=request_options, - omit=OMIT, - ) - try: - if 200 <= _response.status_code < 300: - _data = typing.cast( - typing.List[ThreadSummary], - parse_obj_as( - type_=typing.List[ThreadSummary], # 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), @@ -239,22 +131,15 @@ def get_by_user_id( ), ), ) - 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 ) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response_json ) @@ -264,94 +149,83 @@ class AsyncRawThreadSummaryClient: def __init__(self, *, client_wrapper: AsyncClientWrapper): self._client_wrapper = client_wrapper - async def get_by_graph_id( + async def list( self, - graph_id: str, + graph_uuid: 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, + limit: typing.Optional[int] = None, + cursor: typing.Optional[str] = None, + filters: typing.Optional[typing.Dict[str, typing.Any]] = OMIT, + idempotency_key: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, - ) -> AsyncHttpResponse[typing.List[ThreadSummary]]: + ) -> AsyncPager[ThreadSummary, ThreadSummaryPage]: """ - Returns incremental thread summaries associated with the graph. - 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. + graph_uuid : str + Graph UUID limit : typing.Optional[int] - Maximum number of items to return + Page size - order_by : typing.Optional[str] - Field to sort by. One of "created_at", "valid_at", or "uuid" (default "uuid"). + cursor : typing.Optional[str] + Opaque page cursor - uuid_cursor : typing.Optional[str] - UUID based cursor, used for pagination. Should be the UUID of the last item in the previous page. + filters : typing.Optional[typing.Dict[str, typing.Any]] - Deprecated: prefer Cursor, the opaque cursor returned via the Zep-Next-Cursor response header. + idempotency_key : typing.Optional[str] request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - AsyncHttpResponse[typing.List[ThreadSummary]] - Thread summaries + AsyncPager[ThreadSummary, ThreadSummaryPage] + OK """ _response = await self._client_wrapper.httpx_client.request( - f"graph/thread-summary/graph/{jsonable_encoder(graph_id)}", + f"graphs/{jsonable_encoder(graph_uuid)}/thread-summaries/list", method="POST", - json={ - "cursor": cursor, - "direction": direction, - "filters": convert_and_respect_annotation_metadata( - object_=filters, annotation=SearchFilters, direction="write" - ), + params={ "limit": limit, - "order_by": order_by, - "uuid_cursor": uuid_cursor, + "cursor": cursor, + }, + json={ + "filters": filters, + }, + headers={ + "content-type": "application/json", + "Idempotency-Key": str(idempotency_key) if idempotency_key is not None else None, }, request_options=request_options, omit=OMIT, ) try: if 200 <= _response.status_code < 300: - _data = typing.cast( - typing.List[ThreadSummary], + _parsed_response = typing.cast( + ThreadSummaryPage, parse_obj_as( - type_=typing.List[ThreadSummary], # type: ignore + type_=ThreadSummaryPage, # type: ignore object_=_response.json(), ), ) - return AsyncHttpResponse(response=_response, data=_data) + _items = _parsed_response.items + _parsed_next = _parsed_response.next_cursor + _has_next = _parsed_next is not None and _parsed_next != "" + + async def _get_next(): + return await self.list( + graph_uuid, + limit=limit, + cursor=_parsed_next, + filters=filters, + idempotency_key=idempotency_key, + request_options=request_options, + ) + + return AsyncPager(has_next=_has_next, items=_items, get_next=_get_next, response=_parsed_response) 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, @@ -361,8 +235,8 @@ async def get_by_graph_id( ), ), ) - if _response.status_code == 500: - raise InternalServerError( + if _response.status_code == 401: + raise UnauthorizedError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -372,101 +246,6 @@ async def get_by_graph_id( ), ), ) - _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_by_user_id( - self, - user_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[ThreadSummary]]: - """ - Returns incremental thread summaries generated from messages in each thread associated with the user's graph. - - Parameters - ---------- - user_id : str - User 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[ThreadSummary]] - Thread summaries - """ - _response = await self._client_wrapper.httpx_client.request( - f"graph/thread-summary/user/{jsonable_encoder(user_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, - }, - request_options=request_options, - omit=OMIT, - ) - try: - if 200 <= _response.status_code < 300: - _data = typing.cast( - typing.List[ThreadSummary], - parse_obj_as( - type_=typing.List[ThreadSummary], # 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), @@ -478,22 +257,15 @@ async def get_by_user_id( ), ), ) - 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 ) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response_json ) diff --git a/src/zep_cloud/graph/utils.py b/src/zep_cloud/graph/utils.py deleted file mode 100644 index 384a878f..00000000 --- a/src/zep_cloud/graph/utils.py +++ /dev/null @@ -1,141 +0,0 @@ -from datetime import datetime -from typing import List, Optional - -from dateutil import parser as dateutil_parser -from zep_cloud import EntityEdge, EntityNode, Episode - -DATE_FORMAT = "%Y-%m-%d %H:%M:%S" - - -def parse_iso_datetime(iso_string: str) -> Optional[datetime]: - """Parse ISO datetime string using dateutil parser.""" - if not iso_string: - return None - - try: - return dateutil_parser.isoparse(iso_string) - except (ValueError, TypeError): - return None - -TEMPLATE_STRING = """ -FACTS and ENTITIES{episodes_header} represent relevant context to the current conversation. - -# These are the most relevant facts and their valid date ranges -# format: FACT (Date range: from - to) - -{facts} - - -# These are the most relevant entities -# Name: ENTITY_NAME -# Label: entity_label (if present) -# Attributes: (if present) -# attr_name: attr_value -# Summary: entity summary - -{entities} - -{episodes_section} -""" - - -def format_edge_date_range(edge: EntityEdge) -> str: - """ - Format the date range of an entity edge. - - Args: - edge: The entity edge to format. - - Returns: - A string representation of the date range. - """ - valid_at = "date unknown" - invalid_at = "present" - - if edge.valid_at is not None: - parsed_valid_at = parse_iso_datetime(edge.valid_at) - if parsed_valid_at is not None: - valid_at = parsed_valid_at.strftime(DATE_FORMAT) - if edge.invalid_at is not None: - parsed_invalid_at = parse_iso_datetime(edge.invalid_at) - if parsed_invalid_at is not None: - invalid_at = parsed_invalid_at.strftime(DATE_FORMAT) - - return f"{valid_at} - {invalid_at}" - - -def compose_context_string(edges: List[EntityEdge], nodes: List[EntityNode], episodes: List[Episode]) -> str: - """ - Compose a search context from entity edges, nodes, and episodes. - - Args: - edges: List of entity edges. - nodes: List of entity nodes. - episodes: List of episodes. - - Returns: - A formatted string containing facts, entities, and episodes. - """ - facts = [] - for edge in edges: - fact = f" - {edge.fact} (Date range: {format_edge_date_range(edge)})" - facts.append(fact) - - entities = [] - for node in nodes: - entity_parts = [f"Name: {node.name}"] - - if hasattr(node, 'labels') and node.labels: - labels = list(node.labels) # Create a copy to avoid modifying original - if 'Entity' in labels: - labels.remove('Entity') - if labels: # Only add label if there are remaining labels after removing 'Entity' - entity_parts.append(f"Label: {labels[0]}") - - if hasattr(node, 'attributes') and node.attributes: - # Filter out 'labels' from attributes as it's redundant with the Label field - filtered_attributes = {k: v for k, v in node.attributes.items() if k != 'labels'} - if filtered_attributes: # Only add attributes section if there are non-label attributes - entity_parts.append("Attributes:") - for attr_name, attr_value in filtered_attributes.items(): - entity_parts.append(f" {attr_name}: {attr_value}") - - if node.summary: - entity_parts.append(f"Summary: {node.summary}") - - entity = "\n".join(entity_parts) - entities.append(entity) - - # Format episodes - episodes_list = [] - if episodes: - for episode in episodes: - role_prefix = "" - if hasattr(episode, 'role') and episode.role: - if hasattr(episode, 'role_type') and episode.role_type: - role_prefix = f"{episode.role} ({episode.role_type}): " - else: - role_prefix = f"{episode.role}: " - elif hasattr(episode, 'role_type') and episode.role_type: - role_prefix = f"({episode.role_type}): " - - parsed_timestamp = parse_iso_datetime(episode.created_at) - timestamp = parsed_timestamp.strftime(DATE_FORMAT) if parsed_timestamp is not None else "date unknown" - - episode_str = f" - {role_prefix}{episode.content} ({timestamp})" - episodes_list.append(episode_str) - - facts_str = "\n".join(facts) if facts else "" - entities_str = "\n".join(entities) if entities else "" - episodes_str = "\n".join(episodes_list) if episodes_list else "" - - # Determine if episodes section should be included - episodes_header = ", and EPISODES" if episodes else "" - episodes_section = f"\n# These are the most relevant episodes\n\n{episodes_str}\n" if episodes else "" - - return TEMPLATE_STRING.format( - episodes_header=episodes_header, - facts=facts_str, - entities=entities_str, - episodes_section=episodes_section - ) \ No newline at end of file diff --git a/src/zep_cloud/lookup/__init__.py b/src/zep_cloud/lookup/__init__.py new file mode 100644 index 00000000..5cde0202 --- /dev/null +++ b/src/zep_cloud/lookup/__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/lookup/client.py b/src/zep_cloud/lookup/client.py new file mode 100644 index 00000000..03e3fc6f --- /dev/null +++ b/src/zep_cloud/lookup/client.py @@ -0,0 +1,143 @@ +# 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.lookup_batch_response import LookupBatchResponse +from .raw_client import AsyncRawLookupClient, RawLookupClient + +# this is used as the default value for optional parameters +OMIT = typing.cast(typing.Any, ...) + + +class LookupClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._raw_client = RawLookupClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> RawLookupClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + RawLookupClient + """ + return self._raw_client + + def batch( + self, + *, + graphs: typing.Optional[typing.Sequence[str]] = OMIT, + threads: typing.Optional[typing.Sequence[str]] = OMIT, + users: typing.Optional[typing.Sequence[str]] = OMIT, + idempotency_key: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> LookupBatchResponse: + """ + Parameters + ---------- + graphs : typing.Optional[typing.Sequence[str]] + + threads : typing.Optional[typing.Sequence[str]] + + users : typing.Optional[typing.Sequence[str]] + + idempotency_key : typing.Optional[str] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + LookupBatchResponse + OK + + Examples + -------- + from zep_cloud import Zep + + client = Zep( + api_key="YOUR_API_KEY", + ) + client.lookup.batch() + """ + _response = self._raw_client.batch( + graphs=graphs, + threads=threads, + users=users, + idempotency_key=idempotency_key, + request_options=request_options, + ) + return _response.data + + +class AsyncLookupClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._raw_client = AsyncRawLookupClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> AsyncRawLookupClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + AsyncRawLookupClient + """ + return self._raw_client + + async def batch( + self, + *, + graphs: typing.Optional[typing.Sequence[str]] = OMIT, + threads: typing.Optional[typing.Sequence[str]] = OMIT, + users: typing.Optional[typing.Sequence[str]] = OMIT, + idempotency_key: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> LookupBatchResponse: + """ + Parameters + ---------- + graphs : typing.Optional[typing.Sequence[str]] + + threads : typing.Optional[typing.Sequence[str]] + + users : typing.Optional[typing.Sequence[str]] + + idempotency_key : typing.Optional[str] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + LookupBatchResponse + OK + + Examples + -------- + import asyncio + + from zep_cloud import AsyncZep + + client = AsyncZep( + api_key="YOUR_API_KEY", + ) + + + async def main() -> None: + await client.lookup.batch() + + + asyncio.run(main()) + """ + _response = await self._raw_client.batch( + graphs=graphs, + threads=threads, + users=users, + idempotency_key=idempotency_key, + request_options=request_options, + ) + return _response.data diff --git a/src/zep_cloud/lookup/raw_client.py b/src/zep_cloud/lookup/raw_client.py new file mode 100644 index 00000000..621630a7 --- /dev/null +++ b/src/zep_cloud/lookup/raw_client.py @@ -0,0 +1,228 @@ +# 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.parse_error import ParsingError +from ..core.pydantic_utilities import parse_obj_as +from ..core.request_options import RequestOptions +from ..errors.bad_request_error import BadRequestError +from ..errors.not_found_error import NotFoundError +from ..errors.unauthorized_error import UnauthorizedError +from ..types.api_error import ApiError as types_api_error_ApiError +from ..types.lookup_batch_response import LookupBatchResponse +from pydantic import ValidationError + +# this is used as the default value for optional parameters +OMIT = typing.cast(typing.Any, ...) + + +class RawLookupClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._client_wrapper = client_wrapper + + def batch( + self, + *, + graphs: typing.Optional[typing.Sequence[str]] = OMIT, + threads: typing.Optional[typing.Sequence[str]] = OMIT, + users: typing.Optional[typing.Sequence[str]] = OMIT, + idempotency_key: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[LookupBatchResponse]: + """ + Parameters + ---------- + graphs : typing.Optional[typing.Sequence[str]] + + threads : typing.Optional[typing.Sequence[str]] + + users : typing.Optional[typing.Sequence[str]] + + idempotency_key : typing.Optional[str] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[LookupBatchResponse] + OK + """ + _response = self._client_wrapper.httpx_client.request( + "lookup", + method="POST", + json={ + "graphs": graphs, + "threads": threads, + "users": users, + }, + headers={ + "content-type": "application/json", + "Idempotency-Key": str(idempotency_key) if idempotency_key is not None else None, + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + LookupBatchResponse, + parse_obj_as( + type_=LookupBatchResponse, # 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( + types_api_error_ApiError, + parse_obj_as( + type_=types_api_error_ApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 401: + raise UnauthorizedError( + 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(), + ), + ), + ) + _response_json = _response.json() + except JSONDecodeError: + raise core_api_error_ApiError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.text + ) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) + raise core_api_error_ApiError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response_json + ) + + +class AsyncRawLookupClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._client_wrapper = client_wrapper + + async def batch( + self, + *, + graphs: typing.Optional[typing.Sequence[str]] = OMIT, + threads: typing.Optional[typing.Sequence[str]] = OMIT, + users: typing.Optional[typing.Sequence[str]] = OMIT, + idempotency_key: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[LookupBatchResponse]: + """ + Parameters + ---------- + graphs : typing.Optional[typing.Sequence[str]] + + threads : typing.Optional[typing.Sequence[str]] + + users : typing.Optional[typing.Sequence[str]] + + idempotency_key : typing.Optional[str] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[LookupBatchResponse] + OK + """ + _response = await self._client_wrapper.httpx_client.request( + "lookup", + method="POST", + json={ + "graphs": graphs, + "threads": threads, + "users": users, + }, + headers={ + "content-type": "application/json", + "Idempotency-Key": str(idempotency_key) if idempotency_key is not None else None, + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + LookupBatchResponse, + parse_obj_as( + type_=LookupBatchResponse, # 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( + types_api_error_ApiError, + parse_obj_as( + type_=types_api_error_ApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 401: + raise UnauthorizedError( + 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(), + ), + ), + ) + _response_json = _response.json() + except JSONDecodeError: + raise core_api_error_ApiError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.text + ) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) + raise core_api_error_ApiError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response_json + ) diff --git a/src/zep_cloud/project/client.py b/src/zep_cloud/project/client.py index a5ba6f38..10f6ab6c 100644 --- a/src/zep_cloud/project/client.py +++ b/src/zep_cloud/project/client.py @@ -4,9 +4,13 @@ from ..core.client_wrapper import AsyncClientWrapper, SyncClientWrapper from ..core.request_options import RequestOptions -from ..types.observation_steering_config import ObservationSteeringConfig -from ..types.observation_type import ObservationType -from ..types.project_info_response import ProjectInfoResponse +from ..types.edge_type import EdgeType +from ..types.entity_type import EntityType +from ..types.instructions import Instructions +from ..types.observation_steering import ObservationSteering +from ..types.ontology import Ontology +from ..types.project import Project +from ..types.user_summary_instructions import UserSummaryInstructions from .raw_client import AsyncRawProjectClient, RawProjectClient # this is used as the default value for optional parameters @@ -28,10 +32,8 @@ def with_raw_response(self) -> RawProjectClient: """ return self._raw_client - def get(self, *, request_options: typing.Optional[RequestOptions] = None) -> ProjectInfoResponse: + def get(self, *, request_options: typing.Optional[RequestOptions] = None) -> Project: """ - Retrieve project info based on the provided api key. - Parameters ---------- request_options : typing.Optional[RequestOptions] @@ -39,8 +41,8 @@ def get(self, *, request_options: typing.Optional[RequestOptions] = None) -> Pro Returns ------- - ProjectInfoResponse - Retrieved + Project + OK Examples -------- @@ -55,23 +57,27 @@ def get(self, *, request_options: typing.Optional[RequestOptions] = None) -> Pro return _response.data def update( - self, *, default_time_zone: typing.Optional[str] = OMIT, request_options: typing.Optional[RequestOptions] = None - ) -> ProjectInfoResponse: + self, + *, + default_time_zone: typing.Optional[str] = OMIT, + idempotency_key: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> Project: """ - Sets or clears the project-level fallback time zone for the API key's project. - Parameters ---------- default_time_zone : typing.Optional[str] - The project's IANA fallback time zone. Null clears the existing value. + Omit to leave unchanged, send JSON null to clear, or send a value to set. + + idempotency_key : typing.Optional[str] request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - ProjectInfoResponse - Updated + Project + OK Examples -------- @@ -82,34 +88,59 @@ def update( ) client.project.update() """ - _response = self._raw_client.update(default_time_zone=default_time_zone, request_options=request_options) + _response = self._raw_client.update( + default_time_zone=default_time_zone, idempotency_key=idempotency_key, request_options=request_options + ) return _response.data - def get_observation_steering( + def get_instructions(self, *, request_options: typing.Optional[RequestOptions] = None) -> Instructions: + """ + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Instructions + OK + + Examples + -------- + from zep_cloud import Zep + + client = Zep( + api_key="YOUR_API_KEY", + ) + client.project.get_instructions() + """ + _response = self._raw_client.get_instructions(request_options=request_options) + return _response.data + + def set_instructions( self, *, - user_id: typing.Optional[str] = None, - graph_id: typing.Optional[str] = None, + inherited: typing.Optional[bool] = OMIT, + instructions: typing.Optional[typing.Sequence[typing.Dict[str, typing.Any]]] = OMIT, + idempotency_key: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, - ) -> ObservationSteeringConfig: + ) -> Instructions: """ - Returns project steering or the effective user/graph steering with project fallback. This API is experimental and may change in future releases. - Parameters ---------- - user_id : typing.Optional[str] - User ID for user-specific steering + inherited : typing.Optional[bool] + + instructions : typing.Optional[typing.Sequence[typing.Dict[str, typing.Any]]] - graph_id : typing.Optional[str] - Graph ID for graph-specific steering + idempotency_key : typing.Optional[str] request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - ObservationSteeringConfig - Retrieved + Instructions + OK Examples -------- @@ -118,47 +149,139 @@ def get_observation_steering( client = Zep( api_key="YOUR_API_KEY", ) - client.project.get_observation_steering( - user_id="user_id", - graph_id="graph_id", + client.project.set_instructions() + """ + _response = self._raw_client.set_instructions( + inherited=inherited, + instructions=instructions, + idempotency_key=idempotency_key, + request_options=request_options, ) + return _response.data + + def get_observation_steering( + self, *, request_options: typing.Optional[RequestOptions] = None + ) -> ObservationSteering: """ - _response = self._raw_client.get_observation_steering( - user_id=user_id, graph_id=graph_id, request_options=request_options + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + ObservationSteering + OK + + Examples + -------- + from zep_cloud import Zep + + client = Zep( + api_key="YOUR_API_KEY", ) + client.project.get_observation_steering() + """ + _response = self._raw_client.get_observation_steering(request_options=request_options) return _response.data def set_observation_steering( self, *, - user_id: typing.Optional[str] = None, - graph_id: typing.Optional[str] = None, + inherited: typing.Optional[bool] = OMIT, instruction: typing.Optional[str] = OMIT, - types: typing.Optional[typing.Sequence[ObservationType]] = OMIT, + types: typing.Optional[typing.Sequence[typing.Dict[str, typing.Any]]] = OMIT, + idempotency_key: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, - ) -> ObservationSteeringConfig: + ) -> ObservationSteering: """ - Replaces project, user, or graph steering. An empty configuration clears the project default or removes the user/graph override. Changes affect later materializer runs only. This API is experimental and may change in future releases. + Parameters + ---------- + inherited : typing.Optional[bool] + + instruction : typing.Optional[str] + + types : typing.Optional[typing.Sequence[typing.Dict[str, typing.Any]]] + idempotency_key : typing.Optional[str] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + ObservationSteering + OK + + Examples + -------- + from zep_cloud import Zep + + client = Zep( + api_key="YOUR_API_KEY", + ) + client.project.set_observation_steering() + """ + _response = self._raw_client.set_observation_steering( + inherited=inherited, + instruction=instruction, + types=types, + idempotency_key=idempotency_key, + request_options=request_options, + ) + return _response.data + + def get_ontology(self, *, request_options: typing.Optional[RequestOptions] = None) -> Ontology: + """ Parameters ---------- - user_id : typing.Optional[str] - User ID for user-specific steering + request_options : typing.Optional[RequestOptions] + Request-specific configuration. - graph_id : typing.Optional[str] - Graph ID for graph-specific steering + Returns + ------- + Ontology + OK - instruction : typing.Optional[str] + Examples + -------- + from zep_cloud import Zep + + client = Zep( + api_key="YOUR_API_KEY", + ) + client.project.get_ontology() + """ + _response = self._raw_client.get_ontology(request_options=request_options) + return _response.data + + def set_ontology( + self, + *, + edge_types: typing.Optional[typing.Sequence[EdgeType]] = OMIT, + entity_types: typing.Optional[typing.Sequence[EntityType]] = OMIT, + inherited: typing.Optional[bool] = OMIT, + idempotency_key: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> Ontology: + """ + Parameters + ---------- + edge_types : typing.Optional[typing.Sequence[EdgeType]] - types : typing.Optional[typing.Sequence[ObservationType]] + entity_types : typing.Optional[typing.Sequence[EntityType]] + + inherited : typing.Optional[bool] + + idempotency_key : typing.Optional[str] request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - ObservationSteeringConfig - Updated + Ontology + OK Examples -------- @@ -167,13 +290,82 @@ def set_observation_steering( client = Zep( api_key="YOUR_API_KEY", ) - client.project.set_observation_steering( - user_id="user_id", - graph_id="graph_id", + client.project.set_ontology() + """ + _response = self._raw_client.set_ontology( + edge_types=edge_types, + entity_types=entity_types, + inherited=inherited, + idempotency_key=idempotency_key, + request_options=request_options, ) + return _response.data + + def get_user_summary_instructions( + self, *, request_options: typing.Optional[RequestOptions] = None + ) -> UserSummaryInstructions: """ - _response = self._raw_client.set_observation_steering( - user_id=user_id, graph_id=graph_id, instruction=instruction, types=types, request_options=request_options + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + UserSummaryInstructions + OK + + Examples + -------- + from zep_cloud import Zep + + client = Zep( + api_key="YOUR_API_KEY", + ) + client.project.get_user_summary_instructions() + """ + _response = self._raw_client.get_user_summary_instructions(request_options=request_options) + return _response.data + + def set_user_summary_instructions( + self, + *, + inherited: typing.Optional[bool] = OMIT, + instructions: typing.Optional[typing.Sequence[typing.Dict[str, typing.Any]]] = OMIT, + idempotency_key: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> UserSummaryInstructions: + """ + Parameters + ---------- + inherited : typing.Optional[bool] + + instructions : typing.Optional[typing.Sequence[typing.Dict[str, typing.Any]]] + + idempotency_key : typing.Optional[str] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + UserSummaryInstructions + OK + + Examples + -------- + from zep_cloud import Zep + + client = Zep( + api_key="YOUR_API_KEY", + ) + client.project.set_user_summary_instructions() + """ + _response = self._raw_client.set_user_summary_instructions( + inherited=inherited, + instructions=instructions, + idempotency_key=idempotency_key, + request_options=request_options, ) return _response.data @@ -193,10 +385,8 @@ def with_raw_response(self) -> AsyncRawProjectClient: """ return self._raw_client - async def get(self, *, request_options: typing.Optional[RequestOptions] = None) -> ProjectInfoResponse: + async def get(self, *, request_options: typing.Optional[RequestOptions] = None) -> Project: """ - Retrieve project info based on the provided api key. - Parameters ---------- request_options : typing.Optional[RequestOptions] @@ -204,8 +394,8 @@ async def get(self, *, request_options: typing.Optional[RequestOptions] = None) Returns ------- - ProjectInfoResponse - Retrieved + Project + OK Examples -------- @@ -228,23 +418,27 @@ async def main() -> None: return _response.data async def update( - self, *, default_time_zone: typing.Optional[str] = OMIT, request_options: typing.Optional[RequestOptions] = None - ) -> ProjectInfoResponse: + self, + *, + default_time_zone: typing.Optional[str] = OMIT, + idempotency_key: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> Project: """ - Sets or clears the project-level fallback time zone for the API key's project. - Parameters ---------- default_time_zone : typing.Optional[str] - The project's IANA fallback time zone. Null clears the existing value. + Omit to leave unchanged, send JSON null to clear, or send a value to set. + + idempotency_key : typing.Optional[str] request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - ProjectInfoResponse - Updated + Project + OK Examples -------- @@ -263,34 +457,67 @@ async def main() -> None: asyncio.run(main()) """ - _response = await self._raw_client.update(default_time_zone=default_time_zone, request_options=request_options) + _response = await self._raw_client.update( + default_time_zone=default_time_zone, idempotency_key=idempotency_key, request_options=request_options + ) return _response.data - async def get_observation_steering( + async def get_instructions(self, *, request_options: typing.Optional[RequestOptions] = None) -> Instructions: + """ + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Instructions + OK + + Examples + -------- + import asyncio + + from zep_cloud import AsyncZep + + client = AsyncZep( + api_key="YOUR_API_KEY", + ) + + + async def main() -> None: + await client.project.get_instructions() + + + asyncio.run(main()) + """ + _response = await self._raw_client.get_instructions(request_options=request_options) + return _response.data + + async def set_instructions( self, *, - user_id: typing.Optional[str] = None, - graph_id: typing.Optional[str] = None, + inherited: typing.Optional[bool] = OMIT, + instructions: typing.Optional[typing.Sequence[typing.Dict[str, typing.Any]]] = OMIT, + idempotency_key: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, - ) -> ObservationSteeringConfig: + ) -> Instructions: """ - Returns project steering or the effective user/graph steering with project fallback. This API is experimental and may change in future releases. - Parameters ---------- - user_id : typing.Optional[str] - User ID for user-specific steering + inherited : typing.Optional[bool] - graph_id : typing.Optional[str] - Graph ID for graph-specific steering + instructions : typing.Optional[typing.Sequence[typing.Dict[str, typing.Any]]] + + idempotency_key : typing.Optional[str] request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - ObservationSteeringConfig - Retrieved + Instructions + OK Examples -------- @@ -304,50 +531,80 @@ async def get_observation_steering( async def main() -> None: - await client.project.get_observation_steering( - user_id="user_id", - graph_id="graph_id", - ) + await client.project.set_instructions() asyncio.run(main()) """ - _response = await self._raw_client.get_observation_steering( - user_id=user_id, graph_id=graph_id, request_options=request_options + _response = await self._raw_client.set_instructions( + inherited=inherited, + instructions=instructions, + idempotency_key=idempotency_key, + request_options=request_options, + ) + return _response.data + + async def get_observation_steering( + self, *, request_options: typing.Optional[RequestOptions] = None + ) -> ObservationSteering: + """ + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + ObservationSteering + OK + + Examples + -------- + import asyncio + + from zep_cloud import AsyncZep + + client = AsyncZep( + api_key="YOUR_API_KEY", ) + + + async def main() -> None: + await client.project.get_observation_steering() + + + asyncio.run(main()) + """ + _response = await self._raw_client.get_observation_steering(request_options=request_options) return _response.data async def set_observation_steering( self, *, - user_id: typing.Optional[str] = None, - graph_id: typing.Optional[str] = None, + inherited: typing.Optional[bool] = OMIT, instruction: typing.Optional[str] = OMIT, - types: typing.Optional[typing.Sequence[ObservationType]] = OMIT, + types: typing.Optional[typing.Sequence[typing.Dict[str, typing.Any]]] = OMIT, + idempotency_key: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, - ) -> ObservationSteeringConfig: + ) -> ObservationSteering: """ - Replaces project, user, or graph steering. An empty configuration clears the project default or removes the user/graph override. Changes affect later materializer runs only. This API is experimental and may change in future releases. - Parameters ---------- - user_id : typing.Optional[str] - User ID for user-specific steering - - graph_id : typing.Optional[str] - Graph ID for graph-specific steering + inherited : typing.Optional[bool] instruction : typing.Optional[str] - types : typing.Optional[typing.Sequence[ObservationType]] + types : typing.Optional[typing.Sequence[typing.Dict[str, typing.Any]]] + + idempotency_key : typing.Optional[str] request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - ObservationSteeringConfig - Updated + ObservationSteering + OK Examples -------- @@ -361,15 +618,186 @@ async def set_observation_steering( async def main() -> None: - await client.project.set_observation_steering( - user_id="user_id", - graph_id="graph_id", - ) + await client.project.set_observation_steering() asyncio.run(main()) """ _response = await self._raw_client.set_observation_steering( - user_id=user_id, graph_id=graph_id, instruction=instruction, types=types, request_options=request_options + inherited=inherited, + instruction=instruction, + types=types, + idempotency_key=idempotency_key, + request_options=request_options, + ) + return _response.data + + async def get_ontology(self, *, request_options: typing.Optional[RequestOptions] = None) -> Ontology: + """ + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Ontology + OK + + Examples + -------- + import asyncio + + from zep_cloud import AsyncZep + + client = AsyncZep( + api_key="YOUR_API_KEY", + ) + + + async def main() -> None: + await client.project.get_ontology() + + + asyncio.run(main()) + """ + _response = await self._raw_client.get_ontology(request_options=request_options) + return _response.data + + async def set_ontology( + self, + *, + edge_types: typing.Optional[typing.Sequence[EdgeType]] = OMIT, + entity_types: typing.Optional[typing.Sequence[EntityType]] = OMIT, + inherited: typing.Optional[bool] = OMIT, + idempotency_key: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> Ontology: + """ + Parameters + ---------- + edge_types : typing.Optional[typing.Sequence[EdgeType]] + + entity_types : typing.Optional[typing.Sequence[EntityType]] + + inherited : typing.Optional[bool] + + idempotency_key : typing.Optional[str] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Ontology + OK + + Examples + -------- + import asyncio + + from zep_cloud import AsyncZep + + client = AsyncZep( + api_key="YOUR_API_KEY", + ) + + + async def main() -> None: + await client.project.set_ontology() + + + asyncio.run(main()) + """ + _response = await self._raw_client.set_ontology( + edge_types=edge_types, + entity_types=entity_types, + inherited=inherited, + idempotency_key=idempotency_key, + request_options=request_options, + ) + return _response.data + + async def get_user_summary_instructions( + self, *, request_options: typing.Optional[RequestOptions] = None + ) -> UserSummaryInstructions: + """ + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + UserSummaryInstructions + OK + + Examples + -------- + import asyncio + + from zep_cloud import AsyncZep + + client = AsyncZep( + api_key="YOUR_API_KEY", + ) + + + async def main() -> None: + await client.project.get_user_summary_instructions() + + + asyncio.run(main()) + """ + _response = await self._raw_client.get_user_summary_instructions(request_options=request_options) + return _response.data + + async def set_user_summary_instructions( + self, + *, + inherited: typing.Optional[bool] = OMIT, + instructions: typing.Optional[typing.Sequence[typing.Dict[str, typing.Any]]] = OMIT, + idempotency_key: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> UserSummaryInstructions: + """ + Parameters + ---------- + inherited : typing.Optional[bool] + + instructions : typing.Optional[typing.Sequence[typing.Dict[str, typing.Any]]] + + idempotency_key : typing.Optional[str] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + UserSummaryInstructions + OK + + Examples + -------- + import asyncio + + from zep_cloud import AsyncZep + + client = AsyncZep( + api_key="YOUR_API_KEY", + ) + + + async def main() -> None: + await client.project.set_user_summary_instructions() + + + asyncio.run(main()) + """ + _response = await self._raw_client.set_user_summary_instructions( + inherited=inherited, + instructions=instructions, + idempotency_key=idempotency_key, + request_options=request_options, ) return _response.data diff --git a/src/zep_cloud/project/raw_client.py b/src/zep_cloud/project/raw_client.py index a8ce469c..efd78893 100644 --- a/src/zep_cloud/project/raw_client.py +++ b/src/zep_cloud/project/raw_client.py @@ -6,16 +6,22 @@ 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.parse_error import ParsingError 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 ..errors.unauthorized_error import UnauthorizedError from ..types.api_error import ApiError as types_api_error_ApiError -from ..types.observation_steering_config import ObservationSteeringConfig -from ..types.observation_type import ObservationType -from ..types.project_info_response import ProjectInfoResponse +from ..types.edge_type import EdgeType +from ..types.entity_type import EntityType +from ..types.instructions import Instructions +from ..types.observation_steering import ObservationSteering +from ..types.ontology import Ontology +from ..types.project import Project +from ..types.user_summary_instructions import UserSummaryInstructions +from pydantic import ValidationError # this is used as the default value for optional parameters OMIT = typing.cast(typing.Any, ...) @@ -25,10 +31,8 @@ class RawProjectClient: def __init__(self, *, client_wrapper: SyncClientWrapper): self._client_wrapper = client_wrapper - def get(self, *, request_options: typing.Optional[RequestOptions] = None) -> HttpResponse[ProjectInfoResponse]: + def get(self, *, request_options: typing.Optional[RequestOptions] = None) -> HttpResponse[Project]: """ - Retrieve project info based on the provided api key. - Parameters ---------- request_options : typing.Optional[RequestOptions] @@ -36,20 +40,20 @@ def get(self, *, request_options: typing.Optional[RequestOptions] = None) -> Htt Returns ------- - HttpResponse[ProjectInfoResponse] - Retrieved + HttpResponse[Project] + OK """ _response = self._client_wrapper.httpx_client.request( - "projects/info", + "project", method="GET", request_options=request_options, ) try: if 200 <= _response.status_code < 300: _data = typing.cast( - ProjectInfoResponse, + Project, parse_obj_as( - type_=ProjectInfoResponse, # type: ignore + type_=Project, # type: ignore object_=_response.json(), ), ) @@ -58,15 +62,15 @@ def get(self, *, request_options: typing.Optional[RequestOptions] = None) -> Htt raise BadRequestError( headers=dict(_response.headers), body=typing.cast( - typing.Optional[typing.Any], + types_api_error_ApiError, parse_obj_as( - type_=typing.Optional[typing.Any], # type: ignore + type_=types_api_error_ApiError, # type: ignore object_=_response.json(), ), ), ) - if _response.status_code == 404: - raise NotFoundError( + if _response.status_code == 401: + raise UnauthorizedError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -76,8 +80,8 @@ def get(self, *, request_options: typing.Optional[RequestOptions] = None) -> Htt ), ), ) - if _response.status_code == 500: - raise InternalServerError( + if _response.status_code == 404: + raise NotFoundError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -92,37 +96,46 @@ def get(self, *, request_options: typing.Optional[RequestOptions] = None) -> Htt raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response.text ) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response_json ) def update( - self, *, default_time_zone: typing.Optional[str] = OMIT, request_options: typing.Optional[RequestOptions] = None - ) -> HttpResponse[ProjectInfoResponse]: + self, + *, + default_time_zone: typing.Optional[str] = OMIT, + idempotency_key: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[Project]: """ - Sets or clears the project-level fallback time zone for the API key's project. - Parameters ---------- default_time_zone : typing.Optional[str] - The project's IANA fallback time zone. Null clears the existing value. + Omit to leave unchanged, send JSON null to clear, or send a value to set. + + idempotency_key : typing.Optional[str] request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - HttpResponse[ProjectInfoResponse] - Updated + HttpResponse[Project] + OK """ _response = self._client_wrapper.httpx_client.request( - "projects/info", + "project", method="PATCH", json={ "default_time_zone": default_time_zone, }, headers={ "content-type": "application/json", + "Idempotency-Key": str(idempotency_key) if idempotency_key is not None else None, }, request_options=request_options, omit=OMIT, @@ -130,9 +143,9 @@ def update( try: if 200 <= _response.status_code < 300: _data = typing.cast( - ProjectInfoResponse, + Project, parse_obj_as( - type_=ProjectInfoResponse, # type: ignore + type_=Project, # type: ignore object_=_response.json(), ), ) @@ -141,15 +154,15 @@ def update( raise BadRequestError( headers=dict(_response.headers), body=typing.cast( - typing.Optional[typing.Any], + types_api_error_ApiError, parse_obj_as( - type_=typing.Optional[typing.Any], # type: ignore + type_=types_api_error_ApiError, # type: ignore object_=_response.json(), ), ), ) - if _response.status_code == 404: - raise NotFoundError( + if _response.status_code == 401: + raise UnauthorizedError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -159,8 +172,8 @@ def update( ), ), ) - if _response.status_code == 500: - raise InternalServerError( + if _response.status_code == 404: + raise NotFoundError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -175,51 +188,39 @@ def update( raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response.text ) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response_json ) - def get_observation_steering( - self, - *, - user_id: typing.Optional[str] = None, - graph_id: typing.Optional[str] = None, - request_options: typing.Optional[RequestOptions] = None, - ) -> HttpResponse[ObservationSteeringConfig]: + def get_instructions( + self, *, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[Instructions]: """ - Returns project steering or the effective user/graph steering with project fallback. This API is experimental and may change in future releases. - Parameters ---------- - user_id : typing.Optional[str] - User ID for user-specific steering - - graph_id : typing.Optional[str] - Graph ID for graph-specific steering - request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - HttpResponse[ObservationSteeringConfig] - Retrieved + HttpResponse[Instructions] + OK """ _response = self._client_wrapper.httpx_client.request( - "projects/observation-steering", + "project/instructions", method="GET", - params={ - "user_id": user_id, - "graph_id": graph_id, - }, request_options=request_options, ) try: if 200 <= _response.status_code < 300: _data = typing.cast( - ObservationSteeringConfig, + Instructions, parse_obj_as( - type_=ObservationSteeringConfig, # type: ignore + type_=Instructions, # type: ignore object_=_response.json(), ), ) @@ -228,15 +229,26 @@ def get_observation_steering( raise BadRequestError( headers=dict(_response.headers), body=typing.cast( - typing.Optional[typing.Any], + types_api_error_ApiError, + parse_obj_as( + type_=types_api_error_ApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 401: + raise UnauthorizedError( + headers=dict(_response.headers), + body=typing.cast( + types_api_error_ApiError, parse_obj_as( - type_=typing.Optional[typing.Any], # type: ignore + type_=types_api_error_ApiError, # type: ignore object_=_response.json(), ), ), ) - if _response.status_code == 500: - raise InternalServerError( + if _response.status_code == 404: + raise NotFoundError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -251,57 +263,49 @@ def get_observation_steering( raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response.text ) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response_json ) - def set_observation_steering( + def set_instructions( self, *, - user_id: typing.Optional[str] = None, - graph_id: typing.Optional[str] = None, - instruction: typing.Optional[str] = OMIT, - types: typing.Optional[typing.Sequence[ObservationType]] = OMIT, + inherited: typing.Optional[bool] = OMIT, + instructions: typing.Optional[typing.Sequence[typing.Dict[str, typing.Any]]] = OMIT, + idempotency_key: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, - ) -> HttpResponse[ObservationSteeringConfig]: + ) -> HttpResponse[Instructions]: """ - Replaces project, user, or graph steering. An empty configuration clears the project default or removes the user/graph override. Changes affect later materializer runs only. This API is experimental and may change in future releases. - Parameters ---------- - user_id : typing.Optional[str] - User ID for user-specific steering + inherited : typing.Optional[bool] - graph_id : typing.Optional[str] - Graph ID for graph-specific steering - - instruction : typing.Optional[str] + instructions : typing.Optional[typing.Sequence[typing.Dict[str, typing.Any]]] - types : typing.Optional[typing.Sequence[ObservationType]] + idempotency_key : typing.Optional[str] request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - HttpResponse[ObservationSteeringConfig] - Updated + HttpResponse[Instructions] + OK """ _response = self._client_wrapper.httpx_client.request( - "projects/observation-steering", + "project/instructions", method="PUT", - params={ - "user_id": user_id, - "graph_id": graph_id, - }, json={ - "instruction": instruction, - "types": convert_and_respect_annotation_metadata( - object_=types, annotation=typing.Sequence[ObservationType], direction="write" - ), + "inherited": inherited, + "instructions": instructions, }, headers={ "content-type": "application/json", + "Idempotency-Key": str(idempotency_key) if idempotency_key is not None else None, }, request_options=request_options, omit=OMIT, @@ -309,9 +313,9 @@ def set_observation_steering( try: if 200 <= _response.status_code < 300: _data = typing.cast( - ObservationSteeringConfig, + Instructions, parse_obj_as( - type_=ObservationSteeringConfig, # type: ignore + type_=Instructions, # type: ignore object_=_response.json(), ), ) @@ -320,15 +324,26 @@ def set_observation_steering( raise BadRequestError( headers=dict(_response.headers), body=typing.cast( - typing.Optional[typing.Any], + types_api_error_ApiError, + parse_obj_as( + type_=types_api_error_ApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 401: + raise UnauthorizedError( + headers=dict(_response.headers), + body=typing.cast( + types_api_error_ApiError, parse_obj_as( - type_=typing.Optional[typing.Any], # type: ignore + type_=types_api_error_ApiError, # type: ignore object_=_response.json(), ), ), ) - if _response.status_code == 500: - raise InternalServerError( + if _response.status_code == 404: + raise NotFoundError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -343,21 +358,18 @@ def set_observation_steering( raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response.text ) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response_json ) - -class AsyncRawProjectClient: - def __init__(self, *, client_wrapper: AsyncClientWrapper): - self._client_wrapper = client_wrapper - - async def get( + def get_observation_steering( self, *, request_options: typing.Optional[RequestOptions] = None - ) -> AsyncHttpResponse[ProjectInfoResponse]: + ) -> HttpResponse[ObservationSteering]: """ - Retrieve project info based on the provided api key. - Parameters ---------- request_options : typing.Optional[RequestOptions] @@ -365,37 +377,37 @@ async def get( Returns ------- - AsyncHttpResponse[ProjectInfoResponse] - Retrieved + HttpResponse[ObservationSteering] + OK """ - _response = await self._client_wrapper.httpx_client.request( - "projects/info", + _response = self._client_wrapper.httpx_client.request( + "project/observation-steering", method="GET", request_options=request_options, ) try: if 200 <= _response.status_code < 300: _data = typing.cast( - ProjectInfoResponse, + ObservationSteering, parse_obj_as( - type_=ProjectInfoResponse, # type: ignore + type_=ObservationSteering, # type: ignore object_=_response.json(), ), ) - return AsyncHttpResponse(response=_response, data=_data) + return HttpResponse(response=_response, data=_data) if _response.status_code == 400: raise BadRequestError( headers=dict(_response.headers), body=typing.cast( - typing.Optional[typing.Any], + types_api_error_ApiError, parse_obj_as( - type_=typing.Optional[typing.Any], # type: ignore + type_=types_api_error_ApiError, # type: ignore object_=_response.json(), ), ), ) - if _response.status_code == 404: - raise NotFoundError( + if _response.status_code == 401: + raise UnauthorizedError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -405,8 +417,8 @@ async def get( ), ), ) - if _response.status_code == 500: - raise InternalServerError( + if _response.status_code == 404: + raise NotFoundError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -421,37 +433,53 @@ async def get( raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response.text ) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response_json ) - async def update( - self, *, default_time_zone: typing.Optional[str] = OMIT, request_options: typing.Optional[RequestOptions] = None - ) -> AsyncHttpResponse[ProjectInfoResponse]: + def set_observation_steering( + self, + *, + inherited: typing.Optional[bool] = OMIT, + instruction: typing.Optional[str] = OMIT, + types: typing.Optional[typing.Sequence[typing.Dict[str, typing.Any]]] = OMIT, + idempotency_key: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[ObservationSteering]: """ - Sets or clears the project-level fallback time zone for the API key's project. - Parameters ---------- - default_time_zone : typing.Optional[str] - The project's IANA fallback time zone. Null clears the existing value. + inherited : typing.Optional[bool] + + instruction : typing.Optional[str] + + types : typing.Optional[typing.Sequence[typing.Dict[str, typing.Any]]] + + idempotency_key : typing.Optional[str] request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - AsyncHttpResponse[ProjectInfoResponse] - Updated + HttpResponse[ObservationSteering] + OK """ - _response = await self._client_wrapper.httpx_client.request( - "projects/info", - method="PATCH", + _response = self._client_wrapper.httpx_client.request( + "project/observation-steering", + method="PUT", json={ - "default_time_zone": default_time_zone, + "inherited": inherited, + "instruction": instruction, + "types": types, }, headers={ "content-type": "application/json", + "Idempotency-Key": str(idempotency_key) if idempotency_key is not None else None, }, request_options=request_options, omit=OMIT, @@ -459,26 +487,26 @@ async def update( try: if 200 <= _response.status_code < 300: _data = typing.cast( - ProjectInfoResponse, + ObservationSteering, parse_obj_as( - type_=ProjectInfoResponse, # type: ignore + type_=ObservationSteering, # type: ignore object_=_response.json(), ), ) - return AsyncHttpResponse(response=_response, data=_data) + return HttpResponse(response=_response, data=_data) if _response.status_code == 400: raise BadRequestError( headers=dict(_response.headers), body=typing.cast( - typing.Optional[typing.Any], + types_api_error_ApiError, parse_obj_as( - type_=typing.Optional[typing.Any], # type: ignore + type_=types_api_error_ApiError, # type: ignore object_=_response.json(), ), ), ) - if _response.status_code == 404: - raise NotFoundError( + if _response.status_code == 401: + raise UnauthorizedError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -488,8 +516,8 @@ async def update( ), ), ) - if _response.status_code == 500: - raise InternalServerError( + if _response.status_code == 404: + raise NotFoundError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -504,68 +532,65 @@ async def update( raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response.text ) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response_json ) - async def get_observation_steering( - self, - *, - user_id: typing.Optional[str] = None, - graph_id: typing.Optional[str] = None, - request_options: typing.Optional[RequestOptions] = None, - ) -> AsyncHttpResponse[ObservationSteeringConfig]: + def get_ontology(self, *, request_options: typing.Optional[RequestOptions] = None) -> HttpResponse[Ontology]: """ - Returns project steering or the effective user/graph steering with project fallback. This API is experimental and may change in future releases. - Parameters ---------- - user_id : typing.Optional[str] - User ID for user-specific steering - - graph_id : typing.Optional[str] - Graph ID for graph-specific steering - request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - AsyncHttpResponse[ObservationSteeringConfig] - Retrieved + HttpResponse[Ontology] + OK """ - _response = await self._client_wrapper.httpx_client.request( - "projects/observation-steering", + _response = self._client_wrapper.httpx_client.request( + "project/ontology", method="GET", - params={ - "user_id": user_id, - "graph_id": graph_id, - }, request_options=request_options, ) try: if 200 <= _response.status_code < 300: _data = typing.cast( - ObservationSteeringConfig, + Ontology, parse_obj_as( - type_=ObservationSteeringConfig, # type: ignore + type_=Ontology, # type: ignore object_=_response.json(), ), ) - return AsyncHttpResponse(response=_response, data=_data) + return HttpResponse(response=_response, data=_data) if _response.status_code == 400: raise BadRequestError( headers=dict(_response.headers), body=typing.cast( - typing.Optional[typing.Any], + types_api_error_ApiError, + parse_obj_as( + type_=types_api_error_ApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 401: + raise UnauthorizedError( + headers=dict(_response.headers), + body=typing.cast( + types_api_error_ApiError, parse_obj_as( - type_=typing.Optional[typing.Any], # type: ignore + type_=types_api_error_ApiError, # type: ignore object_=_response.json(), ), ), ) - if _response.status_code == 500: - raise InternalServerError( + if _response.status_code == 404: + raise NotFoundError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -580,57 +605,57 @@ async def get_observation_steering( raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response.text ) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response_json ) - async def set_observation_steering( + def set_ontology( self, *, - user_id: typing.Optional[str] = None, - graph_id: typing.Optional[str] = None, - instruction: typing.Optional[str] = OMIT, - types: typing.Optional[typing.Sequence[ObservationType]] = OMIT, + edge_types: typing.Optional[typing.Sequence[EdgeType]] = OMIT, + entity_types: typing.Optional[typing.Sequence[EntityType]] = OMIT, + inherited: typing.Optional[bool] = OMIT, + idempotency_key: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, - ) -> AsyncHttpResponse[ObservationSteeringConfig]: + ) -> HttpResponse[Ontology]: """ - Replaces project, user, or graph steering. An empty configuration clears the project default or removes the user/graph override. Changes affect later materializer runs only. This API is experimental and may change in future releases. - Parameters ---------- - user_id : typing.Optional[str] - User ID for user-specific steering + edge_types : typing.Optional[typing.Sequence[EdgeType]] - graph_id : typing.Optional[str] - Graph ID for graph-specific steering + entity_types : typing.Optional[typing.Sequence[EntityType]] - instruction : typing.Optional[str] + inherited : typing.Optional[bool] - types : typing.Optional[typing.Sequence[ObservationType]] + idempotency_key : typing.Optional[str] request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - AsyncHttpResponse[ObservationSteeringConfig] - Updated + HttpResponse[Ontology] + OK """ - _response = await self._client_wrapper.httpx_client.request( - "projects/observation-steering", + _response = self._client_wrapper.httpx_client.request( + "project/ontology", method="PUT", - params={ - "user_id": user_id, - "graph_id": graph_id, - }, json={ - "instruction": instruction, - "types": convert_and_respect_annotation_metadata( - object_=types, annotation=typing.Sequence[ObservationType], direction="write" + "edge_types": convert_and_respect_annotation_metadata( + object_=edge_types, annotation=typing.Sequence[EdgeType], direction="write" ), + "entity_types": convert_and_respect_annotation_metadata( + object_=entity_types, annotation=typing.Sequence[EntityType], direction="write" + ), + "inherited": inherited, }, headers={ "content-type": "application/json", + "Idempotency-Key": str(idempotency_key) if idempotency_key is not None else None, }, request_options=request_options, omit=OMIT, @@ -638,26 +663,112 @@ async def set_observation_steering( try: if 200 <= _response.status_code < 300: _data = typing.cast( - ObservationSteeringConfig, + Ontology, parse_obj_as( - type_=ObservationSteeringConfig, # type: ignore + type_=Ontology, # type: ignore object_=_response.json(), ), ) - return AsyncHttpResponse(response=_response, data=_data) + return HttpResponse(response=_response, data=_data) + if _response.status_code == 400: + raise BadRequestError( + 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 == 401: + raise UnauthorizedError( + 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(), + ), + ), + ) + _response_json = _response.json() + except JSONDecodeError: + raise core_api_error_ApiError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.text + ) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) + raise core_api_error_ApiError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response_json + ) + + def get_user_summary_instructions( + self, *, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[UserSummaryInstructions]: + """ + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[UserSummaryInstructions] + OK + """ + _response = self._client_wrapper.httpx_client.request( + "project/user-summary-instructions", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + UserSummaryInstructions, + parse_obj_as( + type_=UserSummaryInstructions, # 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], + types_api_error_ApiError, + parse_obj_as( + type_=types_api_error_ApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 401: + raise UnauthorizedError( + headers=dict(_response.headers), + body=typing.cast( + types_api_error_ApiError, parse_obj_as( - type_=typing.Optional[typing.Any], # type: ignore + type_=types_api_error_ApiError, # type: ignore object_=_response.json(), ), ), ) - if _response.status_code == 500: - raise InternalServerError( + if _response.status_code == 404: + raise NotFoundError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -672,6 +783,967 @@ async def set_observation_steering( raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response.text ) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) + raise core_api_error_ApiError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response_json + ) + + def set_user_summary_instructions( + self, + *, + inherited: typing.Optional[bool] = OMIT, + instructions: typing.Optional[typing.Sequence[typing.Dict[str, typing.Any]]] = OMIT, + idempotency_key: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[UserSummaryInstructions]: + """ + Parameters + ---------- + inherited : typing.Optional[bool] + + instructions : typing.Optional[typing.Sequence[typing.Dict[str, typing.Any]]] + + idempotency_key : typing.Optional[str] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[UserSummaryInstructions] + OK + """ + _response = self._client_wrapper.httpx_client.request( + "project/user-summary-instructions", + method="PUT", + json={ + "inherited": inherited, + "instructions": instructions, + }, + headers={ + "content-type": "application/json", + "Idempotency-Key": str(idempotency_key) if idempotency_key is not None else None, + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + UserSummaryInstructions, + parse_obj_as( + type_=UserSummaryInstructions, # 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( + types_api_error_ApiError, + parse_obj_as( + type_=types_api_error_ApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 401: + raise UnauthorizedError( + 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(), + ), + ), + ) + _response_json = _response.json() + except JSONDecodeError: + raise core_api_error_ApiError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.text + ) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) + raise core_api_error_ApiError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response_json + ) + + +class AsyncRawProjectClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._client_wrapper = client_wrapper + + async def get(self, *, request_options: typing.Optional[RequestOptions] = None) -> AsyncHttpResponse[Project]: + """ + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[Project] + OK + """ + _response = await self._client_wrapper.httpx_client.request( + "project", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Project, + parse_obj_as( + type_=Project, # 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( + types_api_error_ApiError, + parse_obj_as( + type_=types_api_error_ApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 401: + raise UnauthorizedError( + 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(), + ), + ), + ) + _response_json = _response.json() + except JSONDecodeError: + raise core_api_error_ApiError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.text + ) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) + raise core_api_error_ApiError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response_json + ) + + async def update( + self, + *, + default_time_zone: typing.Optional[str] = OMIT, + idempotency_key: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[Project]: + """ + Parameters + ---------- + default_time_zone : typing.Optional[str] + Omit to leave unchanged, send JSON null to clear, or send a value to set. + + idempotency_key : typing.Optional[str] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[Project] + OK + """ + _response = await self._client_wrapper.httpx_client.request( + "project", + method="PATCH", + json={ + "default_time_zone": default_time_zone, + }, + headers={ + "content-type": "application/json", + "Idempotency-Key": str(idempotency_key) if idempotency_key is not None else None, + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Project, + parse_obj_as( + type_=Project, # 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( + types_api_error_ApiError, + parse_obj_as( + type_=types_api_error_ApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 401: + raise UnauthorizedError( + 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(), + ), + ), + ) + _response_json = _response.json() + except JSONDecodeError: + raise core_api_error_ApiError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.text + ) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) + raise core_api_error_ApiError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response_json + ) + + async def get_instructions( + self, *, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[Instructions]: + """ + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[Instructions] + OK + """ + _response = await self._client_wrapper.httpx_client.request( + "project/instructions", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Instructions, + parse_obj_as( + type_=Instructions, # 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( + types_api_error_ApiError, + parse_obj_as( + type_=types_api_error_ApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 401: + raise UnauthorizedError( + 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(), + ), + ), + ) + _response_json = _response.json() + except JSONDecodeError: + raise core_api_error_ApiError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.text + ) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) + raise core_api_error_ApiError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response_json + ) + + async def set_instructions( + self, + *, + inherited: typing.Optional[bool] = OMIT, + instructions: typing.Optional[typing.Sequence[typing.Dict[str, typing.Any]]] = OMIT, + idempotency_key: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[Instructions]: + """ + Parameters + ---------- + inherited : typing.Optional[bool] + + instructions : typing.Optional[typing.Sequence[typing.Dict[str, typing.Any]]] + + idempotency_key : typing.Optional[str] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[Instructions] + OK + """ + _response = await self._client_wrapper.httpx_client.request( + "project/instructions", + method="PUT", + json={ + "inherited": inherited, + "instructions": instructions, + }, + headers={ + "content-type": "application/json", + "Idempotency-Key": str(idempotency_key) if idempotency_key is not None else None, + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Instructions, + parse_obj_as( + type_=Instructions, # 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( + types_api_error_ApiError, + parse_obj_as( + type_=types_api_error_ApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 401: + raise UnauthorizedError( + 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(), + ), + ), + ) + _response_json = _response.json() + except JSONDecodeError: + raise core_api_error_ApiError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.text + ) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) + raise core_api_error_ApiError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response_json + ) + + async def get_observation_steering( + self, *, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[ObservationSteering]: + """ + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[ObservationSteering] + OK + """ + _response = await self._client_wrapper.httpx_client.request( + "project/observation-steering", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + ObservationSteering, + parse_obj_as( + type_=ObservationSteering, # 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( + types_api_error_ApiError, + parse_obj_as( + type_=types_api_error_ApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 401: + raise UnauthorizedError( + 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(), + ), + ), + ) + _response_json = _response.json() + except JSONDecodeError: + raise core_api_error_ApiError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.text + ) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) + raise core_api_error_ApiError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response_json + ) + + async def set_observation_steering( + self, + *, + inherited: typing.Optional[bool] = OMIT, + instruction: typing.Optional[str] = OMIT, + types: typing.Optional[typing.Sequence[typing.Dict[str, typing.Any]]] = OMIT, + idempotency_key: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[ObservationSteering]: + """ + Parameters + ---------- + inherited : typing.Optional[bool] + + instruction : typing.Optional[str] + + types : typing.Optional[typing.Sequence[typing.Dict[str, typing.Any]]] + + idempotency_key : typing.Optional[str] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[ObservationSteering] + OK + """ + _response = await self._client_wrapper.httpx_client.request( + "project/observation-steering", + method="PUT", + json={ + "inherited": inherited, + "instruction": instruction, + "types": types, + }, + headers={ + "content-type": "application/json", + "Idempotency-Key": str(idempotency_key) if idempotency_key is not None else None, + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + ObservationSteering, + parse_obj_as( + type_=ObservationSteering, # 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( + types_api_error_ApiError, + parse_obj_as( + type_=types_api_error_ApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 401: + raise UnauthorizedError( + 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(), + ), + ), + ) + _response_json = _response.json() + except JSONDecodeError: + raise core_api_error_ApiError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.text + ) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) + raise core_api_error_ApiError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response_json + ) + + async def get_ontology( + self, *, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[Ontology]: + """ + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[Ontology] + OK + """ + _response = await self._client_wrapper.httpx_client.request( + "project/ontology", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Ontology, + parse_obj_as( + type_=Ontology, # 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( + types_api_error_ApiError, + parse_obj_as( + type_=types_api_error_ApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 401: + raise UnauthorizedError( + 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(), + ), + ), + ) + _response_json = _response.json() + except JSONDecodeError: + raise core_api_error_ApiError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.text + ) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) + raise core_api_error_ApiError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response_json + ) + + async def set_ontology( + self, + *, + edge_types: typing.Optional[typing.Sequence[EdgeType]] = OMIT, + entity_types: typing.Optional[typing.Sequence[EntityType]] = OMIT, + inherited: typing.Optional[bool] = OMIT, + idempotency_key: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[Ontology]: + """ + Parameters + ---------- + edge_types : typing.Optional[typing.Sequence[EdgeType]] + + entity_types : typing.Optional[typing.Sequence[EntityType]] + + inherited : typing.Optional[bool] + + idempotency_key : typing.Optional[str] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[Ontology] + OK + """ + _response = await self._client_wrapper.httpx_client.request( + "project/ontology", + method="PUT", + json={ + "edge_types": convert_and_respect_annotation_metadata( + object_=edge_types, annotation=typing.Sequence[EdgeType], direction="write" + ), + "entity_types": convert_and_respect_annotation_metadata( + object_=entity_types, annotation=typing.Sequence[EntityType], direction="write" + ), + "inherited": inherited, + }, + headers={ + "content-type": "application/json", + "Idempotency-Key": str(idempotency_key) if idempotency_key is not None else None, + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Ontology, + parse_obj_as( + type_=Ontology, # 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( + types_api_error_ApiError, + parse_obj_as( + type_=types_api_error_ApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 401: + raise UnauthorizedError( + 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(), + ), + ), + ) + _response_json = _response.json() + except JSONDecodeError: + raise core_api_error_ApiError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.text + ) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) + raise core_api_error_ApiError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response_json + ) + + async def get_user_summary_instructions( + self, *, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[UserSummaryInstructions]: + """ + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[UserSummaryInstructions] + OK + """ + _response = await self._client_wrapper.httpx_client.request( + "project/user-summary-instructions", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + UserSummaryInstructions, + parse_obj_as( + type_=UserSummaryInstructions, # 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( + types_api_error_ApiError, + parse_obj_as( + type_=types_api_error_ApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 401: + raise UnauthorizedError( + 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(), + ), + ), + ) + _response_json = _response.json() + except JSONDecodeError: + raise core_api_error_ApiError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.text + ) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) + raise core_api_error_ApiError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response_json + ) + + async def set_user_summary_instructions( + self, + *, + inherited: typing.Optional[bool] = OMIT, + instructions: typing.Optional[typing.Sequence[typing.Dict[str, typing.Any]]] = OMIT, + idempotency_key: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[UserSummaryInstructions]: + """ + Parameters + ---------- + inherited : typing.Optional[bool] + + instructions : typing.Optional[typing.Sequence[typing.Dict[str, typing.Any]]] + + idempotency_key : typing.Optional[str] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[UserSummaryInstructions] + OK + """ + _response = await self._client_wrapper.httpx_client.request( + "project/user-summary-instructions", + method="PUT", + json={ + "inherited": inherited, + "instructions": instructions, + }, + headers={ + "content-type": "application/json", + "Idempotency-Key": str(idempotency_key) if idempotency_key is not None else None, + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + UserSummaryInstructions, + parse_obj_as( + type_=UserSummaryInstructions, # 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( + types_api_error_ApiError, + parse_obj_as( + type_=types_api_error_ApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 401: + raise UnauthorizedError( + 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(), + ), + ), + ) + _response_json = _response.json() + except JSONDecodeError: + raise core_api_error_ApiError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.text + ) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response_json ) diff --git a/src/zep_cloud/task/client.py b/src/zep_cloud/task/client.py index bd546161..6900d8dd 100644 --- a/src/zep_cloud/task/client.py +++ b/src/zep_cloud/task/client.py @@ -3,8 +3,10 @@ import typing from ..core.client_wrapper import AsyncClientWrapper, SyncClientWrapper +from ..core.pagination import AsyncPager, SyncPager from ..core.request_options import RequestOptions -from ..types.get_task_response import GetTaskResponse +from ..types.task import Task +from ..types.task_page import TaskPage from .raw_client import AsyncRawTaskClient, RawTaskClient @@ -23,22 +25,63 @@ def with_raw_response(self) -> RawTaskClient: """ return self._raw_client - def get(self, task_id: str, *, request_options: typing.Optional[RequestOptions] = None) -> GetTaskResponse: + def list( + self, + *, + limit: typing.Optional[int] = None, + cursor: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> SyncPager[Task, TaskPage]: """ - Gets a task by its ID + Parameters + ---------- + limit : typing.Optional[int] + Page size + + cursor : typing.Optional[str] + Opaque page cursor + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + SyncPager[Task, TaskPage] + OK + + Examples + -------- + from zep_cloud import Zep + client = Zep( + api_key="YOUR_API_KEY", + ) + response = client.task.list( + limit=1, + cursor="cursor", + ) + for item in response: + yield item + # alternatively, you can paginate page-by-page + for page in response.iter_pages(): + yield page + """ + return self._raw_client.list(limit=limit, cursor=cursor, request_options=request_options) + + def get(self, task_uuid: str, *, request_options: typing.Optional[RequestOptions] = None) -> Task: + """ Parameters ---------- - task_id : str - Task ID + task_uuid : str + Task UUID request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - GetTaskResponse - Task + Task + OK Examples -------- @@ -48,10 +91,10 @@ def get(self, task_id: str, *, request_options: typing.Optional[RequestOptions] api_key="YOUR_API_KEY", ) client.task.get( - task_id="task_id", + task_uuid="task_uuid", ) """ - _response = self._raw_client.get(task_id, request_options=request_options) + _response = self._raw_client.get(task_uuid, request_options=request_options) return _response.data @@ -70,22 +113,72 @@ def with_raw_response(self) -> AsyncRawTaskClient: """ return self._raw_client - async def get(self, task_id: str, *, request_options: typing.Optional[RequestOptions] = None) -> GetTaskResponse: + async def list( + self, + *, + limit: typing.Optional[int] = None, + cursor: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncPager[Task, TaskPage]: """ - Gets a task by its ID + Parameters + ---------- + limit : typing.Optional[int] + Page size + cursor : typing.Optional[str] + Opaque page cursor + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncPager[Task, TaskPage] + OK + + Examples + -------- + import asyncio + + from zep_cloud import AsyncZep + + client = AsyncZep( + api_key="YOUR_API_KEY", + ) + + + async def main() -> None: + response = await client.task.list( + limit=1, + cursor="cursor", + ) + async for item in response: + yield item + + # alternatively, you can paginate page-by-page + async for page in response.iter_pages(): + yield page + + + asyncio.run(main()) + """ + return await self._raw_client.list(limit=limit, cursor=cursor, request_options=request_options) + + async def get(self, task_uuid: str, *, request_options: typing.Optional[RequestOptions] = None) -> Task: + """ Parameters ---------- - task_id : str - Task ID + task_uuid : str + Task UUID request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - GetTaskResponse - Task + Task + OK Examples -------- @@ -100,11 +193,11 @@ async def get(self, task_id: str, *, request_options: typing.Optional[RequestOpt async def main() -> None: await client.task.get( - task_id="task_id", + task_uuid="task_uuid", ) asyncio.run(main()) """ - _response = await self._raw_client.get(task_id, request_options=request_options) + _response = await self._raw_client.get(task_uuid, request_options=request_options) return _response.data diff --git a/src/zep_cloud/task/raw_client.py b/src/zep_cloud/task/raw_client.py index 1089a3ef..6a40ba70 100644 --- a/src/zep_cloud/task/raw_client.py +++ b/src/zep_cloud/task/raw_client.py @@ -7,54 +7,152 @@ from ..core.client_wrapper import AsyncClientWrapper, SyncClientWrapper from ..core.http_response import AsyncHttpResponse, HttpResponse from ..core.jsonable_encoder import jsonable_encoder +from ..core.pagination import AsyncPager, SyncPager +from ..core.parse_error import ParsingError from ..core.pydantic_utilities import parse_obj_as from ..core.request_options import RequestOptions -from ..errors.internal_server_error import InternalServerError +from ..errors.bad_request_error import BadRequestError from ..errors.not_found_error import NotFoundError +from ..errors.unauthorized_error import UnauthorizedError from ..types.api_error import ApiError as types_api_error_ApiError -from ..types.get_task_response import GetTaskResponse +from ..types.task import Task +from ..types.task_page import TaskPage +from pydantic import ValidationError class RawTaskClient: def __init__(self, *, client_wrapper: SyncClientWrapper): self._client_wrapper = client_wrapper - def get( - self, task_id: str, *, request_options: typing.Optional[RequestOptions] = None - ) -> HttpResponse[GetTaskResponse]: + def list( + self, + *, + limit: typing.Optional[int] = None, + cursor: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> SyncPager[Task, TaskPage]: """ - Gets a task by its ID + Parameters + ---------- + limit : typing.Optional[int] + Page size + + cursor : typing.Optional[str] + Opaque page cursor + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + SyncPager[Task, TaskPage] + OK + """ + _response = self._client_wrapper.httpx_client.request( + "tasks", + method="GET", + params={ + "limit": limit, + "cursor": cursor, + }, + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _parsed_response = typing.cast( + TaskPage, + parse_obj_as( + type_=TaskPage, # type: ignore + object_=_response.json(), + ), + ) + _items = _parsed_response.items + _parsed_next = _parsed_response.next_cursor + _has_next = _parsed_next is not None and _parsed_next != "" + _get_next = lambda: self.list( + limit=limit, + cursor=_parsed_next, + request_options=request_options, + ) + return SyncPager(has_next=_has_next, items=_items, get_next=_get_next, response=_parsed_response) + if _response.status_code == 400: + raise BadRequestError( + 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 == 401: + raise UnauthorizedError( + 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(), + ), + ), + ) + _response_json = _response.json() + except JSONDecodeError: + raise core_api_error_ApiError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.text + ) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) + raise core_api_error_ApiError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response_json + ) + def get(self, task_uuid: str, *, request_options: typing.Optional[RequestOptions] = None) -> HttpResponse[Task]: + """ Parameters ---------- - task_id : str - Task ID + task_uuid : str + Task UUID request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - HttpResponse[GetTaskResponse] - Task + HttpResponse[Task] + OK """ _response = self._client_wrapper.httpx_client.request( - f"tasks/{jsonable_encoder(task_id)}", + f"tasks/{jsonable_encoder(task_uuid)}", method="GET", request_options=request_options, ) try: if 200 <= _response.status_code < 300: _data = typing.cast( - GetTaskResponse, + Task, parse_obj_as( - type_=GetTaskResponse, # type: ignore + type_=Task, # type: ignore object_=_response.json(), ), ) return HttpResponse(response=_response, data=_data) - if _response.status_code == 404: - raise NotFoundError( + if _response.status_code == 400: + raise BadRequestError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -64,8 +162,19 @@ def get( ), ), ) - if _response.status_code == 500: - raise InternalServerError( + if _response.status_code == 401: + raise UnauthorizedError( + 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, @@ -80,6 +189,10 @@ def get( raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response.text ) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response_json ) @@ -89,42 +202,151 @@ class AsyncRawTaskClient: def __init__(self, *, client_wrapper: AsyncClientWrapper): self._client_wrapper = client_wrapper - async def get( - self, task_id: str, *, request_options: typing.Optional[RequestOptions] = None - ) -> AsyncHttpResponse[GetTaskResponse]: + async def list( + self, + *, + limit: typing.Optional[int] = None, + cursor: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncPager[Task, TaskPage]: """ - Gets a task by its ID + Parameters + ---------- + limit : typing.Optional[int] + Page size + + cursor : typing.Optional[str] + Opaque page cursor + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncPager[Task, TaskPage] + OK + """ + _response = await self._client_wrapper.httpx_client.request( + "tasks", + method="GET", + params={ + "limit": limit, + "cursor": cursor, + }, + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _parsed_response = typing.cast( + TaskPage, + parse_obj_as( + type_=TaskPage, # type: ignore + object_=_response.json(), + ), + ) + _items = _parsed_response.items + _parsed_next = _parsed_response.next_cursor + _has_next = _parsed_next is not None and _parsed_next != "" + async def _get_next(): + return await self.list( + limit=limit, + cursor=_parsed_next, + request_options=request_options, + ) + + return AsyncPager(has_next=_has_next, items=_items, get_next=_get_next, response=_parsed_response) + if _response.status_code == 400: + raise BadRequestError( + 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 == 401: + raise UnauthorizedError( + 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(), + ), + ), + ) + _response_json = _response.json() + except JSONDecodeError: + raise core_api_error_ApiError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.text + ) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) + raise core_api_error_ApiError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response_json + ) + + async def get( + self, task_uuid: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[Task]: + """ Parameters ---------- - task_id : str - Task ID + task_uuid : str + Task UUID request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - AsyncHttpResponse[GetTaskResponse] - Task + AsyncHttpResponse[Task] + OK """ _response = await self._client_wrapper.httpx_client.request( - f"tasks/{jsonable_encoder(task_id)}", + f"tasks/{jsonable_encoder(task_uuid)}", method="GET", request_options=request_options, ) try: if 200 <= _response.status_code < 300: _data = typing.cast( - GetTaskResponse, + Task, parse_obj_as( - type_=GetTaskResponse, # type: ignore + type_=Task, # type: ignore object_=_response.json(), ), ) return AsyncHttpResponse(response=_response, data=_data) - if _response.status_code == 404: - raise NotFoundError( + if _response.status_code == 400: + raise BadRequestError( + 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 == 401: + raise UnauthorizedError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -134,8 +356,8 @@ async def get( ), ), ) - if _response.status_code == 500: - raise InternalServerError( + if _response.status_code == 404: + raise NotFoundError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -150,6 +372,10 @@ async def get( raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response.text ) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response_json ) diff --git a/src/zep_cloud/thread/__init__.py b/src/zep_cloud/thread/__init__.py index 4144d499..d917233f 100644 --- a/src/zep_cloud/thread/__init__.py +++ b/src/zep_cloud/thread/__init__.py @@ -2,6 +2,33 @@ # isort: skip_file -from . import message +import typing +from importlib import import_module + +if typing.TYPE_CHECKING: + from . import message +_dynamic_imports: typing.Dict[str, str] = {"message": ".message"} + + +def __getattr__(attr_name: str) -> typing.Any: + module_name = _dynamic_imports.get(attr_name) + if module_name is None: + raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") + try: + module = import_module(module_name, __package__) + if module_name == f".{attr_name}": + return module + else: + return getattr(module, attr_name) + except ImportError as e: + raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e + except AttributeError as e: + raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e + + +def __dir__(): + lazy_attrs = list(_dynamic_imports.keys()) + return sorted(lazy_attrs) + __all__ = ["message"] diff --git a/src/zep_cloud/thread/client.py b/src/zep_cloud/thread/client.py index e4a8add5..17923130 100644 --- a/src/zep_cloud/thread/client.py +++ b/src/zep_cloud/thread/client.py @@ -1,21 +1,27 @@ # This file was auto-generated by Fern from our API Definition. +from __future__ import annotations + import typing from ..core.client_wrapper import AsyncClientWrapper, SyncClientWrapper +from ..core.pagination import AsyncPager, SyncPager from ..core.request_options import RequestOptions -from ..types.add_thread_messages_response import AddThreadMessagesResponse +from ..types.add_message import AddMessage +from ..types.add_messages_result import AddMessagesResult +from ..types.episode import Episode +from ..types.episode_page import EpisodePage from ..types.message import Message -from ..types.message_list_response import MessageListResponse -from ..types.role_type import RoleType -from ..types.success_response import SuccessResponse +from ..types.message_page import MessagePage from ..types.thread import Thread from ..types.thread_context_response import ThreadContextResponse -from ..types.thread_list_response import ThreadListResponse +from ..types.thread_delete_result import ThreadDeleteResult +from ..types.thread_page import ThreadPage from ..types.thread_summary import ThreadSummary -from .message.client import AsyncMessageClient, MessageClient from .raw_client import AsyncRawThreadClient, RawThreadClient +if typing.TYPE_CHECKING: + from .message.client import AsyncMessageClient, MessageClient # this is used as the default value for optional parameters OMIT = typing.cast(typing.Any, ...) @@ -23,7 +29,8 @@ class ThreadClient: def __init__(self, *, client_wrapper: SyncClientWrapper): self._raw_client = RawThreadClient(client_wrapper=client_wrapper) - self.message = MessageClient(client_wrapper=client_wrapper) + self._client_wrapper = client_wrapper + self._message: typing.Optional[MessageClient] = None @property def with_raw_response(self) -> RawThreadClient: @@ -36,39 +43,41 @@ def with_raw_response(self) -> RawThreadClient: """ return self._raw_client - def list_all( + def list( self, *, - page_number: typing.Optional[int] = None, - page_size: typing.Optional[int] = None, + limit: typing.Optional[int] = None, + cursor: typing.Optional[str] = None, order_by: typing.Optional[str] = None, - asc: typing.Optional[bool] = None, + order: typing.Optional[str] = None, + user_uuid: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, - ) -> ThreadListResponse: + ) -> SyncPager[Thread, ThreadPage]: """ - Returns all threads. - Parameters ---------- - page_number : typing.Optional[int] - Page number for pagination, starting from 1 + limit : typing.Optional[int] + Page size - page_size : typing.Optional[int] - Number of threads to retrieve per page. + cursor : typing.Optional[str] + Opaque page cursor order_by : typing.Optional[str] - Field to order the results by: created_at, updated_at, user_id, thread_id. + Sort field - asc : typing.Optional[bool] - Order direction: true for ascending, false for descending. + order : typing.Optional[str] + asc or desc + + user_uuid : typing.Optional[str] + Filter by user UUID request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - ThreadListResponse - List of threads + SyncPager[Thread, ThreadPage] + OK Examples -------- @@ -77,31 +86,44 @@ def list_all( client = Zep( api_key="YOUR_API_KEY", ) - client.thread.list_all( - page_number=1, - page_size=1, + response = client.thread.list( + limit=1, + cursor="cursor", order_by="order_by", - asc=True, + order="order", + user_uuid="user_uuid", ) + for item in response: + yield item + # alternatively, you can paginate page-by-page + for page in response.iter_pages(): + yield page """ - _response = self._raw_client.list_all( - page_number=page_number, page_size=page_size, order_by=order_by, asc=asc, request_options=request_options + return self._raw_client.list( + limit=limit, + cursor=cursor, + order_by=order_by, + order=order, + user_uuid=user_uuid, + request_options=request_options, ) - return _response.data def create( - self, *, thread_id: str, user_id: str, request_options: typing.Optional[RequestOptions] = None + self, + *, + thread_id: typing.Optional[str] = OMIT, + user_uuid: typing.Optional[str] = OMIT, + idempotency_key: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None, ) -> Thread: """ - Start a new thread. - Parameters ---------- - thread_id : str - The unique identifier of the thread. + thread_id : typing.Optional[str] + + user_uuid : typing.Optional[str] - user_id : str - The unique identifier of the user associated with the thread + idempotency_key : typing.Optional[str] request_options : typing.Optional[RequestOptions] Request-specific configuration. @@ -109,7 +131,7 @@ def create( Returns ------- Thread - The thread object. + Created Examples -------- @@ -118,62 +140,142 @@ def create( client = Zep( api_key="YOUR_API_KEY", ) - client.thread.create( - thread_id="thread_id", - user_id="user_id", - ) + client.thread.create() """ - _response = self._raw_client.create(thread_id=thread_id, user_id=user_id, request_options=request_options) + _response = self._raw_client.create( + thread_id=thread_id, user_uuid=user_uuid, idempotency_key=idempotency_key, request_options=request_options + ) return _response.data - def delete(self, thread_id: str, *, request_options: typing.Optional[RequestOptions] = None) -> SuccessResponse: + def lookup( + self, + *, + graph_id: typing.Optional[str] = OMIT, + thread_id: typing.Optional[str] = OMIT, + user_id: typing.Optional[str] = OMIT, + idempotency_key: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> Thread: + """ + Parameters + ---------- + graph_id : typing.Optional[str] + + thread_id : typing.Optional[str] + + user_id : typing.Optional[str] + + idempotency_key : typing.Optional[str] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Thread + OK + + Examples + -------- + from zep_cloud import Zep + + client = Zep( + api_key="YOUR_API_KEY", + ) + client.thread.lookup() """ - Deletes a thread. + _response = self._raw_client.lookup( + graph_id=graph_id, + thread_id=thread_id, + user_id=user_id, + idempotency_key=idempotency_key, + request_options=request_options, + ) + return _response.data + def get(self, thread_uuid: str, *, request_options: typing.Optional[RequestOptions] = None) -> Thread: + """ Parameters ---------- - thread_id : str - The ID of the thread for which memory should be deleted. + thread_uuid : str + Thread UUID request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - SuccessResponse + Thread OK Examples -------- from zep_cloud import Zep + client = Zep( + api_key="YOUR_API_KEY", + ) + client.thread.get( + thread_uuid="thread_uuid", + ) + """ + _response = self._raw_client.get(thread_uuid, request_options=request_options) + return _response.data + + def delete( + self, + thread_uuid: str, + *, + idempotency_key: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> ThreadDeleteResult: + """ + Parameters + ---------- + thread_uuid : str + Thread UUID + + idempotency_key : typing.Optional[str] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + ThreadDeleteResult + Accepted + + Examples + -------- + from zep_cloud import Zep + client = Zep( api_key="YOUR_API_KEY", ) client.thread.delete( - thread_id="threadId", + thread_uuid="thread_uuid", ) """ - _response = self._raw_client.delete(thread_id, request_options=request_options) + _response = self._raw_client.delete( + thread_uuid, idempotency_key=idempotency_key, request_options=request_options + ) return _response.data - def get_user_context( + def get_context( self, - thread_id: str, + thread_uuid: str, *, - template_id: typing.Optional[str] = None, + template_uuid: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, ) -> ThreadContextResponse: """ - Returns most relevant context from the user graph (including memory from any/all past threads) based on the content of the past few messages of the given thread. - Parameters ---------- - thread_id : str - The ID of the current thread (for which context is being retrieved). + thread_uuid : str + Thread UUID - template_id : typing.Optional[str] - Optional template ID to use for custom context rendering. + template_uuid : typing.Optional[str] + Context template UUID request_options : typing.Optional[RequestOptions] Request-specific configuration. @@ -190,48 +292,42 @@ def get_user_context( client = Zep( api_key="YOUR_API_KEY", ) - client.thread.get_user_context( - thread_id="threadId", - template_id="template_id", + client.thread.get_context( + thread_uuid="thread_uuid", + template_uuid="template_uuid", ) """ - _response = self._raw_client.get_user_context( - thread_id, template_id=template_id, request_options=request_options + _response = self._raw_client.get_context( + thread_uuid, template_uuid=template_uuid, request_options=request_options ) return _response.data - def get( + def list_episodes( self, - thread_id: str, + thread_uuid: str, *, limit: typing.Optional[int] = None, - cursor: typing.Optional[int] = None, - lastn: typing.Optional[int] = None, + cursor: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, - ) -> MessageListResponse: + ) -> SyncPager[Episode, EpisodePage]: """ - Returns messages for a thread. - Parameters ---------- - thread_id : str - Thread ID + thread_uuid : str + Thread UUID limit : typing.Optional[int] - Limit the number of results returned + Page size - cursor : typing.Optional[int] - Cursor for pagination - - lastn : typing.Optional[int] - Number of most recent messages to return (overrides limit and cursor) + cursor : typing.Optional[str] + Opaque page cursor request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - MessageListResponse + SyncPager[Episode, EpisodePage] OK Examples @@ -241,162 +337,132 @@ def get( client = Zep( api_key="YOUR_API_KEY", ) - client.thread.get( - thread_id="threadId", + response = client.thread.list_episodes( + thread_uuid="thread_uuid", limit=1, - cursor=1, - lastn=1, + cursor="cursor", ) + for item in response: + yield item + # alternatively, you can paginate page-by-page + for page in response.iter_pages(): + yield page """ - _response = self._raw_client.get( - thread_id, limit=limit, cursor=cursor, lastn=lastn, request_options=request_options - ) - return _response.data + return self._raw_client.list_episodes(thread_uuid, limit=limit, cursor=cursor, request_options=request_options) - def add_messages( + def list_messages( self, - thread_id: str, + thread_uuid: str, *, - messages: typing.Sequence[Message], - ignore_roles: typing.Optional[typing.Sequence[RoleType]] = OMIT, - return_context: typing.Optional[bool] = OMIT, - strict_ontology: typing.Optional[bool] = OMIT, + limit: typing.Optional[int] = None, + cursor: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, - ) -> AddThreadMessagesResponse: + ) -> SyncPager[Message, MessagePage]: """ - Add messages to a thread. - Parameters ---------- - thread_id : str - The ID of the thread to which messages should be added. - - messages : typing.Sequence[Message] - A list of message objects, where each message contains a role and content. + thread_uuid : str + Thread UUID - ignore_roles : typing.Optional[typing.Sequence[RoleType]] - Optional list of role types to ignore when adding messages to graph memory. - The message itself will still be added, retained and used as context for messages - that are added to a user's graph. - - return_context : typing.Optional[bool] - Optionally return context block relevant to the most recent messages. + limit : typing.Optional[int] + Page size - strict_ontology : typing.Optional[bool] - When true, prevents extraction of generic Entity nodes that do not match the configured ontology. + cursor : typing.Optional[str] + Opaque page cursor request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - AddThreadMessagesResponse - An object, optionally containing user context retrieved for the last thread message + SyncPager[Message, MessagePage] + OK Examples -------- - from zep_cloud import Message, Zep + from zep_cloud import Zep client = Zep( api_key="YOUR_API_KEY", ) - client.thread.add_messages( - thread_id="threadId", - messages=[ - Message( - content="content", - role="norole", - ) - ], + response = client.thread.list_messages( + thread_uuid="thread_uuid", + limit=1, + cursor="cursor", ) + for item in response: + yield item + # alternatively, you can paginate page-by-page + for page in response.iter_pages(): + yield page """ - _response = self._raw_client.add_messages( - thread_id, - messages=messages, - ignore_roles=ignore_roles, - return_context=return_context, - strict_ontology=strict_ontology, - request_options=request_options, - ) - return _response.data + return self._raw_client.list_messages(thread_uuid, limit=limit, cursor=cursor, request_options=request_options) - def add_messages_batch( + def add_messages( self, - thread_id: str, + thread_uuid: str, *, - messages: typing.Sequence[Message], - ignore_roles: typing.Optional[typing.Sequence[RoleType]] = OMIT, + ignore_roles: typing.Optional[typing.Sequence[str]] = OMIT, + messages: typing.Optional[typing.Sequence[AddMessage]] = OMIT, return_context: typing.Optional[bool] = OMIT, strict_ontology: typing.Optional[bool] = OMIT, + idempotency_key: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, - ) -> AddThreadMessagesResponse: + ) -> AddMessagesResult: """ - Deprecated. Use the [Batch API](/adding-batch-data) (`client.batch.*` with `type: "thread_message"`) instead. - - Adds messages to a thread in batch mode, processing messages concurrently. - Parameters ---------- - thread_id : str - The ID of the thread to which messages should be added. + thread_uuid : str + Thread UUID - messages : typing.Sequence[Message] - A list of message objects, where each message contains a role and content. + ignore_roles : typing.Optional[typing.Sequence[str]] - ignore_roles : typing.Optional[typing.Sequence[RoleType]] - Optional list of role types to ignore when adding messages to graph memory. - The message itself will still be added, retained and used as context for messages - that are added to a user's graph. + messages : typing.Optional[typing.Sequence[AddMessage]] return_context : typing.Optional[bool] - Optionally return context block relevant to the most recent messages. strict_ontology : typing.Optional[bool] - When true, prevents extraction of generic Entity nodes that do not match the configured ontology. + + idempotency_key : typing.Optional[str] request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - AddThreadMessagesResponse - An object, optionally containing user context retrieved for the last thread message + AddMessagesResult + Accepted Examples -------- - from zep_cloud import Message, Zep + from zep_cloud import Zep client = Zep( api_key="YOUR_API_KEY", ) - client.thread.add_messages_batch( - thread_id="threadId", - messages=[ - Message( - content="content", - role="norole", - ) - ], + client.thread.add_messages( + thread_uuid="thread_uuid", ) """ - _response = self._raw_client.add_messages_batch( - thread_id, - messages=messages, + _response = self._raw_client.add_messages( + thread_uuid, ignore_roles=ignore_roles, + messages=messages, return_context=return_context, strict_ontology=strict_ontology, + idempotency_key=idempotency_key, request_options=request_options, ) return _response.data - def get_summary(self, thread_id: str, *, request_options: typing.Optional[RequestOptions] = None) -> ThreadSummary: + def get_summary( + self, thread_uuid: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> ThreadSummary: """ - Returns the incremental summary generated from messages in the thread. Returns 404 if no summary exists for the thread. - Parameters ---------- - thread_id : str - The thread ID. + thread_uuid : str + Thread UUID request_options : typing.Optional[RequestOptions] Request-specific configuration. @@ -414,17 +480,26 @@ def get_summary(self, thread_id: str, *, request_options: typing.Optional[Reques api_key="YOUR_API_KEY", ) client.thread.get_summary( - thread_id="threadId", + thread_uuid="thread_uuid", ) """ - _response = self._raw_client.get_summary(thread_id, request_options=request_options) + _response = self._raw_client.get_summary(thread_uuid, request_options=request_options) return _response.data + @property + def message(self): + if self._message is None: + from .message.client import MessageClient # noqa: E402 + + self._message = MessageClient(client_wrapper=self._client_wrapper) + return self._message + class AsyncThreadClient: def __init__(self, *, client_wrapper: AsyncClientWrapper): self._raw_client = AsyncRawThreadClient(client_wrapper=client_wrapper) - self.message = AsyncMessageClient(client_wrapper=client_wrapper) + self._client_wrapper = client_wrapper + self._message: typing.Optional[AsyncMessageClient] = None @property def with_raw_response(self) -> AsyncRawThreadClient: @@ -437,39 +512,41 @@ def with_raw_response(self) -> AsyncRawThreadClient: """ return self._raw_client - async def list_all( + async def list( self, *, - page_number: typing.Optional[int] = None, - page_size: typing.Optional[int] = None, + limit: typing.Optional[int] = None, + cursor: typing.Optional[str] = None, order_by: typing.Optional[str] = None, - asc: typing.Optional[bool] = None, + order: typing.Optional[str] = None, + user_uuid: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, - ) -> ThreadListResponse: + ) -> AsyncPager[Thread, ThreadPage]: """ - Returns all threads. - Parameters ---------- - page_number : typing.Optional[int] - Page number for pagination, starting from 1 + limit : typing.Optional[int] + Page size - page_size : typing.Optional[int] - Number of threads to retrieve per page. + cursor : typing.Optional[str] + Opaque page cursor order_by : typing.Optional[str] - Field to order the results by: created_at, updated_at, user_id, thread_id. + Sort field - asc : typing.Optional[bool] - Order direction: true for ascending, false for descending. + order : typing.Optional[str] + asc or desc + + user_uuid : typing.Optional[str] + Filter by user UUID request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - ThreadListResponse - List of threads + AsyncPager[Thread, ThreadPage] + OK Examples -------- @@ -483,34 +560,98 @@ async def list_all( async def main() -> None: - await client.thread.list_all( - page_number=1, - page_size=1, + response = await client.thread.list( + limit=1, + cursor="cursor", order_by="order_by", - asc=True, + order="order", + user_uuid="user_uuid", ) + async for item in response: + yield item + + # alternatively, you can paginate page-by-page + async for page in response.iter_pages(): + yield page asyncio.run(main()) """ - _response = await self._raw_client.list_all( - page_number=page_number, page_size=page_size, order_by=order_by, asc=asc, request_options=request_options + return await self._raw_client.list( + limit=limit, + cursor=cursor, + order_by=order_by, + order=order, + user_uuid=user_uuid, + request_options=request_options, ) - return _response.data async def create( - self, *, thread_id: str, user_id: str, request_options: typing.Optional[RequestOptions] = None + self, + *, + thread_id: typing.Optional[str] = OMIT, + user_uuid: typing.Optional[str] = OMIT, + idempotency_key: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None, ) -> Thread: """ - Start a new thread. + Parameters + ---------- + thread_id : typing.Optional[str] + + user_uuid : typing.Optional[str] + + idempotency_key : typing.Optional[str] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Thread + Created + + Examples + -------- + import asyncio + + from zep_cloud import AsyncZep + + client = AsyncZep( + api_key="YOUR_API_KEY", + ) + + + async def main() -> None: + await client.thread.create() + + + asyncio.run(main()) + """ + _response = await self._raw_client.create( + thread_id=thread_id, user_uuid=user_uuid, idempotency_key=idempotency_key, request_options=request_options + ) + return _response.data + async def lookup( + self, + *, + graph_id: typing.Optional[str] = OMIT, + thread_id: typing.Optional[str] = OMIT, + user_id: typing.Optional[str] = OMIT, + idempotency_key: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> Thread: + """ Parameters ---------- - thread_id : str - The unique identifier of the thread. + graph_id : typing.Optional[str] - user_id : str - The unique identifier of the user associated with the thread + thread_id : typing.Optional[str] + + user_id : typing.Optional[str] + + idempotency_key : typing.Optional[str] request_options : typing.Optional[RequestOptions] Request-specific configuration. @@ -518,7 +659,7 @@ async def create( Returns ------- Thread - The thread object. + OK Examples -------- @@ -532,35 +673,79 @@ async def create( async def main() -> None: - await client.thread.create( - thread_id="thread_id", - user_id="user_id", + await client.thread.lookup() + + + asyncio.run(main()) + """ + _response = await self._raw_client.lookup( + graph_id=graph_id, + thread_id=thread_id, + user_id=user_id, + idempotency_key=idempotency_key, + request_options=request_options, + ) + return _response.data + + async def get(self, thread_uuid: str, *, request_options: typing.Optional[RequestOptions] = None) -> Thread: + """ + Parameters + ---------- + thread_uuid : str + Thread UUID + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Thread + OK + + Examples + -------- + import asyncio + + from zep_cloud import AsyncZep + + client = AsyncZep( + api_key="YOUR_API_KEY", + ) + + + async def main() -> None: + await client.thread.get( + thread_uuid="thread_uuid", ) asyncio.run(main()) """ - _response = await self._raw_client.create(thread_id=thread_id, user_id=user_id, request_options=request_options) + _response = await self._raw_client.get(thread_uuid, request_options=request_options) return _response.data async def delete( - self, thread_id: str, *, request_options: typing.Optional[RequestOptions] = None - ) -> SuccessResponse: + self, + thread_uuid: str, + *, + idempotency_key: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> ThreadDeleteResult: """ - Deletes a thread. - Parameters ---------- - thread_id : str - The ID of the thread for which memory should be deleted. + thread_uuid : str + Thread UUID + + idempotency_key : typing.Optional[str] request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - SuccessResponse - OK + ThreadDeleteResult + Accepted Examples -------- @@ -575,32 +760,32 @@ async def delete( async def main() -> None: await client.thread.delete( - thread_id="threadId", + thread_uuid="thread_uuid", ) asyncio.run(main()) """ - _response = await self._raw_client.delete(thread_id, request_options=request_options) + _response = await self._raw_client.delete( + thread_uuid, idempotency_key=idempotency_key, request_options=request_options + ) return _response.data - async def get_user_context( + async def get_context( self, - thread_id: str, + thread_uuid: str, *, - template_id: typing.Optional[str] = None, + template_uuid: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, ) -> ThreadContextResponse: """ - Returns most relevant context from the user graph (including memory from any/all past threads) based on the content of the past few messages of the given thread. - Parameters ---------- - thread_id : str - The ID of the current thread (for which context is being retrieved). + thread_uuid : str + Thread UUID - template_id : typing.Optional[str] - Optional template ID to use for custom context rendering. + template_uuid : typing.Optional[str] + Context template UUID request_options : typing.Optional[RequestOptions] Request-specific configuration. @@ -622,51 +807,45 @@ async def get_user_context( async def main() -> None: - await client.thread.get_user_context( - thread_id="threadId", - template_id="template_id", + await client.thread.get_context( + thread_uuid="thread_uuid", + template_uuid="template_uuid", ) asyncio.run(main()) """ - _response = await self._raw_client.get_user_context( - thread_id, template_id=template_id, request_options=request_options + _response = await self._raw_client.get_context( + thread_uuid, template_uuid=template_uuid, request_options=request_options ) return _response.data - async def get( + async def list_episodes( self, - thread_id: str, + thread_uuid: str, *, limit: typing.Optional[int] = None, - cursor: typing.Optional[int] = None, - lastn: typing.Optional[int] = None, + cursor: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, - ) -> MessageListResponse: + ) -> AsyncPager[Episode, EpisodePage]: """ - Returns messages for a thread. - Parameters ---------- - thread_id : str - Thread ID + thread_uuid : str + Thread UUID limit : typing.Optional[int] - Limit the number of results returned + Page size - cursor : typing.Optional[int] - Cursor for pagination - - lastn : typing.Optional[int] - Number of most recent messages to return (overrides limit and cursor) + cursor : typing.Optional[str] + Opaque page cursor request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - MessageListResponse + AsyncPager[Episode, EpisodePage] OK Examples @@ -681,66 +860,58 @@ async def get( async def main() -> None: - await client.thread.get( - thread_id="threadId", + response = await client.thread.list_episodes( + thread_uuid="thread_uuid", limit=1, - cursor=1, - lastn=1, + cursor="cursor", ) + async for item in response: + yield item + + # alternatively, you can paginate page-by-page + async for page in response.iter_pages(): + yield page asyncio.run(main()) """ - _response = await self._raw_client.get( - thread_id, limit=limit, cursor=cursor, lastn=lastn, request_options=request_options + return await self._raw_client.list_episodes( + thread_uuid, limit=limit, cursor=cursor, request_options=request_options ) - return _response.data - async def add_messages( + async def list_messages( self, - thread_id: str, + thread_uuid: str, *, - messages: typing.Sequence[Message], - ignore_roles: typing.Optional[typing.Sequence[RoleType]] = OMIT, - return_context: typing.Optional[bool] = OMIT, - strict_ontology: typing.Optional[bool] = OMIT, + limit: typing.Optional[int] = None, + cursor: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, - ) -> AddThreadMessagesResponse: + ) -> AsyncPager[Message, MessagePage]: """ - Add messages to a thread. - Parameters ---------- - thread_id : str - The ID of the thread to which messages should be added. + thread_uuid : str + Thread UUID - messages : typing.Sequence[Message] - A list of message objects, where each message contains a role and content. - - ignore_roles : typing.Optional[typing.Sequence[RoleType]] - Optional list of role types to ignore when adding messages to graph memory. - The message itself will still be added, retained and used as context for messages - that are added to a user's graph. - - return_context : typing.Optional[bool] - Optionally return context block relevant to the most recent messages. + limit : typing.Optional[int] + Page size - strict_ontology : typing.Optional[bool] - When true, prevents extraction of generic Entity nodes that do not match the configured ontology. + cursor : typing.Optional[str] + Opaque page cursor request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - AddThreadMessagesResponse - An object, optionally containing user context retrieved for the last thread message + AsyncPager[Message, MessagePage] + OK Examples -------- import asyncio - from zep_cloud import AsyncZep, Message + from zep_cloud import AsyncZep client = AsyncZep( api_key="YOUR_API_KEY", @@ -748,76 +919,65 @@ async def add_messages( async def main() -> None: - await client.thread.add_messages( - thread_id="threadId", - messages=[ - Message( - content="content", - role="norole", - ) - ], + response = await client.thread.list_messages( + thread_uuid="thread_uuid", + limit=1, + cursor="cursor", ) + async for item in response: + yield item + + # alternatively, you can paginate page-by-page + async for page in response.iter_pages(): + yield page asyncio.run(main()) """ - _response = await self._raw_client.add_messages( - thread_id, - messages=messages, - ignore_roles=ignore_roles, - return_context=return_context, - strict_ontology=strict_ontology, - request_options=request_options, + return await self._raw_client.list_messages( + thread_uuid, limit=limit, cursor=cursor, request_options=request_options ) - return _response.data - async def add_messages_batch( + async def add_messages( self, - thread_id: str, + thread_uuid: str, *, - messages: typing.Sequence[Message], - ignore_roles: typing.Optional[typing.Sequence[RoleType]] = OMIT, + ignore_roles: typing.Optional[typing.Sequence[str]] = OMIT, + messages: typing.Optional[typing.Sequence[AddMessage]] = OMIT, return_context: typing.Optional[bool] = OMIT, strict_ontology: typing.Optional[bool] = OMIT, + idempotency_key: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, - ) -> AddThreadMessagesResponse: + ) -> AddMessagesResult: """ - Deprecated. Use the [Batch API](/adding-batch-data) (`client.batch.*` with `type: "thread_message"`) instead. - - Adds messages to a thread in batch mode, processing messages concurrently. - Parameters ---------- - thread_id : str - The ID of the thread to which messages should be added. + thread_uuid : str + Thread UUID - messages : typing.Sequence[Message] - A list of message objects, where each message contains a role and content. + ignore_roles : typing.Optional[typing.Sequence[str]] - ignore_roles : typing.Optional[typing.Sequence[RoleType]] - Optional list of role types to ignore when adding messages to graph memory. - The message itself will still be added, retained and used as context for messages - that are added to a user's graph. + messages : typing.Optional[typing.Sequence[AddMessage]] return_context : typing.Optional[bool] - Optionally return context block relevant to the most recent messages. strict_ontology : typing.Optional[bool] - When true, prevents extraction of generic Entity nodes that do not match the configured ontology. + + idempotency_key : typing.Optional[str] request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - AddThreadMessagesResponse - An object, optionally containing user context retrieved for the last thread message + AddMessagesResult + Accepted Examples -------- import asyncio - from zep_cloud import AsyncZep, Message + from zep_cloud import AsyncZep client = AsyncZep( api_key="YOUR_API_KEY", @@ -825,39 +985,32 @@ async def add_messages_batch( async def main() -> None: - await client.thread.add_messages_batch( - thread_id="threadId", - messages=[ - Message( - content="content", - role="norole", - ) - ], + await client.thread.add_messages( + thread_uuid="thread_uuid", ) asyncio.run(main()) """ - _response = await self._raw_client.add_messages_batch( - thread_id, - messages=messages, + _response = await self._raw_client.add_messages( + thread_uuid, ignore_roles=ignore_roles, + messages=messages, return_context=return_context, strict_ontology=strict_ontology, + idempotency_key=idempotency_key, request_options=request_options, ) return _response.data async def get_summary( - self, thread_id: str, *, request_options: typing.Optional[RequestOptions] = None + self, thread_uuid: str, *, request_options: typing.Optional[RequestOptions] = None ) -> ThreadSummary: """ - Returns the incremental summary generated from messages in the thread. Returns 404 if no summary exists for the thread. - Parameters ---------- - thread_id : str - The thread ID. + thread_uuid : str + Thread UUID request_options : typing.Optional[RequestOptions] Request-specific configuration. @@ -880,11 +1033,19 @@ async def get_summary( async def main() -> None: await client.thread.get_summary( - thread_id="threadId", + thread_uuid="thread_uuid", ) asyncio.run(main()) """ - _response = await self._raw_client.get_summary(thread_id, request_options=request_options) + _response = await self._raw_client.get_summary(thread_uuid, request_options=request_options) return _response.data + + @property + def message(self): + if self._message is None: + from .message.client import AsyncMessageClient # noqa: E402 + + self._message = AsyncMessageClient(client_wrapper=self._client_wrapper) + return self._message diff --git a/src/zep_cloud/thread/message/client.py b/src/zep_cloud/thread/message/client.py index 0c8adcba..6006e6c0 100644 --- a/src/zep_cloud/thread/message/client.py +++ b/src/zep_cloud/thread/message/client.py @@ -26,22 +26,62 @@ def with_raw_response(self) -> RawMessageClient: """ return self._raw_client + def get( + self, thread_uuid: str, message_uuid: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> Message: + """ + Parameters + ---------- + thread_uuid : str + Thread UUID + + message_uuid : str + Message UUID + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Message + OK + + Examples + -------- + from zep_cloud import Zep + + client = Zep( + api_key="YOUR_API_KEY", + ) + client.thread.message.get( + thread_uuid="thread_uuid", + message_uuid="message_uuid", + ) + """ + _response = self._raw_client.get(thread_uuid, message_uuid, request_options=request_options) + return _response.data + def update( self, + thread_uuid: str, message_uuid: str, *, - metadata: typing.Dict[str, typing.Optional[typing.Any]], + metadata: typing.Optional[typing.Dict[str, typing.Any]] = OMIT, + idempotency_key: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, ) -> Message: """ - Updates a message. - Parameters ---------- + thread_uuid : str + Thread UUID + message_uuid : str - The UUID of the message. + Message UUID - metadata : typing.Dict[str, typing.Optional[typing.Any]] + metadata : typing.Optional[typing.Dict[str, typing.Any]] + + idempotency_key : typing.Optional[str] request_options : typing.Optional[RequestOptions] Request-specific configuration. @@ -49,7 +89,7 @@ def update( Returns ------- Message - The updated message. + OK Examples -------- @@ -59,11 +99,17 @@ def update( api_key="YOUR_API_KEY", ) client.thread.message.update( - message_uuid="messageUUID", - metadata={"key": "value"}, + thread_uuid="thread_uuid", + message_uuid="message_uuid", ) """ - _response = self._raw_client.update(message_uuid, metadata=metadata, request_options=request_options) + _response = self._raw_client.update( + thread_uuid, + message_uuid, + metadata=metadata, + idempotency_key=idempotency_key, + request_options=request_options, + ) return _response.data @@ -82,22 +128,70 @@ def with_raw_response(self) -> AsyncRawMessageClient: """ return self._raw_client + async def get( + self, thread_uuid: str, message_uuid: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> Message: + """ + Parameters + ---------- + thread_uuid : str + Thread UUID + + message_uuid : str + Message UUID + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Message + OK + + Examples + -------- + import asyncio + + from zep_cloud import AsyncZep + + client = AsyncZep( + api_key="YOUR_API_KEY", + ) + + + async def main() -> None: + await client.thread.message.get( + thread_uuid="thread_uuid", + message_uuid="message_uuid", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.get(thread_uuid, message_uuid, request_options=request_options) + return _response.data + async def update( self, + thread_uuid: str, message_uuid: str, *, - metadata: typing.Dict[str, typing.Optional[typing.Any]], + metadata: typing.Optional[typing.Dict[str, typing.Any]] = OMIT, + idempotency_key: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, ) -> Message: """ - Updates a message. - Parameters ---------- + thread_uuid : str + Thread UUID + message_uuid : str - The UUID of the message. + Message UUID - metadata : typing.Dict[str, typing.Optional[typing.Any]] + metadata : typing.Optional[typing.Dict[str, typing.Any]] + + idempotency_key : typing.Optional[str] request_options : typing.Optional[RequestOptions] Request-specific configuration. @@ -105,7 +199,7 @@ async def update( Returns ------- Message - The updated message. + OK Examples -------- @@ -120,12 +214,18 @@ async def update( async def main() -> None: await client.thread.message.update( - message_uuid="messageUUID", - metadata={"key": "value"}, + thread_uuid="thread_uuid", + message_uuid="message_uuid", ) asyncio.run(main()) """ - _response = await self._raw_client.update(message_uuid, metadata=metadata, request_options=request_options) + _response = await self._raw_client.update( + thread_uuid, + message_uuid, + metadata=metadata, + idempotency_key=idempotency_key, + request_options=request_options, + ) return _response.data diff --git a/src/zep_cloud/thread/message/raw_client.py b/src/zep_cloud/thread/message/raw_client.py index 20029489..97053fb5 100644 --- a/src/zep_cloud/thread/message/raw_client.py +++ b/src/zep_cloud/thread/message/raw_client.py @@ -7,12 +7,15 @@ from ...core.client_wrapper import AsyncClientWrapper, SyncClientWrapper from ...core.http_response import AsyncHttpResponse, HttpResponse from ...core.jsonable_encoder import jsonable_encoder +from ...core.parse_error import ParsingError from ...core.pydantic_utilities import parse_obj_as from ...core.request_options import RequestOptions -from ...errors.internal_server_error import InternalServerError +from ...errors.bad_request_error import BadRequestError from ...errors.not_found_error import NotFoundError +from ...errors.unauthorized_error import UnauthorizedError from ...types.api_error import ApiError as types_api_error_ApiError from ...types.message import Message +from pydantic import ValidationError # this is used as the default value for optional parameters OMIT = typing.cast(typing.Any, ...) @@ -22,22 +25,108 @@ class RawMessageClient: def __init__(self, *, client_wrapper: SyncClientWrapper): self._client_wrapper = client_wrapper + def get( + self, thread_uuid: str, message_uuid: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[Message]: + """ + Parameters + ---------- + thread_uuid : str + Thread UUID + + message_uuid : str + Message UUID + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[Message] + OK + """ + _response = self._client_wrapper.httpx_client.request( + f"threads/{jsonable_encoder(thread_uuid)}/messages/{jsonable_encoder(message_uuid)}", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Message, + parse_obj_as( + type_=Message, # 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( + types_api_error_ApiError, + parse_obj_as( + type_=types_api_error_ApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 401: + raise UnauthorizedError( + 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(), + ), + ), + ) + _response_json = _response.json() + except JSONDecodeError: + raise core_api_error_ApiError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.text + ) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) + raise core_api_error_ApiError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response_json + ) + def update( self, + thread_uuid: str, message_uuid: str, *, - metadata: typing.Dict[str, typing.Optional[typing.Any]], + metadata: typing.Optional[typing.Dict[str, typing.Any]] = OMIT, + idempotency_key: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, ) -> HttpResponse[Message]: """ - Updates a message. - Parameters ---------- + thread_uuid : str + Thread UUID + message_uuid : str - The UUID of the message. + Message UUID - metadata : typing.Dict[str, typing.Optional[typing.Any]] + metadata : typing.Optional[typing.Dict[str, typing.Any]] + + idempotency_key : typing.Optional[str] request_options : typing.Optional[RequestOptions] Request-specific configuration. @@ -45,16 +134,17 @@ def update( Returns ------- HttpResponse[Message] - The updated message. + OK """ _response = self._client_wrapper.httpx_client.request( - f"messages/{jsonable_encoder(message_uuid)}", + f"threads/{jsonable_encoder(thread_uuid)}/messages/{jsonable_encoder(message_uuid)}", method="PATCH", json={ "metadata": metadata, }, headers={ "content-type": "application/json", + "Idempotency-Key": str(idempotency_key) if idempotency_key is not None else None, }, request_options=request_options, omit=OMIT, @@ -69,8 +159,19 @@ def update( ), ) return HttpResponse(response=_response, data=_data) - if _response.status_code == 404: - raise NotFoundError( + if _response.status_code == 400: + raise BadRequestError( + 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 == 401: + raise UnauthorizedError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -80,8 +181,8 @@ def update( ), ), ) - if _response.status_code == 500: - raise InternalServerError( + if _response.status_code == 404: + raise NotFoundError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -96,6 +197,10 @@ def update( raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response.text ) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response_json ) @@ -105,22 +210,108 @@ class AsyncRawMessageClient: def __init__(self, *, client_wrapper: AsyncClientWrapper): self._client_wrapper = client_wrapper + async def get( + self, thread_uuid: str, message_uuid: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[Message]: + """ + Parameters + ---------- + thread_uuid : str + Thread UUID + + message_uuid : str + Message UUID + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[Message] + OK + """ + _response = await self._client_wrapper.httpx_client.request( + f"threads/{jsonable_encoder(thread_uuid)}/messages/{jsonable_encoder(message_uuid)}", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Message, + parse_obj_as( + type_=Message, # 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( + types_api_error_ApiError, + parse_obj_as( + type_=types_api_error_ApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 401: + raise UnauthorizedError( + 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(), + ), + ), + ) + _response_json = _response.json() + except JSONDecodeError: + raise core_api_error_ApiError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.text + ) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) + raise core_api_error_ApiError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response_json + ) + async def update( self, + thread_uuid: str, message_uuid: str, *, - metadata: typing.Dict[str, typing.Optional[typing.Any]], + metadata: typing.Optional[typing.Dict[str, typing.Any]] = OMIT, + idempotency_key: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, ) -> AsyncHttpResponse[Message]: """ - Updates a message. - Parameters ---------- + thread_uuid : str + Thread UUID + message_uuid : str - The UUID of the message. + Message UUID - metadata : typing.Dict[str, typing.Optional[typing.Any]] + metadata : typing.Optional[typing.Dict[str, typing.Any]] + + idempotency_key : typing.Optional[str] request_options : typing.Optional[RequestOptions] Request-specific configuration. @@ -128,16 +319,17 @@ async def update( Returns ------- AsyncHttpResponse[Message] - The updated message. + OK """ _response = await self._client_wrapper.httpx_client.request( - f"messages/{jsonable_encoder(message_uuid)}", + f"threads/{jsonable_encoder(thread_uuid)}/messages/{jsonable_encoder(message_uuid)}", method="PATCH", json={ "metadata": metadata, }, headers={ "content-type": "application/json", + "Idempotency-Key": str(idempotency_key) if idempotency_key is not None else None, }, request_options=request_options, omit=OMIT, @@ -152,8 +344,19 @@ async def update( ), ) return AsyncHttpResponse(response=_response, data=_data) - if _response.status_code == 404: - raise NotFoundError( + if _response.status_code == 400: + raise BadRequestError( + 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 == 401: + raise UnauthorizedError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -163,8 +366,8 @@ async def update( ), ), ) - if _response.status_code == 500: - raise InternalServerError( + if _response.status_code == 404: + raise NotFoundError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -179,6 +382,10 @@ async def update( raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response.text ) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response_json ) diff --git a/src/zep_cloud/thread/raw_client.py b/src/zep_cloud/thread/raw_client.py index 94237e62..d75c377b 100644 --- a/src/zep_cloud/thread/raw_client.py +++ b/src/zep_cloud/thread/raw_client.py @@ -7,23 +7,27 @@ from ..core.client_wrapper import AsyncClientWrapper, SyncClientWrapper from ..core.http_response import AsyncHttpResponse, HttpResponse from ..core.jsonable_encoder import jsonable_encoder +from ..core.pagination import AsyncPager, SyncPager +from ..core.parse_error import ParsingError 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 from ..errors.not_found_error import NotFoundError -from ..types.add_thread_messages_response import AddThreadMessagesResponse +from ..errors.unauthorized_error import UnauthorizedError +from ..types.add_message import AddMessage +from ..types.add_messages_result import AddMessagesResult from ..types.api_error import ApiError as types_api_error_ApiError +from ..types.episode import Episode +from ..types.episode_page import EpisodePage from ..types.message import Message -from ..types.message_list_response import MessageListResponse -from ..types.role_type import RoleType -from ..types.success_response import SuccessResponse +from ..types.message_page import MessagePage from ..types.thread import Thread from ..types.thread_context_response import ThreadContextResponse -from ..types.thread_list_response import ThreadListResponse +from ..types.thread_delete_result import ThreadDeleteResult +from ..types.thread_page import ThreadPage from ..types.thread_summary import ThreadSummary +from pydantic import ValidationError # this is used as the default value for optional parameters OMIT = typing.cast(typing.Any, ...) @@ -33,74 +37,99 @@ class RawThreadClient: def __init__(self, *, client_wrapper: SyncClientWrapper): self._client_wrapper = client_wrapper - def list_all( + def list( self, *, - page_number: typing.Optional[int] = None, - page_size: typing.Optional[int] = None, + limit: typing.Optional[int] = None, + cursor: typing.Optional[str] = None, order_by: typing.Optional[str] = None, - asc: typing.Optional[bool] = None, + order: typing.Optional[str] = None, + user_uuid: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, - ) -> HttpResponse[ThreadListResponse]: + ) -> SyncPager[Thread, ThreadPage]: """ - Returns all threads. - Parameters ---------- - page_number : typing.Optional[int] - Page number for pagination, starting from 1 + limit : typing.Optional[int] + Page size - page_size : typing.Optional[int] - Number of threads to retrieve per page. + cursor : typing.Optional[str] + Opaque page cursor order_by : typing.Optional[str] - Field to order the results by: created_at, updated_at, user_id, thread_id. + Sort field + + order : typing.Optional[str] + asc or desc - asc : typing.Optional[bool] - Order direction: true for ascending, false for descending. + user_uuid : typing.Optional[str] + Filter by user UUID request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - HttpResponse[ThreadListResponse] - List of threads + SyncPager[Thread, ThreadPage] + OK """ _response = self._client_wrapper.httpx_client.request( "threads", method="GET", params={ - "page_number": page_number, - "page_size": page_size, + "limit": limit, + "cursor": cursor, "order_by": order_by, - "asc": asc, + "order": order, + "user_uuid": user_uuid, }, request_options=request_options, ) try: if 200 <= _response.status_code < 300: - _data = typing.cast( - ThreadListResponse, + _parsed_response = typing.cast( + ThreadPage, parse_obj_as( - type_=ThreadListResponse, # type: ignore + type_=ThreadPage, # type: ignore object_=_response.json(), ), ) - return HttpResponse(response=_response, data=_data) + _items = _parsed_response.items + _parsed_next = _parsed_response.next_cursor + _has_next = _parsed_next is not None and _parsed_next != "" + _get_next = lambda: self.list( + limit=limit, + cursor=_parsed_next, + order_by=order_by, + order=order, + user_uuid=user_uuid, + request_options=request_options, + ) + return SyncPager(has_next=_has_next, items=_items, get_next=_get_next, response=_parsed_response) if _response.status_code == 400: raise BadRequestError( headers=dict(_response.headers), body=typing.cast( - typing.Optional[typing.Any], + types_api_error_ApiError, + parse_obj_as( + type_=types_api_error_ApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 401: + raise UnauthorizedError( + headers=dict(_response.headers), + body=typing.cast( + types_api_error_ApiError, parse_obj_as( - type_=typing.Optional[typing.Any], # type: ignore + type_=types_api_error_ApiError, # type: ignore object_=_response.json(), ), ), ) - if _response.status_code == 500: - raise InternalServerError( + if _response.status_code == 404: + raise NotFoundError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -115,23 +144,30 @@ def list_all( raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response.text ) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response_json ) def create( - self, *, thread_id: str, user_id: str, request_options: typing.Optional[RequestOptions] = None + self, + *, + thread_id: typing.Optional[str] = OMIT, + user_uuid: typing.Optional[str] = OMIT, + idempotency_key: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None, ) -> HttpResponse[Thread]: """ - Start a new thread. - Parameters ---------- - thread_id : str - The unique identifier of the thread. + thread_id : typing.Optional[str] + + user_uuid : typing.Optional[str] - user_id : str - The unique identifier of the user associated with the thread + idempotency_key : typing.Optional[str] request_options : typing.Optional[RequestOptions] Request-specific configuration. @@ -139,17 +175,18 @@ def create( Returns ------- HttpResponse[Thread] - The thread object. + Created """ _response = self._client_wrapper.httpx_client.request( "threads", method="POST", json={ "thread_id": thread_id, - "user_id": user_id, + "user_uuid": user_uuid, }, headers={ "content-type": "application/json", + "Idempotency-Key": str(idempotency_key) if idempotency_key is not None else None, }, request_options=request_options, omit=OMIT, @@ -168,15 +205,26 @@ def create( raise BadRequestError( headers=dict(_response.headers), body=typing.cast( - typing.Optional[typing.Any], + types_api_error_ApiError, parse_obj_as( - type_=typing.Optional[typing.Any], # type: ignore + type_=types_api_error_ApiError, # type: ignore object_=_response.json(), ), ), ) - if _response.status_code == 500: - raise InternalServerError( + if _response.status_code == 401: + raise UnauthorizedError( + 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, @@ -191,44 +239,165 @@ def create( raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response.text ) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response_json ) - def delete( - self, thread_id: str, *, request_options: typing.Optional[RequestOptions] = None - ) -> HttpResponse[SuccessResponse]: + def lookup( + self, + *, + graph_id: typing.Optional[str] = OMIT, + thread_id: typing.Optional[str] = OMIT, + user_id: typing.Optional[str] = OMIT, + idempotency_key: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[Thread]: """ - Deletes a thread. + Parameters + ---------- + graph_id : typing.Optional[str] + + thread_id : typing.Optional[str] + + user_id : typing.Optional[str] + idempotency_key : typing.Optional[str] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[Thread] + OK + """ + _response = self._client_wrapper.httpx_client.request( + "threads/lookup", + method="POST", + json={ + "graph_id": graph_id, + "thread_id": thread_id, + "user_id": user_id, + }, + headers={ + "content-type": "application/json", + "Idempotency-Key": str(idempotency_key) if idempotency_key is not None else None, + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Thread, + parse_obj_as( + type_=Thread, # 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( + types_api_error_ApiError, + parse_obj_as( + type_=types_api_error_ApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 401: + raise UnauthorizedError( + 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(), + ), + ), + ) + _response_json = _response.json() + except JSONDecodeError: + raise core_api_error_ApiError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.text + ) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) + raise core_api_error_ApiError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response_json + ) + + def get(self, thread_uuid: str, *, request_options: typing.Optional[RequestOptions] = None) -> HttpResponse[Thread]: + """ Parameters ---------- - thread_id : str - The ID of the thread for which memory should be deleted. + thread_uuid : str + Thread UUID request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - HttpResponse[SuccessResponse] + HttpResponse[Thread] OK """ _response = self._client_wrapper.httpx_client.request( - f"threads/{jsonable_encoder(thread_id)}", - method="DELETE", + f"threads/{jsonable_encoder(thread_uuid)}", + method="GET", request_options=request_options, ) try: if 200 <= _response.status_code < 300: _data = typing.cast( - SuccessResponse, + Thread, parse_obj_as( - type_=SuccessResponse, # type: ignore + type_=Thread, # 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( + types_api_error_ApiError, + parse_obj_as( + type_=types_api_error_ApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 401: + raise UnauthorizedError( + 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), @@ -240,8 +409,84 @@ def delete( ), ), ) - if _response.status_code == 500: - raise InternalServerError( + _response_json = _response.json() + except JSONDecodeError: + raise core_api_error_ApiError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.text + ) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) + raise core_api_error_ApiError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response_json + ) + + def delete( + self, + thread_uuid: str, + *, + idempotency_key: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[ThreadDeleteResult]: + """ + Parameters + ---------- + thread_uuid : str + Thread UUID + + idempotency_key : typing.Optional[str] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[ThreadDeleteResult] + Accepted + """ + _response = self._client_wrapper.httpx_client.request( + f"threads/{jsonable_encoder(thread_uuid)}", + method="DELETE", + headers={ + "Idempotency-Key": str(idempotency_key) if idempotency_key is not None else None, + }, + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + ThreadDeleteResult, + parse_obj_as( + type_=ThreadDeleteResult, # 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( + types_api_error_ApiError, + parse_obj_as( + type_=types_api_error_ApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 401: + raise UnauthorizedError( + 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, @@ -256,27 +501,29 @@ def delete( raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response.text ) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response_json ) - def get_user_context( + def get_context( self, - thread_id: str, + thread_uuid: str, *, - template_id: typing.Optional[str] = None, + template_uuid: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, ) -> HttpResponse[ThreadContextResponse]: """ - Returns most relevant context from the user graph (including memory from any/all past threads) based on the content of the past few messages of the given thread. - Parameters ---------- - thread_id : str - The ID of the current thread (for which context is being retrieved). + thread_uuid : str + Thread UUID - template_id : typing.Optional[str] - Optional template ID to use for custom context rendering. + template_uuid : typing.Optional[str] + Context template UUID request_options : typing.Optional[RequestOptions] Request-specific configuration. @@ -287,10 +534,10 @@ def get_user_context( OK """ _response = self._client_wrapper.httpx_client.request( - f"threads/{jsonable_encoder(thread_id)}/context", + f"threads/{jsonable_encoder(thread_uuid)}/context", method="GET", params={ - "template_id": template_id, + "template_uuid": template_uuid, }, request_options=request_options, ) @@ -304,8 +551,8 @@ def get_user_context( ), ) return HttpResponse(response=_response, data=_data) - if _response.status_code == 404: - raise NotFoundError( + if _response.status_code == 400: + raise BadRequestError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -315,8 +562,19 @@ def get_user_context( ), ), ) - if _response.status_code == 500: - raise InternalServerError( + if _response.status_code == 401: + raise UnauthorizedError( + 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, @@ -331,64 +589,92 @@ def get_user_context( raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response.text ) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response_json ) - def get( + def list_episodes( self, - thread_id: str, + thread_uuid: str, *, limit: typing.Optional[int] = None, - cursor: typing.Optional[int] = None, - lastn: typing.Optional[int] = None, + cursor: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, - ) -> HttpResponse[MessageListResponse]: + ) -> SyncPager[Episode, EpisodePage]: """ - Returns messages for a thread. - Parameters ---------- - thread_id : str - Thread ID + thread_uuid : str + Thread UUID limit : typing.Optional[int] - Limit the number of results returned - - cursor : typing.Optional[int] - Cursor for pagination + Page size - lastn : typing.Optional[int] - Number of most recent messages to return (overrides limit and cursor) + cursor : typing.Optional[str] + Opaque page cursor request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - HttpResponse[MessageListResponse] + SyncPager[Episode, EpisodePage] OK """ _response = self._client_wrapper.httpx_client.request( - f"threads/{jsonable_encoder(thread_id)}/messages", + f"threads/{jsonable_encoder(thread_uuid)}/episodes", method="GET", params={ "limit": limit, "cursor": cursor, - "lastn": lastn, }, request_options=request_options, ) try: if 200 <= _response.status_code < 300: - _data = typing.cast( - MessageListResponse, + _parsed_response = typing.cast( + EpisodePage, parse_obj_as( - type_=MessageListResponse, # type: ignore + type_=EpisodePage, # type: ignore object_=_response.json(), ), ) - return HttpResponse(response=_response, data=_data) + _items = _parsed_response.items + _parsed_next = _parsed_response.next_cursor + _has_next = _parsed_next is not None and _parsed_next != "" + _get_next = lambda: self.list_episodes( + thread_uuid, + limit=limit, + cursor=_parsed_next, + request_options=request_options, + ) + return SyncPager(has_next=_has_next, items=_items, get_next=_get_next, response=_parsed_response) + if _response.status_code == 400: + raise BadRequestError( + 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 == 401: + raise UnauthorizedError( + 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), @@ -400,8 +686,99 @@ def get( ), ), ) - if _response.status_code == 500: - raise InternalServerError( + _response_json = _response.json() + except JSONDecodeError: + raise core_api_error_ApiError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.text + ) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) + raise core_api_error_ApiError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response_json + ) + + def list_messages( + self, + thread_uuid: str, + *, + limit: typing.Optional[int] = None, + cursor: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> SyncPager[Message, MessagePage]: + """ + Parameters + ---------- + thread_uuid : str + Thread UUID + + limit : typing.Optional[int] + Page size + + cursor : typing.Optional[str] + Opaque page cursor + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + SyncPager[Message, MessagePage] + OK + """ + _response = self._client_wrapper.httpx_client.request( + f"threads/{jsonable_encoder(thread_uuid)}/messages", + method="GET", + params={ + "limit": limit, + "cursor": cursor, + }, + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _parsed_response = typing.cast( + MessagePage, + parse_obj_as( + type_=MessagePage, # type: ignore + object_=_response.json(), + ), + ) + _items = _parsed_response.items + _parsed_next = _parsed_response.next_cursor + _has_next = _parsed_next is not None and _parsed_next != "" + _get_next = lambda: self.list_messages( + thread_uuid, + limit=limit, + cursor=_parsed_next, + request_options=request_options, + ) + return SyncPager(has_next=_has_next, items=_items, get_next=_get_next, response=_parsed_response) + if _response.status_code == 400: + raise BadRequestError( + 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 == 401: + raise UnauthorizedError( + 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, @@ -416,76 +793,101 @@ def get( raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response.text ) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response_json ) def add_messages( self, - thread_id: str, + thread_uuid: str, *, - messages: typing.Sequence[Message], - ignore_roles: typing.Optional[typing.Sequence[RoleType]] = OMIT, + ignore_roles: typing.Optional[typing.Sequence[str]] = OMIT, + messages: typing.Optional[typing.Sequence[AddMessage]] = OMIT, return_context: typing.Optional[bool] = OMIT, strict_ontology: typing.Optional[bool] = OMIT, + idempotency_key: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, - ) -> HttpResponse[AddThreadMessagesResponse]: + ) -> HttpResponse[AddMessagesResult]: """ - Add messages to a thread. - Parameters ---------- - thread_id : str - The ID of the thread to which messages should be added. + thread_uuid : str + Thread UUID - messages : typing.Sequence[Message] - A list of message objects, where each message contains a role and content. + ignore_roles : typing.Optional[typing.Sequence[str]] - ignore_roles : typing.Optional[typing.Sequence[RoleType]] - Optional list of role types to ignore when adding messages to graph memory. - The message itself will still be added, retained and used as context for messages - that are added to a user's graph. + messages : typing.Optional[typing.Sequence[AddMessage]] return_context : typing.Optional[bool] - Optionally return context block relevant to the most recent messages. strict_ontology : typing.Optional[bool] - When true, prevents extraction of generic Entity nodes that do not match the configured ontology. + + idempotency_key : typing.Optional[str] request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - HttpResponse[AddThreadMessagesResponse] - An object, optionally containing user context retrieved for the last thread message + HttpResponse[AddMessagesResult] + Accepted """ _response = self._client_wrapper.httpx_client.request( - f"threads/{jsonable_encoder(thread_id)}/messages", + f"threads/{jsonable_encoder(thread_uuid)}/messages", method="POST", json={ "ignore_roles": ignore_roles, "messages": convert_and_respect_annotation_metadata( - object_=messages, annotation=typing.Sequence[Message], direction="write" + object_=messages, annotation=typing.Sequence[AddMessage], direction="write" ), "return_context": return_context, "strict_ontology": strict_ontology, }, + headers={ + "content-type": "application/json", + "Idempotency-Key": str(idempotency_key) if idempotency_key is not None else None, + }, request_options=request_options, omit=OMIT, ) try: if 200 <= _response.status_code < 300: _data = typing.cast( - AddThreadMessagesResponse, + AddMessagesResult, parse_obj_as( - type_=AddThreadMessagesResponse, # type: ignore + type_=AddMessagesResult, # type: ignore object_=_response.json(), ), ) return HttpResponse(response=_response, data=_data) - if _response.status_code == 500: - raise InternalServerError( + if _response.status_code == 400: + raise BadRequestError( + 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 == 401: + raise UnauthorizedError( + 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, @@ -500,78 +902,193 @@ def add_messages( raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response.text ) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response_json ) - def add_messages_batch( + def get_summary( + self, thread_uuid: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[ThreadSummary]: + """ + Parameters + ---------- + thread_uuid : str + Thread UUID + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[ThreadSummary] + OK + """ + _response = self._client_wrapper.httpx_client.request( + f"threads/{jsonable_encoder(thread_uuid)}/summary", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + ThreadSummary, + parse_obj_as( + type_=ThreadSummary, # 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( + types_api_error_ApiError, + parse_obj_as( + type_=types_api_error_ApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 401: + raise UnauthorizedError( + 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(), + ), + ), + ) + _response_json = _response.json() + except JSONDecodeError: + raise core_api_error_ApiError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.text + ) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) + raise core_api_error_ApiError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response_json + ) + + +class AsyncRawThreadClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._client_wrapper = client_wrapper + + async def list( self, - thread_id: str, *, - messages: typing.Sequence[Message], - ignore_roles: typing.Optional[typing.Sequence[RoleType]] = OMIT, - return_context: typing.Optional[bool] = OMIT, - strict_ontology: typing.Optional[bool] = OMIT, + limit: typing.Optional[int] = None, + cursor: typing.Optional[str] = None, + order_by: typing.Optional[str] = None, + order: typing.Optional[str] = None, + user_uuid: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, - ) -> HttpResponse[AddThreadMessagesResponse]: + ) -> AsyncPager[Thread, ThreadPage]: """ - Deprecated. Use the [Batch API](/adding-batch-data) (`client.batch.*` with `type: "thread_message"`) instead. - - Adds messages to a thread in batch mode, processing messages concurrently. - Parameters ---------- - thread_id : str - The ID of the thread to which messages should be added. + limit : typing.Optional[int] + Page size - messages : typing.Sequence[Message] - A list of message objects, where each message contains a role and content. + cursor : typing.Optional[str] + Opaque page cursor - ignore_roles : typing.Optional[typing.Sequence[RoleType]] - Optional list of role types to ignore when adding messages to graph memory. - The message itself will still be added, retained and used as context for messages - that are added to a user's graph. + order_by : typing.Optional[str] + Sort field - return_context : typing.Optional[bool] - Optionally return context block relevant to the most recent messages. + order : typing.Optional[str] + asc or desc - strict_ontology : typing.Optional[bool] - When true, prevents extraction of generic Entity nodes that do not match the configured ontology. + user_uuid : typing.Optional[str] + Filter by user UUID request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - HttpResponse[AddThreadMessagesResponse] - An object, optionally containing user context retrieved for the last thread message + AsyncPager[Thread, ThreadPage] + OK """ - _response = self._client_wrapper.httpx_client.request( - f"threads/{jsonable_encoder(thread_id)}/messages-batch", - method="POST", - json={ - "ignore_roles": ignore_roles, - "messages": convert_and_respect_annotation_metadata( - object_=messages, annotation=typing.Sequence[Message], direction="write" - ), - "return_context": return_context, - "strict_ontology": strict_ontology, + _response = await self._client_wrapper.httpx_client.request( + "threads", + method="GET", + params={ + "limit": limit, + "cursor": cursor, + "order_by": order_by, + "order": order, + "user_uuid": user_uuid, }, request_options=request_options, - omit=OMIT, ) try: if 200 <= _response.status_code < 300: - _data = typing.cast( - AddThreadMessagesResponse, + _parsed_response = typing.cast( + ThreadPage, parse_obj_as( - type_=AddThreadMessagesResponse, # type: ignore + type_=ThreadPage, # type: ignore object_=_response.json(), ), ) - return HttpResponse(response=_response, data=_data) - if _response.status_code == 500: - raise InternalServerError( + _items = _parsed_response.items + _parsed_next = _parsed_response.next_cursor + _has_next = _parsed_next is not None and _parsed_next != "" + + async def _get_next(): + return await self.list( + limit=limit, + cursor=_parsed_next, + order_by=order_by, + order=order, + user_uuid=user_uuid, + request_options=request_options, + ) + + return AsyncPager(has_next=_has_next, items=_items, get_next=_get_next, response=_parsed_response) + if _response.status_code == 400: + raise BadRequestError( + 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 == 401: + raise UnauthorizedError( + 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, @@ -586,46 +1103,65 @@ def add_messages_batch( raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response.text ) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response_json ) - def get_summary( - self, thread_id: str, *, request_options: typing.Optional[RequestOptions] = None - ) -> HttpResponse[ThreadSummary]: + async def create( + self, + *, + thread_id: typing.Optional[str] = OMIT, + user_uuid: typing.Optional[str] = OMIT, + idempotency_key: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[Thread]: """ - Returns the incremental summary generated from messages in the thread. Returns 404 if no summary exists for the thread. - Parameters ---------- - thread_id : str - The thread ID. + thread_id : typing.Optional[str] + + user_uuid : typing.Optional[str] + + idempotency_key : typing.Optional[str] request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - HttpResponse[ThreadSummary] - OK + AsyncHttpResponse[Thread] + Created """ - _response = self._client_wrapper.httpx_client.request( - f"threads/{jsonable_encoder(thread_id)}/summary", - method="GET", + _response = await self._client_wrapper.httpx_client.request( + "threads", + method="POST", + json={ + "thread_id": thread_id, + "user_uuid": user_uuid, + }, + headers={ + "content-type": "application/json", + "Idempotency-Key": str(idempotency_key) if idempotency_key is not None else None, + }, request_options=request_options, + omit=OMIT, ) try: if 200 <= _response.status_code < 300: _data = typing.cast( - ThreadSummary, + Thread, parse_obj_as( - type_=ThreadSummary, # type: ignore + type_=Thread, # type: ignore object_=_response.json(), ), ) - return HttpResponse(response=_response, data=_data) - if _response.status_code == 403: - raise ForbiddenError( + return AsyncHttpResponse(response=_response, data=_data) + if _response.status_code == 400: + raise BadRequestError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -635,8 +1171,8 @@ def get_summary( ), ), ) - if _response.status_code == 404: - raise NotFoundError( + if _response.status_code == 401: + raise UnauthorizedError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -646,8 +1182,8 @@ def get_summary( ), ), ) - if _response.status_code == 500: - raise InternalServerError( + if _response.status_code == 404: + raise NotFoundError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -662,66 +1198,63 @@ def get_summary( raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response.text ) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response_json ) - -class AsyncRawThreadClient: - def __init__(self, *, client_wrapper: AsyncClientWrapper): - self._client_wrapper = client_wrapper - - async def list_all( + async def lookup( self, *, - page_number: typing.Optional[int] = None, - page_size: typing.Optional[int] = None, - order_by: typing.Optional[str] = None, - asc: typing.Optional[bool] = None, + graph_id: typing.Optional[str] = OMIT, + thread_id: typing.Optional[str] = OMIT, + user_id: typing.Optional[str] = OMIT, + idempotency_key: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, - ) -> AsyncHttpResponse[ThreadListResponse]: + ) -> AsyncHttpResponse[Thread]: """ - Returns all threads. - Parameters ---------- - page_number : typing.Optional[int] - Page number for pagination, starting from 1 + graph_id : typing.Optional[str] - page_size : typing.Optional[int] - Number of threads to retrieve per page. + thread_id : typing.Optional[str] - order_by : typing.Optional[str] - Field to order the results by: created_at, updated_at, user_id, thread_id. + user_id : typing.Optional[str] - asc : typing.Optional[bool] - Order direction: true for ascending, false for descending. + idempotency_key : typing.Optional[str] request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - AsyncHttpResponse[ThreadListResponse] - List of threads + AsyncHttpResponse[Thread] + OK """ _response = await self._client_wrapper.httpx_client.request( - "threads", - method="GET", - params={ - "page_number": page_number, - "page_size": page_size, - "order_by": order_by, - "asc": asc, + "threads/lookup", + method="POST", + json={ + "graph_id": graph_id, + "thread_id": thread_id, + "user_id": user_id, + }, + headers={ + "content-type": "application/json", + "Idempotency-Key": str(idempotency_key) if idempotency_key is not None else None, }, request_options=request_options, + omit=OMIT, ) try: if 200 <= _response.status_code < 300: _data = typing.cast( - ThreadListResponse, + Thread, parse_obj_as( - type_=ThreadListResponse, # type: ignore + type_=Thread, # type: ignore object_=_response.json(), ), ) @@ -730,15 +1263,26 @@ async def list_all( raise BadRequestError( headers=dict(_response.headers), body=typing.cast( - typing.Optional[typing.Any], + types_api_error_ApiError, + parse_obj_as( + type_=types_api_error_ApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 401: + raise UnauthorizedError( + headers=dict(_response.headers), + body=typing.cast( + types_api_error_ApiError, parse_obj_as( - type_=typing.Optional[typing.Any], # type: ignore + type_=types_api_error_ApiError, # type: ignore object_=_response.json(), ), ), ) - if _response.status_code == 500: - raise InternalServerError( + if _response.status_code == 404: + raise NotFoundError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -753,23 +1297,22 @@ async def list_all( raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response.text ) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response_json ) - async def create( - self, *, thread_id: str, user_id: str, request_options: typing.Optional[RequestOptions] = None + async def get( + self, thread_uuid: str, *, request_options: typing.Optional[RequestOptions] = None ) -> AsyncHttpResponse[Thread]: """ - Start a new thread. - Parameters ---------- - thread_id : str - The unique identifier of the thread. - - user_id : str - The unique identifier of the user associated with the thread + thread_uuid : str + Thread UUID request_options : typing.Optional[RequestOptions] Request-specific configuration. @@ -777,20 +1320,12 @@ async def create( Returns ------- AsyncHttpResponse[Thread] - The thread object. + OK """ _response = await self._client_wrapper.httpx_client.request( - "threads", - method="POST", - json={ - "thread_id": thread_id, - "user_id": user_id, - }, - headers={ - "content-type": "application/json", - }, + f"threads/{jsonable_encoder(thread_uuid)}", + method="GET", request_options=request_options, - omit=OMIT, ) try: if 200 <= _response.status_code < 300: @@ -806,15 +1341,26 @@ async def create( raise BadRequestError( headers=dict(_response.headers), body=typing.cast( - typing.Optional[typing.Any], + types_api_error_ApiError, + parse_obj_as( + type_=types_api_error_ApiError, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 401: + raise UnauthorizedError( + headers=dict(_response.headers), + body=typing.cast( + types_api_error_ApiError, parse_obj_as( - type_=typing.Optional[typing.Any], # type: ignore + type_=types_api_error_ApiError, # type: ignore object_=_response.json(), ), ), ) - if _response.status_code == 500: - raise InternalServerError( + if _response.status_code == 404: + raise NotFoundError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -829,46 +1375,68 @@ async def create( raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response.text ) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response_json ) async def delete( - self, thread_id: str, *, request_options: typing.Optional[RequestOptions] = None - ) -> AsyncHttpResponse[SuccessResponse]: + self, + thread_uuid: str, + *, + idempotency_key: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[ThreadDeleteResult]: """ - Deletes a thread. - Parameters ---------- - thread_id : str - The ID of the thread for which memory should be deleted. + thread_uuid : str + Thread UUID + + idempotency_key : typing.Optional[str] request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - AsyncHttpResponse[SuccessResponse] - OK + AsyncHttpResponse[ThreadDeleteResult] + Accepted """ _response = await self._client_wrapper.httpx_client.request( - f"threads/{jsonable_encoder(thread_id)}", + f"threads/{jsonable_encoder(thread_uuid)}", method="DELETE", + headers={ + "Idempotency-Key": str(idempotency_key) if idempotency_key is not None else None, + }, request_options=request_options, ) try: if 200 <= _response.status_code < 300: _data = typing.cast( - SuccessResponse, + ThreadDeleteResult, parse_obj_as( - type_=SuccessResponse, # type: ignore + type_=ThreadDeleteResult, # type: ignore object_=_response.json(), ), ) return AsyncHttpResponse(response=_response, data=_data) - if _response.status_code == 404: - raise NotFoundError( + if _response.status_code == 400: + raise BadRequestError( + 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 == 401: + raise UnauthorizedError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -878,8 +1446,8 @@ async def delete( ), ), ) - if _response.status_code == 500: - raise InternalServerError( + if _response.status_code == 404: + raise NotFoundError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -894,27 +1462,29 @@ async def delete( raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response.text ) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response_json ) - async def get_user_context( + async def get_context( self, - thread_id: str, + thread_uuid: str, *, - template_id: typing.Optional[str] = None, + template_uuid: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, ) -> AsyncHttpResponse[ThreadContextResponse]: """ - Returns most relevant context from the user graph (including memory from any/all past threads) based on the content of the past few messages of the given thread. - Parameters ---------- - thread_id : str - The ID of the current thread (for which context is being retrieved). + thread_uuid : str + Thread UUID - template_id : typing.Optional[str] - Optional template ID to use for custom context rendering. + template_uuid : typing.Optional[str] + Context template UUID request_options : typing.Optional[RequestOptions] Request-specific configuration. @@ -925,10 +1495,10 @@ async def get_user_context( OK """ _response = await self._client_wrapper.httpx_client.request( - f"threads/{jsonable_encoder(thread_id)}/context", + f"threads/{jsonable_encoder(thread_uuid)}/context", method="GET", params={ - "template_id": template_id, + "template_uuid": template_uuid, }, request_options=request_options, ) @@ -942,8 +1512,19 @@ async def get_user_context( ), ) return AsyncHttpResponse(response=_response, data=_data) - if _response.status_code == 404: - raise NotFoundError( + if _response.status_code == 400: + raise BadRequestError( + 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 == 401: + raise UnauthorizedError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -953,8 +1534,8 @@ async def get_user_context( ), ), ) - if _response.status_code == 500: - raise InternalServerError( + if _response.status_code == 404: + raise NotFoundError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -969,66 +1550,86 @@ async def get_user_context( raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response.text ) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response_json ) - async def get( + async def list_episodes( self, - thread_id: str, + thread_uuid: str, *, limit: typing.Optional[int] = None, - cursor: typing.Optional[int] = None, - lastn: typing.Optional[int] = None, + cursor: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, - ) -> AsyncHttpResponse[MessageListResponse]: + ) -> AsyncPager[Episode, EpisodePage]: """ - Returns messages for a thread. - Parameters ---------- - thread_id : str - Thread ID + thread_uuid : str + Thread UUID limit : typing.Optional[int] - Limit the number of results returned - - cursor : typing.Optional[int] - Cursor for pagination + Page size - lastn : typing.Optional[int] - Number of most recent messages to return (overrides limit and cursor) + cursor : typing.Optional[str] + Opaque page cursor request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - AsyncHttpResponse[MessageListResponse] + AsyncPager[Episode, EpisodePage] OK """ _response = await self._client_wrapper.httpx_client.request( - f"threads/{jsonable_encoder(thread_id)}/messages", + f"threads/{jsonable_encoder(thread_uuid)}/episodes", method="GET", params={ "limit": limit, "cursor": cursor, - "lastn": lastn, }, request_options=request_options, ) try: if 200 <= _response.status_code < 300: - _data = typing.cast( - MessageListResponse, + _parsed_response = typing.cast( + EpisodePage, parse_obj_as( - type_=MessageListResponse, # type: ignore + type_=EpisodePage, # type: ignore object_=_response.json(), ), ) - return AsyncHttpResponse(response=_response, data=_data) - if _response.status_code == 404: - raise NotFoundError( + _items = _parsed_response.items + _parsed_next = _parsed_response.next_cursor + _has_next = _parsed_next is not None and _parsed_next != "" + + async def _get_next(): + return await self.list_episodes( + thread_uuid, + limit=limit, + cursor=_parsed_next, + request_options=request_options, + ) + + return AsyncPager(has_next=_has_next, items=_items, get_next=_get_next, response=_parsed_response) + if _response.status_code == 400: + raise BadRequestError( + 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 == 401: + raise UnauthorizedError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -1038,8 +1639,8 @@ async def get( ), ), ) - if _response.status_code == 500: - raise InternalServerError( + if _response.status_code == 404: + raise NotFoundError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -1054,76 +1655,97 @@ async def get( raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response.text ) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response_json ) - async def add_messages( + async def list_messages( self, - thread_id: str, + thread_uuid: str, *, - messages: typing.Sequence[Message], - ignore_roles: typing.Optional[typing.Sequence[RoleType]] = OMIT, - return_context: typing.Optional[bool] = OMIT, - strict_ontology: typing.Optional[bool] = OMIT, + limit: typing.Optional[int] = None, + cursor: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, - ) -> AsyncHttpResponse[AddThreadMessagesResponse]: + ) -> AsyncPager[Message, MessagePage]: """ - Add messages to a thread. - Parameters ---------- - thread_id : str - The ID of the thread to which messages should be added. - - messages : typing.Sequence[Message] - A list of message objects, where each message contains a role and content. + thread_uuid : str + Thread UUID - ignore_roles : typing.Optional[typing.Sequence[RoleType]] - Optional list of role types to ignore when adding messages to graph memory. - The message itself will still be added, retained and used as context for messages - that are added to a user's graph. - - return_context : typing.Optional[bool] - Optionally return context block relevant to the most recent messages. + limit : typing.Optional[int] + Page size - strict_ontology : typing.Optional[bool] - When true, prevents extraction of generic Entity nodes that do not match the configured ontology. + cursor : typing.Optional[str] + Opaque page cursor request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - AsyncHttpResponse[AddThreadMessagesResponse] - An object, optionally containing user context retrieved for the last thread message + AsyncPager[Message, MessagePage] + OK """ _response = await self._client_wrapper.httpx_client.request( - f"threads/{jsonable_encoder(thread_id)}/messages", - method="POST", - json={ - "ignore_roles": ignore_roles, - "messages": convert_and_respect_annotation_metadata( - object_=messages, annotation=typing.Sequence[Message], direction="write" - ), - "return_context": return_context, - "strict_ontology": strict_ontology, + f"threads/{jsonable_encoder(thread_uuid)}/messages", + method="GET", + params={ + "limit": limit, + "cursor": cursor, }, request_options=request_options, - omit=OMIT, ) try: if 200 <= _response.status_code < 300: - _data = typing.cast( - AddThreadMessagesResponse, + _parsed_response = typing.cast( + MessagePage, parse_obj_as( - type_=AddThreadMessagesResponse, # type: ignore + type_=MessagePage, # type: ignore object_=_response.json(), ), ) - return AsyncHttpResponse(response=_response, data=_data) - if _response.status_code == 500: - raise InternalServerError( + _items = _parsed_response.items + _parsed_next = _parsed_response.next_cursor + _has_next = _parsed_next is not None and _parsed_next != "" + + async def _get_next(): + return await self.list_messages( + thread_uuid, + limit=limit, + cursor=_parsed_next, + request_options=request_options, + ) + + return AsyncPager(has_next=_has_next, items=_items, get_next=_get_next, response=_parsed_response) + if _response.status_code == 400: + raise BadRequestError( + 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 == 401: + raise UnauthorizedError( + 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, @@ -1138,78 +1760,101 @@ async def add_messages( raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response.text ) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response_json ) - async def add_messages_batch( + async def add_messages( self, - thread_id: str, + thread_uuid: str, *, - messages: typing.Sequence[Message], - ignore_roles: typing.Optional[typing.Sequence[RoleType]] = OMIT, + ignore_roles: typing.Optional[typing.Sequence[str]] = OMIT, + messages: typing.Optional[typing.Sequence[AddMessage]] = OMIT, return_context: typing.Optional[bool] = OMIT, strict_ontology: typing.Optional[bool] = OMIT, + idempotency_key: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, - ) -> AsyncHttpResponse[AddThreadMessagesResponse]: + ) -> AsyncHttpResponse[AddMessagesResult]: """ - Deprecated. Use the [Batch API](/adding-batch-data) (`client.batch.*` with `type: "thread_message"`) instead. - - Adds messages to a thread in batch mode, processing messages concurrently. - Parameters ---------- - thread_id : str - The ID of the thread to which messages should be added. + thread_uuid : str + Thread UUID - messages : typing.Sequence[Message] - A list of message objects, where each message contains a role and content. + ignore_roles : typing.Optional[typing.Sequence[str]] - ignore_roles : typing.Optional[typing.Sequence[RoleType]] - Optional list of role types to ignore when adding messages to graph memory. - The message itself will still be added, retained and used as context for messages - that are added to a user's graph. + messages : typing.Optional[typing.Sequence[AddMessage]] return_context : typing.Optional[bool] - Optionally return context block relevant to the most recent messages. strict_ontology : typing.Optional[bool] - When true, prevents extraction of generic Entity nodes that do not match the configured ontology. + + idempotency_key : typing.Optional[str] request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - AsyncHttpResponse[AddThreadMessagesResponse] - An object, optionally containing user context retrieved for the last thread message + AsyncHttpResponse[AddMessagesResult] + Accepted """ _response = await self._client_wrapper.httpx_client.request( - f"threads/{jsonable_encoder(thread_id)}/messages-batch", + f"threads/{jsonable_encoder(thread_uuid)}/messages", method="POST", json={ "ignore_roles": ignore_roles, "messages": convert_and_respect_annotation_metadata( - object_=messages, annotation=typing.Sequence[Message], direction="write" + object_=messages, annotation=typing.Sequence[AddMessage], direction="write" ), "return_context": return_context, "strict_ontology": strict_ontology, }, + headers={ + "content-type": "application/json", + "Idempotency-Key": str(idempotency_key) if idempotency_key is not None else None, + }, request_options=request_options, omit=OMIT, ) try: if 200 <= _response.status_code < 300: _data = typing.cast( - AddThreadMessagesResponse, + AddMessagesResult, parse_obj_as( - type_=AddThreadMessagesResponse, # type: ignore + type_=AddMessagesResult, # type: ignore object_=_response.json(), ), ) return AsyncHttpResponse(response=_response, data=_data) - if _response.status_code == 500: - raise InternalServerError( + if _response.status_code == 400: + raise BadRequestError( + 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 == 401: + raise UnauthorizedError( + 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, @@ -1224,20 +1869,22 @@ async def add_messages_batch( raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response.text ) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response_json ) async def get_summary( - self, thread_id: str, *, request_options: typing.Optional[RequestOptions] = None + self, thread_uuid: str, *, request_options: typing.Optional[RequestOptions] = None ) -> AsyncHttpResponse[ThreadSummary]: """ - Returns the incremental summary generated from messages in the thread. Returns 404 if no summary exists for the thread. - Parameters ---------- - thread_id : str - The thread ID. + thread_uuid : str + Thread UUID request_options : typing.Optional[RequestOptions] Request-specific configuration. @@ -1248,7 +1895,7 @@ async def get_summary( OK """ _response = await self._client_wrapper.httpx_client.request( - f"threads/{jsonable_encoder(thread_id)}/summary", + f"threads/{jsonable_encoder(thread_uuid)}/summary", method="GET", request_options=request_options, ) @@ -1262,8 +1909,8 @@ async def get_summary( ), ) return AsyncHttpResponse(response=_response, data=_data) - if _response.status_code == 403: - raise ForbiddenError( + if _response.status_code == 400: + raise BadRequestError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -1273,8 +1920,8 @@ async def get_summary( ), ), ) - if _response.status_code == 404: - raise NotFoundError( + if _response.status_code == 401: + raise UnauthorizedError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -1284,8 +1931,8 @@ async def get_summary( ), ), ) - if _response.status_code == 500: - raise InternalServerError( + if _response.status_code == 404: + raise NotFoundError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -1300,6 +1947,10 @@ async def get_summary( raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response.text ) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response_json ) diff --git a/src/zep_cloud/types/__init__.py b/src/zep_cloud/types/__init__.py index 535b0a0d..b86be2d2 100644 --- a/src/zep_cloud/types/__init__.py +++ b/src/zep_cloud/types/__init__.py @@ -2,206 +2,250 @@ # isort: skip_file -from .add_node_item import AddNodeItem -from .add_nodes_response import AddNodesResponse -from .add_thread_messages_request import AddThreadMessagesRequest -from .add_thread_messages_response import AddThreadMessagesResponse -from .add_triple_response import AddTripleResponse -from .added_node import AddedNode -from .api_error import ApiError -from .apidata_batch_add_item_role import ApidataBatchAddItemRole -from .apidata_batch_add_item_type import ApidataBatchAddItemType -from .attached_policy_sets_response import AttachedPolicySetsResponse -from .batch_add_item import BatchAddItem -from .batch_item_detail import BatchItemDetail -from .batch_item_kind import BatchItemKind -from .batch_item_list_response import BatchItemListResponse -from .batch_item_status import BatchItemStatus -from .batch_list_response import BatchListResponse -from .batch_progress import BatchProgress -from .batch_status import BatchStatus -from .batch_summary import BatchSummary -from .clone_graph_response import CloneGraphResponse -from .cluster_detect_config import ClusterDetectConfig -from .co_occurrence_detect_config import CoOccurrenceDetectConfig -from .comparison_operator import ComparisonOperator -from .context_template_response import ContextTemplateResponse -from .custom_instruction import CustomInstruction -from .date_filter import DateFilter -from .derived_node import DerivedNode -from .detect_config import DetectConfig -from .detect_patterns_response import DetectPatternsResponse -from .edge_type import EdgeType -from .entity_edge import EntityEdge -from .entity_edge_source_target import EntityEdgeSourceTarget -from .entity_node import EntityNode -from .entity_property import EntityProperty -from .entity_property_type import EntityPropertyType -from .entity_type import EntityType -from .entity_type_response import EntityTypeResponse -from .episode import Episode -from .episode_data import EpisodeData -from .episode_mentions import EpisodeMentions -from .episode_metadata_filter import EpisodeMetadataFilter -from .episode_response import EpisodeResponse -from .get_task_response import GetTaskResponse -from .graph import Graph -from .graph_data_type import GraphDataType -from .graph_edges_request import GraphEdgesRequest -from .graph_episode_list_request import GraphEpisodeListRequest -from .graph_list_response import GraphListResponse -from .graph_node_neighbor import GraphNodeNeighbor -from .graph_nodes_request import GraphNodesRequest -from .graph_observations_request import GraphObservationsRequest -from .graph_search_response_metadata import GraphSearchResponseMetadata -from .graph_search_results import GraphSearchResults -from .graph_search_scope import GraphSearchScope -from .graph_subgraph_response import GraphSubgraphResponse -from .graph_thread_summaries_request import GraphThreadSummariesRequest -from .graphiti_metadata_filter_group_type import GraphitiMetadataFilterGroupType -from .graphiti_saga_node import GraphitiSagaNode -from .hub_detect_config import HubDetectConfig -from .list_context_templates_response import ListContextTemplatesResponse -from .list_custom_instructions_response import ListCustomInstructionsResponse -from .list_user_instructions_response import ListUserInstructionsResponse -from .message import Message -from .message_list_response import MessageListResponse -from .metadata_filter_group import MetadataFilterGroup -from .mutate_user_group_members_request import MutateUserGroupMembersRequest -from .observation_steering_config import ObservationSteeringConfig -from .observation_type import ObservationType -from .path_detect_config import PathDetectConfig -from .pattern_metadata import PatternMetadata -from .pattern_result import PatternResult -from .pattern_seeds import PatternSeeds -from .policy_set_summary_response import PolicySetSummaryResponse -from .project_info import ProjectInfo -from .project_info_response import ProjectInfoResponse -from .property_filter import PropertyFilter -from .recency_weight import RecencyWeight -from .relationship_detect_config import RelationshipDetectConfig -from .reranker import Reranker -from .role_type import RoleType -from .search_filters import SearchFilters -from .success_response import SuccessResponse -from .task_error_response import TaskErrorResponse -from .task_progress import TaskProgress -from .thread import Thread -from .thread_context_response import ThreadContextResponse -from .thread_list_response import ThreadListResponse -from .thread_summary import ThreadSummary -from .user import User -from .user_group import UserGroup -from .user_group_kind import UserGroupKind -from .user_group_list_response import UserGroupListResponse -from .user_group_members_response import UserGroupMembersResponse -from .user_group_membership_response import UserGroupMembershipResponse -from .user_group_quota import UserGroupQuota -from .user_group_response import UserGroupResponse -from .user_instruction import UserInstruction -from .user_list_response import UserListResponse -from .user_membership_groups_response import UserMembershipGroupsResponse -from .user_node_response import UserNodeResponse +import typing +from importlib import import_module + +if typing.TYPE_CHECKING: + from .add_edge_result import AddEdgeResult + from .add_episode_result import AddEpisodeResult + from .add_message import AddMessage + from .add_messages_result import AddMessagesResult + from .add_nodes_result import AddNodesResult + from .added_edge import AddedEdge + from .added_node import AddedNode + from .api_error import ApiError + from .artifact_list_request import ArtifactListRequest + from .async_result import AsyncResult + from .batch import Batch + from .batch_item import BatchItem + from .batch_item_page import BatchItemPage + from .batch_items_response import BatchItemsResponse + from .batch_page import BatchPage + from .clone_graph_result import CloneGraphResult + from .context_edge import ContextEdge + from .context_episode import ContextEpisode + from .context_node import ContextNode + from .context_observation import ContextObservation + from .context_results import ContextResults + from .context_template import ContextTemplate + from .context_template_page import ContextTemplatePage + from .context_thread_summary import ContextThreadSummary + from .create_context_template_request import CreateContextTemplateRequest + from .document_summary import DocumentSummary + from .document_summary_page import DocumentSummaryPage + from .edge import Edge + from .edge_page import EdgePage + from .edge_source_target import EdgeSourceTarget + from .edge_type import EdgeType + from .entity_property import EntityProperty + from .entity_property_type import EntityPropertyType + from .entity_type import EntityType + from .episode import Episode + from .episode_page import EpisodePage + from .error_body import ErrorBody + from .graph import Graph + from .graph_context_response import GraphContextResponse + from .graph_delete_result import GraphDeleteResult + from .graph_page import GraphPage + from .instructions import Instructions + from .lookup_batch_response import LookupBatchResponse + from .lookup_item import LookupItem + from .lookup_request import LookupRequest + from .message import Message + from .message_page import MessagePage + from .neighbor_entry import NeighborEntry + from .neighbor_page import NeighborPage + from .node import Node + from .node_page import NodePage + from .observation import Observation + from .observation_page import ObservationPage + from .observation_steering import ObservationSteering + from .ontology import Ontology + from .process_batch_result import ProcessBatchResult + from .project import Project + from .search_request import SearchRequest + from .subgraph_response import SubgraphResponse + from .task import Task + from .task_page import TaskPage + from .task_progress import TaskProgress + from .thread import Thread + from .thread_context_response import ThreadContextResponse + from .thread_delete_result import ThreadDeleteResult + from .thread_page import ThreadPage + from .thread_summary import ThreadSummary + from .thread_summary_page import ThreadSummaryPage + from .user import User + from .user_delete_result import UserDeleteResult + from .user_page import UserPage + from .user_summary_instructions import UserSummaryInstructions +_dynamic_imports: typing.Dict[str, str] = { + "AddEdgeResult": ".add_edge_result", + "AddEpisodeResult": ".add_episode_result", + "AddMessage": ".add_message", + "AddMessagesResult": ".add_messages_result", + "AddNodesResult": ".add_nodes_result", + "AddedEdge": ".added_edge", + "AddedNode": ".added_node", + "ApiError": ".api_error", + "ArtifactListRequest": ".artifact_list_request", + "AsyncResult": ".async_result", + "Batch": ".batch", + "BatchItem": ".batch_item", + "BatchItemPage": ".batch_item_page", + "BatchItemsResponse": ".batch_items_response", + "BatchPage": ".batch_page", + "CloneGraphResult": ".clone_graph_result", + "ContextEdge": ".context_edge", + "ContextEpisode": ".context_episode", + "ContextNode": ".context_node", + "ContextObservation": ".context_observation", + "ContextResults": ".context_results", + "ContextTemplate": ".context_template", + "ContextTemplatePage": ".context_template_page", + "ContextThreadSummary": ".context_thread_summary", + "CreateContextTemplateRequest": ".create_context_template_request", + "DocumentSummary": ".document_summary", + "DocumentSummaryPage": ".document_summary_page", + "Edge": ".edge", + "EdgePage": ".edge_page", + "EdgeSourceTarget": ".edge_source_target", + "EdgeType": ".edge_type", + "EntityProperty": ".entity_property", + "EntityPropertyType": ".entity_property_type", + "EntityType": ".entity_type", + "Episode": ".episode", + "EpisodePage": ".episode_page", + "ErrorBody": ".error_body", + "Graph": ".graph", + "GraphContextResponse": ".graph_context_response", + "GraphDeleteResult": ".graph_delete_result", + "GraphPage": ".graph_page", + "Instructions": ".instructions", + "LookupBatchResponse": ".lookup_batch_response", + "LookupItem": ".lookup_item", + "LookupRequest": ".lookup_request", + "Message": ".message", + "MessagePage": ".message_page", + "NeighborEntry": ".neighbor_entry", + "NeighborPage": ".neighbor_page", + "Node": ".node", + "NodePage": ".node_page", + "Observation": ".observation", + "ObservationPage": ".observation_page", + "ObservationSteering": ".observation_steering", + "Ontology": ".ontology", + "ProcessBatchResult": ".process_batch_result", + "Project": ".project", + "SearchRequest": ".search_request", + "SubgraphResponse": ".subgraph_response", + "Task": ".task", + "TaskPage": ".task_page", + "TaskProgress": ".task_progress", + "Thread": ".thread", + "ThreadContextResponse": ".thread_context_response", + "ThreadDeleteResult": ".thread_delete_result", + "ThreadPage": ".thread_page", + "ThreadSummary": ".thread_summary", + "ThreadSummaryPage": ".thread_summary_page", + "User": ".user", + "UserDeleteResult": ".user_delete_result", + "UserPage": ".user_page", + "UserSummaryInstructions": ".user_summary_instructions", +} + + +def __getattr__(attr_name: str) -> typing.Any: + module_name = _dynamic_imports.get(attr_name) + if module_name is None: + raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") + try: + module = import_module(module_name, __package__) + if module_name == f".{attr_name}": + return module + else: + return getattr(module, attr_name) + except ImportError as e: + raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e + except AttributeError as e: + raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e + + +def __dir__(): + lazy_attrs = list(_dynamic_imports.keys()) + return sorted(lazy_attrs) + __all__ = [ - "AddNodeItem", - "AddNodesResponse", - "AddThreadMessagesRequest", - "AddThreadMessagesResponse", - "AddTripleResponse", + "AddEdgeResult", + "AddEpisodeResult", + "AddMessage", + "AddMessagesResult", + "AddNodesResult", + "AddedEdge", "AddedNode", "ApiError", - "ApidataBatchAddItemRole", - "ApidataBatchAddItemType", - "AttachedPolicySetsResponse", - "BatchAddItem", - "BatchItemDetail", - "BatchItemKind", - "BatchItemListResponse", - "BatchItemStatus", - "BatchListResponse", - "BatchProgress", - "BatchStatus", - "BatchSummary", - "CloneGraphResponse", - "ClusterDetectConfig", - "CoOccurrenceDetectConfig", - "ComparisonOperator", - "ContextTemplateResponse", - "CustomInstruction", - "DateFilter", - "DerivedNode", - "DetectConfig", - "DetectPatternsResponse", + "ArtifactListRequest", + "AsyncResult", + "Batch", + "BatchItem", + "BatchItemPage", + "BatchItemsResponse", + "BatchPage", + "CloneGraphResult", + "ContextEdge", + "ContextEpisode", + "ContextNode", + "ContextObservation", + "ContextResults", + "ContextTemplate", + "ContextTemplatePage", + "ContextThreadSummary", + "CreateContextTemplateRequest", + "DocumentSummary", + "DocumentSummaryPage", + "Edge", + "EdgePage", + "EdgeSourceTarget", "EdgeType", - "EntityEdge", - "EntityEdgeSourceTarget", - "EntityNode", "EntityProperty", "EntityPropertyType", "EntityType", - "EntityTypeResponse", "Episode", - "EpisodeData", - "EpisodeMentions", - "EpisodeMetadataFilter", - "EpisodeResponse", - "GetTaskResponse", + "EpisodePage", + "ErrorBody", "Graph", - "GraphDataType", - "GraphEdgesRequest", - "GraphEpisodeListRequest", - "GraphListResponse", - "GraphNodeNeighbor", - "GraphNodesRequest", - "GraphObservationsRequest", - "GraphSearchResponseMetadata", - "GraphSearchResults", - "GraphSearchScope", - "GraphSubgraphResponse", - "GraphThreadSummariesRequest", - "GraphitiMetadataFilterGroupType", - "GraphitiSagaNode", - "HubDetectConfig", - "ListContextTemplatesResponse", - "ListCustomInstructionsResponse", - "ListUserInstructionsResponse", + "GraphContextResponse", + "GraphDeleteResult", + "GraphPage", + "Instructions", + "LookupBatchResponse", + "LookupItem", + "LookupRequest", "Message", - "MessageListResponse", - "MetadataFilterGroup", - "MutateUserGroupMembersRequest", - "ObservationSteeringConfig", - "ObservationType", - "PathDetectConfig", - "PatternMetadata", - "PatternResult", - "PatternSeeds", - "PolicySetSummaryResponse", - "ProjectInfo", - "ProjectInfoResponse", - "PropertyFilter", - "RecencyWeight", - "RelationshipDetectConfig", - "Reranker", - "RoleType", - "SearchFilters", - "SuccessResponse", - "TaskErrorResponse", + "MessagePage", + "NeighborEntry", + "NeighborPage", + "Node", + "NodePage", + "Observation", + "ObservationPage", + "ObservationSteering", + "Ontology", + "ProcessBatchResult", + "Project", + "SearchRequest", + "SubgraphResponse", + "Task", + "TaskPage", "TaskProgress", "Thread", "ThreadContextResponse", - "ThreadListResponse", + "ThreadDeleteResult", + "ThreadPage", "ThreadSummary", + "ThreadSummaryPage", "User", - "UserGroup", - "UserGroupKind", - "UserGroupListResponse", - "UserGroupMembersResponse", - "UserGroupMembershipResponse", - "UserGroupQuota", - "UserGroupResponse", - "UserInstruction", - "UserListResponse", - "UserMembershipGroupsResponse", - "UserNodeResponse", + "UserDeleteResult", + "UserPage", + "UserSummaryInstructions", ] diff --git a/src/zep_cloud/types/hub_detect_config.py b/src/zep_cloud/types/add_edge_result.py similarity index 64% rename from src/zep_cloud/types/hub_detect_config.py rename to src/zep_cloud/types/add_edge_result.py index ca45f15d..4736e270 100644 --- a/src/zep_cloud/types/hub_detect_config.py +++ b/src/zep_cloud/types/add_edge_result.py @@ -4,14 +4,18 @@ import pydantic from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel +from .added_edge import AddedEdge +from .task import Task -class HubDetectConfig(UniversalBaseModel): - min_degree: typing.Optional[int] = pydantic.Field(default=None) +class AddEdgeResult(UniversalBaseModel): + edge: typing.Optional[AddedEdge] = pydantic.Field(default=None) """ - Minimum number of connections for a node to be considered a hub. Default: 3, Min: 2 + 8.6: edge is an accept-time acknowledgement, not a full Edge. """ + task: typing.Optional[Task] = None + if IS_PYDANTIC_V2: model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 else: diff --git a/src/zep_cloud/types/clone_graph_response.py b/src/zep_cloud/types/add_episode_result.py similarity index 59% rename from src/zep_cloud/types/clone_graph_response.py rename to src/zep_cloud/types/add_episode_result.py index dec3de27..aee7dcc1 100644 --- a/src/zep_cloud/types/clone_graph_response.py +++ b/src/zep_cloud/types/add_episode_result.py @@ -4,20 +4,18 @@ import pydantic from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel +from .episode import Episode +from .task import Task -class CloneGraphResponse(UniversalBaseModel): - graph_id: typing.Optional[str] = pydantic.Field(default=None) +class AddEpisodeResult(UniversalBaseModel): + episode: typing.Optional[Episode] = pydantic.Field(default=None) """ - graph_id is the ID of the cloned graph + 8.6: only the episode response is a stored resource, so it is a full + Episode readable immediately with processed false. """ - task_id: typing.Optional[str] = pydantic.Field(default=None) - """ - Task ID of the clone graph task - """ - - user_id: typing.Optional[str] = None + task: typing.Optional[Task] = None if IS_PYDANTIC_V2: model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 diff --git a/src/zep_cloud/types/context_template_response.py b/src/zep_cloud/types/add_message.py similarity index 52% rename from src/zep_cloud/types/context_template_response.py rename to src/zep_cloud/types/add_message.py index 766a07ed..c507d014 100644 --- a/src/zep_cloud/types/context_template_response.py +++ b/src/zep_cloud/types/add_message.py @@ -8,21 +8,14 @@ from ..core.serialization import FieldMetadata -class ContextTemplateResponse(UniversalBaseModel): - template: typing.Optional[str] = pydantic.Field(default=None) - """ - The template content. - """ - - template_id: typing.Optional[str] = pydantic.Field(default=None) - """ - Unique identifier for the template (max 100 characters). - """ - - uuid_: typing_extensions.Annotated[typing.Optional[str], FieldMetadata(alias="uuid")] = pydantic.Field(default=None) - """ - Unique identifier for the template. - """ +class AddMessage(UniversalBaseModel): + content: typing.Optional[str] = None + metadata: typing.Optional[typing.Dict[str, typing.Any]] = None + name: typing.Optional[str] = None + role: typing.Optional[str] = None + uuid_: typing_extensions.Annotated[ + typing.Optional[str], FieldMetadata(alias="uuid"), pydantic.Field(alias="uuid") + ] = None if IS_PYDANTIC_V2: model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 diff --git a/src/zep_cloud/types/add_messages_result.py b/src/zep_cloud/types/add_messages_result.py new file mode 100644 index 00000000..c5a1d8d5 --- /dev/null +++ b/src/zep_cloud/types/add_messages_result.py @@ -0,0 +1,23 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel +from .message import Message +from .task import Task + + +class AddMessagesResult(UniversalBaseModel): + context: typing.Optional[str] = None + messages: typing.Optional[typing.List[Message]] = None + task: typing.Optional[Task] = None + + 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/add_node_item.py b/src/zep_cloud/types/add_node_item.py deleted file mode 100644 index c63ea100..00000000 --- a/src/zep_cloud/types/add_node_item.py +++ /dev/null @@ -1,50 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -import pydantic -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel - - -class AddNodeItem(UniversalBaseModel): - attributes: typing.Optional[typing.Dict[str, typing.Optional[typing.Any]]] = pydantic.Field(default=None) - """ - Additional attributes of the node. Values must be scalar types (string, - number, boolean, or null). Nested objects and arrays are not allowed. - """ - - created_at: typing.Optional[str] = pydantic.Field(default=None) - """ - The node creation time. Defaults to the request time when absent. - """ - - label: typing.Optional[str] = pydantic.Field(default=None) - """ - The node's entity type. At most one; the base "Entity" label is added - implicitly by the graph layer on save and does not need to be supplied. - """ - - metadata: typing.Optional[typing.Dict[str, typing.Optional[typing.Any]]] = pydantic.Field(default=None) - """ - Optional metadata attached to the node's shadow episode. Max 10 scalar - key-value pairs. - """ - - name: str = pydantic.Field() - """ - The name of the node. Used to derive the node's search embedding. - """ - - summary: typing.Optional[str] = pydantic.Field(default=None) - """ - A regional summary of the 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/add_nodes_response.py b/src/zep_cloud/types/add_nodes_result.py similarity index 70% rename from src/zep_cloud/types/add_nodes_response.py rename to src/zep_cloud/types/add_nodes_result.py index 5cc6db7b..5f59fff3 100644 --- a/src/zep_cloud/types/add_nodes_response.py +++ b/src/zep_cloud/types/add_nodes_result.py @@ -5,19 +5,17 @@ import pydantic from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel from .added_node import AddedNode +from .task import Task -class AddNodesResponse(UniversalBaseModel): +class AddNodesResult(UniversalBaseModel): nodes: typing.Optional[typing.List[AddedNode]] = pydantic.Field(default=None) """ - The accepted nodes, each carrying the UUID Zep assigned to it, in request - order. + 8.6: nodes is an accept-time acknowledgement carrying the assigned + identifier plus the fields the caller supplied, not a full Node. """ - task_id: typing.Optional[str] = pydantic.Field(default=None) - """ - Task ID of the async add-nodes task. - """ + task: typing.Optional[Task] = None if IS_PYDANTIC_V2: model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 diff --git a/src/zep_cloud/types/add_thread_messages_request.py b/src/zep_cloud/types/add_thread_messages_request.py deleted file mode 100644 index 72df5d95..00000000 --- a/src/zep_cloud/types/add_thread_messages_request.py +++ /dev/null @@ -1,41 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -import pydantic -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -from .message import Message -from .role_type import RoleType - - -class AddThreadMessagesRequest(UniversalBaseModel): - ignore_roles: typing.Optional[typing.List[RoleType]] = pydantic.Field(default=None) - """ - Optional list of role types to ignore when adding messages to graph memory. - The message itself will still be added, retained and used as context for messages - that are added to a user's graph. - """ - - messages: typing.List[Message] = pydantic.Field() - """ - A list of message objects, where each message contains a role and content. - """ - - return_context: typing.Optional[bool] = pydantic.Field(default=None) - """ - Optionally return context block relevant to the most recent messages. - """ - - strict_ontology: typing.Optional[bool] = pydantic.Field(default=None) - """ - When true, prevents extraction of generic Entity nodes that do not match the configured ontology. - """ - - 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/add_triple_response.py b/src/zep_cloud/types/add_triple_response.py deleted file mode 100644 index 9ad9a994..00000000 --- a/src/zep_cloud/types/add_triple_response.py +++ /dev/null @@ -1,27 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -import pydantic -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -from .entity_edge import EntityEdge -from .entity_node import EntityNode - - -class AddTripleResponse(UniversalBaseModel): - edge: typing.Optional[EntityEdge] = None - source_node: typing.Optional[EntityNode] = None - target_node: typing.Optional[EntityNode] = None - task_id: typing.Optional[str] = pydantic.Field(default=None) - """ - Task ID of the add triple task - """ - - 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/added_edge.py b/src/zep_cloud/types/added_edge.py new file mode 100644 index 00000000..40b7f23d --- /dev/null +++ b/src/zep_cloud/types/added_edge.py @@ -0,0 +1,32 @@ +# 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 AddedEdge(UniversalBaseModel): + attributes: typing.Optional[typing.Dict[str, typing.Any]] = None + expired_at: typing.Optional[str] = None + fact: typing.Optional[str] = None + fact_name: typing.Optional[str] = None + invalid_at: typing.Optional[str] = None + metadata: typing.Optional[typing.Dict[str, typing.Any]] = None + source_node: typing.Optional[typing.Dict[str, typing.Any]] = None + target_node: typing.Optional[typing.Dict[str, typing.Any]] = None + uuid_: typing_extensions.Annotated[ + typing.Optional[str], FieldMetadata(alias="uuid"), pydantic.Field(alias="uuid") + ] = None + valid_at: typing.Optional[str] = None + + 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/added_node.py b/src/zep_cloud/types/added_node.py index 096a05eb..8d972bfe 100644 --- a/src/zep_cloud/types/added_node.py +++ b/src/zep_cloud/types/added_node.py @@ -9,7 +9,7 @@ class AddedNode(UniversalBaseModel): - attributes: typing.Optional[typing.Dict[str, typing.Optional[typing.Any]]] = pydantic.Field(default=None) + attributes: typing.Optional[typing.Dict[str, typing.Any]] = pydantic.Field(default=None) """ Additional attributes of the node. """ @@ -24,7 +24,7 @@ class AddedNode(UniversalBaseModel): The node's entity type. """ - metadata: typing.Optional[typing.Dict[str, typing.Optional[typing.Any]]] = pydantic.Field(default=None) + metadata: typing.Optional[typing.Dict[str, typing.Any]] = pydantic.Field(default=None) """ Metadata attached to the node's shadow episode. """ @@ -39,10 +39,11 @@ class AddedNode(UniversalBaseModel): A regional summary of the node. """ - uuid_: typing_extensions.Annotated[typing.Optional[str], FieldMetadata(alias="uuid")] = pydantic.Field(default=None) - """ - The node UUID, assigned by Zep. - """ + uuid_: typing_extensions.Annotated[ + typing.Optional[str], + FieldMetadata(alias="uuid"), + pydantic.Field(alias="uuid", description="The node UUID, assigned by Zep."), + ] = None if IS_PYDANTIC_V2: model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 diff --git a/src/zep_cloud/types/api_error.py b/src/zep_cloud/types/api_error.py index fc8931eb..341bda57 100644 --- a/src/zep_cloud/types/api_error.py +++ b/src/zep_cloud/types/api_error.py @@ -4,10 +4,11 @@ import pydantic from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel +from .error_body import ErrorBody class ApiError(UniversalBaseModel): - message: typing.Optional[str] = None + error: typing.Optional[ErrorBody] = None if IS_PYDANTIC_V2: model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 diff --git a/src/zep_cloud/types/apidata_batch_add_item_role.py b/src/zep_cloud/types/apidata_batch_add_item_role.py deleted file mode 100644 index 4f51bde1..00000000 --- a/src/zep_cloud/types/apidata_batch_add_item_role.py +++ /dev/null @@ -1,7 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -ApidataBatchAddItemRole = typing.Union[ - typing.Literal["norole", "system", "assistant", "user", "function", "tool"], typing.Any -] diff --git a/src/zep_cloud/types/apidata_batch_add_item_type.py b/src/zep_cloud/types/apidata_batch_add_item_type.py deleted file mode 100644 index f659edea..00000000 --- a/src/zep_cloud/types/apidata_batch_add_item_type.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -ApidataBatchAddItemType = typing.Union[typing.Literal["graph_episode", "thread_message"], typing.Any] diff --git a/src/zep_cloud/types/artifact_list_request.py b/src/zep_cloud/types/artifact_list_request.py new file mode 100644 index 00000000..722ce371 --- /dev/null +++ b/src/zep_cloud/types/artifact_list_request.py @@ -0,0 +1,19 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel + + +class ArtifactListRequest(UniversalBaseModel): + filters: typing.Optional[typing.Dict[str, typing.Any]] = None + + 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/custom_instruction.py b/src/zep_cloud/types/async_result.py similarity index 82% rename from src/zep_cloud/types/custom_instruction.py rename to src/zep_cloud/types/async_result.py index 38b6d159..eaa8a12c 100644 --- a/src/zep_cloud/types/custom_instruction.py +++ b/src/zep_cloud/types/async_result.py @@ -4,11 +4,11 @@ import pydantic from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel +from .task import Task -class CustomInstruction(UniversalBaseModel): - name: str - text: str +class AsyncResult(UniversalBaseModel): + task: typing.Optional[Task] = None if IS_PYDANTIC_V2: model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 diff --git a/src/zep_cloud/types/batch.py b/src/zep_cloud/types/batch.py new file mode 100644 index 00000000..7f5429fc --- /dev/null +++ b/src/zep_cloud/types/batch.py @@ -0,0 +1,28 @@ +# 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 Batch(UniversalBaseModel): + created_at: typing.Optional[str] = None + metadata: typing.Optional[typing.Dict[str, typing.Any]] = None + progress: typing.Optional[typing.Dict[str, typing.Any]] = None + status: typing.Optional[str] = None + strict_ontology: typing.Optional[bool] = None + uuid_: typing_extensions.Annotated[ + typing.Optional[str], FieldMetadata(alias="uuid"), pydantic.Field(alias="uuid") + ] = None + + 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/batch_add_item.py b/src/zep_cloud/types/batch_add_item.py deleted file mode 100644 index a95cb4ab..00000000 --- a/src/zep_cloud/types/batch_add_item.py +++ /dev/null @@ -1,33 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -import pydantic -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -from .apidata_batch_add_item_role import ApidataBatchAddItemRole -from .apidata_batch_add_item_type import ApidataBatchAddItemType -from .graph_data_type import GraphDataType - - -class BatchAddItem(UniversalBaseModel): - content: typing.Optional[str] = None - created_at: typing.Optional[str] = None - data: typing.Optional[str] = None - data_type: typing.Optional[GraphDataType] = None - graph_id: typing.Optional[str] = None - metadata: typing.Optional[typing.Dict[str, typing.Optional[typing.Any]]] = None - name: typing.Optional[str] = None - role: typing.Optional[ApidataBatchAddItemRole] = None - source_description: typing.Optional[str] = None - thread_id: typing.Optional[str] = None - type: ApidataBatchAddItemType - user_id: typing.Optional[str] = None - - 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/batch_item.py b/src/zep_cloud/types/batch_item.py new file mode 100644 index 00000000..00505e2b --- /dev/null +++ b/src/zep_cloud/types/batch_item.py @@ -0,0 +1,31 @@ +# 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 BatchItem(UniversalBaseModel): + created_at: typing.Optional[str] = None + episode_uuid: typing.Optional[str] = None + graph_uuid: typing.Optional[str] = None + sequence_index: typing.Optional[int] = None + source_uuid: typing.Optional[str] = None + status: typing.Optional[str] = None + thread_uuid: typing.Optional[str] = None + type: typing.Optional[str] = None + uuid_: typing_extensions.Annotated[ + typing.Optional[str], FieldMetadata(alias="uuid"), pydantic.Field(alias="uuid") + ] = None + + 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/batch_item_detail.py b/src/zep_cloud/types/batch_item_detail.py deleted file mode 100644 index 439cd2fb..00000000 --- a/src/zep_cloud/types/batch_item_detail.py +++ /dev/null @@ -1,41 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -import pydantic -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -from .batch_item_kind import BatchItemKind -from .batch_item_status import BatchItemStatus - - -class BatchItemDetail(UniversalBaseModel): - created_at: 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 - for this batch item. Populated for every item kind and always equal to - SourceUUID — the underlying source row's UUID is reused as the episode - UUID during processing. - """ - - error: typing.Optional[typing.Dict[str, typing.Optional[typing.Any]]] = None - graph_id: typing.Optional[str] = None - graph_uuid: typing.Optional[str] = None - item_id: typing.Optional[str] = None - kind: typing.Optional[BatchItemKind] = None - sequence_index: typing.Optional[int] = None - source_uuid: typing.Optional[str] = None - status: typing.Optional[BatchItemStatus] = None - thread_id: typing.Optional[str] = None - updated_at: typing.Optional[str] = None - user_id: typing.Optional[str] = None - user_uuid: typing.Optional[str] = None - - 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/batch_item_kind.py b/src/zep_cloud/types/batch_item_kind.py deleted file mode 100644 index 5d08ec0a..00000000 --- a/src/zep_cloud/types/batch_item_kind.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -BatchItemKind = typing.Union[typing.Literal["graph_episode", "thread_message"], typing.Any] diff --git a/src/zep_cloud/types/batch_item_page.py b/src/zep_cloud/types/batch_item_page.py new file mode 100644 index 00000000..83419e23 --- /dev/null +++ b/src/zep_cloud/types/batch_item_page.py @@ -0,0 +1,22 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel +from .batch_item import BatchItem + + +class BatchItemPage(UniversalBaseModel): + items: typing.Optional[typing.List[BatchItem]] = None + next_cursor: typing.Optional[str] = None + total_size: typing.Optional[int] = None + + 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/batch_item_status.py b/src/zep_cloud/types/batch_item_status.py deleted file mode 100644 index 276314d0..00000000 --- a/src/zep_cloud/types/batch_item_status.py +++ /dev/null @@ -1,7 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -BatchItemStatus = typing.Union[ - typing.Literal["pending", "queued", "processing", "succeeded", "failed", "skipped", "canceled"], typing.Any -] diff --git a/src/zep_cloud/types/batch_items_response.py b/src/zep_cloud/types/batch_items_response.py new file mode 100644 index 00000000..50c44d74 --- /dev/null +++ b/src/zep_cloud/types/batch_items_response.py @@ -0,0 +1,20 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel +from .batch_item import BatchItem + + +class BatchItemsResponse(UniversalBaseModel): + items: typing.Optional[typing.List[BatchItem]] = None + + 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/batch_item_list_response.py b/src/zep_cloud/types/batch_page.py similarity index 69% rename from src/zep_cloud/types/batch_item_list_response.py rename to src/zep_cloud/types/batch_page.py index b082dcea..b7685aad 100644 --- a/src/zep_cloud/types/batch_item_list_response.py +++ b/src/zep_cloud/types/batch_page.py @@ -4,12 +4,13 @@ import pydantic from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -from .batch_item_detail import BatchItemDetail +from .batch import Batch -class BatchItemListResponse(UniversalBaseModel): - items: typing.Optional[typing.List[BatchItemDetail]] = None - next_cursor: typing.Optional[int] = None +class BatchPage(UniversalBaseModel): + items: typing.Optional[typing.List[Batch]] = None + next_cursor: typing.Optional[str] = None + total_size: typing.Optional[int] = None if IS_PYDANTIC_V2: model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 diff --git a/src/zep_cloud/types/batch_progress.py b/src/zep_cloud/types/batch_progress.py deleted file mode 100644 index 981ff215..00000000 --- a/src/zep_cloud/types/batch_progress.py +++ /dev/null @@ -1,26 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -import pydantic -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel - - -class BatchProgress(UniversalBaseModel): - canceled_items: typing.Optional[int] = None - failed_items: typing.Optional[int] = None - percent_complete: typing.Optional[float] = None - processing_items: typing.Optional[int] = None - queued_items: typing.Optional[int] = None - skipped_items: typing.Optional[int] = None - succeeded_items: typing.Optional[int] = None - total_items: typing.Optional[int] = None - - 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/batch_status.py b/src/zep_cloud/types/batch_status.py deleted file mode 100644 index deacee6b..00000000 --- a/src/zep_cloud/types/batch_status.py +++ /dev/null @@ -1,7 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -BatchStatus = typing.Union[ - typing.Literal["draft", "invalid", "queued", "processing", "succeeded", "partial", "failed", "canceled"], typing.Any -] diff --git a/src/zep_cloud/types/batch_summary.py b/src/zep_cloud/types/batch_summary.py deleted file mode 100644 index aaf0d40e..00000000 --- a/src/zep_cloud/types/batch_summary.py +++ /dev/null @@ -1,31 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -import pydantic -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -from .batch_progress import BatchProgress -from .batch_status import BatchStatus -from .role_type import RoleType - - -class BatchSummary(UniversalBaseModel): - batch_id: typing.Optional[str] = None - completed_at: typing.Optional[str] = None - created_at: typing.Optional[str] = None - ignore_roles: typing.Optional[typing.List[RoleType]] = None - item_count: typing.Optional[int] = None - metadata: typing.Optional[typing.Dict[str, typing.Optional[typing.Any]]] = None - processed_at: typing.Optional[str] = None - progress: typing.Optional[BatchProgress] = None - status: typing.Optional[BatchStatus] = None - updated_at: typing.Optional[str] = None - - 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/clone_graph_result.py b/src/zep_cloud/types/clone_graph_result.py new file mode 100644 index 00000000..3bda3605 --- /dev/null +++ b/src/zep_cloud/types/clone_graph_result.py @@ -0,0 +1,22 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel +from .graph import Graph +from .task import Task + + +class CloneGraphResult(UniversalBaseModel): + graph: typing.Optional[Graph] = None + task: typing.Optional[Task] = None + + 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/cluster_detect_config.py b/src/zep_cloud/types/cluster_detect_config.py deleted file mode 100644 index fa051315..00000000 --- a/src/zep_cloud/types/cluster_detect_config.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -ClusterDetectConfig = typing.Dict[str, typing.Optional[typing.Any]] diff --git a/src/zep_cloud/types/co_occurrence_detect_config.py b/src/zep_cloud/types/co_occurrence_detect_config.py deleted file mode 100644 index 4bddda33..00000000 --- a/src/zep_cloud/types/co_occurrence_detect_config.py +++ /dev/null @@ -1,22 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -import pydantic -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel - - -class CoOccurrenceDetectConfig(UniversalBaseModel): - max_hops: typing.Optional[int] = pydantic.Field(default=None) - """ - Max hops within which to detect co-occurring node types. Default: 3, Max: 5 - """ - - 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/comparison_operator.py b/src/zep_cloud/types/comparison_operator.py deleted file mode 100644 index 8e1df62e..00000000 --- a/src/zep_cloud/types/comparison_operator.py +++ /dev/null @@ -1,7 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -ComparisonOperator = typing.Union[ - typing.Literal["=", "<>", ">", "<", ">=", "<=", "IS NULL", "is_null", "IS NOT NULL", "CONTAINS"], typing.Any -] diff --git a/src/zep_cloud/types/context_edge.py b/src/zep_cloud/types/context_edge.py new file mode 100644 index 00000000..dbe1c8d1 --- /dev/null +++ b/src/zep_cloud/types/context_edge.py @@ -0,0 +1,47 @@ +# 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 ContextEdge(UniversalBaseModel): + attributes: typing.Optional[typing.Dict[str, typing.Any]] = None + created_at: typing.Optional[str] = None + episode_uuids: typing.Optional[typing.List[str]] = None + expired_at: typing.Optional[str] = None + fact: typing.Optional[str] = None + graph_uuid: typing.Optional[str] = None + invalid_at: typing.Optional[str] = None + name: typing.Optional[str] = None + relevance: typing.Optional[float] = None + scope: typing.Optional[str] = None + score: typing.Optional[float] = None + selection_rank: typing.Optional[int] = None + source_node_labels: typing.Optional[typing.List[str]] = None + source_node_name: typing.Optional[str] = pydantic.Field(default=None) + """ + Endpoint-name and label projections are read-time and subject to the + ABAC omission rule of spec-2 section 4, so each is omitted when denied. + """ + + source_node_uuid: typing.Optional[str] = None + target_node_labels: typing.Optional[typing.List[str]] = None + target_node_name: typing.Optional[str] = None + target_node_uuid: typing.Optional[str] = None + uuid_: typing_extensions.Annotated[ + typing.Optional[str], FieldMetadata(alias="uuid"), pydantic.Field(alias="uuid") + ] = None + valid_at: typing.Optional[str] = None + + 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/context_episode.py b/src/zep_cloud/types/context_episode.py new file mode 100644 index 00000000..5d63dfe5 --- /dev/null +++ b/src/zep_cloud/types/context_episode.py @@ -0,0 +1,38 @@ +# 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 ContextEpisode(UniversalBaseModel): + content: typing.Optional[str] = None + created_at: typing.Optional[str] = None + document_id: typing.Optional[str] = None + graph_uuid: typing.Optional[str] = None + metadata: typing.Optional[typing.Dict[str, typing.Any]] = None + processed: typing.Optional[bool] = None + relevance: typing.Optional[float] = None + role: typing.Optional[str] = None + role_name: typing.Optional[str] = None + score: typing.Optional[float] = None + selection_rank: typing.Optional[int] = None + source: typing.Optional[str] = None + source_description: typing.Optional[str] = None + thread_uuid: typing.Optional[str] = None + uuid_: typing_extensions.Annotated[ + typing.Optional[str], FieldMetadata(alias="uuid"), pydantic.Field(alias="uuid") + ] = None + valid_at: typing.Optional[str] = None + + 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/user_group_response.py b/src/zep_cloud/types/context_node.py similarity index 54% rename from src/zep_cloud/types/user_group_response.py rename to src/zep_cloud/types/context_node.py index 74e7c8df..20c6f519 100644 --- a/src/zep_cloud/types/user_group_response.py +++ b/src/zep_cloud/types/context_node.py @@ -6,20 +6,21 @@ import typing_extensions from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel from ..core.serialization import FieldMetadata -from .user_group_kind import UserGroupKind -class UserGroupResponse(UniversalBaseModel): - attached_policy_set_count: typing.Optional[int] = None +class ContextNode(UniversalBaseModel): + attributes: typing.Optional[typing.Dict[str, typing.Any]] = None created_at: typing.Optional[str] = None - description: typing.Optional[str] = None - kind: typing.Optional[UserGroupKind] = None - member_count: typing.Optional[int] = None + graph_uuid: typing.Optional[str] = None + labels: typing.Optional[typing.List[str]] = None name: typing.Optional[str] = None - project_uuid: typing.Optional[str] = None - updated_at: typing.Optional[str] = None - uuid_: typing_extensions.Annotated[typing.Optional[str], FieldMetadata(alias="uuid")] = None - version: typing.Optional[int] = None + relevance: typing.Optional[float] = None + score: typing.Optional[float] = None + selection_rank: typing.Optional[int] = None + summary: typing.Optional[str] = None + uuid_: typing_extensions.Annotated[ + typing.Optional[str], FieldMetadata(alias="uuid"), pydantic.Field(alias="uuid") + ] = None if IS_PYDANTIC_V2: model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 diff --git a/src/zep_cloud/types/context_observation.py b/src/zep_cloud/types/context_observation.py new file mode 100644 index 00000000..60105d8d --- /dev/null +++ b/src/zep_cloud/types/context_observation.py @@ -0,0 +1,33 @@ +# 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 ContextObservation(UniversalBaseModel): + attributes: typing.Optional[typing.Dict[str, typing.Any]] = None + created_at: typing.Optional[str] = None + episode_uuids: typing.Optional[typing.List[str]] = None + graph_uuid: typing.Optional[str] = None + labels: typing.Optional[typing.List[str]] = None + name: typing.Optional[str] = None + relevance: typing.Optional[float] = None + score: typing.Optional[float] = None + selection_rank: typing.Optional[int] = None + summary: typing.Optional[str] = None + uuid_: typing_extensions.Annotated[ + typing.Optional[str], FieldMetadata(alias="uuid"), pydantic.Field(alias="uuid") + ] = None + + 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/context_results.py b/src/zep_cloud/types/context_results.py new file mode 100644 index 00000000..d34d9ef8 --- /dev/null +++ b/src/zep_cloud/types/context_results.py @@ -0,0 +1,28 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel +from .context_edge import ContextEdge +from .context_episode import ContextEpisode +from .context_node import ContextNode +from .context_observation import ContextObservation +from .context_thread_summary import ContextThreadSummary + + +class ContextResults(UniversalBaseModel): + edges: typing.Optional[typing.List[ContextEdge]] = None + episodes: typing.Optional[typing.List[ContextEpisode]] = None + nodes: typing.Optional[typing.List[ContextNode]] = None + observations: typing.Optional[typing.List[ContextObservation]] = None + thread_summaries: typing.Optional[typing.List[ContextThreadSummary]] = None + + 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/policy_set_summary_response.py b/src/zep_cloud/types/context_template.py similarity index 68% rename from src/zep_cloud/types/policy_set_summary_response.py rename to src/zep_cloud/types/context_template.py index a8cb071d..dc877f25 100644 --- a/src/zep_cloud/types/policy_set_summary_response.py +++ b/src/zep_cloud/types/context_template.py @@ -8,11 +8,13 @@ from ..core.serialization import FieldMetadata -class PolicySetSummaryResponse(UniversalBaseModel): - mode: typing.Optional[str] = None +class ContextTemplate(UniversalBaseModel): + created_at: typing.Optional[str] = None name: typing.Optional[str] = None - uuid_: typing_extensions.Annotated[typing.Optional[str], FieldMetadata(alias="uuid")] = None - version: typing.Optional[int] = None + template: typing.Optional[str] = None + uuid_: typing_extensions.Annotated[ + typing.Optional[str], FieldMetadata(alias="uuid"), pydantic.Field(alias="uuid") + ] = None if IS_PYDANTIC_V2: model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 diff --git a/src/zep_cloud/types/context_template_page.py b/src/zep_cloud/types/context_template_page.py new file mode 100644 index 00000000..270fac05 --- /dev/null +++ b/src/zep_cloud/types/context_template_page.py @@ -0,0 +1,22 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel +from .context_template import ContextTemplate + + +class ContextTemplatePage(UniversalBaseModel): + items: typing.Optional[typing.List[ContextTemplate]] = None + next_cursor: typing.Optional[str] = None + total_size: typing.Optional[int] = None + + 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/context_thread_summary.py b/src/zep_cloud/types/context_thread_summary.py new file mode 100644 index 00000000..82ef6720 --- /dev/null +++ b/src/zep_cloud/types/context_thread_summary.py @@ -0,0 +1,36 @@ +# 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 ContextThreadSummary(UniversalBaseModel): + created_at: typing.Optional[str] = None + last_summarized_at: typing.Optional[str] = None + last_summarized_episode_valid_at: typing.Optional[str] = None + relevance: typing.Optional[float] = None + score: typing.Optional[float] = pydantic.Field(default=None) + """ + 8.7 puts score on every search result and relevance on the ones the + cross-encoder scored. Pointers keep both off the listing (8.4). + """ + + selection_rank: typing.Optional[int] = None + summary: typing.Optional[str] = None + thread_uuid: typing.Optional[str] = None + uuid_: typing_extensions.Annotated[ + typing.Optional[str], FieldMetadata(alias="uuid"), pydantic.Field(alias="uuid") + ] = None + + 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/episode_response.py b/src/zep_cloud/types/create_context_template_request.py similarity index 77% rename from src/zep_cloud/types/episode_response.py rename to src/zep_cloud/types/create_context_template_request.py index 8548f0ca..5f81d0af 100644 --- a/src/zep_cloud/types/episode_response.py +++ b/src/zep_cloud/types/create_context_template_request.py @@ -4,11 +4,11 @@ import pydantic from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -from .episode import Episode -class EpisodeResponse(UniversalBaseModel): - episodes: typing.Optional[typing.List[Episode]] = None +class CreateContextTemplateRequest(UniversalBaseModel): + name: typing.Optional[str] = None + template: typing.Optional[str] = None if IS_PYDANTIC_V2: model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 diff --git a/src/zep_cloud/types/date_filter.py b/src/zep_cloud/types/date_filter.py deleted file mode 100644 index 19ab4222..00000000 --- a/src/zep_cloud/types/date_filter.py +++ /dev/null @@ -1,29 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -import pydantic -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -from .comparison_operator import ComparisonOperator - - -class DateFilter(UniversalBaseModel): - comparison_operator: ComparisonOperator = pydantic.Field() - """ - Comparison operator for date filter - """ - - date: typing.Optional[str] = pydantic.Field(default=None) - """ - Date to filter on. Required for non-null operators (`=`, `<>`, `>`, `<`, `>=`, `<=`). - Should be omitted for IS NULL (or is_null) and IS NOT NULL operators. - """ - - 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/derived_node.py b/src/zep_cloud/types/derived_node.py deleted file mode 100644 index 05b398d8..00000000 --- a/src/zep_cloud/types/derived_node.py +++ /dev/null @@ -1,89 +0,0 @@ -# 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 DerivedNode(UniversalBaseModel): - attributes: typing.Optional[typing.Dict[str, typing.Optional[typing.Any]]] = pydantic.Field(default=None) - """ - Additional attributes of the derived node. - """ - - created_at: str = pydantic.Field() - """ - Creation time of the node - """ - - end_at: typing.Optional[str] = pydantic.Field(default=None) - """ - EndAt is the close timestamp of the evidence window. Set when the - underlying pattern is no longer supported (closed observations); - nil for active observations. - """ - - episode_ids: typing.Optional[typing.List[str]] = pydantic.Field(default=None) - """ - Episode UUIDs that support this observation. Only populated for observation nodes in web API responses. - """ - - labels: typing.Optional[typing.List[str]] = pydantic.Field(default=None) - """ - Labels associated with the node - """ - - latest_evidence_at: typing.Optional[str] = pydantic.Field(default=None) - """ - LatestEvidenceAt is the most recent source-episode timestamp from - which this observation drew evidence. - """ - - name: str = pydantic.Field() - """ - Name of the node - """ - - relevance: typing.Optional[float] = pydantic.Field(default=None) - """ - Relevance is an experimental rank-aligned score in [0,1] derived from Score via logit transformation. - Only populated when using cross_encoder reranker; omitted for other reranker types (e.g., RRF). - """ - - score: typing.Optional[float] = pydantic.Field(default=None) - """ - Score is the reranker output: sigmoid-distributed logits [0,1] when using cross_encoder reranker, or RRF ordinal rank when using rrf reranker - """ - - selection_rank: typing.Optional[int] = pydantic.Field(default=None) - """ - SelectionRank is the global cross-scope rank assigned by auto scope selection. - """ - - start_at: typing.Optional[str] = pydantic.Field(default=None) - """ - StartAt is the earliest source-episode timestamp from which this - observation was derived. Only populated for observation nodes. - """ - - summary: typing.Optional[str] = pydantic.Field(default=None) - """ - Region summary of member nodes - """ - - uuid_: typing_extensions.Annotated[str, FieldMetadata(alias="uuid")] = pydantic.Field() - """ - UUID of the 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/detect_config.py b/src/zep_cloud/types/detect_config.py deleted file mode 100644 index e9a40b4d..00000000 --- a/src/zep_cloud/types/detect_config.py +++ /dev/null @@ -1,47 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -import pydantic -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -from .cluster_detect_config import ClusterDetectConfig -from .co_occurrence_detect_config import CoOccurrenceDetectConfig -from .hub_detect_config import HubDetectConfig -from .path_detect_config import PathDetectConfig -from .relationship_detect_config import RelationshipDetectConfig - - -class DetectConfig(UniversalBaseModel): - clusters: typing.Optional[ClusterDetectConfig] = pydantic.Field(default=None) - """ - Detect tightly interconnected groups (triangle topology) - """ - - co_occurrences: typing.Optional[CoOccurrenceDetectConfig] = pydantic.Field(default=None) - """ - Detect node types that co-occur within k hops - """ - - hubs: typing.Optional[HubDetectConfig] = pydantic.Field(default=None) - """ - Detect highly connected hub nodes (star topology) - """ - - paths: typing.Optional[PathDetectConfig] = pydantic.Field(default=None) - """ - Detect frequent multi-hop connection paths - """ - - relationships: typing.Optional[RelationshipDetectConfig] = pydantic.Field(default=None) - """ - Detect common (source_label, edge_type, target_label) relationship triples - """ - - 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/detect_patterns_response.py b/src/zep_cloud/types/detect_patterns_response.py deleted file mode 100644 index 2cc16689..00000000 --- a/src/zep_cloud/types/detect_patterns_response.py +++ /dev/null @@ -1,35 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -import pydantic -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -from .entity_node import EntityNode -from .pattern_metadata import PatternMetadata -from .pattern_result import PatternResult - - -class DetectPatternsResponse(UniversalBaseModel): - metadata: typing.Optional[PatternMetadata] = pydantic.Field(default=None) - """ - Statistics about the detection run - """ - - nodes: typing.Optional[typing.List[EntityNode]] = pydantic.Field(default=None) - """ - Resolved nodes referenced by pattern edges (deduplicated). Only populated when query is set. - """ - - patterns: typing.Optional[typing.List[PatternResult]] = pydantic.Field(default=None) - """ - Detected patterns, sorted by weighted_score descending - """ - - 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/episode_data.py b/src/zep_cloud/types/document_summary.py similarity index 52% rename from src/zep_cloud/types/episode_data.py rename to src/zep_cloud/types/document_summary.py index c17be3d1..cc77b2ab 100644 --- a/src/zep_cloud/types/episode_data.py +++ b/src/zep_cloud/types/document_summary.py @@ -3,20 +3,20 @@ import typing import pydantic +import typing_extensions from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -from .graph_data_type import GraphDataType +from ..core.serialization import FieldMetadata -class EpisodeData(UniversalBaseModel): +class DocumentSummary(UniversalBaseModel): created_at: typing.Optional[str] = None - data: str - metadata: typing.Optional[typing.Dict[str, typing.Optional[typing.Any]]] = pydantic.Field(default=None) - """ - Optional metadata key-value pairs. Max 10 keys. Values must be strings, numbers, booleans, or arrays of scalars. - """ - - source_description: typing.Optional[str] = None - type: GraphDataType + document_id: typing.Optional[str] = None + last_summarized_at: typing.Optional[str] = None + last_summarized_episode_valid_at: typing.Optional[str] = None + summary: typing.Optional[str] = None + uuid_: typing_extensions.Annotated[ + typing.Optional[str], FieldMetadata(alias="uuid"), pydantic.Field(alias="uuid") + ] = None if IS_PYDANTIC_V2: model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 diff --git a/src/zep_cloud/types/document_summary_page.py b/src/zep_cloud/types/document_summary_page.py new file mode 100644 index 00000000..c5404588 --- /dev/null +++ b/src/zep_cloud/types/document_summary_page.py @@ -0,0 +1,22 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel +from .document_summary import DocumentSummary + + +class DocumentSummaryPage(UniversalBaseModel): + items: typing.Optional[typing.List[DocumentSummary]] = None + next_cursor: typing.Optional[str] = None + total_size: typing.Optional[int] = None + + 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/edge.py b/src/zep_cloud/types/edge.py new file mode 100644 index 00000000..3f52bc82 --- /dev/null +++ b/src/zep_cloud/types/edge.py @@ -0,0 +1,46 @@ +# 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 Edge(UniversalBaseModel): + attributes: typing.Optional[typing.Dict[str, typing.Any]] = None + created_at: typing.Optional[str] = None + episode_uuids: typing.Optional[typing.List[str]] = None + expired_at: typing.Optional[str] = None + fact: typing.Optional[str] = None + graph_uuid: typing.Optional[str] = None + invalid_at: typing.Optional[str] = None + name: typing.Optional[str] = None + relevance: typing.Optional[float] = None + scope: typing.Optional[str] = None + score: typing.Optional[float] = None + source_node_labels: typing.Optional[typing.List[str]] = None + source_node_name: typing.Optional[str] = pydantic.Field(default=None) + """ + Endpoint-name and label projections are read-time and subject to the + ABAC omission rule of spec-2 section 4, so each is omitted when denied. + """ + + source_node_uuid: typing.Optional[str] = None + target_node_labels: typing.Optional[typing.List[str]] = None + target_node_name: typing.Optional[str] = None + target_node_uuid: typing.Optional[str] = None + uuid_: typing_extensions.Annotated[ + typing.Optional[str], FieldMetadata(alias="uuid"), pydantic.Field(alias="uuid") + ] = None + valid_at: typing.Optional[str] = None + + 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/batch_list_response.py b/src/zep_cloud/types/edge_page.py similarity index 69% rename from src/zep_cloud/types/batch_list_response.py rename to src/zep_cloud/types/edge_page.py index 0400a33f..403fa7f6 100644 --- a/src/zep_cloud/types/batch_list_response.py +++ b/src/zep_cloud/types/edge_page.py @@ -4,12 +4,13 @@ import pydantic from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -from .batch_summary import BatchSummary +from .edge import Edge -class BatchListResponse(UniversalBaseModel): - batches: typing.Optional[typing.List[BatchSummary]] = None - next_cursor: typing.Optional[int] = None +class EdgePage(UniversalBaseModel): + items: typing.Optional[typing.List[Edge]] = None + next_cursor: typing.Optional[str] = None + total_size: typing.Optional[int] = None if IS_PYDANTIC_V2: model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 diff --git a/src/zep_cloud/types/edge_source_target.py b/src/zep_cloud/types/edge_source_target.py new file mode 100644 index 00000000..c58aa1ff --- /dev/null +++ b/src/zep_cloud/types/edge_source_target.py @@ -0,0 +1,20 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel + + +class EdgeSourceTarget(UniversalBaseModel): + source_entity_type: typing.Optional[str] = None + target_entity_type: typing.Optional[str] = None + + 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/edge_type.py b/src/zep_cloud/types/edge_type.py index 4edbe9db..92fd1be9 100644 --- a/src/zep_cloud/types/edge_type.py +++ b/src/zep_cloud/types/edge_type.py @@ -4,7 +4,7 @@ import pydantic from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -from .entity_edge_source_target import EntityEdgeSourceTarget +from .edge_source_target import EdgeSourceTarget from .entity_property import EntityProperty @@ -12,7 +12,7 @@ class EdgeType(UniversalBaseModel): description: str name: str properties: typing.Optional[typing.List[EntityProperty]] = None - source_targets: typing.Optional[typing.List[EntityEdgeSourceTarget]] = None + source_targets: typing.Optional[typing.List[EdgeSourceTarget]] = None if IS_PYDANTIC_V2: model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 diff --git a/src/zep_cloud/types/entity_edge.py b/src/zep_cloud/types/entity_edge.py deleted file mode 100644 index 91a79a29..00000000 --- a/src/zep_cloud/types/entity_edge.py +++ /dev/null @@ -1,122 +0,0 @@ -# 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 EntityEdge(UniversalBaseModel): - attributes: typing.Optional[typing.Dict[str, typing.Optional[typing.Any]]] = pydantic.Field(default=None) - """ - Additional attributes of the edge. Dependent on edge types - """ - - created_at: str = pydantic.Field() - """ - Creation time of the edge - """ - - episodes: typing.Optional[typing.List[str]] = pydantic.Field(default=None) - """ - List of episode ids that reference these entity edges - """ - - expired_at: typing.Optional[str] = pydantic.Field(default=None) - """ - Datetime of when the node was invalidated - """ - - fact: str = pydantic.Field() - """ - Fact representing the edge and nodes that it connects - """ - - invalid_at: typing.Optional[str] = pydantic.Field(default=None) - """ - Datetime of when the fact stopped being true - """ - - name: str = pydantic.Field() - """ - Name of the edge, relation name - """ - - relevance: typing.Optional[float] = pydantic.Field(default=None) - """ - Relevance is an experimental rank-aligned score in [0,1] derived from Score via logit transformation. - Only populated when using cross_encoder reranker; omitted for other reranker types (e.g., RRF). - """ - - scope: typing.Optional[str] = pydantic.Field(default=None) - """ - Scope of the edge (e.g. "entity", "maybe_related") - """ - - score: typing.Optional[float] = pydantic.Field(default=None) - """ - Score is the reranker output: sigmoid-distributed logits [0,1] when using cross_encoder reranker, or RRF ordinal rank when using rrf reranker - """ - - selection_rank: typing.Optional[int] = pydantic.Field(default=None) - """ - SelectionRank is the global cross-scope rank assigned by auto scope selection. - """ - - 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). - """ - - source_node_name: typing.Optional[str] = pydantic.Field(default=None) - """ - SourceNodeName is the name of the source node at read time. It is a - 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). - """ - - source_node_uuid: str = pydantic.Field() - """ - UUID of the source node - """ - - 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). - """ - - 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). - """ - - target_node_uuid: str = pydantic.Field() - """ - UUID of the target node - """ - - uuid_: typing_extensions.Annotated[str, FieldMetadata(alias="uuid")] = pydantic.Field() - """ - UUID of the edge - """ - - valid_at: typing.Optional[str] = pydantic.Field(default=None) - """ - Datetime of when the fact became true - """ - - 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_source_target.py b/src/zep_cloud/types/entity_edge_source_target.py deleted file mode 100644 index 496ed396..00000000 --- a/src/zep_cloud/types/entity_edge_source_target.py +++ /dev/null @@ -1,27 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -import pydantic -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel - - -class EntityEdgeSourceTarget(UniversalBaseModel): - source: typing.Optional[str] = pydantic.Field(default=None) - """ - Source represents the originating node identifier in the edge type relationship. (optional) - """ - - target: typing.Optional[str] = pydantic.Field(default=None) - """ - Target represents the target node identifier in the edge type relationship. (optional) - """ - - 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_node.py b/src/zep_cloud/types/entity_node.py deleted file mode 100644 index abed7274..00000000 --- a/src/zep_cloud/types/entity_node.py +++ /dev/null @@ -1,65 +0,0 @@ -# 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 EntityNode(UniversalBaseModel): - attributes: typing.Optional[typing.Dict[str, typing.Optional[typing.Any]]] = pydantic.Field(default=None) - """ - Additional attributes of the node. Dependent on node labels - """ - - created_at: str = pydantic.Field() - """ - Creation time of the node - """ - - labels: typing.Optional[typing.List[str]] = pydantic.Field(default=None) - """ - Labels associated with the node - """ - - name: str = pydantic.Field() - """ - Name of the node - """ - - relevance: typing.Optional[float] = pydantic.Field(default=None) - """ - Relevance is an experimental rank-aligned score in [0,1] derived from Score via logit transformation. - Only populated when using cross_encoder reranker; omitted for other reranker types (e.g., RRF). - """ - - score: typing.Optional[float] = pydantic.Field(default=None) - """ - Score is the reranker output: sigmoid-distributed logits [0,1] when using cross_encoder reranker, or RRF ordinal rank when using rrf reranker - """ - - selection_rank: typing.Optional[int] = pydantic.Field(default=None) - """ - SelectionRank is the global cross-scope rank assigned by auto scope selection. - """ - - summary: str = pydantic.Field() - """ - Regional summary of surrounding edges - """ - - uuid_: typing_extensions.Annotated[str, FieldMetadata(alias="uuid")] = pydantic.Field() - """ - UUID of the 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/episode.py b/src/zep_cloud/types/episode.py index fa179eb7..8adb7b53 100644 --- a/src/zep_cloud/types/episode.py +++ b/src/zep_cloud/types/episode.py @@ -6,54 +6,26 @@ import typing_extensions from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel from ..core.serialization import FieldMetadata -from .graph_data_type import GraphDataType -from .role_type import RoleType class Episode(UniversalBaseModel): - content: str - created_at: str - metadata: typing.Optional[typing.Dict[str, typing.Optional[typing.Any]]] = None + content: typing.Optional[str] = None + created_at: typing.Optional[str] = None + document_id: typing.Optional[str] = None + graph_uuid: typing.Optional[str] = None + metadata: typing.Optional[typing.Dict[str, typing.Any]] = None processed: typing.Optional[bool] = None - relevance: typing.Optional[float] = pydantic.Field(default=None) - """ - Relevance is an experimental rank-aligned score in [0,1] derived from Score via logit transformation. - Only populated when using cross_encoder reranker; omitted for other reranker types (e.g., RRF). - """ - - role: typing.Optional[str] = pydantic.Field(default=None) - """ - Optional role, will only be present if the episode was created using memory.add API - """ - - role_type: typing.Optional[RoleType] = pydantic.Field(default=None) - """ - Optional role_type, will only be present if the episode was created using memory.add API - """ - - score: typing.Optional[float] = pydantic.Field(default=None) - """ - Score is the reranker output: sigmoid-distributed logits [0,1] when using cross_encoder reranker, or RRF ordinal rank when using rrf reranker - """ - - selection_rank: typing.Optional[int] = pydantic.Field(default=None) - """ - SelectionRank is the global cross-scope rank assigned by auto scope selection. - """ - - source: typing.Optional[GraphDataType] = None + relevance: typing.Optional[float] = None + role: typing.Optional[str] = None + role_name: typing.Optional[str] = None + score: typing.Optional[float] = None + source: typing.Optional[str] = None source_description: typing.Optional[str] = None - task_id: typing.Optional[str] = pydantic.Field(default=None) - """ - Optional task ID to poll episode processing status. Currently only available for batch ingestion. - """ - - thread_id: typing.Optional[str] = pydantic.Field(default=None) - """ - Optional thread ID, will be present if the episode is part of a thread - """ - - uuid_: typing_extensions.Annotated[str, FieldMetadata(alias="uuid")] + thread_uuid: typing.Optional[str] = None + uuid_: typing_extensions.Annotated[ + typing.Optional[str], FieldMetadata(alias="uuid"), pydantic.Field(alias="uuid") + ] = None + valid_at: typing.Optional[str] = None if IS_PYDANTIC_V2: model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 diff --git a/src/zep_cloud/types/episode_mentions.py b/src/zep_cloud/types/episode_mentions.py deleted file mode 100644 index 9cbcd803..00000000 --- a/src/zep_cloud/types/episode_mentions.py +++ /dev/null @@ -1,22 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -import pydantic -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -from .entity_edge import EntityEdge -from .entity_node import EntityNode - - -class EpisodeMentions(UniversalBaseModel): - edges: typing.Optional[typing.List[EntityEdge]] = None - nodes: typing.Optional[typing.List[EntityNode]] = None - - 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/episode_metadata_filter.py b/src/zep_cloud/types/episode_metadata_filter.py deleted file mode 100644 index db3c39e6..00000000 --- a/src/zep_cloud/types/episode_metadata_filter.py +++ /dev/null @@ -1,33 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -import pydantic -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -from .comparison_operator import ComparisonOperator - - -class EpisodeMetadataFilter(UniversalBaseModel): - comparison_operator: ComparisonOperator = pydantic.Field() - """ - Comparison operator: =, <>, >, <, >=, <=, IS NULL, IS NOT NULL, IN, CONTAINS - """ - - property_name: str = pydantic.Field() - """ - Metadata key to filter on - """ - - property_value: typing.Optional[typing.Optional[typing.Any]] = pydantic.Field(default=None) - """ - Value to compare against. Not required for IS NULL / IS NOT NULL operators. - """ - - 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/episode_page.py b/src/zep_cloud/types/episode_page.py new file mode 100644 index 00000000..24b51d95 --- /dev/null +++ b/src/zep_cloud/types/episode_page.py @@ -0,0 +1,22 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel +from .episode import Episode + + +class EpisodePage(UniversalBaseModel): + items: typing.Optional[typing.List[Episode]] = None + next_cursor: typing.Optional[str] = None + total_size: typing.Optional[int] = None + + 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/task_error_response.py b/src/zep_cloud/types/error_body.py similarity index 74% rename from src/zep_cloud/types/task_error_response.py rename to src/zep_cloud/types/error_body.py index d138f3f9..7369362f 100644 --- a/src/zep_cloud/types/task_error_response.py +++ b/src/zep_cloud/types/error_body.py @@ -6,10 +6,12 @@ from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -class TaskErrorResponse(UniversalBaseModel): +class ErrorBody(UniversalBaseModel): code: typing.Optional[str] = None - details: typing.Optional[typing.Dict[str, typing.Optional[typing.Any]]] = None + details: typing.Optional[typing.Dict[str, typing.Any]] = None message: typing.Optional[str] = None + param: typing.Optional[str] = None + request_id: typing.Optional[str] = None if IS_PYDANTIC_V2: model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 diff --git a/src/zep_cloud/types/graph.py b/src/zep_cloud/types/graph.py index dd05ec34..c321f5bc 100644 --- a/src/zep_cloud/types/graph.py +++ b/src/zep_cloud/types/graph.py @@ -12,12 +12,14 @@ class Graph(UniversalBaseModel): created_at: typing.Optional[str] = None description: typing.Optional[str] = None graph_id: typing.Optional[str] = None - id: typing.Optional[int] = None name: typing.Optional[str] = None - project_uuid: typing.Optional[str] = None time_zone: typing.Optional[str] = None + type: typing.Optional[str] = None updated_at: typing.Optional[str] = None - uuid_: typing_extensions.Annotated[typing.Optional[str], FieldMetadata(alias="uuid")] = None + user_uuid: typing.Optional[str] = None + uuid_: typing_extensions.Annotated[ + typing.Optional[str], FieldMetadata(alias="uuid"), pydantic.Field(alias="uuid") + ] = None if IS_PYDANTIC_V2: model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 diff --git a/src/zep_cloud/types/add_thread_messages_response.py b/src/zep_cloud/types/graph_context_response.py similarity index 72% rename from src/zep_cloud/types/add_thread_messages_response.py rename to src/zep_cloud/types/graph_context_response.py index b5d320e3..a8d0c1ac 100644 --- a/src/zep_cloud/types/add_thread_messages_response.py +++ b/src/zep_cloud/types/graph_context_response.py @@ -4,12 +4,13 @@ import pydantic from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel +from .context_results import ContextResults -class AddThreadMessagesResponse(UniversalBaseModel): +class GraphContextResponse(UniversalBaseModel): context: typing.Optional[str] = None - message_uuids: typing.Optional[typing.List[str]] = None - task_id: typing.Optional[str] = None + results: typing.Optional[ContextResults] = None + truncated: typing.Optional[bool] = None if IS_PYDANTIC_V2: model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 diff --git a/src/zep_cloud/types/graph_data_type.py b/src/zep_cloud/types/graph_data_type.py deleted file mode 100644 index 8797795a..00000000 --- a/src/zep_cloud/types/graph_data_type.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -GraphDataType = typing.Union[typing.Literal["text", "json", "message", "fact_triple"], typing.Any] diff --git a/src/zep_cloud/types/observation_type.py b/src/zep_cloud/types/graph_delete_result.py similarity index 81% rename from src/zep_cloud/types/observation_type.py rename to src/zep_cloud/types/graph_delete_result.py index 14166bfa..0c10eb61 100644 --- a/src/zep_cloud/types/observation_type.py +++ b/src/zep_cloud/types/graph_delete_result.py @@ -4,11 +4,11 @@ import pydantic from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel +from .task import Task -class ObservationType(UniversalBaseModel): - description: str - name: str +class GraphDeleteResult(UniversalBaseModel): + task: typing.Optional[Task] = None if IS_PYDANTIC_V2: model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 diff --git a/src/zep_cloud/types/graph_edges_request.py b/src/zep_cloud/types/graph_edges_request.py deleted file mode 100644 index 77cc19a8..00000000 --- a/src/zep_cloud/types/graph_edges_request.py +++ /dev/null @@ -1,58 +0,0 @@ -# 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, update_forward_refs -from .search_filters import SearchFilters - - -class GraphEdgesRequest(UniversalBaseModel): - cursor: typing.Optional[str] = pydantic.Field(default=None) - """ - 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] = pydantic.Field(default=None) - """ - Sort direction. One of "asc" or "desc" (default "desc"). - """ - - filters: typing.Optional[SearchFilters] = pydantic.Field(default=None) - """ - Optional filters applied to the listed artifacts. Reuses the graph.search filter type. - """ - - limit: typing.Optional[int] = pydantic.Field(default=None) - """ - Maximum number of items to return - """ - - order_by: typing.Optional[str] = pydantic.Field(default=None) - """ - Field to sort by. One of "created_at", "valid_at", or "uuid" (default "uuid"). - """ - - uuid_cursor: typing.Optional[str] = pydantic.Field(default=None) - """ - 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. - """ - - 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 - - -from .metadata_filter_group import MetadataFilterGroup # noqa: E402, F401, I001 - -update_forward_refs(GraphEdgesRequest) diff --git a/src/zep_cloud/types/graph_episode_list_request.py b/src/zep_cloud/types/graph_episode_list_request.py deleted file mode 100644 index e7bd251d..00000000 --- a/src/zep_cloud/types/graph_episode_list_request.py +++ /dev/null @@ -1,45 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -import pydantic -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel - - -class GraphEpisodeListRequest(UniversalBaseModel): - cursor: typing.Optional[str] = pydantic.Field(default=None) - """ - Opaque cursor for pagination, obtained from the Zep-Next-Cursor - response header of the previous page. - """ - - direction: typing.Optional[str] = pydantic.Field(default=None) - """ - Sort direction. One of "asc" or "desc". Defaults to "desc". - """ - - limit: typing.Optional[int] = pydantic.Field(default=None) - """ - 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.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. - """ - - order_by: typing.Optional[str] = pydantic.Field(default=None) - """ - Field to sort by. One of "uuid" or "created_at". Defaults to "uuid". - """ - - 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/graph_list_response.py b/src/zep_cloud/types/graph_list_response.py deleted file mode 100644 index eab1a25d..00000000 --- a/src/zep_cloud/types/graph_list_response.py +++ /dev/null @@ -1,24 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -import pydantic -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -from .graph import Graph - - -class GraphListResponse(UniversalBaseModel): - graphs: typing.Optional[typing.List[Graph]] = None - page_number: typing.Optional[int] = None - page_size: typing.Optional[int] = None - row_count: typing.Optional[int] = None - total_count: typing.Optional[int] = None - - 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/graph_node_neighbor.py b/src/zep_cloud/types/graph_node_neighbor.py deleted file mode 100644 index 22e1f369..00000000 --- a/src/zep_cloud/types/graph_node_neighbor.py +++ /dev/null @@ -1,22 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -import pydantic -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -from .entity_edge import EntityEdge -from .entity_node import EntityNode - - -class GraphNodeNeighbor(UniversalBaseModel): - edges: typing.Optional[typing.List[EntityEdge]] = None - node: typing.Optional[EntityNode] = None - - 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/graph_nodes_request.py b/src/zep_cloud/types/graph_nodes_request.py deleted file mode 100644 index e74a760f..00000000 --- a/src/zep_cloud/types/graph_nodes_request.py +++ /dev/null @@ -1,58 +0,0 @@ -# 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, update_forward_refs -from .search_filters import SearchFilters - - -class GraphNodesRequest(UniversalBaseModel): - cursor: typing.Optional[str] = pydantic.Field(default=None) - """ - 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] = pydantic.Field(default=None) - """ - Sort direction. One of "asc" or "desc" (default "desc"). - """ - - filters: typing.Optional[SearchFilters] = pydantic.Field(default=None) - """ - Optional filters applied to the listed artifacts. Reuses the graph.search filter type. - """ - - limit: typing.Optional[int] = pydantic.Field(default=None) - """ - Maximum number of items to return - """ - - order_by: typing.Optional[str] = pydantic.Field(default=None) - """ - Field to sort by. One of "created_at", "valid_at", or "uuid" (default "uuid"). - """ - - uuid_cursor: typing.Optional[str] = pydantic.Field(default=None) - """ - 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. - """ - - 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 - - -from .metadata_filter_group import MetadataFilterGroup # noqa: E402, F401, I001 - -update_forward_refs(GraphNodesRequest) diff --git a/src/zep_cloud/types/graph_observations_request.py b/src/zep_cloud/types/graph_observations_request.py deleted file mode 100644 index 21f81b7e..00000000 --- a/src/zep_cloud/types/graph_observations_request.py +++ /dev/null @@ -1,58 +0,0 @@ -# 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, update_forward_refs -from .search_filters import SearchFilters - - -class GraphObservationsRequest(UniversalBaseModel): - cursor: typing.Optional[str] = pydantic.Field(default=None) - """ - 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] = pydantic.Field(default=None) - """ - Sort direction. One of "asc" or "desc" (default "desc"). - """ - - filters: typing.Optional[SearchFilters] = pydantic.Field(default=None) - """ - Optional filters applied to the listed artifacts. Reuses the graph.search filter type. - """ - - limit: typing.Optional[int] = pydantic.Field(default=None) - """ - Maximum number of items to return - """ - - order_by: typing.Optional[str] = pydantic.Field(default=None) - """ - Field to sort by. One of "created_at", "valid_at", or "uuid" (default "uuid"). - """ - - uuid_cursor: typing.Optional[str] = pydantic.Field(default=None) - """ - 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. - """ - - 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 - - -from .metadata_filter_group import MetadataFilterGroup # noqa: E402, F401, I001 - -update_forward_refs(GraphObservationsRequest) diff --git a/src/zep_cloud/types/graph_page.py b/src/zep_cloud/types/graph_page.py new file mode 100644 index 00000000..acc12f5b --- /dev/null +++ b/src/zep_cloud/types/graph_page.py @@ -0,0 +1,22 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel +from .graph import Graph + + +class GraphPage(UniversalBaseModel): + items: typing.Optional[typing.List[Graph]] = None + next_cursor: typing.Optional[str] = None + total_size: typing.Optional[int] = None + + 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/graph_search_response_metadata.py b/src/zep_cloud/types/graph_search_response_metadata.py deleted file mode 100644 index 0e173e1d..00000000 --- a/src/zep_cloud/types/graph_search_response_metadata.py +++ /dev/null @@ -1,22 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -import pydantic -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel - - -class GraphSearchResponseMetadata(UniversalBaseModel): - server_latency_ms: typing.Optional[int] = pydantic.Field(default=None) - """ - Server-side processing latency in milliseconds. - """ - - 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/graph_search_results.py b/src/zep_cloud/types/graph_search_results.py deleted file mode 100644 index ec1273ae..00000000 --- a/src/zep_cloud/types/graph_search_results.py +++ /dev/null @@ -1,31 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -import pydantic -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -from .derived_node import DerivedNode -from .entity_edge import EntityEdge -from .entity_node import EntityNode -from .episode import Episode -from .graph_search_response_metadata import GraphSearchResponseMetadata -from .graphiti_saga_node import GraphitiSagaNode - - -class GraphSearchResults(UniversalBaseModel): - context: typing.Optional[str] = None - edges: typing.Optional[typing.List[EntityEdge]] = None - episodes: typing.Optional[typing.List[Episode]] = None - nodes: typing.Optional[typing.List[EntityNode]] = None - observations: typing.Optional[typing.List[DerivedNode]] = None - response: typing.Optional[GraphSearchResponseMetadata] = None - thread_summaries: typing.Optional[typing.List[GraphitiSagaNode]] = None - - 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/graph_search_scope.py b/src/zep_cloud/types/graph_search_scope.py deleted file mode 100644 index 3f117827..00000000 --- a/src/zep_cloud/types/graph_search_scope.py +++ /dev/null @@ -1,7 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -GraphSearchScope = typing.Union[ - typing.Literal["edges", "nodes", "episodes", "thread_summaries", "observations", "auto"], typing.Any -] diff --git a/src/zep_cloud/types/graph_subgraph_response.py b/src/zep_cloud/types/graph_subgraph_response.py deleted file mode 100644 index 8be98728..00000000 --- a/src/zep_cloud/types/graph_subgraph_response.py +++ /dev/null @@ -1,41 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -import pydantic -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -from .entity_edge import EntityEdge -from .entity_node import EntityNode - - -class GraphSubgraphResponse(UniversalBaseModel): - edges: typing.Optional[typing.List[EntityEdge]] = pydantic.Field(default=None) - """ - Every traversed edge that passed the request filters. Both endpoints - of every edge are present in Nodes (edge-endpoint closure). - """ - - nodes: typing.Optional[typing.List[EntityNode]] = pydantic.Field(default=None) - """ - Every admitted seed and every node reached within budget. - """ - - truncated: typing.Optional[bool] = pydantic.Field(default=None) - """ - True whenever any budget or internal limit reduced the result. - """ - - truncation_reason: typing.Optional[str] = pydantic.Field(default=None) - """ - Names the binding limit (for example "max_nodes", "max_edges") when - Truncated is true; nil otherwise. - """ - - 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/graph_thread_summaries_request.py b/src/zep_cloud/types/graph_thread_summaries_request.py deleted file mode 100644 index 0f835d73..00000000 --- a/src/zep_cloud/types/graph_thread_summaries_request.py +++ /dev/null @@ -1,58 +0,0 @@ -# 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, update_forward_refs -from .search_filters import SearchFilters - - -class GraphThreadSummariesRequest(UniversalBaseModel): - cursor: typing.Optional[str] = pydantic.Field(default=None) - """ - 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] = pydantic.Field(default=None) - """ - Sort direction. One of "asc" or "desc" (default "desc"). - """ - - filters: typing.Optional[SearchFilters] = pydantic.Field(default=None) - """ - Optional filters applied to the listed artifacts. Reuses the graph.search filter type. - """ - - limit: typing.Optional[int] = pydantic.Field(default=None) - """ - Maximum number of items to return - """ - - order_by: typing.Optional[str] = pydantic.Field(default=None) - """ - Field to sort by. One of "created_at", "valid_at", or "uuid" (default "uuid"). - """ - - uuid_cursor: typing.Optional[str] = pydantic.Field(default=None) - """ - 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. - """ - - 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 - - -from .metadata_filter_group import MetadataFilterGroup # noqa: E402, F401, I001 - -update_forward_refs(GraphThreadSummariesRequest) diff --git a/src/zep_cloud/types/graphiti_metadata_filter_group_type.py b/src/zep_cloud/types/graphiti_metadata_filter_group_type.py deleted file mode 100644 index c788e943..00000000 --- a/src/zep_cloud/types/graphiti_metadata_filter_group_type.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -GraphitiMetadataFilterGroupType = typing.Union[typing.Literal["and", "or"], typing.Any] diff --git a/src/zep_cloud/types/graphiti_saga_node.py b/src/zep_cloud/types/graphiti_saga_node.py deleted file mode 100644 index 739d35a6..00000000 --- a/src/zep_cloud/types/graphiti_saga_node.py +++ /dev/null @@ -1,73 +0,0 @@ -# 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 GraphitiSagaNode(UniversalBaseModel): - created_at: str = pydantic.Field() - """ - Creation time of the node - """ - - labels: typing.Optional[typing.List[str]] = pydantic.Field(default=None) - """ - Labels associated with the node - """ - - last_summarized_at: typing.Optional[str] = pydantic.Field(default=None) - """ - Wall-clock timestamp of the most recent summary update. Used internally - as the watermark for filtering new episodes by ingestion time. - """ - - last_summarized_episode_valid_at: typing.Optional[str] = pydantic.Field(default=None) - """ - Maximum episode reference time (valid_at) covered by the most recent - summary. Use this field — not LastSummarizedAt — when answering "how - recent is this summary's content in event-time?". - """ - - name: str = pydantic.Field() - """ - Name of the node - """ - - relevance: typing.Optional[float] = pydantic.Field(default=None) - """ - Relevance is an experimental rank-aligned score in [0,1] derived from Score via logit transformation. - Only populated when using cross_encoder reranker; omitted for other reranker types (e.g., RRF). - """ - - score: typing.Optional[float] = pydantic.Field(default=None) - """ - Score is the reranker output: sigmoid-distributed logits [0,1] when using cross_encoder reranker, or RRF ordinal rank when using rrf reranker - """ - - selection_rank: typing.Optional[int] = pydantic.Field(default=None) - """ - SelectionRank is the global cross-scope rank assigned by auto scope selection. - """ - - summary: typing.Optional[str] = pydantic.Field(default=None) - """ - Incremental summary of the thread. - """ - - uuid_: typing_extensions.Annotated[str, FieldMetadata(alias="uuid")] = pydantic.Field() - """ - UUID of the 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/instructions.py b/src/zep_cloud/types/instructions.py new file mode 100644 index 00000000..a308ddc6 --- /dev/null +++ b/src/zep_cloud/types/instructions.py @@ -0,0 +1,20 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel + + +class Instructions(UniversalBaseModel): + inherited: typing.Optional[bool] = None + instructions: typing.Optional[typing.List[typing.Dict[str, typing.Any]]] = None + + 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/list_context_templates_response.py b/src/zep_cloud/types/list_context_templates_response.py deleted file mode 100644 index 8b185456..00000000 --- a/src/zep_cloud/types/list_context_templates_response.py +++ /dev/null @@ -1,20 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -import pydantic -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -from .context_template_response import ContextTemplateResponse - - -class ListContextTemplatesResponse(UniversalBaseModel): - templates: typing.Optional[typing.List[ContextTemplateResponse]] = None - - 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/list_custom_instructions_response.py b/src/zep_cloud/types/list_custom_instructions_response.py deleted file mode 100644 index e7ac97ab..00000000 --- a/src/zep_cloud/types/list_custom_instructions_response.py +++ /dev/null @@ -1,20 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -import pydantic -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -from .custom_instruction import CustomInstruction - - -class ListCustomInstructionsResponse(UniversalBaseModel): - instructions: typing.Optional[typing.List[CustomInstruction]] = None - - 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/lookup_batch_response.py b/src/zep_cloud/types/lookup_batch_response.py new file mode 100644 index 00000000..3aff393d --- /dev/null +++ b/src/zep_cloud/types/lookup_batch_response.py @@ -0,0 +1,20 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel +from .lookup_item import LookupItem + + +class LookupBatchResponse(UniversalBaseModel): + items: typing.Optional[typing.List[LookupItem]] = None + + 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/lookup_item.py b/src/zep_cloud/types/lookup_item.py new file mode 100644 index 00000000..20775236 --- /dev/null +++ b/src/zep_cloud/types/lookup_item.py @@ -0,0 +1,26 @@ +# 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 LookupItem(UniversalBaseModel): + found: typing.Optional[bool] = None + legacy_id: typing.Optional[str] = None + resource_type: typing.Optional[str] = None + uuid_: typing_extensions.Annotated[ + typing.Optional[str], FieldMetadata(alias="uuid"), pydantic.Field(alias="uuid") + ] = None + + 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/lookup_request.py b/src/zep_cloud/types/lookup_request.py new file mode 100644 index 00000000..6783e396 --- /dev/null +++ b/src/zep_cloud/types/lookup_request.py @@ -0,0 +1,21 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel + + +class LookupRequest(UniversalBaseModel): + graph_id: typing.Optional[str] = None + thread_id: typing.Optional[str] = None + user_id: typing.Optional[str] = None + + 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/message.py b/src/zep_cloud/types/message.py index 6fa51810..4cb810fd 100644 --- a/src/zep_cloud/types/message.py +++ b/src/zep_cloud/types/message.py @@ -6,44 +6,19 @@ import typing_extensions from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel from ..core.serialization import FieldMetadata -from .role_type import RoleType class Message(UniversalBaseModel): - content: str = pydantic.Field() - """ - The content of the message. - """ - - created_at: typing.Optional[str] = pydantic.Field(default=None) - """ - The timestamp of when the message was created. - """ - - metadata: typing.Optional[typing.Dict[str, typing.Optional[typing.Any]]] = pydantic.Field(default=None) - """ - The metadata associated with the message. - """ - - name: typing.Optional[str] = pydantic.Field(default=None) - """ - Customizable name of the sender of the message (e.g., "john", "sales_agent"). - """ - - processed: typing.Optional[bool] = pydantic.Field(default=None) - """ - Whether the message has been processed. - """ - - role: RoleType = pydantic.Field() - """ - The role of message sender (e.g., "user", "system"). - """ - - uuid_: typing_extensions.Annotated[typing.Optional[str], FieldMetadata(alias="uuid")] = pydantic.Field(default=None) - """ - The unique identifier of the message. - """ + content: typing.Optional[str] = None + created_at: typing.Optional[str] = None + metadata: typing.Optional[typing.Dict[str, typing.Any]] = None + name: typing.Optional[str] = None + processed: typing.Optional[bool] = None + role: typing.Optional[str] = None + thread_uuid: typing.Optional[str] = None + uuid_: typing_extensions.Annotated[ + typing.Optional[str], FieldMetadata(alias="uuid"), pydantic.Field(alias="uuid") + ] = None if IS_PYDANTIC_V2: model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 diff --git a/src/zep_cloud/types/message_list_response.py b/src/zep_cloud/types/message_list_response.py deleted file mode 100644 index 0e6edb3e..00000000 --- a/src/zep_cloud/types/message_list_response.py +++ /dev/null @@ -1,48 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -import pydantic -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -from .message import Message - - -class MessageListResponse(UniversalBaseModel): - messages: typing.Optional[typing.List[Message]] = pydantic.Field(default=None) - """ - A list of message objects. - """ - - row_count: typing.Optional[int] = pydantic.Field(default=None) - """ - The number of messages returned. - """ - - thread_created_at: typing.Optional[str] = pydantic.Field(default=None) - """ - The thread creation timestamp. - """ - - total_count: typing.Optional[int] = pydantic.Field(default=None) - """ - The total number of messages. - """ - - user_id: typing.Optional[str] = pydantic.Field(default=None) - """ - The user ID associated with this thread. - """ - - user_uuid: typing.Optional[str] = pydantic.Field(default=None) - """ - The opaque user identifier used by dashboard routes. - """ - - 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/message_page.py b/src/zep_cloud/types/message_page.py new file mode 100644 index 00000000..7da041cb --- /dev/null +++ b/src/zep_cloud/types/message_page.py @@ -0,0 +1,22 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel +from .message import Message + + +class MessagePage(UniversalBaseModel): + items: typing.Optional[typing.List[Message]] = None + next_cursor: typing.Optional[str] = None + total_size: typing.Optional[int] = None + + 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/metadata_filter_group.py b/src/zep_cloud/types/metadata_filter_group.py deleted file mode 100644 index 4c662e35..00000000 --- a/src/zep_cloud/types/metadata_filter_group.py +++ /dev/null @@ -1,39 +0,0 @@ -# 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, update_forward_refs -from .episode_metadata_filter import EpisodeMetadataFilter -from .graphiti_metadata_filter_group_type import GraphitiMetadataFilterGroupType - - -class MetadataFilterGroup(UniversalBaseModel): - filters: typing.Optional[typing.List[EpisodeMetadataFilter]] = pydantic.Field(default=None) - """ - Leaf filters (predicates on metadata key-value pairs) - """ - - groups: typing.Optional[typing.List["MetadataFilterGroup"]] = pydantic.Field(default=None) - """ - Nested sub-groups for composing complex boolean expressions - """ - - type: GraphitiMetadataFilterGroupType = pydantic.Field() - """ - Logical operator: "and" or "or" - """ - - 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 - - -update_forward_refs(MetadataFilterGroup) diff --git a/src/zep_cloud/types/neighbor_entry.py b/src/zep_cloud/types/neighbor_entry.py new file mode 100644 index 00000000..9898fbd7 --- /dev/null +++ b/src/zep_cloud/types/neighbor_entry.py @@ -0,0 +1,22 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel +from .edge import Edge +from .node import Node + + +class NeighborEntry(UniversalBaseModel): + edges: typing.Optional[typing.List[Edge]] = None + node: typing.Optional[Node] = None + + 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/neighbor_page.py b/src/zep_cloud/types/neighbor_page.py new file mode 100644 index 00000000..e353facc --- /dev/null +++ b/src/zep_cloud/types/neighbor_page.py @@ -0,0 +1,22 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel +from .neighbor_entry import NeighborEntry + + +class NeighborPage(UniversalBaseModel): + items: typing.Optional[typing.List[NeighborEntry]] = None + next_cursor: typing.Optional[str] = None + total_size: typing.Optional[int] = None + + 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/user_group.py b/src/zep_cloud/types/node.py similarity index 55% rename from src/zep_cloud/types/user_group.py rename to src/zep_cloud/types/node.py index dbae6dea..0a8c9dee 100644 --- a/src/zep_cloud/types/user_group.py +++ b/src/zep_cloud/types/node.py @@ -6,20 +6,20 @@ import typing_extensions from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel from ..core.serialization import FieldMetadata -from .user_group_kind import UserGroupKind -class UserGroup(UniversalBaseModel): - attached_policy_set_count: typing.Optional[int] = None +class Node(UniversalBaseModel): + attributes: typing.Optional[typing.Dict[str, typing.Any]] = None created_at: typing.Optional[str] = None - description: typing.Optional[str] = None - kind: typing.Optional[UserGroupKind] = None - member_count: typing.Optional[int] = None + graph_uuid: typing.Optional[str] = None + labels: typing.Optional[typing.List[str]] = None name: typing.Optional[str] = None - project_uuid: typing.Optional[str] = None - updated_at: typing.Optional[str] = None - uuid_: typing_extensions.Annotated[typing.Optional[str], FieldMetadata(alias="uuid")] = None - version: typing.Optional[int] = None + relevance: typing.Optional[float] = None + score: typing.Optional[float] = None + summary: typing.Optional[str] = None + uuid_: typing_extensions.Annotated[ + typing.Optional[str], FieldMetadata(alias="uuid"), pydantic.Field(alias="uuid") + ] = None if IS_PYDANTIC_V2: model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 diff --git a/src/zep_cloud/types/attached_policy_sets_response.py b/src/zep_cloud/types/node_page.py similarity index 69% rename from src/zep_cloud/types/attached_policy_sets_response.py rename to src/zep_cloud/types/node_page.py index 6a53fede..020bb73f 100644 --- a/src/zep_cloud/types/attached_policy_sets_response.py +++ b/src/zep_cloud/types/node_page.py @@ -4,11 +4,13 @@ import pydantic from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -from .policy_set_summary_response import PolicySetSummaryResponse +from .node import Node -class AttachedPolicySetsResponse(UniversalBaseModel): - policy_sets: typing.Optional[typing.List[PolicySetSummaryResponse]] = None +class NodePage(UniversalBaseModel): + items: typing.Optional[typing.List[Node]] = None + next_cursor: typing.Optional[str] = None + total_size: typing.Optional[int] = None if IS_PYDANTIC_V2: model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 diff --git a/src/zep_cloud/types/observation.py b/src/zep_cloud/types/observation.py new file mode 100644 index 00000000..3c07f063 --- /dev/null +++ b/src/zep_cloud/types/observation.py @@ -0,0 +1,32 @@ +# 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 Observation(UniversalBaseModel): + attributes: typing.Optional[typing.Dict[str, typing.Any]] = None + created_at: typing.Optional[str] = None + episode_uuids: typing.Optional[typing.List[str]] = None + graph_uuid: typing.Optional[str] = None + labels: typing.Optional[typing.List[str]] = None + name: typing.Optional[str] = None + relevance: typing.Optional[float] = None + score: typing.Optional[float] = None + summary: typing.Optional[str] = None + uuid_: typing_extensions.Annotated[ + typing.Optional[str], FieldMetadata(alias="uuid"), pydantic.Field(alias="uuid") + ] = None + + 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/observation_page.py b/src/zep_cloud/types/observation_page.py new file mode 100644 index 00000000..7e618c12 --- /dev/null +++ b/src/zep_cloud/types/observation_page.py @@ -0,0 +1,22 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel +from .observation import Observation + + +class ObservationPage(UniversalBaseModel): + items: typing.Optional[typing.List[Observation]] = None + next_cursor: typing.Optional[str] = None + total_size: typing.Optional[int] = None + + 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/observation_steering_config.py b/src/zep_cloud/types/observation_steering.py similarity index 75% rename from src/zep_cloud/types/observation_steering_config.py rename to src/zep_cloud/types/observation_steering.py index ad1d41df..f82f1abd 100644 --- a/src/zep_cloud/types/observation_steering_config.py +++ b/src/zep_cloud/types/observation_steering.py @@ -4,12 +4,12 @@ import pydantic from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -from .observation_type import ObservationType -class ObservationSteeringConfig(UniversalBaseModel): +class ObservationSteering(UniversalBaseModel): + inherited: typing.Optional[bool] = None instruction: typing.Optional[str] = None - types: typing.Optional[typing.List[ObservationType]] = None + types: typing.Optional[typing.List[typing.Dict[str, typing.Any]]] = None if IS_PYDANTIC_V2: model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 diff --git a/src/zep_cloud/types/entity_type_response.py b/src/zep_cloud/types/ontology.py similarity index 89% rename from src/zep_cloud/types/entity_type_response.py rename to src/zep_cloud/types/ontology.py index 43c3032e..fce76f60 100644 --- a/src/zep_cloud/types/entity_type_response.py +++ b/src/zep_cloud/types/ontology.py @@ -8,9 +8,10 @@ from .entity_type import EntityType -class EntityTypeResponse(UniversalBaseModel): +class Ontology(UniversalBaseModel): edge_types: typing.Optional[typing.List[EdgeType]] = None entity_types: typing.Optional[typing.List[EntityType]] = None + inherited: typing.Optional[bool] = None if IS_PYDANTIC_V2: model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 diff --git a/src/zep_cloud/types/path_detect_config.py b/src/zep_cloud/types/path_detect_config.py deleted file mode 100644 index e5921b09..00000000 --- a/src/zep_cloud/types/path_detect_config.py +++ /dev/null @@ -1,22 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -import pydantic -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel - - -class PathDetectConfig(UniversalBaseModel): - max_hops: typing.Optional[int] = pydantic.Field(default=None) - """ - Max hops from seed nodes for path detection. Default: 3, Max: 5 - """ - - 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/pattern_metadata.py b/src/zep_cloud/types/pattern_metadata.py deleted file mode 100644 index cfc36bdd..00000000 --- a/src/zep_cloud/types/pattern_metadata.py +++ /dev/null @@ -1,32 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -import pydantic -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel - - -class PatternMetadata(UniversalBaseModel): - edges_analyzed: typing.Optional[int] = pydantic.Field(default=None) - """ - Number of edges analyzed - """ - - elapsed_ms: typing.Optional[int] = pydantic.Field(default=None) - """ - Elapsed time in milliseconds - """ - - nodes_analyzed: typing.Optional[int] = pydantic.Field(default=None) - """ - Number of unique nodes analyzed - """ - - 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/pattern_result.py b/src/zep_cloud/types/pattern_result.py deleted file mode 100644 index d6beb767..00000000 --- a/src/zep_cloud/types/pattern_result.py +++ /dev/null @@ -1,63 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -import pydantic -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -from .entity_edge import EntityEdge - - -class PatternResult(UniversalBaseModel): - description: typing.Optional[str] = pydantic.Field(default=None) - """ - Human-readable structural description of the pattern (e.g. "Person -[KNOWS]-> Person"). - Omitted in query mode in favor of Summary. - """ - - edge_types: typing.Optional[typing.List[str]] = pydantic.Field(default=None) - """ - Edge types in the pattern structure - """ - - edges: typing.Optional[typing.List[EntityEdge]] = pydantic.Field(default=None) - """ - Resolved edges for this pattern, sorted by cross-encoder relevance. - Only populated when query is set. - """ - - node_labels: typing.Optional[typing.List[str]] = pydantic.Field(default=None) - """ - Node labels in the pattern structure - """ - - occurrences: typing.Optional[int] = pydantic.Field(default=None) - """ - Raw structural occurrence count (always unweighted). - Reflects pattern frequency in the graph, not the number of resolved edges after filtering. - """ - - summary: typing.Optional[str] = pydantic.Field(default=None) - """ - Fact-derived summary from top reranked edges. Only populated when query is set. - This is the primary display field for QA consumers. - """ - - type: typing.Optional[str] = pydantic.Field(default=None) - """ - Pattern type: relationship, path, co_occurrence, hub, cluster - """ - - weighted_score: typing.Optional[float] = pydantic.Field(default=None) - """ - Weighted structural support — equals occurrences when recency_weight is "none". - Reflects graph-level support, not post-enrichment edge count. - """ - - 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/pattern_seeds.py b/src/zep_cloud/types/pattern_seeds.py deleted file mode 100644 index 40b43a4d..00000000 --- a/src/zep_cloud/types/pattern_seeds.py +++ /dev/null @@ -1,32 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -import pydantic -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel - - -class PatternSeeds(UniversalBaseModel): - edge_types: typing.Optional[typing.List[str]] = pydantic.Field(default=None) - """ - All endpoints of these edge types become seeds - """ - - node_labels: typing.Optional[typing.List[str]] = pydantic.Field(default=None) - """ - All nodes with these labels become seeds - """ - - node_uuids: typing.Optional[typing.List[str]] = pydantic.Field(default=None) - """ - Specific node UUIDs to analyze around. Max 10000 to align with pattern detection seed limits. - """ - - 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/list_user_instructions_response.py b/src/zep_cloud/types/process_batch_result.py similarity index 72% rename from src/zep_cloud/types/list_user_instructions_response.py rename to src/zep_cloud/types/process_batch_result.py index b8b27f27..3930882b 100644 --- a/src/zep_cloud/types/list_user_instructions_response.py +++ b/src/zep_cloud/types/process_batch_result.py @@ -4,11 +4,13 @@ import pydantic from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -from .user_instruction import UserInstruction +from .batch import Batch +from .task import Task -class ListUserInstructionsResponse(UniversalBaseModel): - instructions: typing.Optional[typing.List[UserInstruction]] = None +class ProcessBatchResult(UniversalBaseModel): + batch: typing.Optional[Batch] = None + task: typing.Optional[Task] = None if IS_PYDANTIC_V2: model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 diff --git a/src/zep_cloud/types/project_info.py b/src/zep_cloud/types/project.py similarity index 80% rename from src/zep_cloud/types/project_info.py rename to src/zep_cloud/types/project.py index 86efb2c3..9b82f0b2 100644 --- a/src/zep_cloud/types/project_info.py +++ b/src/zep_cloud/types/project.py @@ -8,12 +8,14 @@ from ..core.serialization import FieldMetadata -class ProjectInfo(UniversalBaseModel): +class Project(UniversalBaseModel): created_at: typing.Optional[str] = None default_time_zone: typing.Optional[str] = None description: typing.Optional[str] = None name: typing.Optional[str] = None - uuid_: typing_extensions.Annotated[typing.Optional[str], FieldMetadata(alias="uuid")] = None + uuid_: typing_extensions.Annotated[ + typing.Optional[str], FieldMetadata(alias="uuid"), pydantic.Field(alias="uuid") + ] = None if IS_PYDANTIC_V2: model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 diff --git a/src/zep_cloud/types/project_info_response.py b/src/zep_cloud/types/project_info_response.py deleted file mode 100644 index fff68af8..00000000 --- a/src/zep_cloud/types/project_info_response.py +++ /dev/null @@ -1,20 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -import pydantic -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -from .project_info import ProjectInfo - - -class ProjectInfoResponse(UniversalBaseModel): - project: typing.Optional[ProjectInfo] = None - - 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/property_filter.py b/src/zep_cloud/types/property_filter.py deleted file mode 100644 index eda06aad..00000000 --- a/src/zep_cloud/types/property_filter.py +++ /dev/null @@ -1,35 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -import pydantic -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -from .comparison_operator import ComparisonOperator - - -class PropertyFilter(UniversalBaseModel): - comparison_operator: ComparisonOperator = pydantic.Field() - """ - Comparison operator for property filter - """ - - property_name: str = pydantic.Field() - """ - Property name to filter on - """ - - property_value: typing.Optional[typing.Optional[typing.Any]] = pydantic.Field(default=None) - """ - Property value to match on. Accepted types: string, int, float64, bool, or nil. - Invalid types (e.g., arrays, objects) will be rejected by validation. - Must be non-nil for non-null operators (`=`, `<>`, `>`, `<`, `>=`, `<=`). - """ - - 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/recency_weight.py b/src/zep_cloud/types/recency_weight.py deleted file mode 100644 index 18d33ada..00000000 --- a/src/zep_cloud/types/recency_weight.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -RecencyWeight = typing.Union[typing.Literal["none", "7_days", "30_days", "90_days"], typing.Any] diff --git a/src/zep_cloud/types/relationship_detect_config.py b/src/zep_cloud/types/relationship_detect_config.py deleted file mode 100644 index 6339bd36..00000000 --- a/src/zep_cloud/types/relationship_detect_config.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -RelationshipDetectConfig = typing.Dict[str, typing.Optional[typing.Any]] diff --git a/src/zep_cloud/types/reranker.py b/src/zep_cloud/types/reranker.py deleted file mode 100644 index 4a627b5e..00000000 --- a/src/zep_cloud/types/reranker.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -Reranker = typing.Union[typing.Literal["rrf", "mmr", "node_distance", "episode_mentions", "cross_encoder"], typing.Any] diff --git a/src/zep_cloud/types/role_type.py b/src/zep_cloud/types/role_type.py deleted file mode 100644 index f7658d6a..00000000 --- a/src/zep_cloud/types/role_type.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -RoleType = typing.Union[typing.Literal["norole", "system", "assistant", "user", "function", "tool"], typing.Any] diff --git a/src/zep_cloud/types/search_filters.py b/src/zep_cloud/types/search_filters.py deleted file mode 100644 index 50954d04..00000000 --- a/src/zep_cloud/types/search_filters.py +++ /dev/null @@ -1,127 +0,0 @@ -# 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, update_forward_refs -from .date_filter import DateFilter -from .property_filter import PropertyFilter - - -class SearchFilters(UniversalBaseModel): - connected_node_uuids: typing.Optional[typing.List[str]] = pydantic.Field(default=None) - """ - List of node UUIDs to filter edges on: an edge matches if its source OR - target node UUID is in this list. Applies to edges only; rejected on - requests whose result type contains no edges. Max 256 entries. - """ - - created_at: typing.Optional[typing.List[typing.List[DateFilter]]] = pydantic.Field(default=None) - """ - 2D array of date filters for the created_at field. - The outer array elements are combined with OR logic. - The inner array elements are combined with AND logic. - Example: `[[{">", date1}, {"<", date2}], [{"=", date3}]]` - This translates to: `(created_at > date1 AND created_at < date2) OR (created_at = date3)` - """ - - edge_types: typing.Optional[typing.List[str]] = pydantic.Field(default=None) - """ - List of edge types to filter on - """ - - edge_uuids: typing.Optional[typing.List[str]] = pydantic.Field(default=None) - """ - List of edge UUIDs to filter on. Max 256 to align with graph-service filter limits. - """ - - episode_metadata_filters: typing.Optional["MetadataFilterGroup"] = pydantic.Field(default=None) - """ - [Experimental] Episode metadata filter. Restricts results to edges/nodes derived from episodes - matching the metadata predicates. Uses explicit AND/OR groups. This feature is experimental and may change in future releases. - """ - - episode_uuids: typing.Optional[typing.List[str]] = pydantic.Field(default=None) - """ - List of episode UUIDs to filter on. An edge matches if it was derived - from any listed episode; a node matches if it is mentioned by any - listed episode. Valid for both edge and node result types. Max 256 - entries. - """ - - exclude_edge_types: typing.Optional[typing.List[str]] = pydantic.Field(default=None) - """ - List of edge types to exclude from results - """ - - exclude_node_labels: typing.Optional[typing.List[str]] = pydantic.Field(default=None) - """ - List of node labels to exclude from results - """ - - expired_at: typing.Optional[typing.List[typing.List[DateFilter]]] = pydantic.Field(default=None) - """ - 2D array of date filters for the expired_at field. - The outer array elements are combined with OR logic. - The inner array elements are combined with AND logic. - Example: `[[{">", date1}, {"<", date2}], [{"=", date3}]]` - This translates to: `(expired_at > date1 AND expired_at < date2) OR (expired_at = date3)` - """ - - invalid_at: typing.Optional[typing.List[typing.List[DateFilter]]] = pydantic.Field(default=None) - """ - 2D array of date filters for the invalid_at field. - The outer array elements are combined with OR logic. - The inner array elements are combined with AND logic. - Example: `[[{">", date1}, {"<", date2}], [{"=", date3}]]` - This translates to: `(invalid_at > date1 AND invalid_at < date2) OR (invalid_at = date3)` - """ - - node_labels: typing.Optional[typing.List[str]] = pydantic.Field(default=None) - """ - List of node labels to filter on - """ - - property_filters: typing.Optional[typing.List[PropertyFilter]] = pydantic.Field(default=None) - """ - List of property filters to apply to nodes and edges - """ - - source_node_uuids: typing.Optional[typing.List[str]] = pydantic.Field(default=None) - """ - List of node UUIDs to filter edges on: an edge matches if its source - node UUID is in this list. Applies to edges only; rejected on requests - whose result type contains no edges. Max 256 entries. - """ - - target_node_uuids: typing.Optional[typing.List[str]] = pydantic.Field(default=None) - """ - List of node UUIDs to filter edges on: an edge matches if its target - node UUID is in this list. Applies to edges only; rejected on requests - whose result type contains no edges. Max 256 entries. - """ - - valid_at: typing.Optional[typing.List[typing.List[DateFilter]]] = pydantic.Field(default=None) - """ - 2D array of date filters for the valid_at field. - The outer array elements are combined with OR logic. - The inner array elements are combined with AND logic. - Example: `[[{">", date1}, {"<", date2}], [{"=", date3}]]` - This translates to: `(valid_at > date1 AND valid_at < date2) OR (valid_at = date3)` - """ - - 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 - - -from .metadata_filter_group import MetadataFilterGroup # noqa: E402, F401, I001 - -update_forward_refs(SearchFilters) diff --git a/src/zep_cloud/types/search_request.py b/src/zep_cloud/types/search_request.py new file mode 100644 index 00000000..6faa83d2 --- /dev/null +++ b/src/zep_cloud/types/search_request.py @@ -0,0 +1,24 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel + + +class SearchRequest(UniversalBaseModel): + bfs_origin_node_uuids: typing.Optional[typing.List[str]] = None + center_node_uuid: typing.Optional[str] = None + filters: typing.Optional[typing.Dict[str, typing.Any]] = None + mmr_lambda: typing.Optional[float] = None + query: typing.Optional[str] = None + reranker: typing.Optional[str] = None + + 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/subgraph_response.py b/src/zep_cloud/types/subgraph_response.py new file mode 100644 index 00000000..b04fca04 --- /dev/null +++ b/src/zep_cloud/types/subgraph_response.py @@ -0,0 +1,24 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel +from .edge import Edge +from .node import Node + + +class SubgraphResponse(UniversalBaseModel): + edges: typing.Optional[typing.List[Edge]] = None + nodes: typing.Optional[typing.List[Node]] = None + truncated: typing.Optional[bool] = None + truncation_reason: typing.Optional[str] = None + + 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/get_task_response.py b/src/zep_cloud/types/task.py similarity index 67% rename from src/zep_cloud/types/get_task_response.py rename to src/zep_cloud/types/task.py index 10e5e310..97f0790a 100644 --- a/src/zep_cloud/types/get_task_response.py +++ b/src/zep_cloud/types/task.py @@ -3,22 +3,26 @@ import typing import pydantic +import typing_extensions from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -from .task_error_response import TaskErrorResponse +from ..core.serialization import FieldMetadata +from .error_body import ErrorBody from .task_progress import TaskProgress -class GetTaskResponse(UniversalBaseModel): +class Task(UniversalBaseModel): completed_at: typing.Optional[str] = None created_at: typing.Optional[str] = None - error: typing.Optional[TaskErrorResponse] = None - params: typing.Optional[typing.Dict[str, typing.Optional[typing.Any]]] = None + error: typing.Optional[ErrorBody] = None progress: typing.Optional[TaskProgress] = None + result: typing.Optional[typing.Dict[str, typing.Any]] = None started_at: typing.Optional[str] = None status: typing.Optional[str] = None - task_id: typing.Optional[str] = None type: typing.Optional[str] = None updated_at: typing.Optional[str] = None + uuid_: typing_extensions.Annotated[ + typing.Optional[str], FieldMetadata(alias="uuid"), pydantic.Field(alias="uuid") + ] = None if IS_PYDANTIC_V2: model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 diff --git a/src/zep_cloud/types/task_page.py b/src/zep_cloud/types/task_page.py new file mode 100644 index 00000000..1cdb4caf --- /dev/null +++ b/src/zep_cloud/types/task_page.py @@ -0,0 +1,22 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel +from .task import Task + + +class TaskPage(UniversalBaseModel): + items: typing.Optional[typing.List[Task]] = None + next_cursor: typing.Optional[str] = None + total_size: typing.Optional[int] = None + + 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/task_progress.py b/src/zep_cloud/types/task_progress.py index 1172079a..460a7af3 100644 --- a/src/zep_cloud/types/task_progress.py +++ b/src/zep_cloud/types/task_progress.py @@ -7,7 +7,6 @@ class TaskProgress(UniversalBaseModel): - message: typing.Optional[str] = None stage: typing.Optional[str] = None if IS_PYDANTIC_V2: diff --git a/src/zep_cloud/types/thread.py b/src/zep_cloud/types/thread.py index 964b51c9..d7f0008e 100644 --- a/src/zep_cloud/types/thread.py +++ b/src/zep_cloud/types/thread.py @@ -10,11 +10,13 @@ class Thread(UniversalBaseModel): created_at: typing.Optional[str] = None - project_uuid: typing.Optional[str] = None + graph_uuid: typing.Optional[str] = None thread_id: typing.Optional[str] = None - user_id: typing.Optional[str] = None + updated_at: typing.Optional[str] = None user_uuid: typing.Optional[str] = None - uuid_: typing_extensions.Annotated[typing.Optional[str], FieldMetadata(alias="uuid")] = None + uuid_: typing_extensions.Annotated[ + typing.Optional[str], FieldMetadata(alias="uuid"), pydantic.Field(alias="uuid") + ] = None if IS_PYDANTIC_V2: model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 diff --git a/src/zep_cloud/types/thread_context_response.py b/src/zep_cloud/types/thread_context_response.py index f6cc4d9b..48d8132c 100644 --- a/src/zep_cloud/types/thread_context_response.py +++ b/src/zep_cloud/types/thread_context_response.py @@ -7,10 +7,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: typing.Optional[str] = None if IS_PYDANTIC_V2: model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 diff --git a/src/zep_cloud/types/success_response.py b/src/zep_cloud/types/thread_delete_result.py similarity index 81% rename from src/zep_cloud/types/success_response.py rename to src/zep_cloud/types/thread_delete_result.py index 08c507ea..28e377ea 100644 --- a/src/zep_cloud/types/success_response.py +++ b/src/zep_cloud/types/thread_delete_result.py @@ -4,10 +4,11 @@ import pydantic from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel +from .task import Task -class SuccessResponse(UniversalBaseModel): - message: typing.Optional[str] = None +class ThreadDeleteResult(UniversalBaseModel): + task: typing.Optional[Task] = None if IS_PYDANTIC_V2: model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 diff --git a/src/zep_cloud/types/thread_list_response.py b/src/zep_cloud/types/thread_page.py similarity index 71% rename from src/zep_cloud/types/thread_list_response.py rename to src/zep_cloud/types/thread_page.py index 8cbb2b6e..4ca285d2 100644 --- a/src/zep_cloud/types/thread_list_response.py +++ b/src/zep_cloud/types/thread_page.py @@ -7,10 +7,10 @@ from .thread import Thread -class ThreadListResponse(UniversalBaseModel): - response_count: typing.Optional[int] = None - threads: typing.Optional[typing.List[Thread]] = None - total_count: typing.Optional[int] = None +class ThreadPage(UniversalBaseModel): + items: typing.Optional[typing.List[Thread]] = None + next_cursor: typing.Optional[str] = None + total_size: typing.Optional[int] = None if IS_PYDANTIC_V2: model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 diff --git a/src/zep_cloud/types/thread_summary.py b/src/zep_cloud/types/thread_summary.py index db33c75d..92be1c90 100644 --- a/src/zep_cloud/types/thread_summary.py +++ b/src/zep_cloud/types/thread_summary.py @@ -9,43 +9,21 @@ class ThreadSummary(UniversalBaseModel): - created_at: typing.Optional[str] = pydantic.Field(default=None) + created_at: typing.Optional[str] = None + last_summarized_at: typing.Optional[str] = None + last_summarized_episode_valid_at: typing.Optional[str] = None + relevance: typing.Optional[float] = None + score: typing.Optional[float] = pydantic.Field(default=None) """ - CreatedAt is when the summary node was first created. + 8.7 puts score on every search result and relevance on the ones the + cross-encoder scored. Pointers keep both off the listing (8.4). """ - last_summarized_at: typing.Optional[str] = pydantic.Field(default=None) - """ - LastSummarizedAt is the wall-clock timestamp of the most recent - summary update. This is an ingestion-time watermark; for the - event-time recency of the summary's content, use - LastSummarizedEpisodeValidAt instead. - """ - - 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. Use this when - answering "how recent is this summary's content in event-time?". - """ - - summary: typing.Optional[str] = pydantic.Field(default=None) - """ - Summary is the incremental summary content. - """ - - thread_id: typing.Optional[str] = pydantic.Field(default=None) - """ - ThreadID is the ID of the thread this summary belongs to. - When a thread was created without an explicit thread_id, this - field falls back to the thread's UUID. Clients should treat it - as an opaque identifier. - """ - - uuid_: typing_extensions.Annotated[typing.Optional[str], FieldMetadata(alias="uuid")] = pydantic.Field(default=None) - """ - UUID of the thread summary node. - """ + summary: typing.Optional[str] = None + thread_uuid: typing.Optional[str] = None + uuid_: typing_extensions.Annotated[ + typing.Optional[str], FieldMetadata(alias="uuid"), pydantic.Field(alias="uuid") + ] = None if IS_PYDANTIC_V2: model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 diff --git a/src/zep_cloud/types/thread_summary_page.py b/src/zep_cloud/types/thread_summary_page.py new file mode 100644 index 00000000..ccc49990 --- /dev/null +++ b/src/zep_cloud/types/thread_summary_page.py @@ -0,0 +1,22 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel +from .thread_summary import ThreadSummary + + +class ThreadSummaryPage(UniversalBaseModel): + items: typing.Optional[typing.List[ThreadSummary]] = None + next_cursor: typing.Optional[str] = None + total_size: typing.Optional[int] = None + + 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/user.py b/src/zep_cloud/types/user.py index 50b8df9a..59fc484e 100644 --- a/src/zep_cloud/types/user.py +++ b/src/zep_cloud/types/user.py @@ -10,31 +10,17 @@ class User(UniversalBaseModel): created_at: typing.Optional[str] = None - deleted_at: typing.Optional[str] = None disable_default_ontology: typing.Optional[bool] = None email: typing.Optional[str] = None first_name: typing.Optional[str] = None - id: typing.Optional[int] = None + graph_uuid: typing.Optional[str] = None last_name: typing.Optional[str] = None - metadata: typing.Optional[typing.Dict[str, typing.Optional[typing.Any]]] = pydantic.Field(default=None) - """ - Deprecated - """ - - project_uuid: typing.Optional[str] = None - session_count: typing.Optional[int] = pydantic.Field(default=None) - """ - Deprecated - """ - + metadata: typing.Optional[typing.Dict[str, typing.Any]] = None time_zone: typing.Optional[str] = None - updated_at: typing.Optional[str] = pydantic.Field(default=None) - """ - Deprecated - """ - user_id: typing.Optional[str] = None - uuid_: typing_extensions.Annotated[typing.Optional[str], FieldMetadata(alias="uuid")] = None + uuid_: typing_extensions.Annotated[ + typing.Optional[str], FieldMetadata(alias="uuid"), pydantic.Field(alias="uuid") + ] = None if IS_PYDANTIC_V2: model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 diff --git a/src/zep_cloud/types/mutate_user_group_members_request.py b/src/zep_cloud/types/user_delete_result.py similarity index 81% rename from src/zep_cloud/types/mutate_user_group_members_request.py rename to src/zep_cloud/types/user_delete_result.py index 26042828..4178a7ab 100644 --- a/src/zep_cloud/types/mutate_user_group_members_request.py +++ b/src/zep_cloud/types/user_delete_result.py @@ -4,10 +4,11 @@ import pydantic from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel +from .task import Task -class MutateUserGroupMembersRequest(UniversalBaseModel): - user_uuids: typing.List[str] +class UserDeleteResult(UniversalBaseModel): + task: typing.Optional[Task] = None if IS_PYDANTIC_V2: model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 diff --git a/src/zep_cloud/types/user_group_kind.py b/src/zep_cloud/types/user_group_kind.py deleted file mode 100644 index 5691b041..00000000 --- a/src/zep_cloud/types/user_group_kind.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -UserGroupKind = typing.Union[typing.Literal["managed", "virtual"], typing.Any] diff --git a/src/zep_cloud/types/user_group_list_response.py b/src/zep_cloud/types/user_group_list_response.py deleted file mode 100644 index 8cc9ee4d..00000000 --- a/src/zep_cloud/types/user_group_list_response.py +++ /dev/null @@ -1,24 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -import pydantic -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -from .user_group import UserGroup -from .user_group_quota import UserGroupQuota - - -class UserGroupListResponse(UniversalBaseModel): - quota: typing.Optional[UserGroupQuota] = None - row_count: typing.Optional[int] = None - total_count: typing.Optional[int] = None - user_groups: typing.Optional[typing.List[UserGroup]] = None - - 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/user_group_members_response.py b/src/zep_cloud/types/user_group_members_response.py deleted file mode 100644 index 5e6aba81..00000000 --- a/src/zep_cloud/types/user_group_members_response.py +++ /dev/null @@ -1,22 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -import pydantic -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -from .user import User - - -class UserGroupMembersResponse(UniversalBaseModel): - row_count: typing.Optional[int] = None - total_count: typing.Optional[int] = None - users: typing.Optional[typing.List[User]] = None - - 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/user_group_membership_response.py b/src/zep_cloud/types/user_group_membership_response.py deleted file mode 100644 index 202da5ec..00000000 --- a/src/zep_cloud/types/user_group_membership_response.py +++ /dev/null @@ -1,21 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -import pydantic -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel - - -class UserGroupMembershipResponse(UniversalBaseModel): - added_count: typing.Optional[int] = None - no_op_count: typing.Optional[int] = None - removed_count: typing.Optional[int] = None - - 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/user_group_quota.py b/src/zep_cloud/types/user_group_quota.py deleted file mode 100644 index c5c13ca7..00000000 --- a/src/zep_cloud/types/user_group_quota.py +++ /dev/null @@ -1,23 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -import pydantic -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel - - -class UserGroupQuota(UniversalBaseModel): - active_managed: typing.Optional[int] = None - allocation: typing.Optional[int] = None - has_feature: typing.Optional[bool] = None - resolved: typing.Optional[bool] = None - unlimited: typing.Optional[bool] = None - - 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/user_instruction.py b/src/zep_cloud/types/user_instruction.py deleted file mode 100644 index fd322630..00000000 --- a/src/zep_cloud/types/user_instruction.py +++ /dev/null @@ -1,20 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -import pydantic -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel - - -class UserInstruction(UniversalBaseModel): - name: str - text: str - - 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/user_membership_groups_response.py b/src/zep_cloud/types/user_membership_groups_response.py deleted file mode 100644 index a087d993..00000000 --- a/src/zep_cloud/types/user_membership_groups_response.py +++ /dev/null @@ -1,20 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -import pydantic -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -from .user_group import UserGroup - - -class UserMembershipGroupsResponse(UniversalBaseModel): - user_groups: typing.Optional[typing.List[UserGroup]] = None - - 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/user_node_response.py b/src/zep_cloud/types/user_node_response.py deleted file mode 100644 index 379eb47f..00000000 --- a/src/zep_cloud/types/user_node_response.py +++ /dev/null @@ -1,20 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -import pydantic -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -from .entity_node import EntityNode - - -class UserNodeResponse(UniversalBaseModel): - node: typing.Optional[EntityNode] = None - - 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/user_list_response.py b/src/zep_cloud/types/user_page.py similarity index 72% rename from src/zep_cloud/types/user_list_response.py rename to src/zep_cloud/types/user_page.py index 0521800c..4c373c00 100644 --- a/src/zep_cloud/types/user_list_response.py +++ b/src/zep_cloud/types/user_page.py @@ -7,10 +7,10 @@ from .user import User -class UserListResponse(UniversalBaseModel): - row_count: typing.Optional[int] = None - total_count: typing.Optional[int] = None - users: typing.Optional[typing.List[User]] = None +class UserPage(UniversalBaseModel): + items: typing.Optional[typing.List[User]] = None + next_cursor: typing.Optional[str] = None + total_size: typing.Optional[int] = None if IS_PYDANTIC_V2: model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 diff --git a/src/zep_cloud/types/user_summary_instructions.py b/src/zep_cloud/types/user_summary_instructions.py new file mode 100644 index 00000000..7d188ae9 --- /dev/null +++ b/src/zep_cloud/types/user_summary_instructions.py @@ -0,0 +1,20 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel + + +class UserSummaryInstructions(UniversalBaseModel): + inherited: typing.Optional[bool] = None + instructions: typing.Optional[typing.List[typing.Dict[str, typing.Any]]] = None + + 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/user/client.py b/src/zep_cloud/user/client.py index e8a84fe4..99b5c97c 100644 --- a/src/zep_cloud/user/client.py +++ b/src/zep_cloud/user/client.py @@ -3,14 +3,13 @@ import typing from ..core.client_wrapper import AsyncClientWrapper, SyncClientWrapper +from ..core.pagination import AsyncPager, SyncPager from ..core.request_options import RequestOptions -from ..types.list_user_instructions_response import ListUserInstructionsResponse -from ..types.success_response import SuccessResponse -from ..types.thread import Thread +from ..types.node import Node from ..types.user import User -from ..types.user_instruction import UserInstruction -from ..types.user_list_response import UserListResponse -from ..types.user_node_response import UserNodeResponse +from ..types.user_delete_result import UserDeleteResult +from ..types.user_page import UserPage +from ..types.user_summary_instructions import UserSummaryInstructions from .raw_client import AsyncRawUserClient, RawUserClient # this is used as the default value for optional parameters @@ -32,111 +31,45 @@ def with_raw_response(self) -> RawUserClient: """ return self._raw_client - def list_user_summary_instructions( - self, *, user_id: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None - ) -> ListUserInstructionsResponse: - """ - Lists all user summary instructions for a project, user. - - Parameters - ---------- - user_id : typing.Optional[str] - User ID to get user-specific instructions - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - ListUserInstructionsResponse - The list of instructions. - - Examples - -------- - from zep_cloud import Zep - - client = Zep( - api_key="YOUR_API_KEY", - ) - client.user.list_user_summary_instructions( - user_id="user_id", - ) - """ - _response = self._raw_client.list_user_summary_instructions(user_id=user_id, request_options=request_options) - return _response.data - - def add_user_summary_instructions( + def create( self, *, - instructions: typing.Sequence[UserInstruction], - user_ids: typing.Optional[typing.Sequence[str]] = OMIT, + disable_default_ontology: typing.Optional[bool] = OMIT, + email: typing.Optional[str] = OMIT, + first_name: typing.Optional[str] = OMIT, + last_name: typing.Optional[str] = OMIT, + metadata: typing.Optional[typing.Dict[str, typing.Any]] = OMIT, + time_zone: typing.Optional[str] = OMIT, + user_id: typing.Optional[str] = OMIT, + idempotency_key: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, - ) -> SuccessResponse: + ) -> User: """ - Adds new summary instructions for users graphs without removing existing ones. If user_ids is empty, adds to project-wide default instructions. - Parameters ---------- - instructions : typing.Sequence[UserInstruction] - Instructions to add to the user summary generation. - - user_ids : typing.Optional[typing.Sequence[str]] - User IDs to add the instructions to. If empty, the instructions are added to the project-wide default. + disable_default_ontology : typing.Optional[bool] - request_options : typing.Optional[RequestOptions] - Request-specific configuration. + email : typing.Optional[str] - Returns - ------- - SuccessResponse - Instructions added successfully + first_name : typing.Optional[str] - Examples - -------- - from zep_cloud import UserInstruction, Zep + last_name : typing.Optional[str] - client = Zep( - api_key="YOUR_API_KEY", - ) - client.user.add_user_summary_instructions( - instructions=[ - UserInstruction( - name="name", - text="text", - ) - ], - ) - """ - _response = self._raw_client.add_user_summary_instructions( - instructions=instructions, user_ids=user_ids, request_options=request_options - ) - return _response.data + metadata : typing.Optional[typing.Dict[str, typing.Any]] - def delete_user_summary_instructions( - self, - *, - instruction_names: typing.Optional[typing.Sequence[str]] = OMIT, - user_ids: typing.Optional[typing.Sequence[str]] = OMIT, - request_options: typing.Optional[RequestOptions] = None, - ) -> SuccessResponse: - """ - Deletes user summary/instructions for users or project wide defaults. + time_zone : typing.Optional[str] - Parameters - ---------- - instruction_names : typing.Optional[typing.Sequence[str]] - Unique identifier for the instructions to be deleted. If empty deletes all instructions. + user_id : typing.Optional[str] - user_ids : typing.Optional[typing.Sequence[str]] - Determines which users will have their custom instructions deleted. If no users are provided, the project-wide custom instructions will be effected. + idempotency_key : typing.Optional[str] request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - SuccessResponse - Instructions deleted successfully + User + Created Examples -------- @@ -145,58 +78,58 @@ def delete_user_summary_instructions( client = Zep( api_key="YOUR_API_KEY", ) - client.user.delete_user_summary_instructions() + client.user.create() """ - _response = self._raw_client.delete_user_summary_instructions( - instruction_names=instruction_names, user_ids=user_ids, request_options=request_options + _response = self._raw_client.create( + disable_default_ontology=disable_default_ontology, + email=email, + first_name=first_name, + last_name=last_name, + metadata=metadata, + time_zone=time_zone, + user_id=user_id, + idempotency_key=idempotency_key, + request_options=request_options, ) return _response.data - def add( + def list( self, *, - user_id: str, - disable_default_ontology: typing.Optional[bool] = OMIT, - email: typing.Optional[str] = OMIT, - first_name: typing.Optional[str] = OMIT, - last_name: typing.Optional[str] = OMIT, - metadata: typing.Optional[typing.Dict[str, typing.Optional[typing.Any]]] = OMIT, - time_zone: typing.Optional[str] = OMIT, + limit: typing.Optional[int] = None, + cursor: typing.Optional[str] = None, + order_by: typing.Optional[str] = None, + order: typing.Optional[str] = None, + search: typing.Optional[str] = OMIT, + idempotency_key: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, - ) -> User: + ) -> SyncPager[User, UserPage]: """ - Adds a user. - Parameters ---------- - user_id : str - The unique identifier of the user. + limit : typing.Optional[int] + Page size - disable_default_ontology : typing.Optional[bool] - When true, disables the use of default/fallback ontology for the user's graph. - - email : typing.Optional[str] - The email address of the user. + cursor : typing.Optional[str] + Opaque page cursor - first_name : typing.Optional[str] - The first name of the user. + order_by : typing.Optional[str] + Sort field - last_name : typing.Optional[str] - The last name of the user. + order : typing.Optional[str] + asc or desc - metadata : typing.Optional[typing.Dict[str, typing.Optional[typing.Any]]] - The metadata associated with the user. + search : typing.Optional[str] - time_zone : typing.Optional[str] - The user's IANA time zone. Null or omission leaves it unset at creation. + idempotency_key : typing.Optional[str] request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - User - The user that was added. + SyncPager[User, UserPage] + OK Examples -------- @@ -205,59 +138,55 @@ def add( client = Zep( api_key="YOUR_API_KEY", ) - client.user.add( - user_id="user_id", + response = client.user.list( + limit=1, + cursor="cursor", + order_by="order_by", + order="order", ) + for item in response: + yield item + # alternatively, you can paginate page-by-page + for page in response.iter_pages(): + yield page """ - _response = self._raw_client.add( - user_id=user_id, - disable_default_ontology=disable_default_ontology, - email=email, - first_name=first_name, - last_name=last_name, - metadata=metadata, - time_zone=time_zone, + return self._raw_client.list( + limit=limit, + cursor=cursor, + order_by=order_by, + order=order, + search=search, + idempotency_key=idempotency_key, request_options=request_options, ) - return _response.data - def list_ordered( + def lookup( self, *, - page_number: typing.Optional[int] = None, - page_size: typing.Optional[int] = None, - search: typing.Optional[str] = None, - order_by: typing.Optional[str] = None, - asc: typing.Optional[bool] = None, + graph_id: typing.Optional[str] = OMIT, + thread_id: typing.Optional[str] = OMIT, + user_id: typing.Optional[str] = OMIT, + idempotency_key: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, - ) -> UserListResponse: + ) -> User: """ - Returns all users. - Parameters ---------- - page_number : typing.Optional[int] - Page number for pagination, starting from 1 - - page_size : typing.Optional[int] - Number of users to retrieve per page + graph_id : typing.Optional[str] - search : typing.Optional[str] - Search term for filtering users by user_id, name, or email + thread_id : typing.Optional[str] - order_by : typing.Optional[str] - Column to sort by (created_at, user_id, email) + user_id : typing.Optional[str] - asc : typing.Optional[bool] - Sort in ascending order + idempotency_key : typing.Optional[str] request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - UserListResponse - Successfully retrieved list of users + User + OK Examples -------- @@ -266,32 +195,23 @@ def list_ordered( client = Zep( api_key="YOUR_API_KEY", ) - client.user.list_ordered( - page_number=1, - page_size=1, - search="search", - order_by="order_by", - asc=True, - ) + client.user.lookup() """ - _response = self._raw_client.list_ordered( - page_number=page_number, - page_size=page_size, - search=search, - order_by=order_by, - asc=asc, + _response = self._raw_client.lookup( + graph_id=graph_id, + thread_id=thread_id, + user_id=user_id, + idempotency_key=idempotency_key, request_options=request_options, ) return _response.data - def get(self, user_id: str, *, request_options: typing.Optional[RequestOptions] = None) -> User: + def get(self, user_uuid: str, *, request_options: typing.Optional[RequestOptions] = None) -> User: """ - Returns a user. - Parameters ---------- - user_id : str - The user_id of the user to get. + user_uuid : str + User UUID request_options : typing.Optional[RequestOptions] Request-specific configuration. @@ -299,7 +219,7 @@ def get(self, user_id: str, *, request_options: typing.Optional[RequestOptions] Returns ------- User - The user that was retrieved. + OK Examples -------- @@ -309,28 +229,34 @@ def get(self, user_id: str, *, request_options: typing.Optional[RequestOptions] api_key="YOUR_API_KEY", ) client.user.get( - user_id="userId", + user_uuid="user_uuid", ) """ - _response = self._raw_client.get(user_id, request_options=request_options) + _response = self._raw_client.get(user_uuid, request_options=request_options) return _response.data - def delete(self, user_id: str, *, request_options: typing.Optional[RequestOptions] = None) -> SuccessResponse: + def delete( + self, + user_uuid: str, + *, + idempotency_key: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> UserDeleteResult: """ - Deletes a user. - Parameters ---------- - user_id : str - User ID + user_uuid : str + User UUID + + idempotency_key : typing.Optional[str] request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - SuccessResponse - OK + UserDeleteResult + Accepted Examples -------- @@ -340,49 +266,49 @@ def delete(self, user_id: str, *, request_options: typing.Optional[RequestOption api_key="YOUR_API_KEY", ) client.user.delete( - user_id="userId", + user_uuid="user_uuid", ) """ - _response = self._raw_client.delete(user_id, request_options=request_options) + _response = self._raw_client.delete(user_uuid, idempotency_key=idempotency_key, request_options=request_options) return _response.data def update( self, - user_id: str, + user_uuid: str, *, disable_default_ontology: typing.Optional[bool] = OMIT, email: typing.Optional[str] = OMIT, first_name: typing.Optional[str] = OMIT, last_name: typing.Optional[str] = OMIT, - metadata: typing.Optional[typing.Dict[str, typing.Optional[typing.Any]]] = OMIT, + metadata: typing.Optional[typing.Dict[str, typing.Any]] = OMIT, time_zone: typing.Optional[str] = OMIT, + idempotency_key: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, ) -> User: """ - Updates a user. - Parameters ---------- - user_id : str - User ID + user_uuid : str + User UUID disable_default_ontology : typing.Optional[bool] - When true, disables the use of default/fallback ontology for the user's graph. + Omit to leave unchanged, send JSON null to clear, or send a value to set. email : typing.Optional[str] - The email address of the user. + Omit to leave unchanged, send JSON null to clear, or send a value to set. first_name : typing.Optional[str] - The first name of the user. + Omit to leave unchanged, send JSON null to clear, or send a value to set. last_name : typing.Optional[str] - The last name of the user. + Omit to leave unchanged, send JSON null to clear, or send a value to set. - metadata : typing.Optional[typing.Dict[str, typing.Optional[typing.Any]]] - The metadata to update + metadata : typing.Optional[typing.Dict[str, typing.Any]] time_zone : typing.Optional[str] - The user's IANA time zone. Null clears the existing value. + Omit to leave unchanged, send JSON null to clear, or send a value to set. + + idempotency_key : typing.Optional[str] request_options : typing.Optional[RequestOptions] Request-specific configuration. @@ -390,7 +316,7 @@ def update( Returns ------- User - The user that was updated. + OK Examples -------- @@ -400,37 +326,36 @@ def update( api_key="YOUR_API_KEY", ) client.user.update( - user_id="userId", + user_uuid="user_uuid", ) """ _response = self._raw_client.update( - user_id, + user_uuid, disable_default_ontology=disable_default_ontology, email=email, first_name=first_name, last_name=last_name, metadata=metadata, time_zone=time_zone, + idempotency_key=idempotency_key, request_options=request_options, ) return _response.data - def get_node(self, user_id: str, *, request_options: typing.Optional[RequestOptions] = None) -> UserNodeResponse: + def get_node(self, user_uuid: str, *, request_options: typing.Optional[RequestOptions] = None) -> Node: """ - Returns a user's node. - Parameters ---------- - user_id : str - The user_id of the user to get the node for. + user_uuid : str + User UUID request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - UserNodeResponse - Response object containing the User node. + Node + OK Examples -------- @@ -440,29 +365,27 @@ def get_node(self, user_id: str, *, request_options: typing.Optional[RequestOpti api_key="YOUR_API_KEY", ) client.user.get_node( - user_id="userId", + user_uuid="user_uuid", ) """ - _response = self._raw_client.get_node(user_id, request_options=request_options) + _response = self._raw_client.get_node(user_uuid, request_options=request_options) return _response.data - def get_threads( - self, user_id: str, *, request_options: typing.Optional[RequestOptions] = None - ) -> typing.List[Thread]: + def get_summary_instructions( + self, user_uuid: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> UserSummaryInstructions: """ - Returns all threads for a user. - Parameters ---------- - user_id : str - User ID + user_uuid : str + User UUID request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - typing.List[Thread] + UserSummaryInstructions OK Examples @@ -472,29 +395,41 @@ def get_threads( client = Zep( api_key="YOUR_API_KEY", ) - client.user.get_threads( - user_id="userId", + client.user.get_summary_instructions( + user_uuid="user_uuid", ) """ - _response = self._raw_client.get_threads(user_id, request_options=request_options) + _response = self._raw_client.get_summary_instructions(user_uuid, request_options=request_options) return _response.data - def warm(self, user_id: str, *, request_options: typing.Optional[RequestOptions] = None) -> SuccessResponse: + def set_summary_instructions( + self, + user_uuid: str, + *, + inherited: typing.Optional[bool] = OMIT, + instructions: typing.Optional[typing.Sequence[typing.Dict[str, typing.Any]]] = OMIT, + idempotency_key: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> UserSummaryInstructions: """ - Hints Zep to warm a user's graph for low-latency search - Parameters ---------- - user_id : str - User ID + user_uuid : str + User UUID + + inherited : typing.Optional[bool] + + instructions : typing.Optional[typing.Sequence[typing.Dict[str, typing.Any]]] + + idempotency_key : typing.Optional[str] request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - SuccessResponse - Warm hint accepted + UserSummaryInstructions + OK Examples -------- @@ -503,11 +438,17 @@ def warm(self, user_id: str, *, request_options: typing.Optional[RequestOptions] client = Zep( api_key="YOUR_API_KEY", ) - client.user.warm( - user_id="userId", + client.user.set_summary_instructions( + user_uuid="user_uuid", ) """ - _response = self._raw_client.warm(user_id, request_options=request_options) + _response = self._raw_client.set_summary_instructions( + user_uuid, + inherited=inherited, + instructions=instructions, + idempotency_key=idempotency_key, + request_options=request_options, + ) return _response.data @@ -526,129 +467,45 @@ def with_raw_response(self) -> AsyncRawUserClient: """ return self._raw_client - async def list_user_summary_instructions( - self, *, user_id: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None - ) -> ListUserInstructionsResponse: - """ - Lists all user summary instructions for a project, user. - - Parameters - ---------- - user_id : typing.Optional[str] - User ID to get user-specific instructions - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - ListUserInstructionsResponse - The list of instructions. - - Examples - -------- - import asyncio - - from zep_cloud import AsyncZep - - client = AsyncZep( - api_key="YOUR_API_KEY", - ) - - - async def main() -> None: - await client.user.list_user_summary_instructions( - user_id="user_id", - ) - - - asyncio.run(main()) - """ - _response = await self._raw_client.list_user_summary_instructions( - user_id=user_id, request_options=request_options - ) - return _response.data - - async def add_user_summary_instructions( + async def create( self, *, - instructions: typing.Sequence[UserInstruction], - user_ids: typing.Optional[typing.Sequence[str]] = OMIT, + disable_default_ontology: typing.Optional[bool] = OMIT, + email: typing.Optional[str] = OMIT, + first_name: typing.Optional[str] = OMIT, + last_name: typing.Optional[str] = OMIT, + metadata: typing.Optional[typing.Dict[str, typing.Any]] = OMIT, + time_zone: typing.Optional[str] = OMIT, + user_id: typing.Optional[str] = OMIT, + idempotency_key: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, - ) -> SuccessResponse: + ) -> User: """ - Adds new summary instructions for users graphs without removing existing ones. If user_ids is empty, adds to project-wide default instructions. - Parameters ---------- - instructions : typing.Sequence[UserInstruction] - Instructions to add to the user summary generation. - - user_ids : typing.Optional[typing.Sequence[str]] - User IDs to add the instructions to. If empty, the instructions are added to the project-wide default. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - SuccessResponse - Instructions added successfully - - Examples - -------- - import asyncio - - from zep_cloud import AsyncZep, UserInstruction + disable_default_ontology : typing.Optional[bool] - client = AsyncZep( - api_key="YOUR_API_KEY", - ) + email : typing.Optional[str] + first_name : typing.Optional[str] - async def main() -> None: - await client.user.add_user_summary_instructions( - instructions=[ - UserInstruction( - name="name", - text="text", - ) - ], - ) + last_name : typing.Optional[str] + metadata : typing.Optional[typing.Dict[str, typing.Any]] - asyncio.run(main()) - """ - _response = await self._raw_client.add_user_summary_instructions( - instructions=instructions, user_ids=user_ids, request_options=request_options - ) - return _response.data - - async def delete_user_summary_instructions( - self, - *, - instruction_names: typing.Optional[typing.Sequence[str]] = OMIT, - user_ids: typing.Optional[typing.Sequence[str]] = OMIT, - request_options: typing.Optional[RequestOptions] = None, - ) -> SuccessResponse: - """ - Deletes user summary/instructions for users or project wide defaults. + time_zone : typing.Optional[str] - Parameters - ---------- - instruction_names : typing.Optional[typing.Sequence[str]] - Unique identifier for the instructions to be deleted. If empty deletes all instructions. + user_id : typing.Optional[str] - user_ids : typing.Optional[typing.Sequence[str]] - Determines which users will have their custom instructions deleted. If no users are provided, the project-wide custom instructions will be effected. + idempotency_key : typing.Optional[str] request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - SuccessResponse - Instructions deleted successfully + User + Created Examples -------- @@ -662,61 +519,61 @@ async def delete_user_summary_instructions( async def main() -> None: - await client.user.delete_user_summary_instructions() + await client.user.create() asyncio.run(main()) """ - _response = await self._raw_client.delete_user_summary_instructions( - instruction_names=instruction_names, user_ids=user_ids, request_options=request_options + _response = await self._raw_client.create( + disable_default_ontology=disable_default_ontology, + email=email, + first_name=first_name, + last_name=last_name, + metadata=metadata, + time_zone=time_zone, + user_id=user_id, + idempotency_key=idempotency_key, + request_options=request_options, ) return _response.data - async def add( + async def list( self, *, - user_id: str, - disable_default_ontology: typing.Optional[bool] = OMIT, - email: typing.Optional[str] = OMIT, - first_name: typing.Optional[str] = OMIT, - last_name: typing.Optional[str] = OMIT, - metadata: typing.Optional[typing.Dict[str, typing.Optional[typing.Any]]] = OMIT, - time_zone: typing.Optional[str] = OMIT, + limit: typing.Optional[int] = None, + cursor: typing.Optional[str] = None, + order_by: typing.Optional[str] = None, + order: typing.Optional[str] = None, + search: typing.Optional[str] = OMIT, + idempotency_key: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, - ) -> User: + ) -> AsyncPager[User, UserPage]: """ - Adds a user. - Parameters ---------- - user_id : str - The unique identifier of the user. + limit : typing.Optional[int] + Page size - disable_default_ontology : typing.Optional[bool] - When true, disables the use of default/fallback ontology for the user's graph. + cursor : typing.Optional[str] + Opaque page cursor - email : typing.Optional[str] - The email address of the user. + order_by : typing.Optional[str] + Sort field - first_name : typing.Optional[str] - The first name of the user. + order : typing.Optional[str] + asc or desc - last_name : typing.Optional[str] - The last name of the user. - - metadata : typing.Optional[typing.Dict[str, typing.Optional[typing.Any]]] - The metadata associated with the user. + search : typing.Optional[str] - time_zone : typing.Optional[str] - The user's IANA time zone. Null or omission leaves it unset at creation. + idempotency_key : typing.Optional[str] request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - User - The user that was added. + AsyncPager[User, UserPage] + OK Examples -------- @@ -730,62 +587,59 @@ async def add( async def main() -> None: - await client.user.add( - user_id="user_id", + response = await client.user.list( + limit=1, + cursor="cursor", + order_by="order_by", + order="order", ) + async for item in response: + yield item + + # alternatively, you can paginate page-by-page + async for page in response.iter_pages(): + yield page asyncio.run(main()) """ - _response = await self._raw_client.add( - user_id=user_id, - disable_default_ontology=disable_default_ontology, - email=email, - first_name=first_name, - last_name=last_name, - metadata=metadata, - time_zone=time_zone, + return await self._raw_client.list( + limit=limit, + cursor=cursor, + order_by=order_by, + order=order, + search=search, + idempotency_key=idempotency_key, request_options=request_options, ) - return _response.data - async def list_ordered( + async def lookup( self, *, - page_number: typing.Optional[int] = None, - page_size: typing.Optional[int] = None, - search: typing.Optional[str] = None, - order_by: typing.Optional[str] = None, - asc: typing.Optional[bool] = None, + graph_id: typing.Optional[str] = OMIT, + thread_id: typing.Optional[str] = OMIT, + user_id: typing.Optional[str] = OMIT, + idempotency_key: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, - ) -> UserListResponse: + ) -> User: """ - Returns all users. - Parameters ---------- - page_number : typing.Optional[int] - Page number for pagination, starting from 1 - - page_size : typing.Optional[int] - Number of users to retrieve per page + graph_id : typing.Optional[str] - search : typing.Optional[str] - Search term for filtering users by user_id, name, or email + thread_id : typing.Optional[str] - order_by : typing.Optional[str] - Column to sort by (created_at, user_id, email) + user_id : typing.Optional[str] - asc : typing.Optional[bool] - Sort in ascending order + idempotency_key : typing.Optional[str] request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - UserListResponse - Successfully retrieved list of users + User + OK Examples -------- @@ -799,35 +653,26 @@ async def list_ordered( async def main() -> None: - await client.user.list_ordered( - page_number=1, - page_size=1, - search="search", - order_by="order_by", - asc=True, - ) + await client.user.lookup() asyncio.run(main()) """ - _response = await self._raw_client.list_ordered( - page_number=page_number, - page_size=page_size, - search=search, - order_by=order_by, - asc=asc, + _response = await self._raw_client.lookup( + graph_id=graph_id, + thread_id=thread_id, + user_id=user_id, + idempotency_key=idempotency_key, request_options=request_options, ) return _response.data - async def get(self, user_id: str, *, request_options: typing.Optional[RequestOptions] = None) -> User: + async def get(self, user_uuid: str, *, request_options: typing.Optional[RequestOptions] = None) -> User: """ - Returns a user. - Parameters ---------- - user_id : str - The user_id of the user to get. + user_uuid : str + User UUID request_options : typing.Optional[RequestOptions] Request-specific configuration. @@ -835,7 +680,7 @@ async def get(self, user_id: str, *, request_options: typing.Optional[RequestOpt Returns ------- User - The user that was retrieved. + OK Examples -------- @@ -850,31 +695,37 @@ async def get(self, user_id: str, *, request_options: typing.Optional[RequestOpt async def main() -> None: await client.user.get( - user_id="userId", + user_uuid="user_uuid", ) asyncio.run(main()) """ - _response = await self._raw_client.get(user_id, request_options=request_options) + _response = await self._raw_client.get(user_uuid, request_options=request_options) return _response.data - async def delete(self, user_id: str, *, request_options: typing.Optional[RequestOptions] = None) -> SuccessResponse: + async def delete( + self, + user_uuid: str, + *, + idempotency_key: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> UserDeleteResult: """ - Deletes a user. - Parameters ---------- - user_id : str - User ID + user_uuid : str + User UUID + + idempotency_key : typing.Optional[str] request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - SuccessResponse - OK + UserDeleteResult + Accepted Examples -------- @@ -889,52 +740,54 @@ async def delete(self, user_id: str, *, request_options: typing.Optional[Request async def main() -> None: await client.user.delete( - user_id="userId", + user_uuid="user_uuid", ) asyncio.run(main()) """ - _response = await self._raw_client.delete(user_id, request_options=request_options) + _response = await self._raw_client.delete( + user_uuid, idempotency_key=idempotency_key, request_options=request_options + ) return _response.data async def update( self, - user_id: str, + user_uuid: str, *, disable_default_ontology: typing.Optional[bool] = OMIT, email: typing.Optional[str] = OMIT, first_name: typing.Optional[str] = OMIT, last_name: typing.Optional[str] = OMIT, - metadata: typing.Optional[typing.Dict[str, typing.Optional[typing.Any]]] = OMIT, + metadata: typing.Optional[typing.Dict[str, typing.Any]] = OMIT, time_zone: typing.Optional[str] = OMIT, + idempotency_key: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, ) -> User: """ - Updates a user. - Parameters ---------- - user_id : str - User ID + user_uuid : str + User UUID disable_default_ontology : typing.Optional[bool] - When true, disables the use of default/fallback ontology for the user's graph. + Omit to leave unchanged, send JSON null to clear, or send a value to set. email : typing.Optional[str] - The email address of the user. + Omit to leave unchanged, send JSON null to clear, or send a value to set. first_name : typing.Optional[str] - The first name of the user. + Omit to leave unchanged, send JSON null to clear, or send a value to set. last_name : typing.Optional[str] - The last name of the user. + Omit to leave unchanged, send JSON null to clear, or send a value to set. - metadata : typing.Optional[typing.Dict[str, typing.Optional[typing.Any]]] - The metadata to update + metadata : typing.Optional[typing.Dict[str, typing.Any]] time_zone : typing.Optional[str] - The user's IANA time zone. Null clears the existing value. + Omit to leave unchanged, send JSON null to clear, or send a value to set. + + idempotency_key : typing.Optional[str] request_options : typing.Optional[RequestOptions] Request-specific configuration. @@ -942,7 +795,7 @@ async def update( Returns ------- User - The user that was updated. + OK Examples -------- @@ -957,42 +810,39 @@ async def update( async def main() -> None: await client.user.update( - user_id="userId", + user_uuid="user_uuid", ) asyncio.run(main()) """ _response = await self._raw_client.update( - user_id, + user_uuid, disable_default_ontology=disable_default_ontology, email=email, first_name=first_name, last_name=last_name, metadata=metadata, time_zone=time_zone, + idempotency_key=idempotency_key, request_options=request_options, ) return _response.data - async def get_node( - self, user_id: str, *, request_options: typing.Optional[RequestOptions] = None - ) -> UserNodeResponse: + async def get_node(self, user_uuid: str, *, request_options: typing.Optional[RequestOptions] = None) -> Node: """ - Returns a user's node. - Parameters ---------- - user_id : str - The user_id of the user to get the node for. + user_uuid : str + User UUID request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - UserNodeResponse - Response object containing the User node. + Node + OK Examples -------- @@ -1007,32 +857,30 @@ async def get_node( async def main() -> None: await client.user.get_node( - user_id="userId", + user_uuid="user_uuid", ) asyncio.run(main()) """ - _response = await self._raw_client.get_node(user_id, request_options=request_options) + _response = await self._raw_client.get_node(user_uuid, request_options=request_options) return _response.data - async def get_threads( - self, user_id: str, *, request_options: typing.Optional[RequestOptions] = None - ) -> typing.List[Thread]: + async def get_summary_instructions( + self, user_uuid: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> UserSummaryInstructions: """ - Returns all threads for a user. - Parameters ---------- - user_id : str - User ID + user_uuid : str + User UUID request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - typing.List[Thread] + UserSummaryInstructions OK Examples @@ -1047,32 +895,44 @@ async def get_threads( async def main() -> None: - await client.user.get_threads( - user_id="userId", + await client.user.get_summary_instructions( + user_uuid="user_uuid", ) asyncio.run(main()) """ - _response = await self._raw_client.get_threads(user_id, request_options=request_options) + _response = await self._raw_client.get_summary_instructions(user_uuid, request_options=request_options) return _response.data - async def warm(self, user_id: str, *, request_options: typing.Optional[RequestOptions] = None) -> SuccessResponse: + async def set_summary_instructions( + self, + user_uuid: str, + *, + inherited: typing.Optional[bool] = OMIT, + instructions: typing.Optional[typing.Sequence[typing.Dict[str, typing.Any]]] = OMIT, + idempotency_key: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> UserSummaryInstructions: """ - Hints Zep to warm a user's graph for low-latency search - Parameters ---------- - user_id : str - User ID + user_uuid : str + User UUID + + inherited : typing.Optional[bool] + + instructions : typing.Optional[typing.Sequence[typing.Dict[str, typing.Any]]] + + idempotency_key : typing.Optional[str] request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - SuccessResponse - Warm hint accepted + UserSummaryInstructions + OK Examples -------- @@ -1086,12 +946,18 @@ async def warm(self, user_id: str, *, request_options: typing.Optional[RequestOp async def main() -> None: - await client.user.warm( - user_id="userId", + await client.user.set_summary_instructions( + user_uuid="user_uuid", ) asyncio.run(main()) """ - _response = await self._raw_client.warm(user_id, request_options=request_options) + _response = await self._raw_client.set_summary_instructions( + user_uuid, + inherited=inherited, + instructions=instructions, + idempotency_key=idempotency_key, + request_options=request_options, + ) return _response.data diff --git a/src/zep_cloud/user/raw_client.py b/src/zep_cloud/user/raw_client.py index ee2aa517..a6012387 100644 --- a/src/zep_cloud/user/raw_client.py +++ b/src/zep_cloud/user/raw_client.py @@ -7,20 +7,20 @@ from ..core.client_wrapper import AsyncClientWrapper, SyncClientWrapper from ..core.http_response import AsyncHttpResponse, HttpResponse from ..core.jsonable_encoder import jsonable_encoder +from ..core.pagination import AsyncPager, SyncPager +from ..core.parse_error import ParsingError 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 ..errors.unauthorized_error import UnauthorizedError from ..types.api_error import ApiError as types_api_error_ApiError -from ..types.list_user_instructions_response import ListUserInstructionsResponse -from ..types.success_response import SuccessResponse -from ..types.thread import Thread +from ..types.node import Node from ..types.user import User -from ..types.user_instruction import UserInstruction -from ..types.user_list_response import UserListResponse -from ..types.user_node_response import UserNodeResponse +from ..types.user_delete_result import UserDeleteResult +from ..types.user_page import UserPage +from ..types.user_summary_instructions import UserSummaryInstructions +from pydantic import ValidationError # this is used as the default value for optional parameters OMIT = typing.cast(typing.Any, ...) @@ -30,39 +30,71 @@ class RawUserClient: def __init__(self, *, client_wrapper: SyncClientWrapper): self._client_wrapper = client_wrapper - def list_user_summary_instructions( - self, *, user_id: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None - ) -> HttpResponse[ListUserInstructionsResponse]: + def create( + self, + *, + disable_default_ontology: typing.Optional[bool] = OMIT, + email: typing.Optional[str] = OMIT, + first_name: typing.Optional[str] = OMIT, + last_name: typing.Optional[str] = OMIT, + metadata: typing.Optional[typing.Dict[str, typing.Any]] = OMIT, + time_zone: typing.Optional[str] = OMIT, + user_id: typing.Optional[str] = OMIT, + idempotency_key: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[User]: """ - Lists all user summary instructions for a project, user. - Parameters ---------- + disable_default_ontology : typing.Optional[bool] + + email : typing.Optional[str] + + first_name : typing.Optional[str] + + last_name : typing.Optional[str] + + metadata : typing.Optional[typing.Dict[str, typing.Any]] + + time_zone : typing.Optional[str] + user_id : typing.Optional[str] - User ID to get user-specific instructions + + idempotency_key : typing.Optional[str] request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - HttpResponse[ListUserInstructionsResponse] - The list of instructions. + HttpResponse[User] + Created """ _response = self._client_wrapper.httpx_client.request( - "user-summary-instructions", - method="GET", - params={ + "users", + method="POST", + json={ + "disable_default_ontology": disable_default_ontology, + "email": email, + "first_name": first_name, + "last_name": last_name, + "metadata": metadata, + "time_zone": time_zone, "user_id": user_id, }, + headers={ + "content-type": "application/json", + "Idempotency-Key": str(idempotency_key) if idempotency_key is not None else None, + }, request_options=request_options, + omit=OMIT, ) try: if 200 <= _response.status_code < 300: _data = typing.cast( - ListUserInstructionsResponse, + User, parse_obj_as( - type_=ListUserInstructionsResponse, # type: ignore + type_=User, # type: ignore object_=_response.json(), ), ) @@ -71,15 +103,15 @@ def list_user_summary_instructions( raise BadRequestError( headers=dict(_response.headers), body=typing.cast( - typing.Optional[typing.Any], + types_api_error_ApiError, parse_obj_as( - type_=typing.Optional[typing.Any], # type: ignore + type_=types_api_error_ApiError, # type: ignore object_=_response.json(), ), ), ) - if _response.status_code == 404: - raise NotFoundError( + if _response.status_code == 401: + raise UnauthorizedError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -89,8 +121,8 @@ def list_user_summary_instructions( ), ), ) - if _response.status_code == 500: - raise InternalServerError( + if _response.status_code == 404: + raise NotFoundError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -105,74 +137,106 @@ def list_user_summary_instructions( raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response.text ) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response_json ) - def add_user_summary_instructions( + def list( self, *, - instructions: typing.Sequence[UserInstruction], - user_ids: typing.Optional[typing.Sequence[str]] = OMIT, + limit: typing.Optional[int] = None, + cursor: typing.Optional[str] = None, + order_by: typing.Optional[str] = None, + order: typing.Optional[str] = None, + search: typing.Optional[str] = OMIT, + idempotency_key: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, - ) -> HttpResponse[SuccessResponse]: + ) -> SyncPager[User, UserPage]: """ - Adds new summary instructions for users graphs without removing existing ones. If user_ids is empty, adds to project-wide default instructions. - Parameters ---------- - instructions : typing.Sequence[UserInstruction] - Instructions to add to the user summary generation. + limit : typing.Optional[int] + Page size + + cursor : typing.Optional[str] + Opaque page cursor + + order_by : typing.Optional[str] + Sort field + + order : typing.Optional[str] + asc or desc - user_ids : typing.Optional[typing.Sequence[str]] - User IDs to add the instructions to. If empty, the instructions are added to the project-wide default. + search : typing.Optional[str] + + idempotency_key : typing.Optional[str] request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - HttpResponse[SuccessResponse] - Instructions added successfully + SyncPager[User, UserPage] + OK """ _response = self._client_wrapper.httpx_client.request( - "user-summary-instructions", + "users/list", method="POST", + params={ + "limit": limit, + "cursor": cursor, + "order_by": order_by, + "order": order, + }, json={ - "instructions": convert_and_respect_annotation_metadata( - object_=instructions, annotation=typing.Sequence[UserInstruction], direction="write" - ), - "user_ids": user_ids, + "search": search, }, headers={ "content-type": "application/json", + "Idempotency-Key": str(idempotency_key) if idempotency_key is not None else None, }, request_options=request_options, omit=OMIT, ) try: if 200 <= _response.status_code < 300: - _data = typing.cast( - SuccessResponse, + _parsed_response = typing.cast( + UserPage, parse_obj_as( - type_=SuccessResponse, # type: ignore + type_=UserPage, # type: ignore object_=_response.json(), ), ) - return HttpResponse(response=_response, data=_data) + _items = _parsed_response.items + _parsed_next = _parsed_response.next_cursor + _has_next = _parsed_next is not None and _parsed_next != "" + _get_next = lambda: self.list( + limit=limit, + cursor=_parsed_next, + order_by=order_by, + order=order, + search=search, + idempotency_key=idempotency_key, + request_options=request_options, + ) + return SyncPager(has_next=_has_next, items=_items, get_next=_get_next, response=_parsed_response) if _response.status_code == 400: raise BadRequestError( headers=dict(_response.headers), body=typing.cast( - typing.Optional[typing.Any], + types_api_error_ApiError, parse_obj_as( - type_=typing.Optional[typing.Any], # type: ignore + type_=types_api_error_ApiError, # type: ignore object_=_response.json(), ), ), ) - if _response.status_code == 404: - raise NotFoundError( + if _response.status_code == 401: + raise UnauthorizedError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -182,8 +246,8 @@ def add_user_summary_instructions( ), ), ) - if _response.status_code == 500: - raise InternalServerError( + if _response.status_code == 404: + raise NotFoundError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -198,45 +262,53 @@ def add_user_summary_instructions( raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response.text ) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response_json ) - def delete_user_summary_instructions( + def lookup( self, *, - instruction_names: typing.Optional[typing.Sequence[str]] = OMIT, - user_ids: typing.Optional[typing.Sequence[str]] = OMIT, + graph_id: typing.Optional[str] = OMIT, + thread_id: typing.Optional[str] = OMIT, + user_id: typing.Optional[str] = OMIT, + idempotency_key: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, - ) -> HttpResponse[SuccessResponse]: + ) -> HttpResponse[User]: """ - Deletes user summary/instructions for users or project wide defaults. - Parameters ---------- - instruction_names : typing.Optional[typing.Sequence[str]] - Unique identifier for the instructions to be deleted. If empty deletes all instructions. + graph_id : typing.Optional[str] + + thread_id : typing.Optional[str] - user_ids : typing.Optional[typing.Sequence[str]] - Determines which users will have their custom instructions deleted. If no users are provided, the project-wide custom instructions will be effected. + user_id : typing.Optional[str] + + idempotency_key : typing.Optional[str] request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - HttpResponse[SuccessResponse] - Instructions deleted successfully + HttpResponse[User] + OK """ _response = self._client_wrapper.httpx_client.request( - "user-summary-instructions", - method="DELETE", + "users/lookup", + method="POST", json={ - "instruction_names": instruction_names, - "user_ids": user_ids, + "graph_id": graph_id, + "thread_id": thread_id, + "user_id": user_id, }, headers={ "content-type": "application/json", + "Idempotency-Key": str(idempotency_key) if idempotency_key is not None else None, }, request_options=request_options, omit=OMIT, @@ -244,9 +316,9 @@ def delete_user_summary_instructions( try: if 200 <= _response.status_code < 300: _data = typing.cast( - SuccessResponse, + User, parse_obj_as( - type_=SuccessResponse, # type: ignore + type_=User, # type: ignore object_=_response.json(), ), ) @@ -255,15 +327,15 @@ def delete_user_summary_instructions( raise BadRequestError( headers=dict(_response.headers), body=typing.cast( - typing.Optional[typing.Any], + types_api_error_ApiError, parse_obj_as( - type_=typing.Optional[typing.Any], # type: ignore + type_=types_api_error_ApiError, # type: ignore object_=_response.json(), ), ), ) - if _response.status_code == 404: - raise NotFoundError( + if _response.status_code == 401: + raise UnauthorizedError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -273,8 +345,8 @@ def delete_user_summary_instructions( ), ), ) - if _response.status_code == 500: - raise InternalServerError( + if _response.status_code == 404: + raise NotFoundError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -289,47 +361,20 @@ def delete_user_summary_instructions( raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response.text ) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response_json ) - def add( - self, - *, - user_id: str, - disable_default_ontology: typing.Optional[bool] = OMIT, - email: typing.Optional[str] = OMIT, - first_name: typing.Optional[str] = OMIT, - last_name: typing.Optional[str] = OMIT, - metadata: typing.Optional[typing.Dict[str, typing.Optional[typing.Any]]] = OMIT, - time_zone: typing.Optional[str] = OMIT, - request_options: typing.Optional[RequestOptions] = None, - ) -> HttpResponse[User]: + def get(self, user_uuid: str, *, request_options: typing.Optional[RequestOptions] = None) -> HttpResponse[User]: """ - Adds a user. - Parameters ---------- - user_id : str - The unique identifier of the user. - - disable_default_ontology : typing.Optional[bool] - When true, disables the use of default/fallback ontology for the user's graph. - - email : typing.Optional[str] - The email address of the user. - - first_name : typing.Optional[str] - The first name of the user. - - last_name : typing.Optional[str] - The last name of the user. - - metadata : typing.Optional[typing.Dict[str, typing.Optional[typing.Any]]] - The metadata associated with the user. - - time_zone : typing.Optional[str] - The user's IANA time zone. Null or omission leaves it unset at creation. + user_uuid : str + User UUID request_options : typing.Optional[RequestOptions] Request-specific configuration. @@ -337,25 +382,12 @@ def add( Returns ------- HttpResponse[User] - The user that was added. + OK """ _response = self._client_wrapper.httpx_client.request( - "users", - method="POST", - json={ - "disable_default_ontology": disable_default_ontology, - "email": email, - "first_name": first_name, - "last_name": last_name, - "metadata": metadata, - "time_zone": time_zone, - "user_id": user_id, - }, - headers={ - "content-type": "application/json", - }, + f"users/{jsonable_encoder(user_uuid)}", + method="GET", request_options=request_options, - omit=OMIT, ) try: if 200 <= _response.status_code < 300: @@ -369,17 +401,6 @@ def add( 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 == 500: - raise InternalServerError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -389,88 +410,19 @@ def add( ), ), ) - _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_ordered( - self, - *, - page_number: typing.Optional[int] = None, - page_size: typing.Optional[int] = None, - search: typing.Optional[str] = None, - order_by: typing.Optional[str] = None, - asc: typing.Optional[bool] = None, - request_options: typing.Optional[RequestOptions] = None, - ) -> HttpResponse[UserListResponse]: - """ - Returns all users. - - Parameters - ---------- - page_number : typing.Optional[int] - Page number for pagination, starting from 1 - - page_size : typing.Optional[int] - Number of users to retrieve per page - - search : typing.Optional[str] - Search term for filtering users by user_id, name, or email - - order_by : typing.Optional[str] - Column to sort by (created_at, user_id, email) - - asc : typing.Optional[bool] - Sort in ascending order - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - HttpResponse[UserListResponse] - Successfully retrieved list of users - """ - _response = self._client_wrapper.httpx_client.request( - "users-ordered", - method="GET", - params={ - "pageNumber": page_number, - "pageSize": page_size, - "search": search, - "order_by": order_by, - "asc": asc, - }, - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - _data = typing.cast( - UserListResponse, - parse_obj_as( - type_=UserListResponse, # type: ignore - object_=_response.json(), - ), - ) - return HttpResponse(response=_response, data=_data) - if _response.status_code == 400: - raise BadRequestError( + if _response.status_code == 401: + raise UnauthorizedError( headers=dict(_response.headers), body=typing.cast( - typing.Optional[typing.Any], + types_api_error_ApiError, parse_obj_as( - type_=typing.Optional[typing.Any], # type: ignore + type_=types_api_error_ApiError, # type: ignore object_=_response.json(), ), ), ) - if _response.status_code == 500: - raise InternalServerError( + if _response.status_code == 404: + raise NotFoundError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -485,44 +437,57 @@ def list_ordered( raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response.text ) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response_json ) - def get(self, user_id: str, *, request_options: typing.Optional[RequestOptions] = None) -> HttpResponse[User]: + def delete( + self, + user_uuid: str, + *, + idempotency_key: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[UserDeleteResult]: """ - Returns a user. - Parameters ---------- - user_id : str - The user_id of the user to get. + user_uuid : str + User UUID + + idempotency_key : typing.Optional[str] request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - HttpResponse[User] - The user that was retrieved. + HttpResponse[UserDeleteResult] + Accepted """ _response = self._client_wrapper.httpx_client.request( - f"users/{jsonable_encoder(user_id)}", - method="GET", + f"users/{jsonable_encoder(user_uuid)}", + method="DELETE", + headers={ + "Idempotency-Key": str(idempotency_key) if idempotency_key is not None else None, + }, request_options=request_options, ) try: if 200 <= _response.status_code < 300: _data = typing.cast( - User, + UserDeleteResult, parse_obj_as( - type_=User, # type: ignore + type_=UserDeleteResult, # type: ignore object_=_response.json(), ), ) return HttpResponse(response=_response, data=_data) - if _response.status_code == 404: - raise NotFoundError( + if _response.status_code == 400: + raise BadRequestError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -532,8 +497,8 @@ def get(self, user_id: str, *, request_options: typing.Optional[RequestOptions] ), ), ) - if _response.status_code == 500: - raise InternalServerError( + if _response.status_code == 401: + raise UnauthorizedError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -543,49 +508,6 @@ def get(self, user_id: str, *, request_options: typing.Optional[RequestOptions] ), ), ) - _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 delete( - self, user_id: str, *, request_options: typing.Optional[RequestOptions] = None - ) -> HttpResponse[SuccessResponse]: - """ - Deletes a user. - - Parameters - ---------- - user_id : str - User ID - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - HttpResponse[SuccessResponse] - OK - """ - _response = self._client_wrapper.httpx_client.request( - f"users/{jsonable_encoder(user_id)}", - method="DELETE", - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - _data = typing.cast( - SuccessResponse, - parse_obj_as( - type_=SuccessResponse, # type: ignore - object_=_response.json(), - ), - ) - return HttpResponse(response=_response, data=_data) if _response.status_code == 404: raise NotFoundError( headers=dict(_response.headers), @@ -597,63 +519,56 @@ def delete( ), ), ) - 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 ) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response_json ) def update( self, - user_id: str, + user_uuid: str, *, disable_default_ontology: typing.Optional[bool] = OMIT, email: typing.Optional[str] = OMIT, first_name: typing.Optional[str] = OMIT, last_name: typing.Optional[str] = OMIT, - metadata: typing.Optional[typing.Dict[str, typing.Optional[typing.Any]]] = OMIT, + metadata: typing.Optional[typing.Dict[str, typing.Any]] = OMIT, time_zone: typing.Optional[str] = OMIT, + idempotency_key: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, ) -> HttpResponse[User]: """ - Updates a user. - Parameters ---------- - user_id : str - User ID + user_uuid : str + User UUID disable_default_ontology : typing.Optional[bool] - When true, disables the use of default/fallback ontology for the user's graph. + Omit to leave unchanged, send JSON null to clear, or send a value to set. email : typing.Optional[str] - The email address of the user. + Omit to leave unchanged, send JSON null to clear, or send a value to set. first_name : typing.Optional[str] - The first name of the user. + Omit to leave unchanged, send JSON null to clear, or send a value to set. last_name : typing.Optional[str] - The last name of the user. + Omit to leave unchanged, send JSON null to clear, or send a value to set. - metadata : typing.Optional[typing.Dict[str, typing.Optional[typing.Any]]] - The metadata to update + metadata : typing.Optional[typing.Dict[str, typing.Any]] time_zone : typing.Optional[str] - The user's IANA time zone. Null clears the existing value. + Omit to leave unchanged, send JSON null to clear, or send a value to set. + + idempotency_key : typing.Optional[str] request_options : typing.Optional[RequestOptions] Request-specific configuration. @@ -661,10 +576,10 @@ def update( Returns ------- HttpResponse[User] - The user that was updated. + OK """ _response = self._client_wrapper.httpx_client.request( - f"users/{jsonable_encoder(user_id)}", + f"users/{jsonable_encoder(user_uuid)}", method="PATCH", json={ "disable_default_ontology": disable_default_ontology, @@ -676,6 +591,7 @@ def update( }, headers={ "content-type": "application/json", + "Idempotency-Key": str(idempotency_key) if idempotency_key is not None else None, }, request_options=request_options, omit=OMIT, @@ -694,15 +610,15 @@ def update( raise BadRequestError( headers=dict(_response.headers), body=typing.cast( - typing.Optional[typing.Any], + types_api_error_ApiError, parse_obj_as( - type_=typing.Optional[typing.Any], # type: ignore + type_=types_api_error_ApiError, # type: ignore object_=_response.json(), ), ), ) - if _response.status_code == 404: - raise NotFoundError( + if _response.status_code == 401: + raise UnauthorizedError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -712,8 +628,8 @@ def update( ), ), ) - if _response.status_code == 500: - raise InternalServerError( + if _response.status_code == 404: + raise NotFoundError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -728,46 +644,48 @@ def update( raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response.text ) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response_json ) def get_node( - self, user_id: str, *, request_options: typing.Optional[RequestOptions] = None - ) -> HttpResponse[UserNodeResponse]: + self, user_uuid: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[Node]: """ - Returns a user's node. - Parameters ---------- - user_id : str - The user_id of the user to get the node for. + user_uuid : str + User UUID request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - HttpResponse[UserNodeResponse] - Response object containing the User node. + HttpResponse[Node] + OK """ _response = self._client_wrapper.httpx_client.request( - f"users/{jsonable_encoder(user_id)}/node", + f"users/{jsonable_encoder(user_uuid)}/node", method="GET", request_options=request_options, ) try: if 200 <= _response.status_code < 300: _data = typing.cast( - UserNodeResponse, + Node, parse_obj_as( - type_=UserNodeResponse, # type: ignore + type_=Node, # type: ignore object_=_response.json(), ), ) return HttpResponse(response=_response, data=_data) - if _response.status_code == 404: - raise NotFoundError( + if _response.status_code == 400: + raise BadRequestError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -777,8 +695,19 @@ def get_node( ), ), ) - if _response.status_code == 500: - raise InternalServerError( + if _response.status_code == 401: + raise UnauthorizedError( + 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, @@ -793,46 +722,48 @@ def get_node( raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response.text ) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response_json ) - def get_threads( - self, user_id: str, *, request_options: typing.Optional[RequestOptions] = None - ) -> HttpResponse[typing.List[Thread]]: + def get_summary_instructions( + self, user_uuid: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[UserSummaryInstructions]: """ - Returns all threads for a user. - Parameters ---------- - user_id : str - User ID + user_uuid : str + User UUID request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - HttpResponse[typing.List[Thread]] + HttpResponse[UserSummaryInstructions] OK """ _response = self._client_wrapper.httpx_client.request( - f"users/{jsonable_encoder(user_id)}/threads", + f"users/{jsonable_encoder(user_uuid)}/summary-instructions", method="GET", request_options=request_options, ) try: if 200 <= _response.status_code < 300: _data = typing.cast( - typing.List[Thread], + UserSummaryInstructions, parse_obj_as( - type_=typing.List[Thread], # type: ignore + type_=UserSummaryInstructions, # type: ignore object_=_response.json(), ), ) return HttpResponse(response=_response, data=_data) - if _response.status_code == 500: - raise InternalServerError( + if _response.status_code == 400: + raise BadRequestError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -842,51 +773,8 @@ def get_threads( ), ), ) - _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 warm( - self, user_id: str, *, request_options: typing.Optional[RequestOptions] = None - ) -> HttpResponse[SuccessResponse]: - """ - Hints Zep to warm a user's graph for low-latency search - - Parameters - ---------- - user_id : str - User ID - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - HttpResponse[SuccessResponse] - Warm hint accepted - """ - _response = self._client_wrapper.httpx_client.request( - f"users/{jsonable_encoder(user_id)}/warm", - method="GET", - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - _data = typing.cast( - SuccessResponse, - parse_obj_as( - type_=SuccessResponse, # type: ignore - object_=_response.json(), - ), - ) - return HttpResponse(response=_response, data=_data) - if _response.status_code == 404: - raise NotFoundError( + if _response.status_code == 401: + raise UnauthorizedError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -896,8 +784,8 @@ def warm( ), ), ) - if _response.status_code == 500: - raise InternalServerError( + if _response.status_code == 404: + raise NotFoundError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -912,65 +800,80 @@ def warm( raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response.text ) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response_json ) - -class AsyncRawUserClient: - def __init__(self, *, client_wrapper: AsyncClientWrapper): - self._client_wrapper = client_wrapper - - async def list_user_summary_instructions( - self, *, user_id: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None - ) -> AsyncHttpResponse[ListUserInstructionsResponse]: + def set_summary_instructions( + self, + user_uuid: str, + *, + inherited: typing.Optional[bool] = OMIT, + instructions: typing.Optional[typing.Sequence[typing.Dict[str, typing.Any]]] = OMIT, + idempotency_key: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[UserSummaryInstructions]: """ - Lists all user summary instructions for a project, user. - Parameters ---------- - user_id : typing.Optional[str] - User ID to get user-specific instructions + user_uuid : str + User UUID + + inherited : typing.Optional[bool] + + instructions : typing.Optional[typing.Sequence[typing.Dict[str, typing.Any]]] + + idempotency_key : typing.Optional[str] request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - AsyncHttpResponse[ListUserInstructionsResponse] - The list of instructions. + HttpResponse[UserSummaryInstructions] + OK """ - _response = await self._client_wrapper.httpx_client.request( - "user-summary-instructions", - method="GET", - params={ - "user_id": user_id, + _response = self._client_wrapper.httpx_client.request( + f"users/{jsonable_encoder(user_uuid)}/summary-instructions", + method="PUT", + json={ + "inherited": inherited, + "instructions": instructions, + }, + headers={ + "content-type": "application/json", + "Idempotency-Key": str(idempotency_key) if idempotency_key is not None else None, }, request_options=request_options, + omit=OMIT, ) try: if 200 <= _response.status_code < 300: _data = typing.cast( - ListUserInstructionsResponse, + UserSummaryInstructions, parse_obj_as( - type_=ListUserInstructionsResponse, # type: ignore + type_=UserSummaryInstructions, # type: ignore object_=_response.json(), ), ) - return AsyncHttpResponse(response=_response, data=_data) + return HttpResponse(response=_response, data=_data) if _response.status_code == 400: raise BadRequestError( headers=dict(_response.headers), body=typing.cast( - typing.Optional[typing.Any], + types_api_error_ApiError, parse_obj_as( - type_=typing.Optional[typing.Any], # type: ignore + type_=types_api_error_ApiError, # type: ignore object_=_response.json(), ), ), ) - if _response.status_code == 404: - raise NotFoundError( + if _response.status_code == 401: + raise UnauthorizedError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -980,8 +883,8 @@ async def list_user_summary_instructions( ), ), ) - if _response.status_code == 500: - raise InternalServerError( + if _response.status_code == 404: + raise NotFoundError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -996,47 +899,74 @@ async def list_user_summary_instructions( raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response.text ) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response_json ) - async def add_user_summary_instructions( + +class AsyncRawUserClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._client_wrapper = client_wrapper + + async def create( self, *, - instructions: typing.Sequence[UserInstruction], - user_ids: typing.Optional[typing.Sequence[str]] = OMIT, + disable_default_ontology: typing.Optional[bool] = OMIT, + email: typing.Optional[str] = OMIT, + first_name: typing.Optional[str] = OMIT, + last_name: typing.Optional[str] = OMIT, + metadata: typing.Optional[typing.Dict[str, typing.Any]] = OMIT, + time_zone: typing.Optional[str] = OMIT, + user_id: typing.Optional[str] = OMIT, + idempotency_key: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, - ) -> AsyncHttpResponse[SuccessResponse]: + ) -> AsyncHttpResponse[User]: """ - Adds new summary instructions for users graphs without removing existing ones. If user_ids is empty, adds to project-wide default instructions. + Parameters + ---------- + disable_default_ontology : typing.Optional[bool] + + email : typing.Optional[str] + + first_name : typing.Optional[str] + + last_name : typing.Optional[str] + + metadata : typing.Optional[typing.Dict[str, typing.Any]] + + time_zone : typing.Optional[str] - Parameters - ---------- - instructions : typing.Sequence[UserInstruction] - Instructions to add to the user summary generation. + user_id : typing.Optional[str] - user_ids : typing.Optional[typing.Sequence[str]] - User IDs to add the instructions to. If empty, the instructions are added to the project-wide default. + idempotency_key : typing.Optional[str] request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - AsyncHttpResponse[SuccessResponse] - Instructions added successfully + AsyncHttpResponse[User] + Created """ _response = await self._client_wrapper.httpx_client.request( - "user-summary-instructions", + "users", method="POST", json={ - "instructions": convert_and_respect_annotation_metadata( - object_=instructions, annotation=typing.Sequence[UserInstruction], direction="write" - ), - "user_ids": user_ids, + "disable_default_ontology": disable_default_ontology, + "email": email, + "first_name": first_name, + "last_name": last_name, + "metadata": metadata, + "time_zone": time_zone, + "user_id": user_id, }, headers={ "content-type": "application/json", + "Idempotency-Key": str(idempotency_key) if idempotency_key is not None else None, }, request_options=request_options, omit=OMIT, @@ -1044,9 +974,9 @@ async def add_user_summary_instructions( try: if 200 <= _response.status_code < 300: _data = typing.cast( - SuccessResponse, + User, parse_obj_as( - type_=SuccessResponse, # type: ignore + type_=User, # type: ignore object_=_response.json(), ), ) @@ -1055,15 +985,15 @@ async def add_user_summary_instructions( raise BadRequestError( headers=dict(_response.headers), body=typing.cast( - typing.Optional[typing.Any], + types_api_error_ApiError, parse_obj_as( - type_=typing.Optional[typing.Any], # type: ignore + type_=types_api_error_ApiError, # type: ignore object_=_response.json(), ), ), ) - if _response.status_code == 404: - raise NotFoundError( + if _response.status_code == 401: + raise UnauthorizedError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -1073,8 +1003,8 @@ async def add_user_summary_instructions( ), ), ) - if _response.status_code == 500: - raise InternalServerError( + if _response.status_code == 404: + raise NotFoundError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -1089,72 +1019,109 @@ async def add_user_summary_instructions( raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response.text ) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response_json ) - async def delete_user_summary_instructions( + async def list( self, *, - instruction_names: typing.Optional[typing.Sequence[str]] = OMIT, - user_ids: typing.Optional[typing.Sequence[str]] = OMIT, + limit: typing.Optional[int] = None, + cursor: typing.Optional[str] = None, + order_by: typing.Optional[str] = None, + order: typing.Optional[str] = None, + search: typing.Optional[str] = OMIT, + idempotency_key: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, - ) -> AsyncHttpResponse[SuccessResponse]: + ) -> AsyncPager[User, UserPage]: """ - Deletes user summary/instructions for users or project wide defaults. - Parameters ---------- - instruction_names : typing.Optional[typing.Sequence[str]] - Unique identifier for the instructions to be deleted. If empty deletes all instructions. + limit : typing.Optional[int] + Page size - user_ids : typing.Optional[typing.Sequence[str]] - Determines which users will have their custom instructions deleted. If no users are provided, the project-wide custom instructions will be effected. + cursor : typing.Optional[str] + Opaque page cursor + + order_by : typing.Optional[str] + Sort field + + order : typing.Optional[str] + asc or desc + + search : typing.Optional[str] + + idempotency_key : typing.Optional[str] request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - AsyncHttpResponse[SuccessResponse] - Instructions deleted successfully + AsyncPager[User, UserPage] + OK """ _response = await self._client_wrapper.httpx_client.request( - "user-summary-instructions", - method="DELETE", + "users/list", + method="POST", + params={ + "limit": limit, + "cursor": cursor, + "order_by": order_by, + "order": order, + }, json={ - "instruction_names": instruction_names, - "user_ids": user_ids, + "search": search, }, headers={ "content-type": "application/json", + "Idempotency-Key": str(idempotency_key) if idempotency_key is not None else None, }, request_options=request_options, omit=OMIT, ) try: if 200 <= _response.status_code < 300: - _data = typing.cast( - SuccessResponse, + _parsed_response = typing.cast( + UserPage, parse_obj_as( - type_=SuccessResponse, # type: ignore + type_=UserPage, # type: ignore object_=_response.json(), ), ) - return AsyncHttpResponse(response=_response, data=_data) + _items = _parsed_response.items + _parsed_next = _parsed_response.next_cursor + _has_next = _parsed_next is not None and _parsed_next != "" + + async def _get_next(): + return await self.list( + limit=limit, + cursor=_parsed_next, + order_by=order_by, + order=order, + search=search, + idempotency_key=idempotency_key, + request_options=request_options, + ) + + return AsyncPager(has_next=_has_next, items=_items, get_next=_get_next, response=_parsed_response) if _response.status_code == 400: raise BadRequestError( headers=dict(_response.headers), body=typing.cast( - typing.Optional[typing.Any], + types_api_error_ApiError, parse_obj_as( - type_=typing.Optional[typing.Any], # type: ignore + type_=types_api_error_ApiError, # type: ignore object_=_response.json(), ), ), ) - if _response.status_code == 404: - raise NotFoundError( + if _response.status_code == 401: + raise UnauthorizedError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -1164,8 +1131,8 @@ async def delete_user_summary_instructions( ), ), ) - if _response.status_code == 500: - raise InternalServerError( + if _response.status_code == 404: + raise NotFoundError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -1180,47 +1147,33 @@ async def delete_user_summary_instructions( raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response.text ) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response_json ) - async def add( + async def lookup( self, *, - user_id: str, - disable_default_ontology: typing.Optional[bool] = OMIT, - email: typing.Optional[str] = OMIT, - first_name: typing.Optional[str] = OMIT, - last_name: typing.Optional[str] = OMIT, - metadata: typing.Optional[typing.Dict[str, typing.Optional[typing.Any]]] = OMIT, - time_zone: typing.Optional[str] = OMIT, + graph_id: typing.Optional[str] = OMIT, + thread_id: typing.Optional[str] = OMIT, + user_id: typing.Optional[str] = OMIT, + idempotency_key: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, ) -> AsyncHttpResponse[User]: """ - Adds a user. - Parameters ---------- - user_id : str - The unique identifier of the user. - - disable_default_ontology : typing.Optional[bool] - When true, disables the use of default/fallback ontology for the user's graph. + graph_id : typing.Optional[str] - email : typing.Optional[str] - The email address of the user. - - first_name : typing.Optional[str] - The first name of the user. - - last_name : typing.Optional[str] - The last name of the user. + thread_id : typing.Optional[str] - metadata : typing.Optional[typing.Dict[str, typing.Optional[typing.Any]]] - The metadata associated with the user. + user_id : typing.Optional[str] - time_zone : typing.Optional[str] - The user's IANA time zone. Null or omission leaves it unset at creation. + idempotency_key : typing.Optional[str] request_options : typing.Optional[RequestOptions] Request-specific configuration. @@ -1228,22 +1181,19 @@ async def add( Returns ------- AsyncHttpResponse[User] - The user that was added. + OK """ _response = await self._client_wrapper.httpx_client.request( - "users", + "users/lookup", method="POST", json={ - "disable_default_ontology": disable_default_ontology, - "email": email, - "first_name": first_name, - "last_name": last_name, - "metadata": metadata, - "time_zone": time_zone, + "graph_id": graph_id, + "thread_id": thread_id, "user_id": user_id, }, headers={ "content-type": "application/json", + "Idempotency-Key": str(idempotency_key) if idempotency_key is not None else None, }, request_options=request_options, omit=OMIT, @@ -1260,17 +1210,6 @@ async def add( 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 == 500: - raise InternalServerError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -1280,88 +1219,19 @@ async def add( ), ), ) - _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_ordered( - self, - *, - page_number: typing.Optional[int] = None, - page_size: typing.Optional[int] = None, - search: typing.Optional[str] = None, - order_by: typing.Optional[str] = None, - asc: typing.Optional[bool] = None, - request_options: typing.Optional[RequestOptions] = None, - ) -> AsyncHttpResponse[UserListResponse]: - """ - Returns all users. - - Parameters - ---------- - page_number : typing.Optional[int] - Page number for pagination, starting from 1 - - page_size : typing.Optional[int] - Number of users to retrieve per page - - search : typing.Optional[str] - Search term for filtering users by user_id, name, or email - - order_by : typing.Optional[str] - Column to sort by (created_at, user_id, email) - - asc : typing.Optional[bool] - Sort in ascending order - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - AsyncHttpResponse[UserListResponse] - Successfully retrieved list of users - """ - _response = await self._client_wrapper.httpx_client.request( - "users-ordered", - method="GET", - params={ - "pageNumber": page_number, - "pageSize": page_size, - "search": search, - "order_by": order_by, - "asc": asc, - }, - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - _data = typing.cast( - UserListResponse, - parse_obj_as( - type_=UserListResponse, # type: ignore - object_=_response.json(), - ), - ) - return AsyncHttpResponse(response=_response, data=_data) - if _response.status_code == 400: - raise BadRequestError( + if _response.status_code == 401: + raise UnauthorizedError( headers=dict(_response.headers), body=typing.cast( - typing.Optional[typing.Any], + types_api_error_ApiError, parse_obj_as( - type_=typing.Optional[typing.Any], # type: ignore + type_=types_api_error_ApiError, # type: ignore object_=_response.json(), ), ), ) - if _response.status_code == 500: - raise InternalServerError( + if _response.status_code == 404: + raise NotFoundError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -1376,20 +1246,22 @@ async def list_ordered( raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response.text ) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response_json ) async def get( - self, user_id: str, *, request_options: typing.Optional[RequestOptions] = None + self, user_uuid: str, *, request_options: typing.Optional[RequestOptions] = None ) -> AsyncHttpResponse[User]: """ - Returns a user. - Parameters ---------- - user_id : str - The user_id of the user to get. + user_uuid : str + User UUID request_options : typing.Optional[RequestOptions] Request-specific configuration. @@ -1397,10 +1269,10 @@ async def get( Returns ------- AsyncHttpResponse[User] - The user that was retrieved. + OK """ _response = await self._client_wrapper.httpx_client.request( - f"users/{jsonable_encoder(user_id)}", + f"users/{jsonable_encoder(user_uuid)}", method="GET", request_options=request_options, ) @@ -1414,8 +1286,19 @@ async def get( ), ) return AsyncHttpResponse(response=_response, data=_data) - if _response.status_code == 404: - raise NotFoundError( + if _response.status_code == 400: + raise BadRequestError( + 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 == 401: + raise UnauthorizedError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -1425,8 +1308,8 @@ async def get( ), ), ) - if _response.status_code == 500: - raise InternalServerError( + if _response.status_code == 404: + raise NotFoundError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -1441,46 +1324,68 @@ async def get( raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response.text ) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response_json ) async def delete( - self, user_id: str, *, request_options: typing.Optional[RequestOptions] = None - ) -> AsyncHttpResponse[SuccessResponse]: + self, + user_uuid: str, + *, + idempotency_key: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[UserDeleteResult]: """ - Deletes a user. - Parameters ---------- - user_id : str - User ID + user_uuid : str + User UUID + + idempotency_key : typing.Optional[str] request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - AsyncHttpResponse[SuccessResponse] - OK + AsyncHttpResponse[UserDeleteResult] + Accepted """ _response = await self._client_wrapper.httpx_client.request( - f"users/{jsonable_encoder(user_id)}", + f"users/{jsonable_encoder(user_uuid)}", method="DELETE", + headers={ + "Idempotency-Key": str(idempotency_key) if idempotency_key is not None else None, + }, request_options=request_options, ) try: if 200 <= _response.status_code < 300: _data = typing.cast( - SuccessResponse, + UserDeleteResult, parse_obj_as( - type_=SuccessResponse, # type: ignore + type_=UserDeleteResult, # type: ignore object_=_response.json(), ), ) return AsyncHttpResponse(response=_response, data=_data) - if _response.status_code == 404: - raise NotFoundError( + if _response.status_code == 400: + raise BadRequestError( + 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 == 401: + raise UnauthorizedError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -1490,8 +1395,8 @@ async def delete( ), ), ) - if _response.status_code == 500: - raise InternalServerError( + if _response.status_code == 404: + raise NotFoundError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -1506,47 +1411,51 @@ async def delete( raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response.text ) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response_json ) async def update( self, - user_id: str, + user_uuid: str, *, disable_default_ontology: typing.Optional[bool] = OMIT, email: typing.Optional[str] = OMIT, first_name: typing.Optional[str] = OMIT, last_name: typing.Optional[str] = OMIT, - metadata: typing.Optional[typing.Dict[str, typing.Optional[typing.Any]]] = OMIT, + metadata: typing.Optional[typing.Dict[str, typing.Any]] = OMIT, time_zone: typing.Optional[str] = OMIT, + idempotency_key: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, ) -> AsyncHttpResponse[User]: """ - Updates a user. - Parameters ---------- - user_id : str - User ID + user_uuid : str + User UUID disable_default_ontology : typing.Optional[bool] - When true, disables the use of default/fallback ontology for the user's graph. + Omit to leave unchanged, send JSON null to clear, or send a value to set. email : typing.Optional[str] - The email address of the user. + Omit to leave unchanged, send JSON null to clear, or send a value to set. first_name : typing.Optional[str] - The first name of the user. + Omit to leave unchanged, send JSON null to clear, or send a value to set. last_name : typing.Optional[str] - The last name of the user. + Omit to leave unchanged, send JSON null to clear, or send a value to set. - metadata : typing.Optional[typing.Dict[str, typing.Optional[typing.Any]]] - The metadata to update + metadata : typing.Optional[typing.Dict[str, typing.Any]] time_zone : typing.Optional[str] - The user's IANA time zone. Null clears the existing value. + Omit to leave unchanged, send JSON null to clear, or send a value to set. + + idempotency_key : typing.Optional[str] request_options : typing.Optional[RequestOptions] Request-specific configuration. @@ -1554,10 +1463,10 @@ async def update( Returns ------- AsyncHttpResponse[User] - The user that was updated. + OK """ _response = await self._client_wrapper.httpx_client.request( - f"users/{jsonable_encoder(user_id)}", + f"users/{jsonable_encoder(user_uuid)}", method="PATCH", json={ "disable_default_ontology": disable_default_ontology, @@ -1569,6 +1478,7 @@ async def update( }, headers={ "content-type": "application/json", + "Idempotency-Key": str(idempotency_key) if idempotency_key is not None else None, }, request_options=request_options, omit=OMIT, @@ -1587,15 +1497,15 @@ async def update( raise BadRequestError( headers=dict(_response.headers), body=typing.cast( - typing.Optional[typing.Any], + types_api_error_ApiError, parse_obj_as( - type_=typing.Optional[typing.Any], # type: ignore + type_=types_api_error_ApiError, # type: ignore object_=_response.json(), ), ), ) - if _response.status_code == 404: - raise NotFoundError( + if _response.status_code == 401: + raise UnauthorizedError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -1605,8 +1515,8 @@ async def update( ), ), ) - if _response.status_code == 500: - raise InternalServerError( + if _response.status_code == 404: + raise NotFoundError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -1621,46 +1531,59 @@ async def update( raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response.text ) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response_json ) async def get_node( - self, user_id: str, *, request_options: typing.Optional[RequestOptions] = None - ) -> AsyncHttpResponse[UserNodeResponse]: + self, user_uuid: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[Node]: """ - Returns a user's node. - Parameters ---------- - user_id : str - The user_id of the user to get the node for. + user_uuid : str + User UUID request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - AsyncHttpResponse[UserNodeResponse] - Response object containing the User node. + AsyncHttpResponse[Node] + OK """ _response = await self._client_wrapper.httpx_client.request( - f"users/{jsonable_encoder(user_id)}/node", + f"users/{jsonable_encoder(user_uuid)}/node", method="GET", request_options=request_options, ) try: if 200 <= _response.status_code < 300: _data = typing.cast( - UserNodeResponse, + Node, parse_obj_as( - type_=UserNodeResponse, # type: ignore + type_=Node, # type: ignore object_=_response.json(), ), ) return AsyncHttpResponse(response=_response, data=_data) - if _response.status_code == 404: - raise NotFoundError( + if _response.status_code == 400: + raise BadRequestError( + 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 == 401: + raise UnauthorizedError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -1670,8 +1593,8 @@ async def get_node( ), ), ) - if _response.status_code == 500: - raise InternalServerError( + if _response.status_code == 404: + raise NotFoundError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -1686,46 +1609,70 @@ async def get_node( raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response.text ) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response_json ) - async def get_threads( - self, user_id: str, *, request_options: typing.Optional[RequestOptions] = None - ) -> AsyncHttpResponse[typing.List[Thread]]: + async def get_summary_instructions( + self, user_uuid: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[UserSummaryInstructions]: """ - Returns all threads for a user. - Parameters ---------- - user_id : str - User ID + user_uuid : str + User UUID request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - AsyncHttpResponse[typing.List[Thread]] + AsyncHttpResponse[UserSummaryInstructions] OK """ _response = await self._client_wrapper.httpx_client.request( - f"users/{jsonable_encoder(user_id)}/threads", + f"users/{jsonable_encoder(user_uuid)}/summary-instructions", method="GET", request_options=request_options, ) try: if 200 <= _response.status_code < 300: _data = typing.cast( - typing.List[Thread], + UserSummaryInstructions, parse_obj_as( - type_=typing.List[Thread], # type: ignore + type_=UserSummaryInstructions, # type: ignore object_=_response.json(), ), ) return AsyncHttpResponse(response=_response, data=_data) - if _response.status_code == 500: - raise InternalServerError( + if _response.status_code == 400: + raise BadRequestError( + 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 == 401: + raise UnauthorizedError( + 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, @@ -1740,46 +1687,80 @@ async def get_threads( raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response.text ) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response_json ) - async def warm( - self, user_id: str, *, request_options: typing.Optional[RequestOptions] = None - ) -> AsyncHttpResponse[SuccessResponse]: + async def set_summary_instructions( + self, + user_uuid: str, + *, + inherited: typing.Optional[bool] = OMIT, + instructions: typing.Optional[typing.Sequence[typing.Dict[str, typing.Any]]] = OMIT, + idempotency_key: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[UserSummaryInstructions]: """ - Hints Zep to warm a user's graph for low-latency search - Parameters ---------- - user_id : str - User ID + user_uuid : str + User UUID + + inherited : typing.Optional[bool] + + instructions : typing.Optional[typing.Sequence[typing.Dict[str, typing.Any]]] + + idempotency_key : typing.Optional[str] request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - AsyncHttpResponse[SuccessResponse] - Warm hint accepted + AsyncHttpResponse[UserSummaryInstructions] + OK """ _response = await self._client_wrapper.httpx_client.request( - f"users/{jsonable_encoder(user_id)}/warm", - method="GET", + f"users/{jsonable_encoder(user_uuid)}/summary-instructions", + method="PUT", + json={ + "inherited": inherited, + "instructions": instructions, + }, + headers={ + "content-type": "application/json", + "Idempotency-Key": str(idempotency_key) if idempotency_key is not None else None, + }, request_options=request_options, + omit=OMIT, ) try: if 200 <= _response.status_code < 300: _data = typing.cast( - SuccessResponse, + UserSummaryInstructions, parse_obj_as( - type_=SuccessResponse, # type: ignore + type_=UserSummaryInstructions, # type: ignore object_=_response.json(), ), ) return AsyncHttpResponse(response=_response, data=_data) - if _response.status_code == 404: - raise NotFoundError( + if _response.status_code == 400: + raise BadRequestError( + 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 == 401: + raise UnauthorizedError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -1789,8 +1770,8 @@ async def warm( ), ), ) - if _response.status_code == 500: - raise InternalServerError( + if _response.status_code == 404: + raise NotFoundError( headers=dict(_response.headers), body=typing.cast( types_api_error_ApiError, @@ -1805,6 +1786,10 @@ async def warm( raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response.text ) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) raise core_api_error_ApiError( status_code=_response.status_code, headers=dict(_response.headers), body=_response_json ) diff --git a/src/zep_cloud/user_group/client.py b/src/zep_cloud/user_group/client.py deleted file mode 100644 index 631a7b4f..00000000 --- a/src/zep_cloud/user_group/client.py +++ /dev/null @@ -1,1450 +0,0 @@ -# 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.attached_policy_sets_response import AttachedPolicySetsResponse -from ..types.user_group_list_response import UserGroupListResponse -from ..types.user_group_members_response import UserGroupMembersResponse -from ..types.user_group_membership_response import UserGroupMembershipResponse -from ..types.user_group_response import UserGroupResponse -from ..types.user_membership_groups_response import UserMembershipGroupsResponse -from .raw_client import AsyncRawUserGroupClient, RawUserGroupClient - -# this is used as the default value for optional parameters -OMIT = typing.cast(typing.Any, ...) - - -class UserGroupClient: - def __init__(self, *, client_wrapper: SyncClientWrapper): - self._raw_client = RawUserGroupClient(client_wrapper=client_wrapper) - - @property - def with_raw_response(self) -> RawUserGroupClient: - """ - Retrieves a raw implementation of this client that returns raw responses. - - Returns - ------- - RawUserGroupClient - """ - return self._raw_client - - def list_policy_sets( - self, group_uuid: str, *, project_id: str, request_options: typing.Optional[RequestOptions] = None - ) -> AttachedPolicySetsResponse: - """ - Parameters - ---------- - group_uuid : str - UserGroup UUID - - project_id : str - Project UUID - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - AttachedPolicySetsResponse - OK - - Examples - -------- - from zep_cloud import Zep - - client = Zep( - api_key="YOUR_API_KEY", - ) - client.user_group.list_policy_sets( - group_uuid="groupUUID", - project_id="projectId", - ) - """ - _response = self._raw_client.list_policy_sets( - group_uuid, project_id=project_id, request_options=request_options - ) - return _response.data - - def attach_policy_set( - self, - group_uuid: str, - *, - project_id: str, - policy_set_uuid: str, - request_options: typing.Optional[RequestOptions] = None, - ) -> AttachedPolicySetsResponse: - """ - Parameters - ---------- - group_uuid : str - UserGroup UUID - - project_id : str - Project UUID - - policy_set_uuid : str - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - AttachedPolicySetsResponse - OK - - Examples - -------- - from zep_cloud import Zep - - client = Zep( - api_key="YOUR_API_KEY", - ) - client.user_group.attach_policy_set( - group_uuid="groupUUID", - project_id="projectId", - policy_set_uuid="policy_set_uuid", - ) - """ - _response = self._raw_client.attach_policy_set( - group_uuid, project_id=project_id, policy_set_uuid=policy_set_uuid, request_options=request_options - ) - return _response.data - - def detach_policy_set( - self, - group_uuid: str, - policy_set_uuid: str, - *, - project_id: str, - request_options: typing.Optional[RequestOptions] = None, - ) -> AttachedPolicySetsResponse: - """ - Parameters - ---------- - group_uuid : str - UserGroup UUID - - policy_set_uuid : str - Policy set UUID - - project_id : str - Project UUID - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - AttachedPolicySetsResponse - OK - - Examples - -------- - from zep_cloud import Zep - - client = Zep( - api_key="YOUR_API_KEY", - ) - client.user_group.detach_policy_set( - group_uuid="groupUUID", - policy_set_uuid="policySetUUID", - project_id="projectId", - ) - """ - _response = self._raw_client.detach_policy_set( - group_uuid, policy_set_uuid, project_id=project_id, request_options=request_options - ) - return _response.data - - def list( - self, - *, - project_id: str, - page_number: int, - page_size: int, - search: typing.Optional[str] = None, - request_options: typing.Optional[RequestOptions] = None, - ) -> UserGroupListResponse: - """ - Parameters - ---------- - project_id : str - Project UUID - - page_number : int - Page number - - page_size : int - Page size - - search : typing.Optional[str] - Name search - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - UserGroupListResponse - OK - - Examples - -------- - from zep_cloud import Zep - - client = Zep( - api_key="YOUR_API_KEY", - ) - client.user_group.list( - project_id="projectId", - page_number=1, - page_size=1, - search="search", - ) - """ - _response = self._raw_client.list( - project_id=project_id, - page_number=page_number, - page_size=page_size, - search=search, - request_options=request_options, - ) - return _response.data - - def create( - self, - *, - project_id: str, - name: str, - description: typing.Optional[str] = OMIT, - request_options: typing.Optional[RequestOptions] = None, - ) -> UserGroupResponse: - """ - Parameters - ---------- - project_id : str - Project UUID - - name : str - - description : typing.Optional[str] - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - UserGroupResponse - Created - - Examples - -------- - from zep_cloud import Zep - - client = Zep( - api_key="YOUR_API_KEY", - ) - client.user_group.create( - project_id="projectId", - name="name", - ) - """ - _response = self._raw_client.create( - project_id=project_id, name=name, description=description, request_options=request_options - ) - return _response.data - - def list_for_user( - self, user_uuid: str, *, project_id: str, request_options: typing.Optional[RequestOptions] = None - ) -> UserMembershipGroupsResponse: - """ - Parameters - ---------- - user_uuid : str - User UUID - - project_id : str - Project UUID - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - UserMembershipGroupsResponse - OK - - Examples - -------- - from zep_cloud import Zep - - client = Zep( - api_key="YOUR_API_KEY", - ) - client.user_group.list_for_user( - user_uuid="userUUID", - project_id="projectId", - ) - """ - _response = self._raw_client.list_for_user(user_uuid, project_id=project_id, request_options=request_options) - return _response.data - - def get( - self, group_uuid: str, *, project_id: str, request_options: typing.Optional[RequestOptions] = None - ) -> UserGroupResponse: - """ - Parameters - ---------- - group_uuid : str - UserGroup UUID - - project_id : str - Project UUID - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - UserGroupResponse - OK - - Examples - -------- - from zep_cloud import Zep - - client = Zep( - api_key="YOUR_API_KEY", - ) - client.user_group.get( - group_uuid="groupUUID", - project_id="projectId", - ) - """ - _response = self._raw_client.get(group_uuid, project_id=project_id, request_options=request_options) - return _response.data - - def delete( - self, group_uuid: str, *, project_id: str, request_options: typing.Optional[RequestOptions] = None - ) -> None: - """ - Parameters - ---------- - group_uuid : str - UserGroup UUID - - project_id : str - Project UUID - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - None - - Examples - -------- - from zep_cloud import Zep - - client = Zep( - api_key="YOUR_API_KEY", - ) - client.user_group.delete( - group_uuid="groupUUID", - project_id="projectId", - ) - """ - _response = self._raw_client.delete(group_uuid, project_id=project_id, request_options=request_options) - return _response.data - - def update( - self, - group_uuid: str, - *, - project_id: str, - expected_version: int, - description: typing.Optional[str] = OMIT, - name: typing.Optional[str] = OMIT, - request_options: typing.Optional[RequestOptions] = None, - ) -> UserGroupResponse: - """ - Parameters - ---------- - group_uuid : str - UserGroup UUID - - project_id : str - Project UUID - - expected_version : int - - description : typing.Optional[str] - - name : typing.Optional[str] - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - UserGroupResponse - OK - - Examples - -------- - from zep_cloud import Zep - - client = Zep( - api_key="YOUR_API_KEY", - ) - client.user_group.update( - group_uuid="groupUUID", - project_id="projectId", - expected_version=1, - ) - """ - _response = self._raw_client.update( - group_uuid, - project_id=project_id, - expected_version=expected_version, - description=description, - name=name, - request_options=request_options, - ) - return _response.data - - def list_members( - self, - group_uuid: str, - *, - project_id: str, - page_number: int, - page_size: int, - search: typing.Optional[str] = None, - request_options: typing.Optional[RequestOptions] = None, - ) -> UserGroupMembersResponse: - """ - Parameters - ---------- - group_uuid : str - UserGroup UUID - - project_id : str - Project UUID - - page_number : int - Page number - - page_size : int - Page size - - search : typing.Optional[str] - User search - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - UserGroupMembersResponse - OK - - Examples - -------- - from zep_cloud import Zep - - client = Zep( - api_key="YOUR_API_KEY", - ) - client.user_group.list_members( - group_uuid="groupUUID", - project_id="projectId", - page_number=1, - page_size=1, - search="search", - ) - """ - _response = self._raw_client.list_members( - group_uuid, - project_id=project_id, - page_number=page_number, - page_size=page_size, - search=search, - request_options=request_options, - ) - return _response.data - - def add_members( - self, - group_uuid: str, - *, - project_id: str, - user_uuids: typing.Sequence[str], - request_options: typing.Optional[RequestOptions] = None, - ) -> UserGroupMembershipResponse: - """ - Parameters - ---------- - group_uuid : str - UserGroup UUID - - project_id : str - Project UUID - - user_uuids : typing.Sequence[str] - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - UserGroupMembershipResponse - OK - - Examples - -------- - from zep_cloud import Zep - - client = Zep( - api_key="YOUR_API_KEY", - ) - client.user_group.add_members( - group_uuid="groupUUID", - project_id="projectId", - user_uuids=["user_uuids"], - ) - """ - _response = self._raw_client.add_members( - group_uuid, project_id=project_id, user_uuids=user_uuids, request_options=request_options - ) - return _response.data - - def remove_members( - self, - group_uuid: str, - *, - project_id: str, - user_uuids: typing.Sequence[str], - request_options: typing.Optional[RequestOptions] = None, - ) -> UserGroupMembershipResponse: - """ - Parameters - ---------- - group_uuid : str - UserGroup UUID - - project_id : str - Project UUID - - user_uuids : typing.Sequence[str] - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - UserGroupMembershipResponse - OK - - Examples - -------- - from zep_cloud import Zep - - client = Zep( - api_key="YOUR_API_KEY", - ) - client.user_group.remove_members( - group_uuid="groupUUID", - project_id="projectId", - user_uuids=["user_uuids"], - ) - """ - _response = self._raw_client.remove_members( - group_uuid, project_id=project_id, user_uuids=user_uuids, request_options=request_options - ) - return _response.data - - def list_member_candidates( - self, - group_uuid: str, - *, - project_id: str, - page_number: int, - page_size: int, - search: typing.Optional[str] = None, - request_options: typing.Optional[RequestOptions] = None, - ) -> UserGroupMembersResponse: - """ - Parameters - ---------- - group_uuid : str - UserGroup UUID - - project_id : str - Project UUID - - page_number : int - Page number - - page_size : int - Page size - - search : typing.Optional[str] - User search - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - UserGroupMembersResponse - OK - - Examples - -------- - from zep_cloud import Zep - - client = Zep( - api_key="YOUR_API_KEY", - ) - client.user_group.list_member_candidates( - group_uuid="groupUUID", - project_id="projectId", - page_number=1, - page_size=1, - search="search", - ) - """ - _response = self._raw_client.list_member_candidates( - group_uuid, - project_id=project_id, - page_number=page_number, - page_size=page_size, - search=search, - request_options=request_options, - ) - return _response.data - - def remove_member( - self, - group_uuid: str, - user_uuid: str, - *, - project_id: str, - request_options: typing.Optional[RequestOptions] = None, - ) -> UserGroupMembershipResponse: - """ - Parameters - ---------- - group_uuid : str - UserGroup UUID - - user_uuid : str - User UUID - - project_id : str - Project UUID - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - UserGroupMembershipResponse - OK - - Examples - -------- - from zep_cloud import Zep - - client = Zep( - api_key="YOUR_API_KEY", - ) - client.user_group.remove_member( - group_uuid="groupUUID", - user_uuid="userUUID", - project_id="projectId", - ) - """ - _response = self._raw_client.remove_member( - group_uuid, user_uuid, project_id=project_id, request_options=request_options - ) - return _response.data - - -class AsyncUserGroupClient: - def __init__(self, *, client_wrapper: AsyncClientWrapper): - self._raw_client = AsyncRawUserGroupClient(client_wrapper=client_wrapper) - - @property - def with_raw_response(self) -> AsyncRawUserGroupClient: - """ - Retrieves a raw implementation of this client that returns raw responses. - - Returns - ------- - AsyncRawUserGroupClient - """ - return self._raw_client - - async def list_policy_sets( - self, group_uuid: str, *, project_id: str, request_options: typing.Optional[RequestOptions] = None - ) -> AttachedPolicySetsResponse: - """ - Parameters - ---------- - group_uuid : str - UserGroup UUID - - project_id : str - Project UUID - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - AttachedPolicySetsResponse - OK - - Examples - -------- - import asyncio - - from zep_cloud import AsyncZep - - client = AsyncZep( - api_key="YOUR_API_KEY", - ) - - - async def main() -> None: - await client.user_group.list_policy_sets( - group_uuid="groupUUID", - project_id="projectId", - ) - - - asyncio.run(main()) - """ - _response = await self._raw_client.list_policy_sets( - group_uuid, project_id=project_id, request_options=request_options - ) - return _response.data - - async def attach_policy_set( - self, - group_uuid: str, - *, - project_id: str, - policy_set_uuid: str, - request_options: typing.Optional[RequestOptions] = None, - ) -> AttachedPolicySetsResponse: - """ - Parameters - ---------- - group_uuid : str - UserGroup UUID - - project_id : str - Project UUID - - policy_set_uuid : str - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - AttachedPolicySetsResponse - OK - - Examples - -------- - import asyncio - - from zep_cloud import AsyncZep - - client = AsyncZep( - api_key="YOUR_API_KEY", - ) - - - async def main() -> None: - await client.user_group.attach_policy_set( - group_uuid="groupUUID", - project_id="projectId", - policy_set_uuid="policy_set_uuid", - ) - - - asyncio.run(main()) - """ - _response = await self._raw_client.attach_policy_set( - group_uuid, project_id=project_id, policy_set_uuid=policy_set_uuid, request_options=request_options - ) - return _response.data - - async def detach_policy_set( - self, - group_uuid: str, - policy_set_uuid: str, - *, - project_id: str, - request_options: typing.Optional[RequestOptions] = None, - ) -> AttachedPolicySetsResponse: - """ - Parameters - ---------- - group_uuid : str - UserGroup UUID - - policy_set_uuid : str - Policy set UUID - - project_id : str - Project UUID - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - AttachedPolicySetsResponse - OK - - Examples - -------- - import asyncio - - from zep_cloud import AsyncZep - - client = AsyncZep( - api_key="YOUR_API_KEY", - ) - - - async def main() -> None: - await client.user_group.detach_policy_set( - group_uuid="groupUUID", - policy_set_uuid="policySetUUID", - project_id="projectId", - ) - - - asyncio.run(main()) - """ - _response = await self._raw_client.detach_policy_set( - group_uuid, policy_set_uuid, project_id=project_id, request_options=request_options - ) - return _response.data - - async def list( - self, - *, - project_id: str, - page_number: int, - page_size: int, - search: typing.Optional[str] = None, - request_options: typing.Optional[RequestOptions] = None, - ) -> UserGroupListResponse: - """ - Parameters - ---------- - project_id : str - Project UUID - - page_number : int - Page number - - page_size : int - Page size - - search : typing.Optional[str] - Name search - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - UserGroupListResponse - OK - - Examples - -------- - import asyncio - - from zep_cloud import AsyncZep - - client = AsyncZep( - api_key="YOUR_API_KEY", - ) - - - async def main() -> None: - await client.user_group.list( - project_id="projectId", - page_number=1, - page_size=1, - search="search", - ) - - - asyncio.run(main()) - """ - _response = await self._raw_client.list( - project_id=project_id, - page_number=page_number, - page_size=page_size, - search=search, - request_options=request_options, - ) - return _response.data - - async def create( - self, - *, - project_id: str, - name: str, - description: typing.Optional[str] = OMIT, - request_options: typing.Optional[RequestOptions] = None, - ) -> UserGroupResponse: - """ - Parameters - ---------- - project_id : str - Project UUID - - name : str - - description : typing.Optional[str] - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - UserGroupResponse - Created - - Examples - -------- - import asyncio - - from zep_cloud import AsyncZep - - client = AsyncZep( - api_key="YOUR_API_KEY", - ) - - - async def main() -> None: - await client.user_group.create( - project_id="projectId", - name="name", - ) - - - asyncio.run(main()) - """ - _response = await self._raw_client.create( - project_id=project_id, name=name, description=description, request_options=request_options - ) - return _response.data - - async def list_for_user( - self, user_uuid: str, *, project_id: str, request_options: typing.Optional[RequestOptions] = None - ) -> UserMembershipGroupsResponse: - """ - Parameters - ---------- - user_uuid : str - User UUID - - project_id : str - Project UUID - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - UserMembershipGroupsResponse - OK - - Examples - -------- - import asyncio - - from zep_cloud import AsyncZep - - client = AsyncZep( - api_key="YOUR_API_KEY", - ) - - - async def main() -> None: - await client.user_group.list_for_user( - user_uuid="userUUID", - project_id="projectId", - ) - - - asyncio.run(main()) - """ - _response = await self._raw_client.list_for_user( - user_uuid, project_id=project_id, request_options=request_options - ) - return _response.data - - async def get( - self, group_uuid: str, *, project_id: str, request_options: typing.Optional[RequestOptions] = None - ) -> UserGroupResponse: - """ - Parameters - ---------- - group_uuid : str - UserGroup UUID - - project_id : str - Project UUID - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - UserGroupResponse - OK - - Examples - -------- - import asyncio - - from zep_cloud import AsyncZep - - client = AsyncZep( - api_key="YOUR_API_KEY", - ) - - - async def main() -> None: - await client.user_group.get( - group_uuid="groupUUID", - project_id="projectId", - ) - - - asyncio.run(main()) - """ - _response = await self._raw_client.get(group_uuid, project_id=project_id, request_options=request_options) - return _response.data - - async def delete( - self, group_uuid: str, *, project_id: str, request_options: typing.Optional[RequestOptions] = None - ) -> None: - """ - Parameters - ---------- - group_uuid : str - UserGroup UUID - - project_id : str - Project UUID - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - None - - Examples - -------- - import asyncio - - from zep_cloud import AsyncZep - - client = AsyncZep( - api_key="YOUR_API_KEY", - ) - - - async def main() -> None: - await client.user_group.delete( - group_uuid="groupUUID", - project_id="projectId", - ) - - - asyncio.run(main()) - """ - _response = await self._raw_client.delete(group_uuid, project_id=project_id, request_options=request_options) - return _response.data - - async def update( - self, - group_uuid: str, - *, - project_id: str, - expected_version: int, - description: typing.Optional[str] = OMIT, - name: typing.Optional[str] = OMIT, - request_options: typing.Optional[RequestOptions] = None, - ) -> UserGroupResponse: - """ - Parameters - ---------- - group_uuid : str - UserGroup UUID - - project_id : str - Project UUID - - expected_version : int - - description : typing.Optional[str] - - name : typing.Optional[str] - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - UserGroupResponse - OK - - Examples - -------- - import asyncio - - from zep_cloud import AsyncZep - - client = AsyncZep( - api_key="YOUR_API_KEY", - ) - - - async def main() -> None: - await client.user_group.update( - group_uuid="groupUUID", - project_id="projectId", - expected_version=1, - ) - - - asyncio.run(main()) - """ - _response = await self._raw_client.update( - group_uuid, - project_id=project_id, - expected_version=expected_version, - description=description, - name=name, - request_options=request_options, - ) - return _response.data - - async def list_members( - self, - group_uuid: str, - *, - project_id: str, - page_number: int, - page_size: int, - search: typing.Optional[str] = None, - request_options: typing.Optional[RequestOptions] = None, - ) -> UserGroupMembersResponse: - """ - Parameters - ---------- - group_uuid : str - UserGroup UUID - - project_id : str - Project UUID - - page_number : int - Page number - - page_size : int - Page size - - search : typing.Optional[str] - User search - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - UserGroupMembersResponse - OK - - Examples - -------- - import asyncio - - from zep_cloud import AsyncZep - - client = AsyncZep( - api_key="YOUR_API_KEY", - ) - - - async def main() -> None: - await client.user_group.list_members( - group_uuid="groupUUID", - project_id="projectId", - page_number=1, - page_size=1, - search="search", - ) - - - asyncio.run(main()) - """ - _response = await self._raw_client.list_members( - group_uuid, - project_id=project_id, - page_number=page_number, - page_size=page_size, - search=search, - request_options=request_options, - ) - return _response.data - - async def add_members( - self, - group_uuid: str, - *, - project_id: str, - user_uuids: typing.Sequence[str], - request_options: typing.Optional[RequestOptions] = None, - ) -> UserGroupMembershipResponse: - """ - Parameters - ---------- - group_uuid : str - UserGroup UUID - - project_id : str - Project UUID - - user_uuids : typing.Sequence[str] - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - UserGroupMembershipResponse - OK - - Examples - -------- - import asyncio - - from zep_cloud import AsyncZep - - client = AsyncZep( - api_key="YOUR_API_KEY", - ) - - - async def main() -> None: - await client.user_group.add_members( - group_uuid="groupUUID", - project_id="projectId", - user_uuids=["user_uuids"], - ) - - - asyncio.run(main()) - """ - _response = await self._raw_client.add_members( - group_uuid, project_id=project_id, user_uuids=user_uuids, request_options=request_options - ) - return _response.data - - async def remove_members( - self, - group_uuid: str, - *, - project_id: str, - user_uuids: typing.Sequence[str], - request_options: typing.Optional[RequestOptions] = None, - ) -> UserGroupMembershipResponse: - """ - Parameters - ---------- - group_uuid : str - UserGroup UUID - - project_id : str - Project UUID - - user_uuids : typing.Sequence[str] - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - UserGroupMembershipResponse - OK - - Examples - -------- - import asyncio - - from zep_cloud import AsyncZep - - client = AsyncZep( - api_key="YOUR_API_KEY", - ) - - - async def main() -> None: - await client.user_group.remove_members( - group_uuid="groupUUID", - project_id="projectId", - user_uuids=["user_uuids"], - ) - - - asyncio.run(main()) - """ - _response = await self._raw_client.remove_members( - group_uuid, project_id=project_id, user_uuids=user_uuids, request_options=request_options - ) - return _response.data - - async def list_member_candidates( - self, - group_uuid: str, - *, - project_id: str, - page_number: int, - page_size: int, - search: typing.Optional[str] = None, - request_options: typing.Optional[RequestOptions] = None, - ) -> UserGroupMembersResponse: - """ - Parameters - ---------- - group_uuid : str - UserGroup UUID - - project_id : str - Project UUID - - page_number : int - Page number - - page_size : int - Page size - - search : typing.Optional[str] - User search - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - UserGroupMembersResponse - OK - - Examples - -------- - import asyncio - - from zep_cloud import AsyncZep - - client = AsyncZep( - api_key="YOUR_API_KEY", - ) - - - async def main() -> None: - await client.user_group.list_member_candidates( - group_uuid="groupUUID", - project_id="projectId", - page_number=1, - page_size=1, - search="search", - ) - - - asyncio.run(main()) - """ - _response = await self._raw_client.list_member_candidates( - group_uuid, - project_id=project_id, - page_number=page_number, - page_size=page_size, - search=search, - request_options=request_options, - ) - return _response.data - - async def remove_member( - self, - group_uuid: str, - user_uuid: str, - *, - project_id: str, - request_options: typing.Optional[RequestOptions] = None, - ) -> UserGroupMembershipResponse: - """ - Parameters - ---------- - group_uuid : str - UserGroup UUID - - user_uuid : str - User UUID - - project_id : str - Project UUID - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - UserGroupMembershipResponse - OK - - Examples - -------- - import asyncio - - from zep_cloud import AsyncZep - - client = AsyncZep( - api_key="YOUR_API_KEY", - ) - - - async def main() -> None: - await client.user_group.remove_member( - group_uuid="groupUUID", - user_uuid="userUUID", - project_id="projectId", - ) - - - asyncio.run(main()) - """ - _response = await self._raw_client.remove_member( - group_uuid, user_uuid, project_id=project_id, request_options=request_options - ) - return _response.data diff --git a/src/zep_cloud/user_group/raw_client.py b/src/zep_cloud/user_group/raw_client.py deleted file mode 100644 index 3253fc42..00000000 --- a/src/zep_cloud/user_group/raw_client.py +++ /dev/null @@ -1,2487 +0,0 @@ -# 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 ..errors.bad_request_error import BadRequestError -from ..errors.conflict_error import ConflictError -from ..errors.forbidden_error import ForbiddenError -from ..errors.not_found_error import NotFoundError -from ..types.api_error import ApiError as types_api_error_ApiError -from ..types.attached_policy_sets_response import AttachedPolicySetsResponse -from ..types.user_group_list_response import UserGroupListResponse -from ..types.user_group_members_response import UserGroupMembersResponse -from ..types.user_group_membership_response import UserGroupMembershipResponse -from ..types.user_group_response import UserGroupResponse -from ..types.user_membership_groups_response import UserMembershipGroupsResponse - -# this is used as the default value for optional parameters -OMIT = typing.cast(typing.Any, ...) - - -class RawUserGroupClient: - def __init__(self, *, client_wrapper: SyncClientWrapper): - self._client_wrapper = client_wrapper - - def list_policy_sets( - self, group_uuid: str, *, project_id: str, request_options: typing.Optional[RequestOptions] = None - ) -> HttpResponse[AttachedPolicySetsResponse]: - """ - Parameters - ---------- - group_uuid : str - UserGroup UUID - - project_id : str - Project UUID - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - HttpResponse[AttachedPolicySetsResponse] - OK - """ - _response = self._client_wrapper.httpx_client.request( - f"abac/user-groups/{jsonable_encoder(group_uuid)}/policy-sets", - method="GET", - params={ - "projectId": project_id, - }, - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - _data = typing.cast( - AttachedPolicySetsResponse, - parse_obj_as( - type_=AttachedPolicySetsResponse, # 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(), - ), - ), - ) - _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 attach_policy_set( - self, - group_uuid: str, - *, - project_id: str, - policy_set_uuid: str, - request_options: typing.Optional[RequestOptions] = None, - ) -> HttpResponse[AttachedPolicySetsResponse]: - """ - Parameters - ---------- - group_uuid : str - UserGroup UUID - - project_id : str - Project UUID - - policy_set_uuid : str - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - HttpResponse[AttachedPolicySetsResponse] - OK - """ - _response = self._client_wrapper.httpx_client.request( - f"abac/user-groups/{jsonable_encoder(group_uuid)}/policy-sets", - method="POST", - params={ - "projectId": project_id, - }, - json={ - "policy_set_uuid": policy_set_uuid, - }, - headers={ - "content-type": "application/json", - }, - request_options=request_options, - omit=OMIT, - ) - try: - if 200 <= _response.status_code < 300: - _data = typing.cast( - AttachedPolicySetsResponse, - parse_obj_as( - type_=AttachedPolicySetsResponse, # 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 == 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(), - ), - ), - ) - _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 detach_policy_set( - self, - group_uuid: str, - policy_set_uuid: str, - *, - project_id: str, - request_options: typing.Optional[RequestOptions] = None, - ) -> HttpResponse[AttachedPolicySetsResponse]: - """ - Parameters - ---------- - group_uuid : str - UserGroup UUID - - policy_set_uuid : str - Policy set UUID - - project_id : str - Project UUID - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - HttpResponse[AttachedPolicySetsResponse] - OK - """ - _response = self._client_wrapper.httpx_client.request( - f"abac/user-groups/{jsonable_encoder(group_uuid)}/policy-sets/{jsonable_encoder(policy_set_uuid)}", - method="DELETE", - params={ - "projectId": project_id, - }, - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - _data = typing.cast( - AttachedPolicySetsResponse, - parse_obj_as( - type_=AttachedPolicySetsResponse, # 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(), - ), - ), - ) - _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( - self, - *, - project_id: str, - page_number: int, - page_size: int, - search: typing.Optional[str] = None, - request_options: typing.Optional[RequestOptions] = None, - ) -> HttpResponse[UserGroupListResponse]: - """ - Parameters - ---------- - project_id : str - Project UUID - - page_number : int - Page number - - page_size : int - Page size - - search : typing.Optional[str] - Name search - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - HttpResponse[UserGroupListResponse] - OK - """ - _response = self._client_wrapper.httpx_client.request( - "user-groups", - method="GET", - params={ - "projectId": project_id, - "pageNumber": page_number, - "pageSize": page_size, - "search": search, - }, - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - _data = typing.cast( - UserGroupListResponse, - parse_obj_as( - type_=UserGroupListResponse, # 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 == 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(), - ), - ), - ) - _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 create( - self, - *, - project_id: str, - name: str, - description: typing.Optional[str] = OMIT, - request_options: typing.Optional[RequestOptions] = None, - ) -> HttpResponse[UserGroupResponse]: - """ - Parameters - ---------- - project_id : str - Project UUID - - name : str - - description : typing.Optional[str] - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - HttpResponse[UserGroupResponse] - Created - """ - _response = self._client_wrapper.httpx_client.request( - "user-groups", - method="POST", - params={ - "projectId": project_id, - }, - json={ - "description": description, - "name": name, - }, - headers={ - "content-type": "application/json", - }, - request_options=request_options, - omit=OMIT, - ) - try: - if 200 <= _response.status_code < 300: - _data = typing.cast( - UserGroupResponse, - parse_obj_as( - type_=UserGroupResponse, # 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 == 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 == 409: - raise ConflictError( - 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_for_user( - self, user_uuid: str, *, project_id: str, request_options: typing.Optional[RequestOptions] = None - ) -> HttpResponse[UserMembershipGroupsResponse]: - """ - Parameters - ---------- - user_uuid : str - User UUID - - project_id : str - Project UUID - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - HttpResponse[UserMembershipGroupsResponse] - OK - """ - _response = self._client_wrapper.httpx_client.request( - f"user-groups/users/{jsonable_encoder(user_uuid)}", - method="GET", - params={ - "projectId": project_id, - }, - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - _data = typing.cast( - UserMembershipGroupsResponse, - parse_obj_as( - type_=UserMembershipGroupsResponse, # 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(), - ), - ), - ) - _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, group_uuid: str, *, project_id: str, request_options: typing.Optional[RequestOptions] = None - ) -> HttpResponse[UserGroupResponse]: - """ - Parameters - ---------- - group_uuid : str - UserGroup UUID - - project_id : str - Project UUID - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - HttpResponse[UserGroupResponse] - OK - """ - _response = self._client_wrapper.httpx_client.request( - f"user-groups/{jsonable_encoder(group_uuid)}", - method="GET", - params={ - "projectId": project_id, - }, - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - _data = typing.cast( - UserGroupResponse, - parse_obj_as( - type_=UserGroupResponse, # 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(), - ), - ), - ) - _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 delete( - self, group_uuid: str, *, project_id: str, request_options: typing.Optional[RequestOptions] = None - ) -> HttpResponse[None]: - """ - Parameters - ---------- - group_uuid : str - UserGroup UUID - - project_id : str - Project UUID - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - HttpResponse[None] - """ - _response = self._client_wrapper.httpx_client.request( - f"user-groups/{jsonable_encoder(group_uuid)}", - method="DELETE", - params={ - "projectId": project_id, - }, - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - return HttpResponse(response=_response, data=None) - 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 == 409: - raise ConflictError( - 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 update( - self, - group_uuid: str, - *, - project_id: str, - expected_version: int, - description: typing.Optional[str] = OMIT, - name: typing.Optional[str] = OMIT, - request_options: typing.Optional[RequestOptions] = None, - ) -> HttpResponse[UserGroupResponse]: - """ - Parameters - ---------- - group_uuid : str - UserGroup UUID - - project_id : str - Project UUID - - expected_version : int - - description : typing.Optional[str] - - name : typing.Optional[str] - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - HttpResponse[UserGroupResponse] - OK - """ - _response = self._client_wrapper.httpx_client.request( - f"user-groups/{jsonable_encoder(group_uuid)}", - method="PATCH", - params={ - "projectId": project_id, - }, - json={ - "description": description, - "expected_version": expected_version, - "name": name, - }, - headers={ - "content-type": "application/json", - }, - request_options=request_options, - omit=OMIT, - ) - try: - if 200 <= _response.status_code < 300: - _data = typing.cast( - UserGroupResponse, - parse_obj_as( - type_=UserGroupResponse, # 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 == 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 == 409: - raise ConflictError( - 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_members( - self, - group_uuid: str, - *, - project_id: str, - page_number: int, - page_size: int, - search: typing.Optional[str] = None, - request_options: typing.Optional[RequestOptions] = None, - ) -> HttpResponse[UserGroupMembersResponse]: - """ - Parameters - ---------- - group_uuid : str - UserGroup UUID - - project_id : str - Project UUID - - page_number : int - Page number - - page_size : int - Page size - - search : typing.Optional[str] - User search - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - HttpResponse[UserGroupMembersResponse] - OK - """ - _response = self._client_wrapper.httpx_client.request( - f"user-groups/{jsonable_encoder(group_uuid)}/members", - method="GET", - params={ - "projectId": project_id, - "pageNumber": page_number, - "pageSize": page_size, - "search": search, - }, - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - _data = typing.cast( - UserGroupMembersResponse, - parse_obj_as( - type_=UserGroupMembersResponse, # 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(), - ), - ), - ) - _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 add_members( - self, - group_uuid: str, - *, - project_id: str, - user_uuids: typing.Sequence[str], - request_options: typing.Optional[RequestOptions] = None, - ) -> HttpResponse[UserGroupMembershipResponse]: - """ - Parameters - ---------- - group_uuid : str - UserGroup UUID - - project_id : str - Project UUID - - user_uuids : typing.Sequence[str] - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - HttpResponse[UserGroupMembershipResponse] - OK - """ - _response = self._client_wrapper.httpx_client.request( - f"user-groups/{jsonable_encoder(group_uuid)}/members", - method="POST", - params={ - "projectId": project_id, - }, - json={ - "user_uuids": user_uuids, - }, - headers={ - "content-type": "application/json", - }, - request_options=request_options, - omit=OMIT, - ) - try: - if 200 <= _response.status_code < 300: - _data = typing.cast( - UserGroupMembershipResponse, - parse_obj_as( - type_=UserGroupMembershipResponse, # 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 == 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 == 409: - raise ConflictError( - 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 remove_members( - self, - group_uuid: str, - *, - project_id: str, - user_uuids: typing.Sequence[str], - request_options: typing.Optional[RequestOptions] = None, - ) -> HttpResponse[UserGroupMembershipResponse]: - """ - Parameters - ---------- - group_uuid : str - UserGroup UUID - - project_id : str - Project UUID - - user_uuids : typing.Sequence[str] - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - HttpResponse[UserGroupMembershipResponse] - OK - """ - _response = self._client_wrapper.httpx_client.request( - f"user-groups/{jsonable_encoder(group_uuid)}/members/bulk-remove", - method="POST", - params={ - "projectId": project_id, - }, - json={ - "user_uuids": user_uuids, - }, - headers={ - "content-type": "application/json", - }, - request_options=request_options, - omit=OMIT, - ) - try: - if 200 <= _response.status_code < 300: - _data = typing.cast( - UserGroupMembershipResponse, - parse_obj_as( - type_=UserGroupMembershipResponse, # 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 == 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 == 409: - raise ConflictError( - 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_member_candidates( - self, - group_uuid: str, - *, - project_id: str, - page_number: int, - page_size: int, - search: typing.Optional[str] = None, - request_options: typing.Optional[RequestOptions] = None, - ) -> HttpResponse[UserGroupMembersResponse]: - """ - Parameters - ---------- - group_uuid : str - UserGroup UUID - - project_id : str - Project UUID - - page_number : int - Page number - - page_size : int - Page size - - search : typing.Optional[str] - User search - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - HttpResponse[UserGroupMembersResponse] - OK - """ - _response = self._client_wrapper.httpx_client.request( - f"user-groups/{jsonable_encoder(group_uuid)}/members/candidates", - method="GET", - params={ - "projectId": project_id, - "pageNumber": page_number, - "pageSize": page_size, - "search": search, - }, - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - _data = typing.cast( - UserGroupMembersResponse, - parse_obj_as( - type_=UserGroupMembersResponse, # 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 == 409: - raise ConflictError( - 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 remove_member( - self, - group_uuid: str, - user_uuid: str, - *, - project_id: str, - request_options: typing.Optional[RequestOptions] = None, - ) -> HttpResponse[UserGroupMembershipResponse]: - """ - Parameters - ---------- - group_uuid : str - UserGroup UUID - - user_uuid : str - User UUID - - project_id : str - Project UUID - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - HttpResponse[UserGroupMembershipResponse] - OK - """ - _response = self._client_wrapper.httpx_client.request( - f"user-groups/{jsonable_encoder(group_uuid)}/members/{jsonable_encoder(user_uuid)}", - method="DELETE", - params={ - "projectId": project_id, - }, - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - _data = typing.cast( - UserGroupMembershipResponse, - parse_obj_as( - type_=UserGroupMembershipResponse, # 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 == 409: - raise ConflictError( - 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 AsyncRawUserGroupClient: - def __init__(self, *, client_wrapper: AsyncClientWrapper): - self._client_wrapper = client_wrapper - - async def list_policy_sets( - self, group_uuid: str, *, project_id: str, request_options: typing.Optional[RequestOptions] = None - ) -> AsyncHttpResponse[AttachedPolicySetsResponse]: - """ - Parameters - ---------- - group_uuid : str - UserGroup UUID - - project_id : str - Project UUID - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - AsyncHttpResponse[AttachedPolicySetsResponse] - OK - """ - _response = await self._client_wrapper.httpx_client.request( - f"abac/user-groups/{jsonable_encoder(group_uuid)}/policy-sets", - method="GET", - params={ - "projectId": project_id, - }, - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - _data = typing.cast( - AttachedPolicySetsResponse, - parse_obj_as( - type_=AttachedPolicySetsResponse, # 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(), - ), - ), - ) - _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 attach_policy_set( - self, - group_uuid: str, - *, - project_id: str, - policy_set_uuid: str, - request_options: typing.Optional[RequestOptions] = None, - ) -> AsyncHttpResponse[AttachedPolicySetsResponse]: - """ - Parameters - ---------- - group_uuid : str - UserGroup UUID - - project_id : str - Project UUID - - policy_set_uuid : str - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - AsyncHttpResponse[AttachedPolicySetsResponse] - OK - """ - _response = await self._client_wrapper.httpx_client.request( - f"abac/user-groups/{jsonable_encoder(group_uuid)}/policy-sets", - method="POST", - params={ - "projectId": project_id, - }, - json={ - "policy_set_uuid": policy_set_uuid, - }, - headers={ - "content-type": "application/json", - }, - request_options=request_options, - omit=OMIT, - ) - try: - if 200 <= _response.status_code < 300: - _data = typing.cast( - AttachedPolicySetsResponse, - parse_obj_as( - type_=AttachedPolicySetsResponse, # 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 == 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(), - ), - ), - ) - _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 detach_policy_set( - self, - group_uuid: str, - policy_set_uuid: str, - *, - project_id: str, - request_options: typing.Optional[RequestOptions] = None, - ) -> AsyncHttpResponse[AttachedPolicySetsResponse]: - """ - Parameters - ---------- - group_uuid : str - UserGroup UUID - - policy_set_uuid : str - Policy set UUID - - project_id : str - Project UUID - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - AsyncHttpResponse[AttachedPolicySetsResponse] - OK - """ - _response = await self._client_wrapper.httpx_client.request( - f"abac/user-groups/{jsonable_encoder(group_uuid)}/policy-sets/{jsonable_encoder(policy_set_uuid)}", - method="DELETE", - params={ - "projectId": project_id, - }, - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - _data = typing.cast( - AttachedPolicySetsResponse, - parse_obj_as( - type_=AttachedPolicySetsResponse, # 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(), - ), - ), - ) - _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( - self, - *, - project_id: str, - page_number: int, - page_size: int, - search: typing.Optional[str] = None, - request_options: typing.Optional[RequestOptions] = None, - ) -> AsyncHttpResponse[UserGroupListResponse]: - """ - Parameters - ---------- - project_id : str - Project UUID - - page_number : int - Page number - - page_size : int - Page size - - search : typing.Optional[str] - Name search - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - AsyncHttpResponse[UserGroupListResponse] - OK - """ - _response = await self._client_wrapper.httpx_client.request( - "user-groups", - method="GET", - params={ - "projectId": project_id, - "pageNumber": page_number, - "pageSize": page_size, - "search": search, - }, - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - _data = typing.cast( - UserGroupListResponse, - parse_obj_as( - type_=UserGroupListResponse, # 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 == 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(), - ), - ), - ) - _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 create( - self, - *, - project_id: str, - name: str, - description: typing.Optional[str] = OMIT, - request_options: typing.Optional[RequestOptions] = None, - ) -> AsyncHttpResponse[UserGroupResponse]: - """ - Parameters - ---------- - project_id : str - Project UUID - - name : str - - description : typing.Optional[str] - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - AsyncHttpResponse[UserGroupResponse] - Created - """ - _response = await self._client_wrapper.httpx_client.request( - "user-groups", - method="POST", - params={ - "projectId": project_id, - }, - json={ - "description": description, - "name": name, - }, - headers={ - "content-type": "application/json", - }, - request_options=request_options, - omit=OMIT, - ) - try: - if 200 <= _response.status_code < 300: - _data = typing.cast( - UserGroupResponse, - parse_obj_as( - type_=UserGroupResponse, # 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 == 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 == 409: - raise ConflictError( - 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_for_user( - self, user_uuid: str, *, project_id: str, request_options: typing.Optional[RequestOptions] = None - ) -> AsyncHttpResponse[UserMembershipGroupsResponse]: - """ - Parameters - ---------- - user_uuid : str - User UUID - - project_id : str - Project UUID - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - AsyncHttpResponse[UserMembershipGroupsResponse] - OK - """ - _response = await self._client_wrapper.httpx_client.request( - f"user-groups/users/{jsonable_encoder(user_uuid)}", - method="GET", - params={ - "projectId": project_id, - }, - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - _data = typing.cast( - UserMembershipGroupsResponse, - parse_obj_as( - type_=UserMembershipGroupsResponse, # 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(), - ), - ), - ) - _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, group_uuid: str, *, project_id: str, request_options: typing.Optional[RequestOptions] = None - ) -> AsyncHttpResponse[UserGroupResponse]: - """ - Parameters - ---------- - group_uuid : str - UserGroup UUID - - project_id : str - Project UUID - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - AsyncHttpResponse[UserGroupResponse] - OK - """ - _response = await self._client_wrapper.httpx_client.request( - f"user-groups/{jsonable_encoder(group_uuid)}", - method="GET", - params={ - "projectId": project_id, - }, - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - _data = typing.cast( - UserGroupResponse, - parse_obj_as( - type_=UserGroupResponse, # 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(), - ), - ), - ) - _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 delete( - self, group_uuid: str, *, project_id: str, request_options: typing.Optional[RequestOptions] = None - ) -> AsyncHttpResponse[None]: - """ - Parameters - ---------- - group_uuid : str - UserGroup UUID - - project_id : str - Project UUID - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - AsyncHttpResponse[None] - """ - _response = await self._client_wrapper.httpx_client.request( - f"user-groups/{jsonable_encoder(group_uuid)}", - method="DELETE", - params={ - "projectId": project_id, - }, - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - return AsyncHttpResponse(response=_response, data=None) - 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 == 409: - raise ConflictError( - 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 update( - self, - group_uuid: str, - *, - project_id: str, - expected_version: int, - description: typing.Optional[str] = OMIT, - name: typing.Optional[str] = OMIT, - request_options: typing.Optional[RequestOptions] = None, - ) -> AsyncHttpResponse[UserGroupResponse]: - """ - Parameters - ---------- - group_uuid : str - UserGroup UUID - - project_id : str - Project UUID - - expected_version : int - - description : typing.Optional[str] - - name : typing.Optional[str] - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - AsyncHttpResponse[UserGroupResponse] - OK - """ - _response = await self._client_wrapper.httpx_client.request( - f"user-groups/{jsonable_encoder(group_uuid)}", - method="PATCH", - params={ - "projectId": project_id, - }, - json={ - "description": description, - "expected_version": expected_version, - "name": name, - }, - headers={ - "content-type": "application/json", - }, - request_options=request_options, - omit=OMIT, - ) - try: - if 200 <= _response.status_code < 300: - _data = typing.cast( - UserGroupResponse, - parse_obj_as( - type_=UserGroupResponse, # 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 == 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 == 409: - raise ConflictError( - 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_members( - self, - group_uuid: str, - *, - project_id: str, - page_number: int, - page_size: int, - search: typing.Optional[str] = None, - request_options: typing.Optional[RequestOptions] = None, - ) -> AsyncHttpResponse[UserGroupMembersResponse]: - """ - Parameters - ---------- - group_uuid : str - UserGroup UUID - - project_id : str - Project UUID - - page_number : int - Page number - - page_size : int - Page size - - search : typing.Optional[str] - User search - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - AsyncHttpResponse[UserGroupMembersResponse] - OK - """ - _response = await self._client_wrapper.httpx_client.request( - f"user-groups/{jsonable_encoder(group_uuid)}/members", - method="GET", - params={ - "projectId": project_id, - "pageNumber": page_number, - "pageSize": page_size, - "search": search, - }, - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - _data = typing.cast( - UserGroupMembersResponse, - parse_obj_as( - type_=UserGroupMembersResponse, # 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(), - ), - ), - ) - _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 add_members( - self, - group_uuid: str, - *, - project_id: str, - user_uuids: typing.Sequence[str], - request_options: typing.Optional[RequestOptions] = None, - ) -> AsyncHttpResponse[UserGroupMembershipResponse]: - """ - Parameters - ---------- - group_uuid : str - UserGroup UUID - - project_id : str - Project UUID - - user_uuids : typing.Sequence[str] - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - AsyncHttpResponse[UserGroupMembershipResponse] - OK - """ - _response = await self._client_wrapper.httpx_client.request( - f"user-groups/{jsonable_encoder(group_uuid)}/members", - method="POST", - params={ - "projectId": project_id, - }, - json={ - "user_uuids": user_uuids, - }, - headers={ - "content-type": "application/json", - }, - request_options=request_options, - omit=OMIT, - ) - try: - if 200 <= _response.status_code < 300: - _data = typing.cast( - UserGroupMembershipResponse, - parse_obj_as( - type_=UserGroupMembershipResponse, # 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 == 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 == 409: - raise ConflictError( - 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 remove_members( - self, - group_uuid: str, - *, - project_id: str, - user_uuids: typing.Sequence[str], - request_options: typing.Optional[RequestOptions] = None, - ) -> AsyncHttpResponse[UserGroupMembershipResponse]: - """ - Parameters - ---------- - group_uuid : str - UserGroup UUID - - project_id : str - Project UUID - - user_uuids : typing.Sequence[str] - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - AsyncHttpResponse[UserGroupMembershipResponse] - OK - """ - _response = await self._client_wrapper.httpx_client.request( - f"user-groups/{jsonable_encoder(group_uuid)}/members/bulk-remove", - method="POST", - params={ - "projectId": project_id, - }, - json={ - "user_uuids": user_uuids, - }, - headers={ - "content-type": "application/json", - }, - request_options=request_options, - omit=OMIT, - ) - try: - if 200 <= _response.status_code < 300: - _data = typing.cast( - UserGroupMembershipResponse, - parse_obj_as( - type_=UserGroupMembershipResponse, # 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 == 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 == 409: - raise ConflictError( - 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_member_candidates( - self, - group_uuid: str, - *, - project_id: str, - page_number: int, - page_size: int, - search: typing.Optional[str] = None, - request_options: typing.Optional[RequestOptions] = None, - ) -> AsyncHttpResponse[UserGroupMembersResponse]: - """ - Parameters - ---------- - group_uuid : str - UserGroup UUID - - project_id : str - Project UUID - - page_number : int - Page number - - page_size : int - Page size - - search : typing.Optional[str] - User search - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - AsyncHttpResponse[UserGroupMembersResponse] - OK - """ - _response = await self._client_wrapper.httpx_client.request( - f"user-groups/{jsonable_encoder(group_uuid)}/members/candidates", - method="GET", - params={ - "projectId": project_id, - "pageNumber": page_number, - "pageSize": page_size, - "search": search, - }, - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - _data = typing.cast( - UserGroupMembersResponse, - parse_obj_as( - type_=UserGroupMembersResponse, # 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 == 409: - raise ConflictError( - 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 remove_member( - self, - group_uuid: str, - user_uuid: str, - *, - project_id: str, - request_options: typing.Optional[RequestOptions] = None, - ) -> AsyncHttpResponse[UserGroupMembershipResponse]: - """ - Parameters - ---------- - group_uuid : str - UserGroup UUID - - user_uuid : str - User UUID - - project_id : str - Project UUID - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - AsyncHttpResponse[UserGroupMembershipResponse] - OK - """ - _response = await self._client_wrapper.httpx_client.request( - f"user-groups/{jsonable_encoder(group_uuid)}/members/{jsonable_encoder(user_uuid)}", - method="DELETE", - params={ - "projectId": project_id, - }, - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - _data = typing.cast( - UserGroupMembershipResponse, - parse_obj_as( - type_=UserGroupMembershipResponse, # 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 == 409: - raise ConflictError( - 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/tests/graph/__init__.py b/tests/graph/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/tests/graph/test_utils.py b/tests/graph/test_utils.py deleted file mode 100644 index 7ed919cb..00000000 --- a/tests/graph/test_utils.py +++ /dev/null @@ -1,313 +0,0 @@ -from datetime import datetime -from typing import Any, Dict, Optional - -import pytest - -from zep_cloud import EntityEdge, EntityNode, Episode -from zep_cloud.graph.utils import compose_context_string, format_edge_date_range - - -class TestFormatEdgeDateRange: - def test_format_edge_date_range_with_valid_dates(self): - edge = EntityEdge( - fact="Test fact", - name="test_edge", - uuid_="edge-123", - created_at="2024-01-01T09:00:00Z", - source_node_uuid="source-123", - target_node_uuid="target-123", - valid_at="2024-01-01T10:00:00Z", - invalid_at="2024-01-02T10:00:00Z" - ) - result = format_edge_date_range(edge) - assert result == "2024-01-01 10:00:00 - 2024-01-02 10:00:00" - - def test_format_edge_date_range_with_none_dates(self): - edge = EntityEdge( - fact="Test fact", - name="test_edge", - uuid_="edge-123", - created_at="2024-01-01T09:00:00Z", - source_node_uuid="source-123", - target_node_uuid="target-123", - valid_at=None, - invalid_at=None - ) - result = format_edge_date_range(edge) - assert result == "date unknown - present" - - def test_format_edge_date_range_with_partial_dates(self): - edge = EntityEdge( - fact="Test fact", - name="test_edge", - uuid_="edge-123", - created_at="2024-01-01T09:00:00Z", - source_node_uuid="source-123", - target_node_uuid="target-123", - valid_at="2024-01-01T10:00:00Z", - invalid_at=None - ) - result = format_edge_date_range(edge) - assert result == "2024-01-01 10:00:00 - present" - - -class TestComposeContextString: - def test_empty_inputs(self): - result = compose_context_string([], [], []) - assert "FACTS and ENTITIES represent relevant context" in result - assert "" in result - assert "" in result - assert "EPISODES" not in result - - def test_facts_only(self): - edge = EntityEdge( - fact="User likes pizza", - name="likes", - uuid_="edge-123", - created_at="2024-01-01T09:00:00Z", - source_node_uuid="user-123", - target_node_uuid="pizza-123", - valid_at="2024-01-01T10:00:00Z", - invalid_at="2024-01-02T10:00:00Z" - ) - result = compose_context_string([edge], [], []) - - assert "User likes pizza (Date range: 2024-01-01 10:00:00 - 2024-01-02 10:00:00)" in result - assert "" in result - assert "" in result - assert "EPISODES" not in result - - def test_entities_basic(self): - node = EntityNode( - name="John", - summary="A user", - uuid_="node-123", - created_at="2024-01-01T09:00:00Z" - ) - result = compose_context_string([], [node], []) - - assert "Name: John" in result - assert "Summary: A user" in result - assert "" in result - - def test_entities_with_label_and_attributes(self): - node = EntityNode( - name="John", - summary="A user", - uuid_="node-123", - created_at="2024-01-01T09:00:00Z", - labels=["Person"], - attributes={"age": "30", "city": "New York"} - ) - result = compose_context_string([], [node], []) - - assert "Name: John" in result - assert "Label: Person" in result - assert "Attributes:" in result - assert "age: 30" in result - assert "city: New York" in result - assert "Summary: A user" in result - - def test_entities_with_entity_label_removed(self): - node = EntityNode( - name="Alice", - summary="A customer", - uuid_="node-456", - created_at="2024-01-01T09:00:00Z", - labels=["Entity", "Customer"] - ) - result = compose_context_string([], [node], []) - - assert "Name: Alice" in result - assert "Label: Customer" in result # Should show Customer, not Entity - assert "Label: Entity" not in result # Should not show Entity - assert "Summary: A customer" in result - - def test_entities_with_only_entity_label(self): - node = EntityNode( - name="Bob", - summary="A person", - uuid_="node-789", - created_at="2024-01-01T09:00:00Z", - labels=["Entity"] - ) - result = compose_context_string([], [node], []) - - assert "Name: Bob" in result - # Check that the entities section doesn't contain "Label: " (with space after colon) - entities_section = result[result.find(""):result.find("")] - assert "Label: " not in entities_section # Should not show any label since only Entity was present - assert "Summary: A person" in result - - def test_entities_with_labels_attribute_filtered(self): - node = EntityNode( - name="stores", - summary="Physical locations for shopping", - uuid_="node-123", - created_at="2024-01-01T09:00:00Z", - labels=["Location", "Entity"], - attributes={"labels": ["Location", "Entity"], "location_type": "physical"} - ) - result = compose_context_string([], [node], []) - - assert "Name: stores" in result - assert "Label: Location" in result # Should show Location (first non-Entity label) - assert "Attributes:" in result - assert "location_type: physical" in result - assert "labels:" not in result # Should not show labels in attributes - assert "Summary: Physical locations for shopping" in result - - def test_episodes_basic(self): - episode = Episode( - content="Hello there!", - created_at="2024-01-01T10:00:00Z", - uuid_="episode-123" - ) - result = compose_context_string([], [], [episode]) - - assert "FACTS and ENTITIES, and EPISODES represent" in result - assert "" in result - assert "Hello there! (2024-01-01 10:00:00)" in result - - def test_episodes_with_role(self): - episode = Episode( - content="Hello there!", - created_at="2024-01-01T10:00:00Z", - uuid_="episode-123", - role="user" - ) - result = compose_context_string([], [], [episode]) - - assert "user: Hello there! (2024-01-01 10:00:00)" in result - - def test_episodes_with_role_and_type(self): - # Create a mock episode with role_type since Episode model uses enum - class MockEpisode: - def __init__(self): - self.content = "Hello there!" - self.created_at = "2024-01-01T10:00:00Z" - self.role = "assistant" - self.role_type = "ai" - - episode = MockEpisode() - result = compose_context_string([], [], [episode]) - - assert "assistant (ai): Hello there! (2024-01-01 10:00:00)" in result - - def test_episodes_with_role_type_only(self): - class MockEpisode: - def __init__(self): - self.content = "Hello there!" - self.created_at = "2024-01-01T10:00:00Z" - self.role = None - self.role_type = "system" - - episode = MockEpisode() - result = compose_context_string([], [], [episode]) - - assert "(system): Hello there! (2024-01-01 10:00:00)" in result - - - def test_complete_context_with_all_elements(self): - edge = EntityEdge( - fact="User prefers coffee", - name="prefers", - uuid_="edge-123", - created_at="2024-01-01T07:00:00Z", - source_node_uuid="user-123", - target_node_uuid="coffee-123", - valid_at="2024-01-01T08:00:00Z", - invalid_at=None - ) - - node = EntityNode( - name="Alice", - summary="Regular customer", - uuid_="node-123", - created_at="2024-01-01T07:00:00Z", - labels=["Customer"], - attributes={"tier": "gold", "visits": "25"} - ) - - class MockEpisode: - def __init__(self): - self.content = "I'd like my usual coffee" - self.created_at = "2024-01-01T09:00:00Z" - self.role = "user" - self.role_type = "customer" - - episode = MockEpisode() - - result = compose_context_string([edge], [node], [episode]) - - # Check for all sections - assert "FACTS and ENTITIES, and EPISODES represent" in result - assert "" in result - assert "" in result - assert "" in result - - # Check content - assert "User prefers coffee" in result - assert "Name: Alice" in result - assert "tier: gold" in result - assert "Summary: Regular customer" in result - assert "user (customer): I'd like my usual coffee" in result - - def test_multiple_items(self): - edges = [ - EntityEdge( - fact="Fact 1", - name="edge1", - uuid_="edge-1", - created_at="2024-01-01T09:00:00Z", - source_node_uuid="source-1", - target_node_uuid="target-1", - valid_at="2024-01-01T10:00:00Z" - ), - EntityEdge( - fact="Fact 2", - name="edge2", - uuid_="edge-2", - created_at="2024-01-02T09:00:00Z", - source_node_uuid="source-2", - target_node_uuid="target-2", - valid_at="2024-01-02T10:00:00Z" - ) - ] - - nodes = [ - EntityNode( - name="Node1", - summary="Summary 1", - uuid_="node-1", - created_at="2024-01-01T09:00:00Z" - ), - EntityNode( - name="Node2", - summary="Summary 2", - uuid_="node-2", - created_at="2024-01-01T09:00:00Z" - ) - ] - - episodes = [ - Episode( - content="Message 1", - created_at="2024-01-01T10:00:00Z", - uuid_="episode-1" - ), - Episode( - content="Message 2", - created_at="2024-01-01T11:00:00Z", - uuid_="episode-2" - ) - ] - - result = compose_context_string(edges, nodes, episodes) - - assert "Fact 1" in result - assert "Fact 2" in result - assert "Node1" in result - assert "Node2" in result - assert "Message 1" in result - assert "Message 2" in result \ No newline at end of file diff --git a/tests/utils/test_http_client.py b/tests/utils/test_http_client.py index d8c4c778..87b8183d 100644 --- a/tests/utils/test_http_client.py +++ b/tests/utils/test_http_client.py @@ -1,13 +1,57 @@ # This file was auto-generated by Fern from our API Definition. -from zep_cloud.core.http_client import get_request_body +from typing import Any, Dict + +import pytest + +from zep_cloud.core.http_client import ( + AsyncHttpClient, + HttpClient, + _build_url, + get_request_body, + remove_none_from_dict, +) from zep_cloud.core.request_options import RequestOptions +# Stub clients for testing HttpClient and AsyncHttpClient +class _DummySyncClient: + """A minimal stub for httpx.Client that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyAsyncClient: + """A minimal stub for httpx.AsyncClient that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + async def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyResponse: + """A minimal stub for httpx.Response.""" + + status_code = 200 + headers: Dict[str, str] = {} + + def get_request_options() -> RequestOptions: return {"additional_body_parameters": {"see you": "later"}} +def get_request_options_with_none() -> RequestOptions: + return {"additional_body_parameters": {"see you": "later", "optional": None}} + + def test_get_json_request_body() -> None: json_body, data_body = get_request_body(json={"hello": "world"}, data=None, request_options=None, omit=None) assert json_body == {"hello": "world"} @@ -48,14 +92,209 @@ def test_get_none_request_body() -> None: def test_get_empty_json_request_body() -> None: + """Test that implicit empty bodies (json=None) are collapsed to None.""" unrelated_request_options: RequestOptions = {"max_retries": 3} json_body, data_body = get_request_body(json=None, data=None, request_options=unrelated_request_options, omit=None) assert json_body is None assert data_body is None - json_body_extras, data_body_extras = get_request_body( - json={}, data=None, request_options=unrelated_request_options, omit=None + +def test_explicit_empty_json_body_is_preserved() -> None: + """Test that explicit empty bodies (json={}) are preserved and sent as {}. + + This is important for endpoints where the request body is required but all + fields are optional. The server expects valid JSON ({}) not an empty body. + """ + unrelated_request_options: RequestOptions = {"max_retries": 3} + + # Explicit json={} should be preserved + json_body, data_body = get_request_body(json={}, data=None, request_options=unrelated_request_options, omit=None) + assert json_body == {} + assert data_body is None + + # Explicit data={} should also be preserved + json_body2, data_body2 = get_request_body(json=None, data={}, request_options=unrelated_request_options, omit=None) + assert json_body2 is None + assert data_body2 == {} + + +def test_json_body_preserves_none_values() -> None: + """Test that JSON bodies preserve None values (they become JSON null).""" + json_body, data_body = get_request_body( + json={"hello": "world", "optional": None}, data=None, request_options=None, omit=None ) + # JSON bodies should preserve None values + assert json_body == {"hello": "world", "optional": None} + assert data_body is None - assert json_body_extras is None - assert data_body_extras is None + +def test_data_body_preserves_none_values_without_multipart() -> None: + """Test that data bodies preserve None values when not using multipart. + + The filtering of None values happens in HttpClient.request/stream methods, + not in get_request_body. This test verifies get_request_body doesn't filter None. + """ + json_body, data_body = get_request_body( + json=None, data={"hello": "world", "optional": None}, request_options=None, omit=None + ) + # get_request_body should preserve None values in data body + # The filtering happens later in HttpClient.request when multipart is detected + assert data_body == {"hello": "world", "optional": None} + assert json_body is None + + +def test_remove_none_from_dict_filters_none_values() -> None: + """Test that remove_none_from_dict correctly filters out None values.""" + original = {"hello": "world", "optional": None, "another": "value", "also_none": None} + filtered = remove_none_from_dict(original) + assert filtered == {"hello": "world", "another": "value"} + # Original should not be modified + assert original == {"hello": "world", "optional": None, "another": "value", "also_none": None} + + +def test_remove_none_from_dict_empty_dict() -> None: + """Test that remove_none_from_dict handles empty dict.""" + assert remove_none_from_dict({}) == {} + + +def test_remove_none_from_dict_all_none() -> None: + """Test that remove_none_from_dict handles dict with all None values.""" + assert remove_none_from_dict({"a": None, "b": None}) == {} + + +def test_http_client_does_not_pass_empty_params_list() -> None: + """Test that HttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + ) + + # Use a path with query params (e.g., pagination cursor URL) + http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +def test_http_client_passes_encoded_params_when_present() -> None: + """Test that HttpClient passes encoded params when params are provided.""" + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + ) + + http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] + + +@pytest.mark.asyncio +async def test_async_http_client_does_not_pass_empty_params_list() -> None: + """Test that AsyncHttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + async_base_headers=None, + ) + + # Use a path with query params (e.g., pagination cursor URL) + await http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +@pytest.mark.asyncio +async def test_async_http_client_passes_encoded_params_when_present() -> None: + """Test that AsyncHttpClient passes encoded params when params are provided.""" + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + async_base_headers=None, + ) + + await http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] + + +def test_basic_url_joining() -> None: + """Test basic URL joining with a simple base URL and path.""" + result = _build_url("https://api.example.com", "/users") + assert result == "https://api.example.com/users" + + +def test_basic_url_joining_trailing_slash() -> None: + """Test basic URL joining with a simple base URL and path.""" + result = _build_url("https://api.example.com/", "/users") + assert result == "https://api.example.com/users" + + +def test_preserves_base_url_path_prefix() -> None: + """Test that path prefixes in base URL are preserved. + + This is the critical bug fix - urllib.parse.urljoin() would strip + the path prefix when the path starts with '/'. + """ + result = _build_url("https://cloud.example.com/org/tenant/api", "/users") + assert result == "https://cloud.example.com/org/tenant/api/users" + + +def test_preserves_base_url_path_prefix_trailing_slash() -> None: + """Test that path prefixes in base URL are preserved.""" + result = _build_url("https://cloud.example.com/org/tenant/api/", "/users") + assert result == "https://cloud.example.com/org/tenant/api/users" diff --git a/tests/utils/test_query_encoding.py b/tests/utils/test_query_encoding.py index a8b82d03..14af2ca0 100644 --- a/tests/utils/test_query_encoding.py +++ b/tests/utils/test_query_encoding.py @@ -1,6 +1,5 @@ # This file was auto-generated by Fern from our API Definition. - from zep_cloud.core.query_encoder import encode_query