diff --git a/.release-please-manifest.json b/.release-please-manifest.json
index c7159c1..1332969 100644
--- a/.release-please-manifest.json
+++ b/.release-please-manifest.json
@@ -1,3 +1,3 @@
{
- ".": "0.0.2"
+ ".": "0.0.1"
}
\ No newline at end of file
diff --git a/.stats.yml b/.stats.yml
index 4dc183b..7973879 100644
--- a/.stats.yml
+++ b/.stats.yml
@@ -1,4 +1,4 @@
configured_endpoints: 19
openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/evermind%2Feveros-85b37aa2a2bf87c039a27e33652dcfcab2b64f5e90a058e7c4a49b12a8094e02.yml
openapi_spec_hash: 31f3985c07b07d8c3bd06e0622fd575e
-config_hash: 973539bf905a8a047d5838c0265a00f0
+config_hash: 88946f527d4ca03aa70d121a51f89e1f
diff --git a/CHANGELOG.md b/CHANGELOG.md
deleted file mode 100644
index 3f15e25..0000000
--- a/CHANGELOG.md
+++ /dev/null
@@ -1,11 +0,0 @@
-# Changelog
-
-## 0.0.2 (2026-04-01)
-
-Full Changelog: [v0.0.1...v0.0.2](https://github.com/evermemos/everos-python/compare/v0.0.1...v0.0.2)
-
-### Chores
-
-* update SDK settings ([68703ac](https://github.com/evermemos/everos-python/commit/68703ac328d743e307d9dfd30bd7b52b8d894dba))
-* update SDK settings ([bdb213a](https://github.com/evermemos/everos-python/commit/bdb213a592f0665a038128b222b6ccdc882a29fd))
-* update SDK settings ([5c4bbb3](https://github.com/evermemos/everos-python/commit/5c4bbb3c610ae5bda82cb23c40861d0071066e4b))
diff --git a/LICENSE b/LICENSE
index 5d63548..7c2e458 100644
--- a/LICENSE
+++ b/LICENSE
@@ -186,7 +186,7 @@
same "printed page" as the copyright notice for easier
identification within third-party archives.
- Copyright 2026 Everos
+ Copyright 2026 Ever OS
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
diff --git a/README.md b/README.md
index be1bd4b..e8fd21f 100644
--- a/README.md
+++ b/README.md
@@ -1,9 +1,9 @@
-# Everos Python API library
+# EverOS API library
[)](https://pypi.org/project/everos/)
-The Everos Python library provides convenient access to the Everos REST API from any Python 3.9+
+The EverOS library provides convenient access to the Ever OS REST API from any Python 3.9+
application. The library includes type definitions for all request params and response fields,
and offers both synchronous and asynchronous clients powered by [httpx](https://github.com/encode/httpx).
@@ -26,23 +26,21 @@ The full API of this library can be found in [api.md](api.md).
```python
import os
-from everos import Everos
+from everos import EverOS
-client = Everos(
+client = EverOS(
api_key=os.environ.get("EVEROS_API_KEY"), # This is the default and can be omitted
- # or 'production' | 'test' | 'environment_3'; defaults to "production".
- environment="environment_1",
)
-add_response = client.v1.memories.create(
+add_response = client.v1.memories.add(
messages=[
{
- "content": "x",
"role": "user",
- "timestamp": 0,
+ "timestamp": 1705318800000,
+ "content": "Hello, how are you?",
}
],
- user_id="user_id",
+ user_id="user_123",
)
print(add_response.data)
```
@@ -54,30 +52,28 @@ so that your API Key is not stored in source control.
## Async usage
-Simply import `AsyncEveros` instead of `Everos` and use `await` with each API call:
+Simply import `AsyncEverOS` instead of `EverOS` and use `await` with each API call:
```python
import os
import asyncio
-from everos import AsyncEveros
+from everos import AsyncEverOS
-client = AsyncEveros(
+client = AsyncEverOS(
api_key=os.environ.get("EVEROS_API_KEY"), # This is the default and can be omitted
- # or 'production' | 'test' | 'environment_3'; defaults to "production".
- environment="environment_1",
)
async def main() -> None:
- add_response = await client.v1.memories.create(
+ add_response = await client.v1.memories.add(
messages=[
{
- "content": "x",
"role": "user",
- "timestamp": 0,
+ "timestamp": 1705318800000,
+ "content": "Hello, how are you?",
}
],
- user_id="user_id",
+ user_id="user_123",
)
print(add_response.data)
@@ -104,23 +100,23 @@ Then you can enable it by instantiating the client with `http_client=DefaultAioH
import os
import asyncio
from everos import DefaultAioHttpClient
-from everos import AsyncEveros
+from everos import AsyncEverOS
async def main() -> None:
- async with AsyncEveros(
+ async with AsyncEverOS(
api_key=os.environ.get("EVEROS_API_KEY"), # This is the default and can be omitted
http_client=DefaultAioHttpClient(),
) as client:
- add_response = await client.v1.memories.create(
+ add_response = await client.v1.memories.add(
messages=[
{
- "content": "x",
"role": "user",
- "timestamp": 0,
+ "timestamp": 1705318800000,
+ "content": "Hello, how are you?",
}
],
- user_id="user_id",
+ user_id="user_123",
)
print(add_response.data)
@@ -142,9 +138,9 @@ Typed requests and responses provide autocomplete and documentation within your
Nested parameters are dictionaries, typed using `TypedDict`, for example:
```python
-from everos import Everos
+from everos import EverOS
-client = Everos()
+client = EverOS()
settings_api_response = client.v1.settings.update(
llm_custom_setting={},
@@ -163,20 +159,20 @@ All errors inherit from `everos.APIError`.
```python
import everos
-from everos import Everos
+from everos import EverOS
-client = Everos()
+client = EverOS()
try:
- client.v1.memories.create(
+ client.v1.memories.add(
messages=[
{
- "content": "x",
"role": "user",
- "timestamp": 0,
+ "timestamp": 1705318800000,
+ "content": "Hello, how are you?",
}
],
- user_id="user_id",
+ user_id="user_123",
)
except everos.APIConnectionError as e:
print("The server could not be reached")
@@ -211,24 +207,24 @@ Connection errors (for example, due to a network connectivity problem), 408 Requ
You can use the `max_retries` option to configure or disable retry settings:
```python
-from everos import Everos
+from everos import EverOS
# Configure the default for all requests:
-client = Everos(
+client = EverOS(
# default is 2
max_retries=0,
)
# Or, configure per-request:
-client.with_options(max_retries=5).v1.memories.create(
+client.with_options(max_retries=5).v1.memories.add(
messages=[
{
- "content": "x",
"role": "user",
- "timestamp": 0,
+ "timestamp": 1705318800000,
+ "content": "Hello, how are you?",
}
],
- user_id="user_id",
+ user_id="user_123",
)
```
@@ -238,29 +234,29 @@ By default requests time out after 1 minute. You can configure this with a `time
which accepts a float or an [`httpx.Timeout`](https://www.python-httpx.org/advanced/timeouts/#fine-tuning-the-configuration) object:
```python
-from everos import Everos
+from everos import EverOS
# Configure the default for all requests:
-client = Everos(
+client = EverOS(
# 20 seconds (default is 1 minute)
timeout=20.0,
)
# More granular control:
-client = Everos(
+client = EverOS(
timeout=httpx.Timeout(60.0, read=5.0, write=10.0, connect=2.0),
)
# Override per-request:
-client.with_options(timeout=5.0).v1.memories.create(
+client.with_options(timeout=5.0).v1.memories.add(
messages=[
{
- "content": "x",
"role": "user",
- "timestamp": 0,
+ "timestamp": 1705318800000,
+ "content": "Hello, how are you?",
}
],
- user_id="user_id",
+ user_id="user_123",
)
```
@@ -274,10 +270,10 @@ Note that requests that time out are [retried twice by default](#retries).
We use the standard library [`logging`](https://docs.python.org/3/library/logging.html) module.
-You can enable logging by setting the environment variable `EVEROS_LOG` to `info`.
+You can enable logging by setting the environment variable `EVER_OS_LOG` to `info`.
```shell
-$ export EVEROS_LOG=info
+$ export EVER_OS_LOG=info
```
Or to `debug` for more verbose logging.
@@ -299,20 +295,20 @@ if response.my_field is None:
The "raw" Response object can be accessed by prefixing `.with_raw_response.` to any HTTP method call, e.g.,
```py
-from everos import Everos
+from everos import EverOS
-client = Everos()
-response = client.v1.memories.with_raw_response.create(
+client = EverOS()
+response = client.v1.memories.with_raw_response.add(
messages=[{
- "content": "x",
"role": "user",
- "timestamp": 0,
+ "timestamp": 1705318800000,
+ "content": "Hello, how are you?",
}],
- user_id="user_id",
+ user_id="user_123",
)
print(response.headers.get('X-My-Header'))
-memory = response.parse() # get the object that `v1.memories.create()` would have returned
+memory = response.parse() # get the object that `v1.memories.add()` would have returned
print(memory.data)
```
@@ -327,15 +323,15 @@ The above interface eagerly reads the full response body when you make the reque
To stream the response body, use `.with_streaming_response` instead, which requires a context manager and only reads the response body once you call `.read()`, `.text()`, `.json()`, `.iter_bytes()`, `.iter_text()`, `.iter_lines()` or `.parse()`. In the async client, these are async methods.
```python
-with client.v1.memories.with_streaming_response.create(
+with client.v1.memories.with_streaming_response.add(
messages=[
{
- "content": "x",
"role": "user",
- "timestamp": 0,
+ "timestamp": 1705318800000,
+ "content": "Hello, how are you?",
}
],
- user_id="user_id",
+ user_id="user_123",
) as response:
print(response.headers.get("X-My-Header"))
@@ -389,10 +385,10 @@ You can directly override the [httpx client](https://www.python-httpx.org/api/#c
```python
import httpx
-from everos import Everos, DefaultHttpxClient
+from everos import EverOS, DefaultHttpxClient
-client = Everos(
- # Or use the `EVEROS_BASE_URL` env var
+client = EverOS(
+ # Or use the `EVER_OS_BASE_URL` env var
base_url="http://my.test.server.example.com:8083",
http_client=DefaultHttpxClient(
proxy="http://my.test.proxy.example.com",
@@ -412,9 +408,9 @@ client.with_options(http_client=DefaultHttpxClient(...))
By default the library closes underlying HTTP connections whenever the client is [garbage collected](https://docs.python.org/3/reference/datamodel.html#object.__del__). You can manually close the client using the `.close()` method if desired, or with a context manager that closes when exiting.
```py
-from everos import Everos
+from everos import EverOS
-with Everos() as client:
+with EverOS() as client:
# make requests here
...
diff --git a/SECURITY.md b/SECURITY.md
index 1124bff..cfb4d4f 100644
--- a/SECURITY.md
+++ b/SECURITY.md
@@ -16,7 +16,7 @@ before making any information public.
## Reporting Non-SDK Related Security Issues
If you encounter security issues that are not directly related to SDKs but pertain to the services
-or products provided by Everos, please follow the respective company's security reporting guidelines.
+or products provided by Ever OS, please follow the respective company's security reporting guidelines.
---
diff --git a/api.md b/api.md
index 0a16971..e31b483 100644
--- a/api.md
+++ b/api.md
@@ -1,100 +1,154 @@
# V1
+## Groups
+
Types:
```python
-from everos.types import V1QueryTaskStatusResponse
+from everos.types.v1 import CreateGroupRequest, GroupAPIResponse, GroupResponse, PatchGroupRequest
```
Methods:
-- client.v1.query_task_status(task_id) -> V1QueryTaskStatusResponse
+- client.v1.groups.create(\*\*params) -> GroupAPIResponse
+- client.v1.groups.retrieve(group_id) -> GroupAPIResponse
+- client.v1.groups.patch(group_id, \*\*params) -> GroupAPIResponse
-## Memories
+## Settings
Types:
```python
from everos.types.v1 import (
- AddResponse,
- ContentItem,
- FlushResponse,
- MemoryGetResponse,
- MemorySearchResponse,
+ LlmCustomSetting,
+ LlmProviderConfig,
+ SettingsAPIResponse,
+ SettingsResponse,
+ UpdateSettingsRequest,
)
```
Methods:
-- client.v1.memories.create(\*\*params) -> AddResponse
-- client.v1.memories.delete(\*\*params) -> None
-- client.v1.memories.flush(\*\*params) -> FlushResponse
-- client.v1.memories.get(\*\*params) -> MemoryGetResponse
-- client.v1.memories.search(\*\*params) -> MemorySearchResponse
+- client.v1.settings.retrieve() -> SettingsAPIResponse
+- client.v1.settings.update(\*\*params) -> SettingsAPIResponse
-### Group
+## Senders
+
+Types:
+
+```python
+from everos.types.v1 import (
+ CreateSenderRequest,
+ PatchSenderRequest,
+ SenderAPIResponse,
+ SenderResponse,
+)
+```
Methods:
-- client.v1.memories.group.create(\*\*params) -> AddResponse
-- client.v1.memories.group.flush(\*\*params) -> FlushResponse
+- client.v1.senders.create(\*\*params) -> SenderAPIResponse
+- client.v1.senders.retrieve(sender_id) -> SenderAPIResponse
+- client.v1.senders.patch(sender_id, \*\*params) -> SenderAPIResponse
-### Agent
+## Memories
+
+Types:
+
+```python
+from everos.types.v1 import (
+ AddResponse,
+ AddResult,
+ AsyncAddResponse,
+ AsyncAddResult,
+ ContentItem,
+ DeleteMemoriesRequest,
+ EpisodeItem,
+ FlushResponse,
+ FlushResult,
+ GetMemRequest,
+ GetMemResponse,
+ GetMemoriesResponse,
+ MessageItem,
+ PersonalAddRequest,
+ PersonalFlushRequest,
+ ProfileItem,
+ RawMessageDto,
+ SearchMemoriesRequest,
+ SearchMemoriesResponse,
+ SearchMemoriesResponseData,
+)
+```
Methods:
-- client.v1.memories.agent.create(\*\*params) -> AddResponse
-- client.v1.memories.agent.flush(\*\*params) -> FlushResponse
+- client.v1.memories.delete(\*\*params) -> None
+- client.v1.memories.add(\*\*params) -> AddResponse
+- client.v1.memories.flush(\*\*params) -> FlushResponse
+- client.v1.memories.get(\*\*params) -> GetMemoriesResponse
+- client.v1.memories.search(\*\*params) -> SearchMemoriesResponse
-## Groups
+### Agent
Types:
```python
-from everos.types.v1 import GroupAPIResponse
+from everos.types.v1.memories import (
+ AgentAddRequest,
+ AgentCaseItem,
+ AgentFlushRequest,
+ AgentMessageItem,
+ AgentSkillItem,
+ ToolCall,
+ ToolCallFunction,
+)
```
Methods:
-- client.v1.groups.retrieve(group_id) -> GroupAPIResponse
-- client.v1.groups.update(group_id, \*\*params) -> GroupAPIResponse
-- client.v1.groups.create_or_update(\*\*params) -> GroupAPIResponse
+- client.v1.memories.agent.add(\*\*params) -> AddResponse
+- client.v1.memories.agent.flush(\*\*params) -> FlushResponse
-## Senders
+### Group
Types:
```python
-from everos.types.v1 import SenderAPIResponse
+from everos.types.v1.memories import GroupAddRequest, GroupFlushRequest, GroupMessageItem
```
Methods:
-- client.v1.senders.retrieve(sender_id) -> SenderAPIResponse
-- client.v1.senders.update(sender_id, \*\*params) -> SenderAPIResponse
-- client.v1.senders.create_or_update(\*\*params) -> SenderAPIResponse
+- client.v1.memories.group.add(\*\*params) -> AddResponse
+- client.v1.memories.group.flush(\*\*params) -> FlushResponse
## Object
Types:
```python
-from everos.types.v1 import ObjectGetPresignedURLResponse
+from everos.types.v1 import (
+ ObjectSignItem,
+ ObjectSignItemRequest,
+ ObjectSignRequest,
+ ObjectSignResponse,
+ ObjectSignedInfo,
+)
```
Methods:
-- client.v1.object.get_presigned_url(\*\*params) -> ObjectGetPresignedURLResponse
+- client.v1.object.sign(\*\*params) -> ObjectSignResponse
-## Settings
+## Tasks
Types:
```python
-from everos.types.v1 import LlmProviderConfig, SettingsAPIResponse
+from everos.types.v1 import GetTaskStatusResponse, TaskStatusResult
```
Methods:
-- client.v1.settings.retrieve() -> SettingsAPIResponse
-- client.v1.settings.update(\*\*params) -> SettingsAPIResponse
+- client.v1.tasks.retrieve(task_id) -> GetTaskStatusResponse
diff --git a/pyproject.toml b/pyproject.toml
index 6205708..6ae998e 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,11 +1,11 @@
[project]
name = "everos"
-version = "0.0.2"
-description = "The official Python library for the everos API"
+version = "0.0.1"
+description = "The official Python library for the EverOS API"
dynamic = ["readme"]
license = "Apache-2.0"
authors = [
-{ name = "Everos", email = "" },
+{ name = "Ever OS", email = "" },
]
dependencies = [
diff --git a/src/everos/__init__.py b/src/everos/__init__.py
index 81014ea..3cea963 100644
--- a/src/everos/__init__.py
+++ b/src/everos/__init__.py
@@ -5,25 +5,14 @@
from . import types
from ._types import NOT_GIVEN, Omit, NoneType, NotGiven, Transport, ProxiesTypes, omit, not_given
from ._utils import file_from_path
-from ._client import (
- ENVIRONMENTS,
- Client,
- Everos,
- Stream,
- Timeout,
- Transport,
- AsyncClient,
- AsyncEveros,
- AsyncStream,
- RequestOptions,
-)
+from ._client import Client, EverOS, Stream, Timeout, Transport, AsyncClient, AsyncEverOS, AsyncStream, RequestOptions
from ._models import BaseModel
from ._version import __title__, __version__
from ._response import APIResponse as APIResponse, AsyncAPIResponse as AsyncAPIResponse
from ._constants import DEFAULT_TIMEOUT, DEFAULT_MAX_RETRIES, DEFAULT_CONNECTION_LIMITS
from ._exceptions import (
APIError,
- EverosError,
+ EverOSError,
ConflictError,
NotFoundError,
APIStatusError,
@@ -52,7 +41,7 @@
"not_given",
"Omit",
"omit",
- "EverosError",
+ "EverOSError",
"APIError",
"APIStatusError",
"APITimeoutError",
@@ -72,9 +61,8 @@
"AsyncClient",
"Stream",
"AsyncStream",
- "Everos",
- "AsyncEveros",
- "ENVIRONMENTS",
+ "EverOS",
+ "AsyncEverOS",
"file_from_path",
"BaseModel",
"DEFAULT_TIMEOUT",
diff --git a/src/everos/_base_client.py b/src/everos/_base_client.py
index f0d5a83..135c61e 100644
--- a/src/everos/_base_client.py
+++ b/src/everos/_base_client.py
@@ -63,7 +63,7 @@
)
from ._utils import is_dict, is_list, asyncify, is_given, lru_cache, is_mapping
from ._compat import PYDANTIC_V1, model_copy, model_dump
-from ._models import GenericModel, SecurityOptions, FinalRequestOptions, validate_type, construct_type
+from ._models import GenericModel, FinalRequestOptions, validate_type, construct_type
from ._response import (
APIResponse,
BaseAPIResponse,
@@ -432,27 +432,9 @@ def _make_status_error(
) -> _exceptions.APIStatusError:
raise NotImplementedError()
- def _auth_headers(
- self,
- security: SecurityOptions, # noqa: ARG002
- ) -> dict[str, str]:
- return {}
-
- def _auth_query(
- self,
- security: SecurityOptions, # noqa: ARG002
- ) -> dict[str, str]:
- return {}
-
- def _custom_auth(
- self,
- security: SecurityOptions, # noqa: ARG002
- ) -> httpx.Auth | None:
- return None
-
def _build_headers(self, options: FinalRequestOptions, *, retries_taken: int = 0) -> httpx.Headers:
custom_headers = options.headers or {}
- headers_dict = _merge_mappings({**self._auth_headers(options.security), **self.default_headers}, custom_headers)
+ headers_dict = _merge_mappings(self.default_headers, custom_headers)
self._validate_headers(headers_dict, custom_headers)
# headers are case-insensitive while dictionaries are not.
@@ -524,7 +506,7 @@ def _build_request(
raise RuntimeError(f"Unexpected JSON data type, {type(json_data)}, cannot merge with `extra_body`")
headers = self._build_headers(options, retries_taken=retries_taken)
- params = _merge_mappings({**self._auth_query(options.security), **self.default_query}, options.params)
+ params = _merge_mappings(self.default_query, options.params)
content_type = headers.get("Content-Type")
files = options.files
@@ -689,6 +671,7 @@ def default_headers(self) -> dict[str, str | Omit]:
"Content-Type": "application/json",
"User-Agent": self.user_agent,
**self.platform_headers(),
+ **self.auth_headers,
**self._custom_headers,
}
@@ -1007,9 +990,8 @@ def request(
self._prepare_request(request)
kwargs: HttpxSendArgs = {}
- custom_auth = self._custom_auth(options.security)
- if custom_auth is not None:
- kwargs["auth"] = custom_auth
+ if self.custom_auth is not None:
+ kwargs["auth"] = self.custom_auth
if options.follow_redirects is not None:
kwargs["follow_redirects"] = options.follow_redirects
@@ -1970,7 +1952,6 @@ def make_request_options(
idempotency_key: str | None = None,
timeout: float | httpx.Timeout | None | NotGiven = not_given,
post_parser: PostParser | NotGiven = not_given,
- security: SecurityOptions | None = None,
) -> RequestOptions:
"""Create a dict of type RequestOptions without keys of NotGiven values."""
options: RequestOptions = {}
@@ -1996,9 +1977,6 @@ def make_request_options(
# internal
options["post_parser"] = post_parser # type: ignore
- if security is not None:
- options["security"] = security
-
return options
diff --git a/src/everos/_client.py b/src/everos/_client.py
index c425616..4794eeb 100644
--- a/src/everos/_client.py
+++ b/src/everos/_client.py
@@ -3,8 +3,8 @@
from __future__ import annotations
import os
-from typing import TYPE_CHECKING, Any, Dict, Mapping, cast
-from typing_extensions import Self, Literal, override
+from typing import TYPE_CHECKING, Any, Mapping
+from typing_extensions import Self, override
import httpx
@@ -21,10 +21,9 @@
)
from ._utils import is_given, get_async_library
from ._compat import cached_property
-from ._models import SecurityOptions
from ._version import __version__
from ._streaming import Stream as Stream, AsyncStream as AsyncStream
-from ._exceptions import EverosError, APIStatusError
+from ._exceptions import EverOSError, APIStatusError
from ._base_client import (
DEFAULT_MAX_RETRIES,
SyncAPIClient,
@@ -35,38 +34,18 @@
from .resources import v1
from .resources.v1.v1 import V1Resource, AsyncV1Resource
-__all__ = [
- "ENVIRONMENTS",
- "Timeout",
- "Transport",
- "ProxiesTypes",
- "RequestOptions",
- "Everos",
- "AsyncEveros",
- "Client",
- "AsyncClient",
-]
-
-ENVIRONMENTS: Dict[str, str] = {
- "production": "http://localhost:9527",
- "environment_1": "https://dev-gateway.aws.evermind.ai",
- "test": "https://test-gateway.aws.evermind.ai",
- "environment_3": "https://api.evermind.ai",
-}
-
-
-class Everos(SyncAPIClient):
+__all__ = ["Timeout", "Transport", "ProxiesTypes", "RequestOptions", "EverOS", "AsyncEverOS", "Client", "AsyncClient"]
+
+
+class EverOS(SyncAPIClient):
# client options
api_key: str
- _environment: Literal["production", "environment_1", "test", "environment_3"] | NotGiven
-
def __init__(
self,
*,
api_key: str | None = None,
- environment: Literal["production", "environment_1", "test", "environment_3"] | NotGiven = not_given,
- base_url: str | httpx.URL | None | NotGiven = not_given,
+ base_url: str | httpx.URL | None = None,
timeout: float | Timeout | None | NotGiven = not_given,
max_retries: int = DEFAULT_MAX_RETRIES,
default_headers: Mapping[str, str] | None = None,
@@ -85,43 +64,22 @@ def __init__(
# part of our public interface in the future.
_strict_response_validation: bool = False,
) -> None:
- """Construct a new synchronous Everos client instance.
+ """Construct a new synchronous EverOS client instance.
This automatically infers the `api_key` argument from the `EVEROS_API_KEY` environment variable if it is not provided.
"""
if api_key is None:
api_key = os.environ.get("EVEROS_API_KEY")
if api_key is None:
- raise EverosError(
+ raise EverOSError(
"The api_key client option must be set either by passing api_key to the client or by setting the EVEROS_API_KEY environment variable"
)
self.api_key = api_key
- self._environment = environment
-
- base_url_env = os.environ.get("EVEROS_BASE_URL")
- if is_given(base_url) and base_url is not None:
- # cast required because mypy doesn't understand the type narrowing
- base_url = cast("str | httpx.URL", base_url) # pyright: ignore[reportUnnecessaryCast]
- elif is_given(environment):
- if base_url_env and base_url is not None:
- raise ValueError(
- "Ambiguous URL; The `EVEROS_BASE_URL` env var and the `environment` argument are given. If you want to use the environment, you must pass base_url=None",
- )
-
- try:
- base_url = ENVIRONMENTS[environment]
- except KeyError as exc:
- raise ValueError(f"Unknown environment: {environment}") from exc
- elif base_url_env is not None:
- base_url = base_url_env
- else:
- self._environment = environment = "production"
-
- try:
- base_url = ENVIRONMENTS[environment]
- except KeyError as exc:
- raise ValueError(f"Unknown environment: {environment}") from exc
+ if base_url is None:
+ base_url = os.environ.get("EVER_OS_BASE_URL")
+ if base_url is None:
+ base_url = f"https://api.evermind.ai"
super().__init__(
version=__version__,
@@ -133,35 +91,35 @@ def __init__(
custom_query=default_query,
_strict_response_validation=_strict_response_validation,
)
+ # custom: inject multimodal-aware MemoriesResource
+ from .lib._multimodal import MemoriesResourceWithMultimodal as _MemMultimodal
+ from .resources.v1.v1 import V1Resource as _V1Resource
+ _v1 = _V1Resource(self)
+ _v1.__dict__["memories"] = _MemMultimodal(self)
+ self.__dict__["v1"] = _v1
@cached_property
def v1(self) -> V1Resource:
- """Async task status tracking"""
from .resources.v1 import V1Resource
return V1Resource(self)
@cached_property
- def with_raw_response(self) -> EverosWithRawResponse:
- return EverosWithRawResponse(self)
+ def with_raw_response(self) -> EverOSWithRawResponse:
+ return EverOSWithRawResponse(self)
@cached_property
- def with_streaming_response(self) -> EverosWithStreamedResponse:
- return EverosWithStreamedResponse(self)
+ def with_streaming_response(self) -> EverOSWithStreamedResponse:
+ return EverOSWithStreamedResponse(self)
@property
@override
def qs(self) -> Querystring:
return Querystring(array_format="comma")
- @override
- def _auth_headers(self, security: SecurityOptions) -> dict[str, str]:
- return {
- **(self._bearer_auth if security.get("bearer_auth", False) else {}),
- }
-
@property
- def _bearer_auth(self) -> dict[str, str]:
+ @override
+ def auth_headers(self) -> dict[str, str]:
api_key = self.api_key
return {"Authorization": f"Bearer {api_key}"}
@@ -178,7 +136,6 @@ def copy(
self,
*,
api_key: str | None = None,
- environment: Literal["production", "environment_1", "test", "environment_3"] | None = None,
base_url: str | httpx.URL | None = None,
timeout: float | Timeout | None | NotGiven = not_given,
http_client: httpx.Client | None = None,
@@ -214,7 +171,6 @@ def copy(
return self.__class__(
api_key=api_key or self.api_key,
base_url=base_url or self.base_url,
- environment=environment or self._environment,
timeout=self.timeout if isinstance(timeout, NotGiven) else timeout,
http_client=http_client,
max_retries=max_retries if is_given(max_retries) else self.max_retries,
@@ -261,18 +217,15 @@ def _make_status_error(
return APIStatusError(err_msg, response=response, body=body)
-class AsyncEveros(AsyncAPIClient):
+class AsyncEverOS(AsyncAPIClient):
# client options
api_key: str
- _environment: Literal["production", "environment_1", "test", "environment_3"] | NotGiven
-
def __init__(
self,
*,
api_key: str | None = None,
- environment: Literal["production", "environment_1", "test", "environment_3"] | NotGiven = not_given,
- base_url: str | httpx.URL | None | NotGiven = not_given,
+ base_url: str | httpx.URL | None = None,
timeout: float | Timeout | None | NotGiven = not_given,
max_retries: int = DEFAULT_MAX_RETRIES,
default_headers: Mapping[str, str] | None = None,
@@ -291,43 +244,22 @@ def __init__(
# part of our public interface in the future.
_strict_response_validation: bool = False,
) -> None:
- """Construct a new async AsyncEveros client instance.
+ """Construct a new async AsyncEverOS client instance.
This automatically infers the `api_key` argument from the `EVEROS_API_KEY` environment variable if it is not provided.
"""
if api_key is None:
api_key = os.environ.get("EVEROS_API_KEY")
if api_key is None:
- raise EverosError(
+ raise EverOSError(
"The api_key client option must be set either by passing api_key to the client or by setting the EVEROS_API_KEY environment variable"
)
self.api_key = api_key
- self._environment = environment
-
- base_url_env = os.environ.get("EVEROS_BASE_URL")
- if is_given(base_url) and base_url is not None:
- # cast required because mypy doesn't understand the type narrowing
- base_url = cast("str | httpx.URL", base_url) # pyright: ignore[reportUnnecessaryCast]
- elif is_given(environment):
- if base_url_env and base_url is not None:
- raise ValueError(
- "Ambiguous URL; The `EVEROS_BASE_URL` env var and the `environment` argument are given. If you want to use the environment, you must pass base_url=None",
- )
-
- try:
- base_url = ENVIRONMENTS[environment]
- except KeyError as exc:
- raise ValueError(f"Unknown environment: {environment}") from exc
- elif base_url_env is not None:
- base_url = base_url_env
- else:
- self._environment = environment = "production"
-
- try:
- base_url = ENVIRONMENTS[environment]
- except KeyError as exc:
- raise ValueError(f"Unknown environment: {environment}") from exc
+ if base_url is None:
+ base_url = os.environ.get("EVER_OS_BASE_URL")
+ if base_url is None:
+ base_url = f"https://api.evermind.ai"
super().__init__(
version=__version__,
@@ -339,35 +271,35 @@ def __init__(
custom_query=default_query,
_strict_response_validation=_strict_response_validation,
)
+ # custom: inject multimodal-aware AsyncMemoriesResource
+ from .lib._multimodal import AsyncMemoriesResourceWithMultimodal as _AsyncMemMultimodal
+ from .resources.v1.v1 import AsyncV1Resource as _AsyncV1Resource
+ _v1 = _AsyncV1Resource(self)
+ _v1.__dict__["memories"] = _AsyncMemMultimodal(self)
+ self.__dict__["v1"] = _v1
@cached_property
def v1(self) -> AsyncV1Resource:
- """Async task status tracking"""
from .resources.v1 import AsyncV1Resource
return AsyncV1Resource(self)
@cached_property
- def with_raw_response(self) -> AsyncEverosWithRawResponse:
- return AsyncEverosWithRawResponse(self)
+ def with_raw_response(self) -> AsyncEverOSWithRawResponse:
+ return AsyncEverOSWithRawResponse(self)
@cached_property
- def with_streaming_response(self) -> AsyncEverosWithStreamedResponse:
- return AsyncEverosWithStreamedResponse(self)
+ def with_streaming_response(self) -> AsyncEverOSWithStreamedResponse:
+ return AsyncEverOSWithStreamedResponse(self)
@property
@override
def qs(self) -> Querystring:
return Querystring(array_format="comma")
- @override
- def _auth_headers(self, security: SecurityOptions) -> dict[str, str]:
- return {
- **(self._bearer_auth if security.get("bearer_auth", False) else {}),
- }
-
@property
- def _bearer_auth(self) -> dict[str, str]:
+ @override
+ def auth_headers(self) -> dict[str, str]:
api_key = self.api_key
return {"Authorization": f"Bearer {api_key}"}
@@ -384,7 +316,6 @@ def copy(
self,
*,
api_key: str | None = None,
- environment: Literal["production", "environment_1", "test", "environment_3"] | None = None,
base_url: str | httpx.URL | None = None,
timeout: float | Timeout | None | NotGiven = not_given,
http_client: httpx.AsyncClient | None = None,
@@ -420,7 +351,6 @@ def copy(
return self.__class__(
api_key=api_key or self.api_key,
base_url=base_url or self.base_url,
- environment=environment or self._environment,
timeout=self.timeout if isinstance(timeout, NotGiven) else timeout,
http_client=http_client,
max_retries=max_retries if is_given(max_retries) else self.max_retries,
@@ -467,62 +397,58 @@ def _make_status_error(
return APIStatusError(err_msg, response=response, body=body)
-class EverosWithRawResponse:
- _client: Everos
+class EverOSWithRawResponse:
+ _client: EverOS
- def __init__(self, client: Everos) -> None:
+ def __init__(self, client: EverOS) -> None:
self._client = client
@cached_property
def v1(self) -> v1.V1ResourceWithRawResponse:
- """Async task status tracking"""
from .resources.v1 import V1ResourceWithRawResponse
return V1ResourceWithRawResponse(self._client.v1)
-class AsyncEverosWithRawResponse:
- _client: AsyncEveros
+class AsyncEverOSWithRawResponse:
+ _client: AsyncEverOS
- def __init__(self, client: AsyncEveros) -> None:
+ def __init__(self, client: AsyncEverOS) -> None:
self._client = client
@cached_property
def v1(self) -> v1.AsyncV1ResourceWithRawResponse:
- """Async task status tracking"""
from .resources.v1 import AsyncV1ResourceWithRawResponse
return AsyncV1ResourceWithRawResponse(self._client.v1)
-class EverosWithStreamedResponse:
- _client: Everos
+class EverOSWithStreamedResponse:
+ _client: EverOS
- def __init__(self, client: Everos) -> None:
+ def __init__(self, client: EverOS) -> None:
self._client = client
@cached_property
def v1(self) -> v1.V1ResourceWithStreamingResponse:
- """Async task status tracking"""
from .resources.v1 import V1ResourceWithStreamingResponse
return V1ResourceWithStreamingResponse(self._client.v1)
-class AsyncEverosWithStreamedResponse:
- _client: AsyncEveros
+class AsyncEverOSWithStreamedResponse:
+ _client: AsyncEverOS
- def __init__(self, client: AsyncEveros) -> None:
+ def __init__(self, client: AsyncEverOS) -> None:
self._client = client
@cached_property
def v1(self) -> v1.AsyncV1ResourceWithStreamingResponse:
- """Async task status tracking"""
from .resources.v1 import AsyncV1ResourceWithStreamingResponse
return AsyncV1ResourceWithStreamingResponse(self._client.v1)
-Client = Everos
+Client = EverOS
-AsyncClient = AsyncEveros
+AsyncClient = AsyncEverOS
diff --git a/src/everos/_exceptions.py b/src/everos/_exceptions.py
index bad1672..c7c803e 100644
--- a/src/everos/_exceptions.py
+++ b/src/everos/_exceptions.py
@@ -18,11 +18,11 @@
]
-class EverosError(Exception):
+class EverOSError(Exception):
pass
-class APIError(EverosError):
+class APIError(EverOSError):
message: str
request: httpx.Request
diff --git a/src/everos/_models.py b/src/everos/_models.py
index e22dd2a..29070e0 100644
--- a/src/everos/_models.py
+++ b/src/everos/_models.py
@@ -791,10 +791,6 @@ def _create_pydantic_model(type_: _T) -> Type[RootModel[_T]]:
return RootModel[type_] # type: ignore
-class SecurityOptions(TypedDict, total=False):
- bearer_auth: bool
-
-
class FinalRequestOptionsInput(TypedDict, total=False):
method: Required[str]
url: Required[str]
@@ -808,7 +804,6 @@ class FinalRequestOptionsInput(TypedDict, total=False):
json_data: Body
extra_json: AnyMapping
follow_redirects: bool
- security: SecurityOptions
@final
@@ -823,7 +818,6 @@ class FinalRequestOptions(pydantic.BaseModel):
idempotency_key: Union[str, None] = None
post_parser: Union[Callable[[Any], Any], NotGiven] = NotGiven()
follow_redirects: Union[bool, None] = None
- security: SecurityOptions = {"bearer_auth": True}
content: Union[bytes, bytearray, IO[bytes], Iterable[bytes], AsyncIterable[bytes], None] = None
# It should be noted that we cannot use `json` here as that would override
diff --git a/src/everos/_resource.py b/src/everos/_resource.py
index 3ca9433..f29e404 100644
--- a/src/everos/_resource.py
+++ b/src/everos/_resource.py
@@ -8,13 +8,13 @@
import anyio
if TYPE_CHECKING:
- from ._client import Everos, AsyncEveros
+ from ._client import EverOS, AsyncEverOS
class SyncAPIResource:
- _client: Everos
+ _client: EverOS
- def __init__(self, client: Everos) -> None:
+ def __init__(self, client: EverOS) -> None:
self._client = client
self._get = client.get
self._post = client.post
@@ -28,9 +28,9 @@ def _sleep(self, seconds: float) -> None:
class AsyncAPIResource:
- _client: AsyncEveros
+ _client: AsyncEverOS
- def __init__(self, client: AsyncEveros) -> None:
+ def __init__(self, client: AsyncEverOS) -> None:
self._client = client
self._get = client.get
self._post = client.post
diff --git a/src/everos/_response.py b/src/everos/_response.py
index 7f733bf..8d2f175 100644
--- a/src/everos/_response.py
+++ b/src/everos/_response.py
@@ -29,7 +29,7 @@
from ._models import BaseModel, is_basemodel
from ._constants import RAW_RESPONSE_HEADER, OVERRIDE_CAST_TO_HEADER
from ._streaming import Stream, AsyncStream, is_stream_class_type, extract_stream_chunk_type
-from ._exceptions import EverosError, APIResponseValidationError
+from ._exceptions import EverOSError, APIResponseValidationError
if TYPE_CHECKING:
from ._models import FinalRequestOptions
@@ -563,7 +563,7 @@ def __init__(self) -> None:
)
-class StreamAlreadyConsumed(EverosError):
+class StreamAlreadyConsumed(EverOSError):
"""
Attempted to read or stream content, but the content has already
been streamed.
diff --git a/src/everos/_streaming.py b/src/everos/_streaming.py
index 5e47a83..8a34414 100644
--- a/src/everos/_streaming.py
+++ b/src/everos/_streaming.py
@@ -12,7 +12,7 @@
from ._utils import extract_type_var_from_base
if TYPE_CHECKING:
- from ._client import Everos, AsyncEveros
+ from ._client import EverOS, AsyncEverOS
from ._models import FinalRequestOptions
@@ -31,7 +31,7 @@ def __init__(
*,
cast_to: type[_T],
response: httpx.Response,
- client: Everos,
+ client: EverOS,
options: Optional[FinalRequestOptions] = None,
) -> None:
self.response = response
@@ -96,7 +96,7 @@ def __init__(
*,
cast_to: type[_T],
response: httpx.Response,
- client: AsyncEveros,
+ client: AsyncEverOS,
options: Optional[FinalRequestOptions] = None,
) -> None:
self.response = response
diff --git a/src/everos/_types.py b/src/everos/_types.py
index eca6477..42c8888 100644
--- a/src/everos/_types.py
+++ b/src/everos/_types.py
@@ -36,7 +36,7 @@
from httpx import URL, Proxy, Timeout, Response, BaseTransport, AsyncBaseTransport
if TYPE_CHECKING:
- from ._models import BaseModel, SecurityOptions
+ from ._models import BaseModel
from ._response import APIResponse, AsyncAPIResponse
Transport = BaseTransport
@@ -121,7 +121,6 @@ class RequestOptions(TypedDict, total=False):
extra_json: AnyMapping
idempotency_key: str
follow_redirects: bool
- security: SecurityOptions
# Sentinel class used until PEP 0661 is accepted
diff --git a/src/everos/_utils/_logs.py b/src/everos/_utils/_logs.py
index 98b7480..1514cea 100644
--- a/src/everos/_utils/_logs.py
+++ b/src/everos/_utils/_logs.py
@@ -14,7 +14,7 @@ def _basic_config() -> None:
def setup_logging() -> None:
- env = os.environ.get("EVEROS_LOG")
+ env = os.environ.get("EVER_OS_LOG")
if env == "debug":
_basic_config()
logger.setLevel(logging.DEBUG)
diff --git a/src/everos/_version.py b/src/everos/_version.py
index 9ad6d9e..81c5c09 100644
--- a/src/everos/_version.py
+++ b/src/everos/_version.py
@@ -1,4 +1,4 @@
# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
__title__ = "everos"
-__version__ = "0.0.2" # x-release-please-version
+__version__ = "0.0.1" # x-release-please-version
diff --git a/src/everos/lib/__init__.py b/src/everos/lib/__init__.py
new file mode 100644
index 0000000..1222675
--- /dev/null
+++ b/src/everos/lib/__init__.py
@@ -0,0 +1,12 @@
+"""EverOS SDK custom helpers — multimodal upload support."""
+
+from ._errors import UploadError, MultimodalError, FileResolveError
+from ._multimodal import MemoriesResourceWithMultimodal, AsyncMemoriesResourceWithMultimodal
+
+__all__ = [
+ "MultimodalError",
+ "FileResolveError",
+ "UploadError",
+ "MemoriesResourceWithMultimodal",
+ "AsyncMemoriesResourceWithMultimodal",
+]
diff --git a/src/everos/lib/_detect.py b/src/everos/lib/_detect.py
new file mode 100644
index 0000000..f6dbcd9
--- /dev/null
+++ b/src/everos/lib/_detect.py
@@ -0,0 +1,116 @@
+"""Scan messages for content items that reference uploadable files.
+
+Detection rules (applied to each content item where type != 'text'):
+ - uri starts with http:// or https:// → HTTP file, download then upload
+ - uri is an existing local file path → local file, upload directly
+ - anything else → treat as existing object_key, pass through
+"""
+
+from __future__ import annotations
+
+import os
+from typing import Any, List, Literal
+from dataclasses import dataclass
+
+# ContentItem type → object.sign file_type
+_CONTENT_TYPE_TO_FILE_TYPE: dict[str, Literal["image", "file", "video"]] = {
+ "image": "image",
+ "audio": "file",
+ "doc": "file",
+ "pdf": "file",
+ "html": "file",
+ "email": "file",
+ # "video": "video", # reserved, not in current ContentItemParam
+}
+
+
+@dataclass
+class UploadTask:
+ """Describes a single content item that needs to be uploaded."""
+
+ msg_idx: int
+ """Index of the message in the messages list."""
+
+ content_idx: int
+ """Index of the content item within message.content."""
+
+ uri: str
+ """Original uri value from the content item."""
+
+ uri_type: Literal["local", "http"]
+ """Whether the uri is a local file path or an HTTP(S) URL."""
+
+ content_type: str
+ """ContentItem type (e.g. 'image', 'doc')."""
+
+ file_type: Literal["image", "file", "video"]
+ """file_type for the object.sign API."""
+
+ file_name: str | None
+ """ContentItem name field (may be None)."""
+
+ file_ext: str | None
+ """ContentItem ext field (may be None)."""
+
+
+def _is_http_uri(uri: str) -> bool:
+ return uri.startswith("http://") or uri.startswith("https://")
+
+
+def _is_local_file(uri: str) -> bool:
+ """Return True only if uri refers to an existing local file.
+
+ MinIO object keys contain server-generated UUID segments and will never
+ match a local path, so os.path.isfile() is a reliable discriminator.
+ """
+ if "://" in uri:
+ return False
+ return os.path.isfile(os.path.expanduser(uri))
+
+
+def scan_messages(messages: list[dict[str, Any]]) -> List[UploadTask]:
+ """Scan messages and return a list of UploadTask for content items that need uploading.
+
+ Returns an empty list when no uploads are needed (caller can skip multimodal path).
+ Does NOT modify messages.
+ """
+ tasks: list[UploadTask] = []
+
+ for msg_idx, msg in enumerate(messages):
+ content = msg.get("content")
+
+ # str shorthand → plain text, skip
+ if content is None or isinstance(content, str):
+ continue
+
+ for ci_idx, item in enumerate(content):
+ item_type = item.get("type", "text")
+
+ if item_type == "text":
+ continue
+
+ uri = item.get("uri")
+ if not uri or not isinstance(uri, str):
+ continue
+
+ if _is_http_uri(uri):
+ uri_type: Literal["local", "http"] = "http"
+ elif _is_local_file(uri):
+ uri_type = "local"
+ else:
+ continue # already an object_key or unsupported scheme
+
+ file_type = _CONTENT_TYPE_TO_FILE_TYPE.get(item_type, "file")
+
+ tasks.append(UploadTask(
+ msg_idx=msg_idx,
+ content_idx=ci_idx,
+ uri=uri,
+ uri_type=uri_type,
+ content_type=item_type,
+ file_type=file_type,
+ file_name=item.get("name"),
+ file_ext=item.get("ext"),
+ ))
+
+ return tasks
diff --git a/src/everos/lib/_errors.py b/src/everos/lib/_errors.py
new file mode 100644
index 0000000..13d407b
--- /dev/null
+++ b/src/everos/lib/_errors.py
@@ -0,0 +1,18 @@
+"""Multimodal upload exceptions."""
+
+from __future__ import annotations
+
+
+class MultimodalError(Exception):
+ """Base exception for multimodal upload operations."""
+ pass
+
+
+class FileResolveError(MultimodalError):
+ """Failed to resolve a file input (path not found, URL download failure, size exceeded, etc.)."""
+ pass
+
+
+class UploadError(MultimodalError):
+ """Failed to sign or upload a file to S3."""
+ pass
diff --git a/src/everos/lib/_files.py b/src/everos/lib/_files.py
new file mode 100644
index 0000000..73ec38b
--- /dev/null
+++ b/src/everos/lib/_files.py
@@ -0,0 +1,208 @@
+"""File input abstraction and streaming file resolution.
+
+ResolvedFile holds a local file path (never raw bytes), so uploads are always
+streamed rather than loaded into memory.
+"""
+
+from __future__ import annotations
+
+import os
+import tempfile
+import mimetypes
+from typing import BinaryIO
+from pathlib import Path
+from dataclasses import field, dataclass
+from urllib.parse import unquote, urlparse
+
+import httpx
+
+from ._errors import FileResolveError
+
+_DEFAULT_MAX_DOWNLOAD_SIZE = 100 * 1024 * 1024 # 100 MB
+_DEFAULT_DOWNLOAD_TIMEOUT = 60.0 # seconds
+_STREAM_CHUNK_SIZE = 64 * 1024 # 64 KB
+
+
+@dataclass
+class FileInput:
+ """Unified file input. Exactly one of path / url must be set.
+
+ Examples::
+
+ FileInput(path="./photo.jpg")
+ FileInput(url="https://example.com/image.png")
+ """
+
+ path: str | Path | None = None
+ url: str | None = None
+ content_type: str | None = None
+ filename: str | None = None
+
+ def __post_init__(self) -> None:
+ sources = sum(x is not None for x in [self.path, self.url])
+ if sources == 0:
+ raise ValueError("FileInput requires exactly one of: path, url")
+ if sources > 1:
+ raise ValueError("FileInput accepts only one of: path, url")
+
+
+@dataclass
+class ResolvedFile:
+ """Resolved file — holds a local path, never raw bytes.
+
+ Attributes:
+ file_path: Absolute path to the local file (original or downloaded temp file).
+ content_type: MIME type.
+ filename: Original filename.
+ size: File size in bytes.
+ is_temp: True if this is a downloaded temp file that must be cleaned up.
+ """
+
+ file_path: Path
+ content_type: str
+ filename: str
+ size: int
+ is_temp: bool = field(default=False)
+
+ def cleanup(self) -> None:
+ """Delete temp file if applicable. Safe to call multiple times."""
+ if self.is_temp and self.file_path.exists():
+ self.file_path.unlink()
+
+ def open(self) -> BinaryIO:
+ """Open for streaming read."""
+ return open(self.file_path, "rb") # type: ignore[return-value]
+
+
+# ── Public resolution functions ───────────────────────────────────────────────
+
+
+def resolve_file(
+ file_input: FileInput,
+ *,
+ max_download_size: int = _DEFAULT_MAX_DOWNLOAD_SIZE,
+ download_timeout: float = _DEFAULT_DOWNLOAD_TIMEOUT,
+) -> ResolvedFile:
+ """Resolve a FileInput to a ResolvedFile (synchronous)."""
+ if file_input.path is not None:
+ return _resolve_from_path(file_input)
+ else:
+ return _resolve_from_url(file_input, max_download_size, download_timeout)
+
+
+async def async_resolve_file(
+ file_input: FileInput,
+ *,
+ max_download_size: int = _DEFAULT_MAX_DOWNLOAD_SIZE,
+ download_timeout: float = _DEFAULT_DOWNLOAD_TIMEOUT,
+) -> ResolvedFile:
+ """Resolve a FileInput to a ResolvedFile (asynchronous)."""
+ if file_input.path is not None:
+ import anyio
+ return await anyio.to_thread.run_sync(lambda: _resolve_from_path(file_input)) # type: ignore[attr-defined]
+ else:
+ return await _async_resolve_from_url(file_input, max_download_size, download_timeout)
+
+
+# ── Internal helpers ──────────────────────────────────────────────────────────
+
+
+def _resolve_from_path(fi: FileInput) -> ResolvedFile:
+ p = Path(os.path.expanduser(str(fi.path))).resolve()
+ if not p.exists():
+ raise FileResolveError(f"File not found: {p}")
+ if not p.is_file():
+ raise FileResolveError(f"Not a file: {p}")
+
+ size = p.stat().st_size
+ ct = fi.content_type or mimetypes.guess_type(str(p))[0] or "application/octet-stream"
+ fn = fi.filename or p.name
+ return ResolvedFile(file_path=p, content_type=ct, filename=fn, size=size, is_temp=False)
+
+
+def _resolve_from_url(fi: FileInput, max_size: int, timeout: float) -> ResolvedFile:
+ """Stream-download URL to a temp file; abort if size exceeds max_size."""
+ tmp_fd, tmp_path = tempfile.mkstemp(prefix="everos_")
+ tmp_file = Path(tmp_path)
+ downloaded = 0
+ ct_header = "application/octet-stream"
+
+ try:
+ with httpx.Client(timeout=timeout, follow_redirects=True) as client:
+ with client.stream("GET", fi.url) as resp: # type: ignore[arg-type]
+ resp.raise_for_status()
+ ct_header = resp.headers.get("content-type", ct_header).split(";")[0].strip()
+
+ with os.fdopen(tmp_fd, "wb") as f:
+ for chunk in resp.iter_bytes(chunk_size=_STREAM_CHUNK_SIZE):
+ downloaded += len(chunk)
+ if downloaded > max_size:
+ raise FileResolveError(
+ f"Download from {fi.url} exceeded {max_size} bytes limit "
+ f"({downloaded} bytes so far). "
+ "Use the low-level object.sign() API for large files."
+ )
+ f.write(chunk)
+ tmp_fd = -1 # fdopen closed it
+
+ except FileResolveError:
+ tmp_file.unlink(missing_ok=True)
+ raise
+ except Exception as exc:
+ tmp_file.unlink(missing_ok=True)
+ if tmp_fd >= 0:
+ os.close(tmp_fd)
+ raise FileResolveError(f"Failed to download {fi.url}: {exc}") from exc
+
+ ct = fi.content_type or ct_header
+ fn = fi.filename or _filename_from_url(fi.url) # type: ignore[arg-type]
+ return ResolvedFile(
+ file_path=tmp_file, content_type=ct, filename=fn,
+ size=downloaded, is_temp=True,
+ )
+
+
+async def _async_resolve_from_url(fi: FileInput, max_size: int, timeout: float) -> ResolvedFile:
+ """Async streaming download to temp file."""
+ tmp_fd, tmp_path = tempfile.mkstemp(prefix="everos_")
+ tmp_file = Path(tmp_path)
+ downloaded = 0
+ ct_header = "application/octet-stream"
+
+ try:
+ async with httpx.AsyncClient(timeout=timeout, follow_redirects=True) as client:
+ async with client.stream("GET", fi.url) as resp: # type: ignore[arg-type]
+ resp.raise_for_status()
+ ct_header = resp.headers.get("content-type", ct_header).split(";")[0].strip()
+
+ with os.fdopen(tmp_fd, "wb") as f:
+ async for chunk in resp.aiter_bytes(chunk_size=_STREAM_CHUNK_SIZE):
+ downloaded += len(chunk)
+ if downloaded > max_size:
+ raise FileResolveError(
+ f"Download from {fi.url} exceeded {max_size} bytes limit."
+ )
+ f.write(chunk)
+ tmp_fd = -1
+
+ except FileResolveError:
+ tmp_file.unlink(missing_ok=True)
+ raise
+ except Exception as exc:
+ tmp_file.unlink(missing_ok=True)
+ if tmp_fd >= 0:
+ os.close(tmp_fd)
+ raise FileResolveError(f"Failed to download {fi.url}: {exc}") from exc
+
+ ct = fi.content_type or ct_header
+ fn = fi.filename or _filename_from_url(fi.url) # type: ignore[arg-type]
+ return ResolvedFile(
+ file_path=tmp_file, content_type=ct, filename=fn,
+ size=downloaded, is_temp=True,
+ )
+
+
+def _filename_from_url(url: str) -> str:
+ parsed = urlparse(url)
+ name = Path(unquote(parsed.path)).name
+ return name if name else "download"
diff --git a/src/everos/lib/_multimodal.py b/src/everos/lib/_multimodal.py
new file mode 100644
index 0000000..b70285e
--- /dev/null
+++ b/src/everos/lib/_multimodal.py
@@ -0,0 +1,348 @@
+"""Multimodal add orchestration: detect → resolve → batch sign → concurrent upload → replace uri → add.
+
+MemoriesResourceWithMultimodal overrides add() while keeping the same signature.
+The user calls client.v1.memories.add() exactly as before; the SDK handles uploads transparently.
+"""
+
+from __future__ import annotations
+
+import os
+import copy
+import logging
+from typing import Any, List, Iterable, Optional, cast
+from typing_extensions import override
+from concurrent.futures import Future, ThreadPoolExecutor, as_completed
+
+import httpx
+
+from ._files import FileInput, ResolvedFile, resolve_file, async_resolve_file
+from .._types import Body, Omit, Query, Headers, NotGiven, omit, not_given
+from ._detect import UploadTask, scan_messages
+from ._errors import MultimodalError, FileResolveError
+from ._upload import SignedFile, UploadResult, batch_sign, s3_post_upload, async_batch_sign, async_s3_post_upload
+from ..types.v1.add_response import AddResponse
+from ..types.v1.message_item_param import MessageItemParam
+from ..resources.v1.memories.memories import MemoriesResource, AsyncMemoriesResource
+
+log: logging.Logger = logging.getLogger("everos")
+
+_DEFAULT_MAX_WORKERS = 4
+
+
+# ── Sync resource override ────────────────────────────────────────────────────
+
+
+class MemoriesResourceWithMultimodal(MemoriesResource):
+ """Inherits MemoriesResource and overrides add() with transparent multimodal upload."""
+
+ @override
+ def add(
+ self,
+ *,
+ messages: Iterable[MessageItemParam],
+ user_id: str,
+ async_mode: bool | Omit = omit,
+ session_id: Optional[str] | Omit = omit,
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> AddResponse:
+ """add() with automatic multimodal upload.
+
+ Scans content items in each message:
+ - uri is a local file path → sign + upload → replace with object_key
+ - uri is an http(s) URL → download → sign + upload → replace with object_key
+ - uri is already an object_key (or any other value) → pass through unchanged
+
+ Signature is identical to the generated add().
+ """
+ msg_list = _materialise(messages)
+ upload_tasks = scan_messages(msg_list)
+
+ if not upload_tasks:
+ log.debug("No uploadable content detected, passing through to add()")
+ return super().add(
+ messages=cast(Iterable[MessageItemParam], msg_list),
+ user_id=user_id, async_mode=async_mode,
+ session_id=session_id, extra_headers=extra_headers,
+ extra_query=extra_query, extra_body=extra_body, timeout=timeout,
+ )
+
+ log.debug("Multimodal detected: %d file(s) to upload", len(upload_tasks))
+ for i, t in enumerate(upload_tasks):
+ log.debug(
+ " [%d] messages[%d].content[%d] type=%s uri=%s (%s)",
+ i, t.msg_idx, t.content_idx, t.content_type, t.uri, t.uri_type,
+ )
+
+ msg_list = copy.deepcopy(msg_list)
+ resolved_files: List[ResolvedFile] = []
+
+ try:
+ _resolve_all(upload_tasks, resolved_files)
+ signed_files = batch_sign(self._client, upload_tasks, resolved_files)
+ results = _concurrent_upload(signed_files, upload_tasks)
+ _replace_uris(msg_list, upload_tasks, results)
+ log.debug("Multimodal upload complete: %d file(s), calling add()", len(upload_tasks))
+ return super().add(
+ messages=cast(Iterable[MessageItemParam], msg_list),
+ user_id=user_id, async_mode=async_mode,
+ session_id=session_id, extra_headers=extra_headers,
+ extra_query=extra_query, extra_body=extra_body, timeout=timeout,
+ )
+
+ finally:
+ _cleanup(resolved_files)
+
+
+# ── Async resource override ───────────────────────────────────────────────────
+
+
+class AsyncMemoriesResourceWithMultimodal(AsyncMemoriesResource):
+ """Async version — mirrors MemoriesResourceWithMultimodal."""
+
+ @override
+ async def add(
+ self,
+ *,
+ messages: Iterable[MessageItemParam],
+ user_id: str,
+ async_mode: bool | Omit = omit,
+ session_id: Optional[str] | Omit = omit,
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> AddResponse:
+ """Async add() with automatic multimodal upload. Signature identical to generated add()."""
+ msg_list = _materialise(messages)
+ upload_tasks = scan_messages(msg_list)
+
+ if not upload_tasks:
+ log.debug("No uploadable content detected, passing through to add() (async)")
+ return await super().add(
+ messages=cast(Iterable[MessageItemParam], msg_list),
+ user_id=user_id, async_mode=async_mode,
+ session_id=session_id, extra_headers=extra_headers,
+ extra_query=extra_query, extra_body=extra_body, timeout=timeout,
+ )
+
+ log.debug("Multimodal detected: %d file(s) to upload (async)", len(upload_tasks))
+
+ msg_list = copy.deepcopy(msg_list)
+ resolved_files: List[ResolvedFile] = []
+
+ try:
+ await _async_resolve_all(upload_tasks, resolved_files)
+ signed_files = await async_batch_sign(self._client, upload_tasks, resolved_files)
+ results = await _async_concurrent_upload(signed_files, upload_tasks)
+ _replace_uris(msg_list, upload_tasks, results)
+ log.debug("Multimodal upload complete: %d file(s), calling add() (async)", len(upload_tasks))
+ return await super().add(
+ messages=cast(Iterable[MessageItemParam], msg_list),
+ user_id=user_id, async_mode=async_mode,
+ session_id=session_id, extra_headers=extra_headers,
+ extra_query=extra_query, extra_body=extra_body, timeout=timeout,
+ )
+
+ finally:
+ _cleanup(resolved_files)
+
+
+# ── Shared helpers ────────────────────────────────────────────────────────────
+
+
+def _materialise(messages: Iterable[MessageItemParam]) -> list[dict[str, Any]]:
+ """Convert iterable of messages to list[dict] for scanning and mutation."""
+ return [dict(m) if isinstance(m, dict) else m for m in messages] # type: ignore[misc]
+
+
+def _resolve_all(upload_tasks: List[UploadTask], resolved_files: List[ResolvedFile]) -> None:
+ """Resolve files sequentially (preserves order)."""
+ for task in upload_tasks:
+ try:
+ fi = FileInput(path=task.uri) if task.uri_type == "local" else FileInput(url=task.uri)
+ resolved_files.append(resolve_file(fi))
+ log.debug(
+ "Resolved %s → %d bytes %s",
+ task.uri, resolved_files[-1].size, resolved_files[-1].content_type,
+ )
+ except FileResolveError:
+ raise
+ except Exception as exc:
+ raise FileResolveError(
+ f"Failed to resolve messages[{task.msg_idx}].content[{task.content_idx}] "
+ f"(uri={task.uri}): {exc}"
+ ) from exc
+
+
+async def _async_resolve_all(
+ upload_tasks: List[UploadTask], resolved_files: List[ResolvedFile]
+) -> None:
+ """Async parallel resolve (preserves order via gather).
+
+ On failure, cleans up ALL already-resolved temp files (both those appended to
+ resolved_files and those only present in the gather results list).
+ """
+ import asyncio
+
+ async def _resolve_one(task: UploadTask) -> ResolvedFile:
+ fi = FileInput(path=task.uri) if task.uri_type == "local" else FileInput(url=task.uri)
+ return await async_resolve_file(fi)
+
+ coros = [_resolve_one(t) for t in upload_tasks]
+ results = await asyncio.gather(*coros, return_exceptions=True)
+
+ for i, result in enumerate(results):
+ if isinstance(result, Exception):
+ # Clean up ALL resolved temp files: those already appended AND those
+ # only present in results (gather ran them concurrently).
+ for r in results:
+ if isinstance(r, ResolvedFile):
+ r.cleanup()
+ task = upload_tasks[i]
+ raise FileResolveError(
+ f"Failed to resolve messages[{task.msg_idx}].content[{task.content_idx}] "
+ f"(uri={task.uri}): {result}"
+ ) from result
+ resolved_files.append(result) # type: ignore[arg-type]
+ log.debug("Resolved %s → %d bytes", upload_tasks[i].uri, result.size) # type: ignore[union-attr]
+
+
+def _concurrent_upload(
+ signed_files: List[SignedFile],
+ upload_tasks: List[UploadTask],
+ max_workers: int = _DEFAULT_MAX_WORKERS,
+) -> List[UploadResult]:
+ """Upload files concurrently (order-preserving, best-effort fail-fast).
+
+ On the first upload failure, cancels futures not yet started. Futures already
+ running cannot be interrupted; the ThreadPoolExecutor context manager blocks
+ until they complete before this function returns.
+ """
+ n = len(signed_files)
+ results: List[UploadResult | None] = [None] * n
+
+ with ThreadPoolExecutor(max_workers=min(max_workers, n)) as executor:
+ future_to_idx: dict[Future[UploadResult], int] = {}
+ futures: list[Future[UploadResult]] = []
+ for i, sf in enumerate(signed_files):
+ fut = executor.submit(s3_post_upload, sf)
+ futures.append(fut)
+ future_to_idx[fut] = i
+
+ first_error: Exception | None = None
+ first_error_idx = -1
+
+ for fut in as_completed(future_to_idx):
+ idx = future_to_idx[fut]
+ try:
+ results[idx] = fut.result()
+ except Exception as exc:
+ first_error = exc
+ first_error_idx = idx
+ for other in futures:
+ if other is not fut:
+ other.cancel()
+ break
+
+ if first_error is not None:
+ task = upload_tasks[first_error_idx]
+ raise MultimodalError(
+ f"Upload failed at messages[{task.msg_idx}].content[{task.content_idx}] "
+ f"(uri={task.uri}): {first_error}"
+ ) from first_error
+
+ return results # type: ignore[return-value]
+
+
+async def _async_concurrent_upload(
+ signed_files: List[SignedFile],
+ upload_tasks: List[UploadTask],
+) -> List[UploadResult]:
+ """Async concurrent upload (order-preserving, fail-fast via FIRST_EXCEPTION).
+
+ When FIRST_EXCEPTION fires, all tasks in `done` are already finished.
+ With no error, pending is always empty (asyncio.wait returns all in done).
+ """
+ import asyncio
+
+ n = len(signed_files)
+ results: List[UploadResult | None] = [None] * n
+ task_to_idx: dict[asyncio.Task[UploadResult], int] = {}
+ async_tasks: list[asyncio.Task[UploadResult]] = []
+
+ for i, sf in enumerate(signed_files):
+ t = asyncio.create_task(async_s3_post_upload(sf), name=f"upload-{i}")
+ async_tasks.append(t)
+ task_to_idx[t] = i
+
+ done, pending = await asyncio.wait(async_tasks, return_when=asyncio.FIRST_EXCEPTION)
+
+ first_error: BaseException | None = None
+ first_error_idx = -1
+
+ for t in done:
+ idx = task_to_idx[t]
+ exc = t.exception()
+ if exc is not None:
+ if first_error is None or idx < first_error_idx:
+ first_error = exc
+ first_error_idx = idx
+ else:
+ results[idx] = t.result()
+
+ if first_error is not None:
+ for t in pending:
+ t.cancel()
+ if pending:
+ done_after_cancel, _ = await asyncio.wait(pending)
+ # Consume exceptions to suppress unobserved-exception warnings (Python 3.11+)
+ for t in done_after_cancel:
+ if not t.cancelled():
+ t.exception()
+ task = upload_tasks[first_error_idx]
+ raise MultimodalError(
+ f"Upload failed at messages[{task.msg_idx}].content[{task.content_idx}]: {first_error}"
+ ) from first_error # type: ignore[arg-type]
+
+ # No error: pending is always empty when FIRST_EXCEPTION returns all tasks done.
+ # (Defensive guard kept for clarity, but unreachable in practice.)
+ assert not pending, "unexpected pending tasks after FIRST_EXCEPTION with no error"
+
+ return results # type: ignore[return-value]
+
+
+def _replace_uris(
+ msg_list: list[dict[str, Any]],
+ upload_tasks: List[UploadTask],
+ results: List[UploadResult],
+) -> None:
+ """Replace uri in each content item with the uploaded object_key."""
+ for i, task in enumerate(upload_tasks):
+ result = results[i]
+ content_item = msg_list[task.msg_idx]["content"][task.content_idx]
+ log.debug(
+ "Replaced messages[%d].content[%d].uri: %s → %s",
+ task.msg_idx, task.content_idx, task.uri, result.object_key,
+ )
+ content_item["uri"] = result.object_key
+ # auto-fill name and ext if absent
+ if not content_item.get("name") and result.filename:
+ content_item["name"] = result.filename
+ if not content_item.get("ext") and result.filename:
+ ext = os.path.splitext(result.filename)[1].lstrip(".")
+ if ext:
+ content_item["ext"] = ext
+
+
+def _cleanup(resolved_files: List[ResolvedFile]) -> None:
+ """Clean up temp files. Always called from finally blocks."""
+ cleaned = 0
+ for r in resolved_files:
+ if r.is_temp:
+ r.cleanup()
+ cleaned += 1
+ if cleaned:
+ log.debug("Cleaned up %d temp file(s)", cleaned)
diff --git a/src/everos/lib/_upload.py b/src/everos/lib/_upload.py
new file mode 100644
index 0000000..3d55a40
--- /dev/null
+++ b/src/everos/lib/_upload.py
@@ -0,0 +1,258 @@
+"""Batch sign + S3 POST form upload.
+
+One call to object.sign() signs all files; then each file is uploaded via S3
+presigned POST (multipart/form-data with policy fields + file).
+"""
+
+from __future__ import annotations
+
+import time
+import uuid
+import asyncio
+from typing import TYPE_CHECKING, List
+from dataclasses import dataclass
+
+import httpx
+
+from ._files import ResolvedFile
+from ._detect import UploadTask
+from ._errors import UploadError
+
+if TYPE_CHECKING:
+ from .._client import EverOS, AsyncEverOS
+
+import logging
+
+log: logging.Logger = logging.getLogger("everos")
+
+_UPLOAD_TIMEOUT = 120.0
+_MAX_UPLOAD_RETRIES = 3
+_RETRY_BACKOFF_S = 1.0 # seconds to sleep between S3 upload retries
+
+
+@dataclass
+class SignedFile:
+ """Holds sign response data for a single file, ready for upload."""
+
+ resolved: ResolvedFile
+ task: UploadTask
+ object_key: str
+ upload_url: str
+ upload_fields: dict[str, str]
+
+
+@dataclass
+class UploadResult:
+ """Result of a single successful upload."""
+
+ object_key: str
+ filename: str
+ content_type: str
+ size: int
+
+
+# ── Sync ──────────────────────────────────────────────────────────────────────
+
+
+def batch_sign(
+ client: EverOS,
+ upload_tasks: List[UploadTask],
+ resolved_files: List[ResolvedFile],
+) -> List[SignedFile]:
+ """Sign all files in a single API call. Sign uses SDK built-in retry."""
+ object_list = [
+ {
+ "file_id": f"sdk-{uuid.uuid4().hex}",
+ "file_name": resolved.filename,
+ "file_type": task.file_type,
+ }
+ for task, resolved in zip(upload_tasks, resolved_files)
+ ]
+
+ log.debug("Signing %d file(s) in one batch request", len(object_list))
+ sign_resp = client.v1.object.sign(object_list=object_list) # type: ignore[arg-type]
+
+ if sign_resp.status != 0 or sign_resp.error != "OK":
+ raise UploadError(
+ f"Sign failed: status={sign_resp.status}, error={sign_resp.error}"
+ )
+
+ items = sign_resp.result.data.object_list if (sign_resp.result and sign_resp.result.data) else []
+ if not items or len(items) != len(upload_tasks):
+ raise UploadError(
+ f"Sign returned {len(items or [])} items, expected {len(upload_tasks)}"
+ )
+
+ signed_files: list[SignedFile] = []
+ for i, item in enumerate(items):
+ if not item.object_key or not item.object_signed_info or not item.object_signed_info.url:
+ raise UploadError(f"Sign response item {i} is missing object_key or upload_url")
+ log.debug(" Signed: %s → %s", resolved_files[i].filename, item.object_key)
+ signed_files.append(SignedFile(
+ resolved=resolved_files[i],
+ task=upload_tasks[i],
+ object_key=item.object_key,
+ upload_url=item.object_signed_info.url,
+ upload_fields=dict(item.object_signed_info.fields or {}),
+ ))
+ return signed_files
+
+
+def s3_post_upload(signed: SignedFile) -> UploadResult:
+ """Upload a single file via S3 presigned POST form. Retries on 5xx / timeout."""
+ resolved = signed.resolved
+ last_error: Exception | None = None
+
+ for attempt in range(_MAX_UPLOAD_RETRIES):
+ try:
+ log.debug(
+ "Uploading %s (%s bytes) to S3 (attempt %d/%d)",
+ resolved.filename, resolved.size, attempt + 1, _MAX_UPLOAD_RETRIES,
+ )
+ with resolved.open() as fh:
+ resp = httpx.post(
+ signed.upload_url,
+ data=signed.upload_fields,
+ files={"file": (resolved.filename, fh, resolved.content_type)},
+ timeout=_UPLOAD_TIMEOUT,
+ )
+
+ if resp.status_code in (200, 201, 204):
+ log.debug("Uploaded %s → HTTP %d", resolved.filename, resp.status_code)
+ return UploadResult(
+ object_key=signed.object_key,
+ filename=resolved.filename,
+ content_type=resolved.content_type,
+ size=resolved.size,
+ )
+
+ if resp.status_code < 500:
+ raise UploadError(
+ f"S3 upload failed (HTTP {resp.status_code}): {resp.text[:400]}"
+ )
+
+ last_error = UploadError(f"S3 server error: HTTP {resp.status_code}")
+ log.warning(
+ "S3 upload retry %d/%d for %s: HTTP %d",
+ attempt + 1, _MAX_UPLOAD_RETRIES, resolved.filename, resp.status_code,
+ )
+
+ except httpx.TimeoutException as exc:
+ last_error = exc
+ log.warning(
+ "S3 upload retry %d/%d for %s: timeout",
+ attempt + 1, _MAX_UPLOAD_RETRIES, resolved.filename,
+ )
+
+ if attempt < _MAX_UPLOAD_RETRIES - 1:
+ time.sleep(_RETRY_BACKOFF_S)
+
+ raise UploadError(
+ f"S3 upload failed after {_MAX_UPLOAD_RETRIES} attempts for {resolved.filename}"
+ ) from last_error
+
+
+# ── Async ─────────────────────────────────────────────────────────────────────
+
+
+async def async_batch_sign(
+ client: AsyncEverOS,
+ upload_tasks: List[UploadTask],
+ resolved_files: List[ResolvedFile],
+) -> List[SignedFile]:
+ """Async batch sign — mirrors sync version."""
+ object_list = [
+ {
+ "file_id": f"sdk-{uuid.uuid4().hex}",
+ "file_name": resolved.filename,
+ "file_type": task.file_type,
+ }
+ for task, resolved in zip(upload_tasks, resolved_files)
+ ]
+
+ log.debug("Signing %d file(s) in one batch request (async)", len(object_list))
+ sign_resp = await client.v1.object.sign(object_list=object_list) # type: ignore[arg-type]
+
+ if sign_resp.status != 0 or sign_resp.error != "OK":
+ raise UploadError(
+ f"Sign failed: status={sign_resp.status}, error={sign_resp.error}"
+ )
+
+ items = sign_resp.result.data.object_list if (sign_resp.result and sign_resp.result.data) else []
+ if not items or len(items) != len(upload_tasks):
+ raise UploadError(
+ f"Sign returned {len(items or [])} items, expected {len(upload_tasks)}"
+ )
+
+ signed_files: list[SignedFile] = []
+ for i, item in enumerate(items):
+ if not item.object_key or not item.object_signed_info or not item.object_signed_info.url:
+ raise UploadError(f"Sign response item {i} is missing object_key or upload_url")
+ log.debug(" Signed: %s → %s", resolved_files[i].filename, item.object_key)
+ signed_files.append(SignedFile(
+ resolved=resolved_files[i],
+ task=upload_tasks[i],
+ object_key=item.object_key,
+ upload_url=item.object_signed_info.url,
+ upload_fields=dict(item.object_signed_info.fields or {}),
+ ))
+ return signed_files
+
+
+async def async_s3_post_upload(signed: SignedFile) -> UploadResult:
+ """Async S3 POST upload with retry."""
+ import anyio
+
+ resolved = signed.resolved
+ last_error: Exception | None = None
+
+ for attempt in range(_MAX_UPLOAD_RETRIES):
+ try:
+ log.debug(
+ "Uploading %s (%s bytes) to S3 async (attempt %d/%d)",
+ resolved.filename, resolved.size, attempt + 1, _MAX_UPLOAD_RETRIES,
+ )
+ fh = await anyio.to_thread.run_sync(resolved.open) # type: ignore[attr-defined]
+ try:
+ async with httpx.AsyncClient(timeout=_UPLOAD_TIMEOUT) as http:
+ resp = await http.post(
+ signed.upload_url,
+ data=signed.upload_fields,
+ files={"file": (resolved.filename, fh, resolved.content_type)}, # type: ignore[arg-type]
+ )
+ finally:
+ await anyio.to_thread.run_sync(fh.close) # type: ignore[attr-defined]
+
+ if resp.status_code in (200, 201, 204):
+ log.debug("Uploaded %s → HTTP %d", resolved.filename, resp.status_code)
+ return UploadResult(
+ object_key=signed.object_key,
+ filename=resolved.filename,
+ content_type=resolved.content_type,
+ size=resolved.size,
+ )
+
+ if resp.status_code < 500:
+ raise UploadError(
+ f"S3 upload failed (HTTP {resp.status_code}): {resp.text[:400]}"
+ )
+
+ last_error = UploadError(f"S3 server error: HTTP {resp.status_code}")
+ log.warning(
+ "S3 upload retry %d/%d for %s: HTTP %d",
+ attempt + 1, _MAX_UPLOAD_RETRIES, resolved.filename, resp.status_code,
+ )
+
+ except httpx.TimeoutException as exc:
+ last_error = exc
+ log.warning(
+ "S3 upload retry %d/%d for %s: timeout",
+ attempt + 1, _MAX_UPLOAD_RETRIES, resolved.filename,
+ )
+
+ if attempt < _MAX_UPLOAD_RETRIES - 1:
+ await asyncio.sleep(_RETRY_BACKOFF_S)
+
+ raise UploadError(
+ f"S3 upload failed after {_MAX_UPLOAD_RETRIES} attempts for {resolved.filename}"
+ ) from last_error
diff --git a/src/everos/resources/v1/__init__.py b/src/everos/resources/v1/__init__.py
index 11564d9..447a6b8 100644
--- a/src/everos/resources/v1/__init__.py
+++ b/src/everos/resources/v1/__init__.py
@@ -8,6 +8,14 @@
V1ResourceWithStreamingResponse,
AsyncV1ResourceWithStreamingResponse,
)
+from .tasks import (
+ TasksResource,
+ AsyncTasksResource,
+ TasksResourceWithRawResponse,
+ AsyncTasksResourceWithRawResponse,
+ TasksResourceWithStreamingResponse,
+ AsyncTasksResourceWithStreamingResponse,
+)
from .groups import (
GroupsResource,
AsyncGroupsResource,
@@ -50,36 +58,42 @@
)
__all__ = [
- "MemoriesResource",
- "AsyncMemoriesResource",
- "MemoriesResourceWithRawResponse",
- "AsyncMemoriesResourceWithRawResponse",
- "MemoriesResourceWithStreamingResponse",
- "AsyncMemoriesResourceWithStreamingResponse",
"GroupsResource",
"AsyncGroupsResource",
"GroupsResourceWithRawResponse",
"AsyncGroupsResourceWithRawResponse",
"GroupsResourceWithStreamingResponse",
"AsyncGroupsResourceWithStreamingResponse",
+ "SettingsResource",
+ "AsyncSettingsResource",
+ "SettingsResourceWithRawResponse",
+ "AsyncSettingsResourceWithRawResponse",
+ "SettingsResourceWithStreamingResponse",
+ "AsyncSettingsResourceWithStreamingResponse",
"SendersResource",
"AsyncSendersResource",
"SendersResourceWithRawResponse",
"AsyncSendersResourceWithRawResponse",
"SendersResourceWithStreamingResponse",
"AsyncSendersResourceWithStreamingResponse",
+ "MemoriesResource",
+ "AsyncMemoriesResource",
+ "MemoriesResourceWithRawResponse",
+ "AsyncMemoriesResourceWithRawResponse",
+ "MemoriesResourceWithStreamingResponse",
+ "AsyncMemoriesResourceWithStreamingResponse",
"ObjectResource",
"AsyncObjectResource",
"ObjectResourceWithRawResponse",
"AsyncObjectResourceWithRawResponse",
"ObjectResourceWithStreamingResponse",
"AsyncObjectResourceWithStreamingResponse",
- "SettingsResource",
- "AsyncSettingsResource",
- "SettingsResourceWithRawResponse",
- "AsyncSettingsResourceWithRawResponse",
- "SettingsResourceWithStreamingResponse",
- "AsyncSettingsResourceWithStreamingResponse",
+ "TasksResource",
+ "AsyncTasksResource",
+ "TasksResourceWithRawResponse",
+ "AsyncTasksResourceWithRawResponse",
+ "TasksResourceWithStreamingResponse",
+ "AsyncTasksResourceWithStreamingResponse",
"V1Resource",
"AsyncV1Resource",
"V1ResourceWithRawResponse",
diff --git a/src/everos/resources/v1/groups.py b/src/everos/resources/v1/groups.py
index 874d33d..a4eb134 100644
--- a/src/everos/resources/v1/groups.py
+++ b/src/everos/resources/v1/groups.py
@@ -9,7 +9,7 @@
from ..._types import Body, Omit, Query, Headers, NotGiven, omit, not_given
from ..._utils import path_template, maybe_transform, async_maybe_transform
from ..._compat import cached_property
-from ...types.v1 import group_update_params, group_create_or_update_params
+from ...types.v1 import group_patch_params, group_create_params
from ..._resource import SyncAPIResource, AsyncAPIResource
from ..._response import (
to_raw_response_wrapper,
@@ -45,10 +45,12 @@ def with_streaming_response(self) -> GroupsResourceWithStreamingResponse:
"""
return GroupsResourceWithStreamingResponse(self)
- def retrieve(
+ def create(
self,
- group_id: str,
*,
+ group_id: str,
+ description: Optional[str] | Omit = omit,
+ name: Optional[str] | Omit = omit,
# Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
# The extra values given here take precedence over values defined on the client or passed to this method.
extra_headers: Headers | None = None,
@@ -57,9 +59,15 @@ def retrieve(
timeout: float | httpx.Timeout | None | NotGiven = not_given,
) -> GroupAPIResponse:
"""
- Retrieve a group's details by its group_id.
+ Create a new group or update an existing one (upsert by group_id).
Args:
+ group_id: Group identifier (unique)
+
+ description: Group description
+
+ name: Group display name
+
extra_headers: Send extra headers
extra_query: Add additional query parameters to the request
@@ -68,22 +76,26 @@ def retrieve(
timeout: Override the client-level default timeout for this request, in seconds
"""
- if not group_id:
- raise ValueError(f"Expected a non-empty value for `group_id` but received {group_id!r}")
- return self._get(
- path_template("/api/v1/groups/{group_id}", group_id=group_id),
+ return self._post(
+ "/api/v1/groups",
+ body=maybe_transform(
+ {
+ "group_id": group_id,
+ "description": description,
+ "name": name,
+ },
+ group_create_params.GroupCreateParams,
+ ),
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
),
cast_to=GroupAPIResponse,
)
- def update(
+ def retrieve(
self,
group_id: str,
*,
- description: Optional[str] | Omit = omit,
- name: Optional[str] | Omit = omit,
# Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
# The extra values given here take precedence over values defined on the client or passed to this method.
extra_headers: Headers | None = None,
@@ -91,16 +103,10 @@ def update(
extra_body: Body | None = None,
timeout: float | httpx.Timeout | None | NotGiven = not_given,
) -> GroupAPIResponse:
- """Partially update a group's fields.
-
- At least one of name or description must be
- provided.
+ """
+ Retrieve a group's details by its group_id.
Args:
- description: New group description
-
- name: New group display name
-
extra_headers: Send extra headers
extra_query: Add additional query parameters to the request
@@ -111,25 +117,18 @@ def update(
"""
if not group_id:
raise ValueError(f"Expected a non-empty value for `group_id` but received {group_id!r}")
- return self._patch(
+ return self._get(
path_template("/api/v1/groups/{group_id}", group_id=group_id),
- body=maybe_transform(
- {
- "description": description,
- "name": name,
- },
- group_update_params.GroupUpdateParams,
- ),
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
),
cast_to=GroupAPIResponse,
)
- def create_or_update(
+ def patch(
self,
- *,
group_id: str,
+ *,
description: Optional[str] | Omit = omit,
name: Optional[str] | Omit = omit,
# Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
@@ -139,15 +138,15 @@ def create_or_update(
extra_body: Body | None = None,
timeout: float | httpx.Timeout | None | NotGiven = not_given,
) -> GroupAPIResponse:
- """
- Create a new group or update an existing one (upsert by group_id).
+ """Partially update a group's fields.
- Args:
- group_id: Group identifier (unique)
+ At least one of name or description must be
+ provided.
- description: Group description
+ Args:
+ description: New group description
- name: Group display name
+ name: New group display name
extra_headers: Send extra headers
@@ -157,15 +156,16 @@ def create_or_update(
timeout: Override the client-level default timeout for this request, in seconds
"""
- return self._post(
- "/api/v1/groups",
+ if not group_id:
+ raise ValueError(f"Expected a non-empty value for `group_id` but received {group_id!r}")
+ return self._patch(
+ path_template("/api/v1/groups/{group_id}", group_id=group_id),
body=maybe_transform(
{
- "group_id": group_id,
"description": description,
"name": name,
},
- group_create_or_update_params.GroupCreateOrUpdateParams,
+ group_patch_params.GroupPatchParams,
),
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
@@ -196,10 +196,12 @@ def with_streaming_response(self) -> AsyncGroupsResourceWithStreamingResponse:
"""
return AsyncGroupsResourceWithStreamingResponse(self)
- async def retrieve(
+ async def create(
self,
- group_id: str,
*,
+ group_id: str,
+ description: Optional[str] | Omit = omit,
+ name: Optional[str] | Omit = omit,
# Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
# The extra values given here take precedence over values defined on the client or passed to this method.
extra_headers: Headers | None = None,
@@ -208,9 +210,15 @@ async def retrieve(
timeout: float | httpx.Timeout | None | NotGiven = not_given,
) -> GroupAPIResponse:
"""
- Retrieve a group's details by its group_id.
+ Create a new group or update an existing one (upsert by group_id).
Args:
+ group_id: Group identifier (unique)
+
+ description: Group description
+
+ name: Group display name
+
extra_headers: Send extra headers
extra_query: Add additional query parameters to the request
@@ -219,22 +227,26 @@ async def retrieve(
timeout: Override the client-level default timeout for this request, in seconds
"""
- if not group_id:
- raise ValueError(f"Expected a non-empty value for `group_id` but received {group_id!r}")
- return await self._get(
- path_template("/api/v1/groups/{group_id}", group_id=group_id),
+ return await self._post(
+ "/api/v1/groups",
+ body=await async_maybe_transform(
+ {
+ "group_id": group_id,
+ "description": description,
+ "name": name,
+ },
+ group_create_params.GroupCreateParams,
+ ),
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
),
cast_to=GroupAPIResponse,
)
- async def update(
+ async def retrieve(
self,
group_id: str,
*,
- description: Optional[str] | Omit = omit,
- name: Optional[str] | Omit = omit,
# Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
# The extra values given here take precedence over values defined on the client or passed to this method.
extra_headers: Headers | None = None,
@@ -242,16 +254,10 @@ async def update(
extra_body: Body | None = None,
timeout: float | httpx.Timeout | None | NotGiven = not_given,
) -> GroupAPIResponse:
- """Partially update a group's fields.
-
- At least one of name or description must be
- provided.
+ """
+ Retrieve a group's details by its group_id.
Args:
- description: New group description
-
- name: New group display name
-
extra_headers: Send extra headers
extra_query: Add additional query parameters to the request
@@ -262,25 +268,18 @@ async def update(
"""
if not group_id:
raise ValueError(f"Expected a non-empty value for `group_id` but received {group_id!r}")
- return await self._patch(
+ return await self._get(
path_template("/api/v1/groups/{group_id}", group_id=group_id),
- body=await async_maybe_transform(
- {
- "description": description,
- "name": name,
- },
- group_update_params.GroupUpdateParams,
- ),
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
),
cast_to=GroupAPIResponse,
)
- async def create_or_update(
+ async def patch(
self,
- *,
group_id: str,
+ *,
description: Optional[str] | Omit = omit,
name: Optional[str] | Omit = omit,
# Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
@@ -290,15 +289,15 @@ async def create_or_update(
extra_body: Body | None = None,
timeout: float | httpx.Timeout | None | NotGiven = not_given,
) -> GroupAPIResponse:
- """
- Create a new group or update an existing one (upsert by group_id).
+ """Partially update a group's fields.
- Args:
- group_id: Group identifier (unique)
+ At least one of name or description must be
+ provided.
- description: Group description
+ Args:
+ description: New group description
- name: Group display name
+ name: New group display name
extra_headers: Send extra headers
@@ -308,15 +307,16 @@ async def create_or_update(
timeout: Override the client-level default timeout for this request, in seconds
"""
- return await self._post(
- "/api/v1/groups",
+ if not group_id:
+ raise ValueError(f"Expected a non-empty value for `group_id` but received {group_id!r}")
+ return await self._patch(
+ path_template("/api/v1/groups/{group_id}", group_id=group_id),
body=await async_maybe_transform(
{
- "group_id": group_id,
"description": description,
"name": name,
},
- group_create_or_update_params.GroupCreateOrUpdateParams,
+ group_patch_params.GroupPatchParams,
),
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
@@ -329,14 +329,14 @@ class GroupsResourceWithRawResponse:
def __init__(self, groups: GroupsResource) -> None:
self._groups = groups
+ self.create = to_raw_response_wrapper(
+ groups.create,
+ )
self.retrieve = to_raw_response_wrapper(
groups.retrieve,
)
- self.update = to_raw_response_wrapper(
- groups.update,
- )
- self.create_or_update = to_raw_response_wrapper(
- groups.create_or_update,
+ self.patch = to_raw_response_wrapper(
+ groups.patch,
)
@@ -344,14 +344,14 @@ class AsyncGroupsResourceWithRawResponse:
def __init__(self, groups: AsyncGroupsResource) -> None:
self._groups = groups
+ self.create = async_to_raw_response_wrapper(
+ groups.create,
+ )
self.retrieve = async_to_raw_response_wrapper(
groups.retrieve,
)
- self.update = async_to_raw_response_wrapper(
- groups.update,
- )
- self.create_or_update = async_to_raw_response_wrapper(
- groups.create_or_update,
+ self.patch = async_to_raw_response_wrapper(
+ groups.patch,
)
@@ -359,14 +359,14 @@ class GroupsResourceWithStreamingResponse:
def __init__(self, groups: GroupsResource) -> None:
self._groups = groups
+ self.create = to_streamed_response_wrapper(
+ groups.create,
+ )
self.retrieve = to_streamed_response_wrapper(
groups.retrieve,
)
- self.update = to_streamed_response_wrapper(
- groups.update,
- )
- self.create_or_update = to_streamed_response_wrapper(
- groups.create_or_update,
+ self.patch = to_streamed_response_wrapper(
+ groups.patch,
)
@@ -374,12 +374,12 @@ class AsyncGroupsResourceWithStreamingResponse:
def __init__(self, groups: AsyncGroupsResource) -> None:
self._groups = groups
+ self.create = async_to_streamed_response_wrapper(
+ groups.create,
+ )
self.retrieve = async_to_streamed_response_wrapper(
groups.retrieve,
)
- self.update = async_to_streamed_response_wrapper(
- groups.update,
- )
- self.create_or_update = async_to_streamed_response_wrapper(
- groups.create_or_update,
+ self.patch = async_to_streamed_response_wrapper(
+ groups.patch,
)
diff --git a/src/everos/resources/v1/memories/__init__.py b/src/everos/resources/v1/memories/__init__.py
index 5642e4a..1e620a4 100644
--- a/src/everos/resources/v1/memories/__init__.py
+++ b/src/everos/resources/v1/memories/__init__.py
@@ -26,18 +26,18 @@
)
__all__ = [
- "GroupResource",
- "AsyncGroupResource",
- "GroupResourceWithRawResponse",
- "AsyncGroupResourceWithRawResponse",
- "GroupResourceWithStreamingResponse",
- "AsyncGroupResourceWithStreamingResponse",
"AgentResource",
"AsyncAgentResource",
"AgentResourceWithRawResponse",
"AsyncAgentResourceWithRawResponse",
"AgentResourceWithStreamingResponse",
"AsyncAgentResourceWithStreamingResponse",
+ "GroupResource",
+ "AsyncGroupResource",
+ "GroupResourceWithRawResponse",
+ "AsyncGroupResourceWithRawResponse",
+ "GroupResourceWithStreamingResponse",
+ "AsyncGroupResourceWithStreamingResponse",
"MemoriesResource",
"AsyncMemoriesResource",
"MemoriesResourceWithRawResponse",
diff --git a/src/everos/resources/v1/memories/agent.py b/src/everos/resources/v1/memories/agent.py
index cf20da1..5790b34 100644
--- a/src/everos/resources/v1/memories/agent.py
+++ b/src/everos/resources/v1/memories/agent.py
@@ -17,9 +17,10 @@
async_to_streamed_response_wrapper,
)
from ...._base_client import make_request_options
-from ....types.v1.memories import agent_flush_params, agent_create_params
+from ....types.v1.memories import agent_add_params, agent_flush_params
from ....types.v1.add_response import AddResponse
from ....types.v1.flush_response import FlushResponse
+from ....types.v1.memories.agent_message_item_param import AgentMessageItemParam
__all__ = ["AgentResource", "AsyncAgentResource"]
@@ -46,10 +47,10 @@ def with_streaming_response(self) -> AgentResourceWithStreamingResponse:
"""
return AgentResourceWithStreamingResponse(self)
- def create(
+ def add(
self,
*,
- messages: Iterable[agent_create_params.Message],
+ messages: Iterable[AgentMessageItemParam],
user_id: str,
async_mode: bool | Omit = omit,
session_id: Optional[str] | Omit = omit,
@@ -92,7 +93,7 @@ def create(
"async_mode": async_mode,
"session_id": session_id,
},
- agent_create_params.AgentCreateParams,
+ agent_add_params.AgentAddParams,
),
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
@@ -167,10 +168,10 @@ def with_streaming_response(self) -> AsyncAgentResourceWithStreamingResponse:
"""
return AsyncAgentResourceWithStreamingResponse(self)
- async def create(
+ async def add(
self,
*,
- messages: Iterable[agent_create_params.Message],
+ messages: Iterable[AgentMessageItemParam],
user_id: str,
async_mode: bool | Omit = omit,
session_id: Optional[str] | Omit = omit,
@@ -213,7 +214,7 @@ async def create(
"async_mode": async_mode,
"session_id": session_id,
},
- agent_create_params.AgentCreateParams,
+ agent_add_params.AgentAddParams,
),
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
@@ -270,8 +271,8 @@ class AgentResourceWithRawResponse:
def __init__(self, agent: AgentResource) -> None:
self._agent = agent
- self.create = to_raw_response_wrapper(
- agent.create,
+ self.add = to_raw_response_wrapper(
+ agent.add,
)
self.flush = to_raw_response_wrapper(
agent.flush,
@@ -282,8 +283,8 @@ class AsyncAgentResourceWithRawResponse:
def __init__(self, agent: AsyncAgentResource) -> None:
self._agent = agent
- self.create = async_to_raw_response_wrapper(
- agent.create,
+ self.add = async_to_raw_response_wrapper(
+ agent.add,
)
self.flush = async_to_raw_response_wrapper(
agent.flush,
@@ -294,8 +295,8 @@ class AgentResourceWithStreamingResponse:
def __init__(self, agent: AgentResource) -> None:
self._agent = agent
- self.create = to_streamed_response_wrapper(
- agent.create,
+ self.add = to_streamed_response_wrapper(
+ agent.add,
)
self.flush = to_streamed_response_wrapper(
agent.flush,
@@ -306,8 +307,8 @@ class AsyncAgentResourceWithStreamingResponse:
def __init__(self, agent: AsyncAgentResource) -> None:
self._agent = agent
- self.create = async_to_streamed_response_wrapper(
- agent.create,
+ self.add = async_to_streamed_response_wrapper(
+ agent.add,
)
self.flush = async_to_streamed_response_wrapper(
agent.flush,
diff --git a/src/everos/resources/v1/memories/group.py b/src/everos/resources/v1/memories/group.py
index 3643608..5350d16 100644
--- a/src/everos/resources/v1/memories/group.py
+++ b/src/everos/resources/v1/memories/group.py
@@ -17,9 +17,10 @@
async_to_streamed_response_wrapper,
)
from ...._base_client import make_request_options
-from ....types.v1.memories import group_flush_params, group_create_params
+from ....types.v1.memories import group_add_params, group_flush_params
from ....types.v1.add_response import AddResponse
from ....types.v1.flush_response import FlushResponse
+from ....types.v1.memories.group_message_item_param import GroupMessageItemParam
__all__ = ["GroupResource", "AsyncGroupResource"]
@@ -46,11 +47,11 @@ def with_streaming_response(self) -> GroupResourceWithStreamingResponse:
"""
return GroupResourceWithStreamingResponse(self)
- def create(
+ def add(
self,
*,
group_id: str,
- messages: Iterable[group_create_params.Message],
+ messages: Iterable[GroupMessageItemParam],
async_mode: bool | Omit = omit,
group_meta: Optional[Dict[str, object]] | Omit = omit,
# Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
@@ -92,7 +93,7 @@ def create(
"async_mode": async_mode,
"group_meta": group_meta,
},
- group_create_params.GroupCreateParams,
+ group_add_params.GroupAddParams,
),
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
@@ -157,11 +158,11 @@ def with_streaming_response(self) -> AsyncGroupResourceWithStreamingResponse:
"""
return AsyncGroupResourceWithStreamingResponse(self)
- async def create(
+ async def add(
self,
*,
group_id: str,
- messages: Iterable[group_create_params.Message],
+ messages: Iterable[GroupMessageItemParam],
async_mode: bool | Omit = omit,
group_meta: Optional[Dict[str, object]] | Omit = omit,
# Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
@@ -203,7 +204,7 @@ async def create(
"async_mode": async_mode,
"group_meta": group_meta,
},
- group_create_params.GroupCreateParams,
+ group_add_params.GroupAddParams,
),
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
@@ -250,8 +251,8 @@ class GroupResourceWithRawResponse:
def __init__(self, group: GroupResource) -> None:
self._group = group
- self.create = to_raw_response_wrapper(
- group.create,
+ self.add = to_raw_response_wrapper(
+ group.add,
)
self.flush = to_raw_response_wrapper(
group.flush,
@@ -262,8 +263,8 @@ class AsyncGroupResourceWithRawResponse:
def __init__(self, group: AsyncGroupResource) -> None:
self._group = group
- self.create = async_to_raw_response_wrapper(
- group.create,
+ self.add = async_to_raw_response_wrapper(
+ group.add,
)
self.flush = async_to_raw_response_wrapper(
group.flush,
@@ -274,8 +275,8 @@ class GroupResourceWithStreamingResponse:
def __init__(self, group: GroupResource) -> None:
self._group = group
- self.create = to_streamed_response_wrapper(
- group.create,
+ self.add = to_streamed_response_wrapper(
+ group.add,
)
self.flush = to_streamed_response_wrapper(
group.flush,
@@ -286,8 +287,8 @@ class AsyncGroupResourceWithStreamingResponse:
def __init__(self, group: AsyncGroupResource) -> None:
self._group = group
- self.create = async_to_streamed_response_wrapper(
- group.create,
+ self.add = async_to_streamed_response_wrapper(
+ group.add,
)
self.flush = async_to_streamed_response_wrapper(
group.flush,
diff --git a/src/everos/resources/v1/memories/memories.py b/src/everos/resources/v1/memories/memories.py
index 340bfc6..764a85c 100644
--- a/src/everos/resources/v1/memories/memories.py
+++ b/src/everos/resources/v1/memories/memories.py
@@ -27,9 +27,9 @@
from ...._utils import maybe_transform, async_maybe_transform
from ...._compat import cached_property
from ....types.v1 import (
+ memory_add_params,
memory_get_params,
memory_flush_params,
- memory_create_params,
memory_delete_params,
memory_search_params,
)
@@ -43,8 +43,9 @@
from ...._base_client import make_request_options
from ....types.v1.add_response import AddResponse
from ....types.v1.flush_response import FlushResponse
-from ....types.v1.memory_get_response import MemoryGetResponse
-from ....types.v1.memory_search_response import MemorySearchResponse
+from ....types.v1.message_item_param import MessageItemParam
+from ....types.v1.get_memories_response import GetMemoriesResponse
+from ....types.v1.search_memories_response import SearchMemoriesResponse
__all__ = ["MemoriesResource", "AsyncMemoriesResource"]
@@ -53,14 +54,14 @@ class MemoriesResource(SyncAPIResource):
"""Memory ingestion, retrieval, search, and deletion"""
@cached_property
- def group(self) -> GroupResource:
+ def agent(self) -> AgentResource:
"""Memory ingestion, retrieval, search, and deletion"""
- return GroupResource(self._client)
+ return AgentResource(self._client)
@cached_property
- def agent(self) -> AgentResource:
+ def group(self) -> GroupResource:
"""Memory ingestion, retrieval, search, and deletion"""
- return AgentResource(self._client)
+ return GroupResource(self._client)
@cached_property
def with_raw_response(self) -> MemoriesResourceWithRawResponse:
@@ -81,34 +82,43 @@ def with_streaming_response(self) -> MemoriesResourceWithStreamingResponse:
"""
return MemoriesResourceWithStreamingResponse(self)
- def create(
+ def delete(
self,
*,
- messages: Iterable[memory_create_params.Message],
- user_id: str,
- async_mode: bool | Omit = omit,
+ group_id: Optional[str] | Omit = omit,
+ memory_id: Optional[str] | Omit = omit,
+ sender_id: Optional[str] | Omit = omit,
session_id: Optional[str] | Omit = omit,
+ user_id: Optional[str] | Omit = omit,
# Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
# The extra values given here take precedence over values defined on the client or passed to this method.
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
timeout: float | httpx.Timeout | None | NotGiven = not_given,
- ) -> AddResponse:
- """Store batch messages into personal memory space.
+ ) -> None:
+ """
+ Soft delete memories by ID or by filter conditions.
- Messages are accumulated and
- boundary detection triggers memory extraction automatically.
+ Two mutually exclusive modes:
+
+ - **By ID**: provide `memory_id` only (no other fields allowed)
+ - **By filters**: provide `user_id` and/or `group_id` (session_id, sender_id
+ optional)
+
+ Filter semantics: omitted fields skip filtering; `null` or `""` matches
+ null/empty records; string value = exact match.
Args:
- messages: Batch message array (1-500 items)
+ group_id: Group ID scope for batch delete
- user_id: Owner user ID
+ memory_id: MemCell ID for single delete
- async_mode: Enable async processing. When true, returns 202 with task_id; when false,
- processes synchronously and returns 200.
+ sender_id: Sender filter, matches participants array (batch delete only)
- session_id: Session identifier
+ session_id: Session filter (batch delete only)
+
+ user_id: User ID scope for batch delete
extra_headers: Send extra headers
@@ -118,60 +128,53 @@ def create(
timeout: Override the client-level default timeout for this request, in seconds
"""
+ extra_headers = {"Accept": "*/*", **(extra_headers or {})}
return self._post(
- "/api/v1/memories",
+ "/api/v1/memories/delete",
body=maybe_transform(
{
- "messages": messages,
- "user_id": user_id,
- "async_mode": async_mode,
+ "group_id": group_id,
+ "memory_id": memory_id,
+ "sender_id": sender_id,
"session_id": session_id,
+ "user_id": user_id,
},
- memory_create_params.MemoryCreateParams,
+ memory_delete_params.MemoryDeleteParams,
),
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
),
- cast_to=AddResponse,
+ cast_to=NoneType,
)
- def delete(
+ def add(
self,
*,
- group_id: Optional[str] | Omit = omit,
- memory_id: Optional[str] | Omit = omit,
- sender_id: Optional[str] | Omit = omit,
+ messages: Iterable[MessageItemParam],
+ user_id: str,
+ async_mode: bool | Omit = omit,
session_id: Optional[str] | Omit = omit,
- user_id: Optional[str] | Omit = omit,
# Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
# The extra values given here take precedence over values defined on the client or passed to this method.
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
timeout: float | httpx.Timeout | None | NotGiven = not_given,
- ) -> None:
- """
- Soft delete memories by ID or by filter conditions.
-
- Two mutually exclusive modes:
-
- - **By ID**: provide `memory_id` only (no other fields allowed)
- - **By filters**: provide `user_id` and/or `group_id` (session_id, sender_id
- optional)
+ ) -> AddResponse:
+ """Store batch messages into personal memory space.
- Filter semantics: omitted fields skip filtering; `null` or `""` matches
- null/empty records; string value = exact match.
+ Messages are accumulated and
+ boundary detection triggers memory extraction automatically.
Args:
- group_id: Group ID scope for batch delete
-
- memory_id: MemCell ID for single delete
+ messages: Batch message array (1-500 items)
- sender_id: Sender filter, matches participants array (batch delete only)
+ user_id: Owner user ID
- session_id: Session filter (batch delete only)
+ async_mode: Enable async processing. When true, returns 202 with task_id; when false,
+ processes synchronously and returns 200.
- user_id: User ID scope for batch delete
+ session_id: Session identifier
extra_headers: Send extra headers
@@ -181,23 +184,21 @@ def delete(
timeout: Override the client-level default timeout for this request, in seconds
"""
- extra_headers = {"Accept": "*/*", **(extra_headers or {})}
return self._post(
- "/api/v1/memories/delete",
+ "/api/v1/memories",
body=maybe_transform(
{
- "group_id": group_id,
- "memory_id": memory_id,
- "sender_id": sender_id,
- "session_id": session_id,
+ "messages": messages,
"user_id": user_id,
+ "async_mode": async_mode,
+ "session_id": session_id,
},
- memory_delete_params.MemoryDeleteParams,
+ memory_add_params.MemoryAddParams,
),
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
),
- cast_to=NoneType,
+ cast_to=AddResponse,
)
def flush(
@@ -260,7 +261,7 @@ def get(
extra_query: Query | None = None,
extra_body: Body | None = None,
timeout: float | httpx.Timeout | None | NotGiven = not_given,
- ) -> MemoryGetResponse:
+ ) -> GetMemoriesResponse:
"""Retrieve structured memories using filters DSL with pagination.
Supports
@@ -327,7 +328,7 @@ def get(
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
),
- cast_to=MemoryGetResponse,
+ cast_to=GetMemoriesResponse,
)
def search(
@@ -346,7 +347,7 @@ def search(
extra_query: Query | None = None,
extra_body: Body | None = None,
timeout: float | httpx.Timeout | None | NotGiven = not_given,
- ) -> MemorySearchResponse:
+ ) -> SearchMemoriesResponse:
"""
Unified memory search endpoint supporting multiple memory types and retrieval
methods.
@@ -412,7 +413,7 @@ def search(
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
),
- cast_to=MemorySearchResponse,
+ cast_to=SearchMemoriesResponse,
)
@@ -420,14 +421,14 @@ class AsyncMemoriesResource(AsyncAPIResource):
"""Memory ingestion, retrieval, search, and deletion"""
@cached_property
- def group(self) -> AsyncGroupResource:
+ def agent(self) -> AsyncAgentResource:
"""Memory ingestion, retrieval, search, and deletion"""
- return AsyncGroupResource(self._client)
+ return AsyncAgentResource(self._client)
@cached_property
- def agent(self) -> AsyncAgentResource:
+ def group(self) -> AsyncGroupResource:
"""Memory ingestion, retrieval, search, and deletion"""
- return AsyncAgentResource(self._client)
+ return AsyncGroupResource(self._client)
@cached_property
def with_raw_response(self) -> AsyncMemoriesResourceWithRawResponse:
@@ -448,34 +449,43 @@ def with_streaming_response(self) -> AsyncMemoriesResourceWithStreamingResponse:
"""
return AsyncMemoriesResourceWithStreamingResponse(self)
- async def create(
+ async def delete(
self,
*,
- messages: Iterable[memory_create_params.Message],
- user_id: str,
- async_mode: bool | Omit = omit,
+ group_id: Optional[str] | Omit = omit,
+ memory_id: Optional[str] | Omit = omit,
+ sender_id: Optional[str] | Omit = omit,
session_id: Optional[str] | Omit = omit,
+ user_id: Optional[str] | Omit = omit,
# Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
# The extra values given here take precedence over values defined on the client or passed to this method.
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
timeout: float | httpx.Timeout | None | NotGiven = not_given,
- ) -> AddResponse:
- """Store batch messages into personal memory space.
+ ) -> None:
+ """
+ Soft delete memories by ID or by filter conditions.
- Messages are accumulated and
- boundary detection triggers memory extraction automatically.
+ Two mutually exclusive modes:
+
+ - **By ID**: provide `memory_id` only (no other fields allowed)
+ - **By filters**: provide `user_id` and/or `group_id` (session_id, sender_id
+ optional)
+
+ Filter semantics: omitted fields skip filtering; `null` or `""` matches
+ null/empty records; string value = exact match.
Args:
- messages: Batch message array (1-500 items)
+ group_id: Group ID scope for batch delete
- user_id: Owner user ID
+ memory_id: MemCell ID for single delete
- async_mode: Enable async processing. When true, returns 202 with task_id; when false,
- processes synchronously and returns 200.
+ sender_id: Sender filter, matches participants array (batch delete only)
- session_id: Session identifier
+ session_id: Session filter (batch delete only)
+
+ user_id: User ID scope for batch delete
extra_headers: Send extra headers
@@ -485,60 +495,53 @@ async def create(
timeout: Override the client-level default timeout for this request, in seconds
"""
+ extra_headers = {"Accept": "*/*", **(extra_headers or {})}
return await self._post(
- "/api/v1/memories",
+ "/api/v1/memories/delete",
body=await async_maybe_transform(
{
- "messages": messages,
- "user_id": user_id,
- "async_mode": async_mode,
+ "group_id": group_id,
+ "memory_id": memory_id,
+ "sender_id": sender_id,
"session_id": session_id,
+ "user_id": user_id,
},
- memory_create_params.MemoryCreateParams,
+ memory_delete_params.MemoryDeleteParams,
),
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
),
- cast_to=AddResponse,
+ cast_to=NoneType,
)
- async def delete(
+ async def add(
self,
*,
- group_id: Optional[str] | Omit = omit,
- memory_id: Optional[str] | Omit = omit,
- sender_id: Optional[str] | Omit = omit,
+ messages: Iterable[MessageItemParam],
+ user_id: str,
+ async_mode: bool | Omit = omit,
session_id: Optional[str] | Omit = omit,
- user_id: Optional[str] | Omit = omit,
# Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
# The extra values given here take precedence over values defined on the client or passed to this method.
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
timeout: float | httpx.Timeout | None | NotGiven = not_given,
- ) -> None:
- """
- Soft delete memories by ID or by filter conditions.
-
- Two mutually exclusive modes:
-
- - **By ID**: provide `memory_id` only (no other fields allowed)
- - **By filters**: provide `user_id` and/or `group_id` (session_id, sender_id
- optional)
+ ) -> AddResponse:
+ """Store batch messages into personal memory space.
- Filter semantics: omitted fields skip filtering; `null` or `""` matches
- null/empty records; string value = exact match.
+ Messages are accumulated and
+ boundary detection triggers memory extraction automatically.
Args:
- group_id: Group ID scope for batch delete
-
- memory_id: MemCell ID for single delete
+ messages: Batch message array (1-500 items)
- sender_id: Sender filter, matches participants array (batch delete only)
+ user_id: Owner user ID
- session_id: Session filter (batch delete only)
+ async_mode: Enable async processing. When true, returns 202 with task_id; when false,
+ processes synchronously and returns 200.
- user_id: User ID scope for batch delete
+ session_id: Session identifier
extra_headers: Send extra headers
@@ -548,23 +551,21 @@ async def delete(
timeout: Override the client-level default timeout for this request, in seconds
"""
- extra_headers = {"Accept": "*/*", **(extra_headers or {})}
return await self._post(
- "/api/v1/memories/delete",
+ "/api/v1/memories",
body=await async_maybe_transform(
{
- "group_id": group_id,
- "memory_id": memory_id,
- "sender_id": sender_id,
- "session_id": session_id,
+ "messages": messages,
"user_id": user_id,
+ "async_mode": async_mode,
+ "session_id": session_id,
},
- memory_delete_params.MemoryDeleteParams,
+ memory_add_params.MemoryAddParams,
),
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
),
- cast_to=NoneType,
+ cast_to=AddResponse,
)
async def flush(
@@ -627,7 +628,7 @@ async def get(
extra_query: Query | None = None,
extra_body: Body | None = None,
timeout: float | httpx.Timeout | None | NotGiven = not_given,
- ) -> MemoryGetResponse:
+ ) -> GetMemoriesResponse:
"""Retrieve structured memories using filters DSL with pagination.
Supports
@@ -694,7 +695,7 @@ async def get(
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
),
- cast_to=MemoryGetResponse,
+ cast_to=GetMemoriesResponse,
)
async def search(
@@ -713,7 +714,7 @@ async def search(
extra_query: Query | None = None,
extra_body: Body | None = None,
timeout: float | httpx.Timeout | None | NotGiven = not_given,
- ) -> MemorySearchResponse:
+ ) -> SearchMemoriesResponse:
"""
Unified memory search endpoint supporting multiple memory types and retrieval
methods.
@@ -779,7 +780,7 @@ async def search(
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
),
- cast_to=MemorySearchResponse,
+ cast_to=SearchMemoriesResponse,
)
@@ -787,12 +788,12 @@ class MemoriesResourceWithRawResponse:
def __init__(self, memories: MemoriesResource) -> None:
self._memories = memories
- self.create = to_raw_response_wrapper(
- memories.create,
- )
self.delete = to_raw_response_wrapper(
memories.delete,
)
+ self.add = to_raw_response_wrapper(
+ memories.add,
+ )
self.flush = to_raw_response_wrapper(
memories.flush,
)
@@ -804,26 +805,26 @@ def __init__(self, memories: MemoriesResource) -> None:
)
@cached_property
- def group(self) -> GroupResourceWithRawResponse:
+ def agent(self) -> AgentResourceWithRawResponse:
"""Memory ingestion, retrieval, search, and deletion"""
- return GroupResourceWithRawResponse(self._memories.group)
+ return AgentResourceWithRawResponse(self._memories.agent)
@cached_property
- def agent(self) -> AgentResourceWithRawResponse:
+ def group(self) -> GroupResourceWithRawResponse:
"""Memory ingestion, retrieval, search, and deletion"""
- return AgentResourceWithRawResponse(self._memories.agent)
+ return GroupResourceWithRawResponse(self._memories.group)
class AsyncMemoriesResourceWithRawResponse:
def __init__(self, memories: AsyncMemoriesResource) -> None:
self._memories = memories
- self.create = async_to_raw_response_wrapper(
- memories.create,
- )
self.delete = async_to_raw_response_wrapper(
memories.delete,
)
+ self.add = async_to_raw_response_wrapper(
+ memories.add,
+ )
self.flush = async_to_raw_response_wrapper(
memories.flush,
)
@@ -835,26 +836,26 @@ def __init__(self, memories: AsyncMemoriesResource) -> None:
)
@cached_property
- def group(self) -> AsyncGroupResourceWithRawResponse:
+ def agent(self) -> AsyncAgentResourceWithRawResponse:
"""Memory ingestion, retrieval, search, and deletion"""
- return AsyncGroupResourceWithRawResponse(self._memories.group)
+ return AsyncAgentResourceWithRawResponse(self._memories.agent)
@cached_property
- def agent(self) -> AsyncAgentResourceWithRawResponse:
+ def group(self) -> AsyncGroupResourceWithRawResponse:
"""Memory ingestion, retrieval, search, and deletion"""
- return AsyncAgentResourceWithRawResponse(self._memories.agent)
+ return AsyncGroupResourceWithRawResponse(self._memories.group)
class MemoriesResourceWithStreamingResponse:
def __init__(self, memories: MemoriesResource) -> None:
self._memories = memories
- self.create = to_streamed_response_wrapper(
- memories.create,
- )
self.delete = to_streamed_response_wrapper(
memories.delete,
)
+ self.add = to_streamed_response_wrapper(
+ memories.add,
+ )
self.flush = to_streamed_response_wrapper(
memories.flush,
)
@@ -866,26 +867,26 @@ def __init__(self, memories: MemoriesResource) -> None:
)
@cached_property
- def group(self) -> GroupResourceWithStreamingResponse:
+ def agent(self) -> AgentResourceWithStreamingResponse:
"""Memory ingestion, retrieval, search, and deletion"""
- return GroupResourceWithStreamingResponse(self._memories.group)
+ return AgentResourceWithStreamingResponse(self._memories.agent)
@cached_property
- def agent(self) -> AgentResourceWithStreamingResponse:
+ def group(self) -> GroupResourceWithStreamingResponse:
"""Memory ingestion, retrieval, search, and deletion"""
- return AgentResourceWithStreamingResponse(self._memories.agent)
+ return GroupResourceWithStreamingResponse(self._memories.group)
class AsyncMemoriesResourceWithStreamingResponse:
def __init__(self, memories: AsyncMemoriesResource) -> None:
self._memories = memories
- self.create = async_to_streamed_response_wrapper(
- memories.create,
- )
self.delete = async_to_streamed_response_wrapper(
memories.delete,
)
+ self.add = async_to_streamed_response_wrapper(
+ memories.add,
+ )
self.flush = async_to_streamed_response_wrapper(
memories.flush,
)
@@ -897,11 +898,11 @@ def __init__(self, memories: AsyncMemoriesResource) -> None:
)
@cached_property
- def group(self) -> AsyncGroupResourceWithStreamingResponse:
+ def agent(self) -> AsyncAgentResourceWithStreamingResponse:
"""Memory ingestion, retrieval, search, and deletion"""
- return AsyncGroupResourceWithStreamingResponse(self._memories.group)
+ return AsyncAgentResourceWithStreamingResponse(self._memories.agent)
@cached_property
- def agent(self) -> AsyncAgentResourceWithStreamingResponse:
+ def group(self) -> AsyncGroupResourceWithStreamingResponse:
"""Memory ingestion, retrieval, search, and deletion"""
- return AsyncAgentResourceWithStreamingResponse(self._memories.agent)
+ return AsyncGroupResourceWithStreamingResponse(self._memories.group)
diff --git a/src/everos/resources/v1/object.py b/src/everos/resources/v1/object.py
index a4dc96a..0c81c2e 100644
--- a/src/everos/resources/v1/object.py
+++ b/src/everos/resources/v1/object.py
@@ -9,7 +9,7 @@
from ..._types import Body, Query, Headers, NotGiven, not_given
from ..._utils import maybe_transform, async_maybe_transform
from ..._compat import cached_property
-from ...types.v1 import object_get_presigned_url_params
+from ...types.v1 import object_sign_params
from ..._resource import SyncAPIResource, AsyncAPIResource
from ..._response import (
to_raw_response_wrapper,
@@ -18,7 +18,8 @@
async_to_streamed_response_wrapper,
)
from ..._base_client import make_request_options
-from ...types.v1.object_get_presigned_url_response import ObjectGetPresignedURLResponse
+from ...types.v1.object_sign_response import ObjectSignResponse
+from ...types.v1.object_sign_item_request_param import ObjectSignItemRequestParam
__all__ = ["ObjectResource", "AsyncObjectResource"]
@@ -45,17 +46,17 @@ def with_streaming_response(self) -> ObjectResourceWithStreamingResponse:
"""
return ObjectResourceWithStreamingResponse(self)
- def get_presigned_url(
+ def sign(
self,
*,
- object_list: Iterable[object_get_presigned_url_params.ObjectList],
+ object_list: Iterable[ObjectSignItemRequestParam],
# Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
# The extra values given here take precedence over values defined on the client or passed to this method.
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
timeout: float | httpx.Timeout | None | NotGiven = not_given,
- ) -> ObjectGetPresignedURLResponse:
+ ) -> ObjectSignResponse:
"""Generate a pre-signed S3 upload URL for multimodal content.
Gateway
@@ -79,13 +80,11 @@ def get_presigned_url(
"""
return self._post(
"/api/v1/object/sign",
- body=maybe_transform(
- {"object_list": object_list}, object_get_presigned_url_params.ObjectGetPresignedURLParams
- ),
+ body=maybe_transform({"object_list": object_list}, object_sign_params.ObjectSignParams),
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
),
- cast_to=ObjectGetPresignedURLResponse,
+ cast_to=ObjectSignResponse,
)
@@ -111,17 +110,17 @@ def with_streaming_response(self) -> AsyncObjectResourceWithStreamingResponse:
"""
return AsyncObjectResourceWithStreamingResponse(self)
- async def get_presigned_url(
+ async def sign(
self,
*,
- object_list: Iterable[object_get_presigned_url_params.ObjectList],
+ object_list: Iterable[ObjectSignItemRequestParam],
# Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
# The extra values given here take precedence over values defined on the client or passed to this method.
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
timeout: float | httpx.Timeout | None | NotGiven = not_given,
- ) -> ObjectGetPresignedURLResponse:
+ ) -> ObjectSignResponse:
"""Generate a pre-signed S3 upload URL for multimodal content.
Gateway
@@ -145,13 +144,11 @@ async def get_presigned_url(
"""
return await self._post(
"/api/v1/object/sign",
- body=await async_maybe_transform(
- {"object_list": object_list}, object_get_presigned_url_params.ObjectGetPresignedURLParams
- ),
+ body=await async_maybe_transform({"object_list": object_list}, object_sign_params.ObjectSignParams),
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
),
- cast_to=ObjectGetPresignedURLResponse,
+ cast_to=ObjectSignResponse,
)
@@ -159,8 +156,8 @@ class ObjectResourceWithRawResponse:
def __init__(self, object: ObjectResource) -> None:
self._object = object
- self.get_presigned_url = to_raw_response_wrapper(
- object.get_presigned_url,
+ self.sign = to_raw_response_wrapper(
+ object.sign,
)
@@ -168,8 +165,8 @@ class AsyncObjectResourceWithRawResponse:
def __init__(self, object: AsyncObjectResource) -> None:
self._object = object
- self.get_presigned_url = async_to_raw_response_wrapper(
- object.get_presigned_url,
+ self.sign = async_to_raw_response_wrapper(
+ object.sign,
)
@@ -177,8 +174,8 @@ class ObjectResourceWithStreamingResponse:
def __init__(self, object: ObjectResource) -> None:
self._object = object
- self.get_presigned_url = to_streamed_response_wrapper(
- object.get_presigned_url,
+ self.sign = to_streamed_response_wrapper(
+ object.sign,
)
@@ -186,6 +183,6 @@ class AsyncObjectResourceWithStreamingResponse:
def __init__(self, object: AsyncObjectResource) -> None:
self._object = object
- self.get_presigned_url = async_to_streamed_response_wrapper(
- object.get_presigned_url,
+ self.sign = async_to_streamed_response_wrapper(
+ object.sign,
)
diff --git a/src/everos/resources/v1/senders.py b/src/everos/resources/v1/senders.py
index f4449c2..96f260c 100644
--- a/src/everos/resources/v1/senders.py
+++ b/src/everos/resources/v1/senders.py
@@ -9,7 +9,7 @@
from ..._types import Body, Omit, Query, Headers, NotGiven, omit, not_given
from ..._utils import path_template, maybe_transform, async_maybe_transform
from ..._compat import cached_property
-from ...types.v1 import sender_update_params, sender_create_or_update_params
+from ...types.v1 import sender_patch_params, sender_create_params
from ..._resource import SyncAPIResource, AsyncAPIResource
from ..._response import (
to_raw_response_wrapper,
@@ -45,10 +45,11 @@ def with_streaming_response(self) -> SendersResourceWithStreamingResponse:
"""
return SendersResourceWithStreamingResponse(self)
- def retrieve(
+ def create(
self,
- sender_id: str,
*,
+ sender_id: str,
+ name: Optional[str] | Omit = omit,
# Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
# The extra values given here take precedence over values defined on the client or passed to this method.
extra_headers: Headers | None = None,
@@ -57,9 +58,13 @@ def retrieve(
timeout: float | httpx.Timeout | None | NotGiven = not_given,
) -> SenderAPIResponse:
"""
- Retrieve a sender's details by its sender_id.
+ Create a new sender or update an existing one (upsert by sender_id).
Args:
+ sender_id: Sender identifier (unique)
+
+ name: Sender display name
+
extra_headers: Send extra headers
extra_query: Add additional query parameters to the request
@@ -68,21 +73,25 @@ def retrieve(
timeout: Override the client-level default timeout for this request, in seconds
"""
- if not sender_id:
- raise ValueError(f"Expected a non-empty value for `sender_id` but received {sender_id!r}")
- return self._get(
- path_template("/api/v1/senders/{sender_id}", sender_id=sender_id),
+ return self._post(
+ "/api/v1/senders",
+ body=maybe_transform(
+ {
+ "sender_id": sender_id,
+ "name": name,
+ },
+ sender_create_params.SenderCreateParams,
+ ),
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
),
cast_to=SenderAPIResponse,
)
- def update(
+ def retrieve(
self,
sender_id: str,
*,
- name: Optional[str] | Omit = omit,
# Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
# The extra values given here take precedence over values defined on the client or passed to this method.
extra_headers: Headers | None = None,
@@ -91,11 +100,9 @@ def update(
timeout: float | httpx.Timeout | None | NotGiven = not_given,
) -> SenderAPIResponse:
"""
- Update a sender's display name.
+ Retrieve a sender's details by its sender_id.
Args:
- name: New sender display name
-
extra_headers: Send extra headers
extra_query: Add additional query parameters to the request
@@ -106,19 +113,18 @@ def update(
"""
if not sender_id:
raise ValueError(f"Expected a non-empty value for `sender_id` but received {sender_id!r}")
- return self._patch(
+ return self._get(
path_template("/api/v1/senders/{sender_id}", sender_id=sender_id),
- body=maybe_transform({"name": name}, sender_update_params.SenderUpdateParams),
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
),
cast_to=SenderAPIResponse,
)
- def create_or_update(
+ def patch(
self,
- *,
sender_id: str,
+ *,
name: Optional[str] | Omit = omit,
# Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
# The extra values given here take precedence over values defined on the client or passed to this method.
@@ -128,12 +134,10 @@ def create_or_update(
timeout: float | httpx.Timeout | None | NotGiven = not_given,
) -> SenderAPIResponse:
"""
- Create a new sender or update an existing one (upsert by sender_id).
+ Update a sender's display name.
Args:
- sender_id: Sender identifier (unique)
-
- name: Sender display name
+ name: New sender display name
extra_headers: Send extra headers
@@ -143,15 +147,11 @@ def create_or_update(
timeout: Override the client-level default timeout for this request, in seconds
"""
- return self._post(
- "/api/v1/senders",
- body=maybe_transform(
- {
- "sender_id": sender_id,
- "name": name,
- },
- sender_create_or_update_params.SenderCreateOrUpdateParams,
- ),
+ if not sender_id:
+ raise ValueError(f"Expected a non-empty value for `sender_id` but received {sender_id!r}")
+ return self._patch(
+ path_template("/api/v1/senders/{sender_id}", sender_id=sender_id),
+ body=maybe_transform({"name": name}, sender_patch_params.SenderPatchParams),
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
),
@@ -181,10 +181,11 @@ def with_streaming_response(self) -> AsyncSendersResourceWithStreamingResponse:
"""
return AsyncSendersResourceWithStreamingResponse(self)
- async def retrieve(
+ async def create(
self,
- sender_id: str,
*,
+ sender_id: str,
+ name: Optional[str] | Omit = omit,
# Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
# The extra values given here take precedence over values defined on the client or passed to this method.
extra_headers: Headers | None = None,
@@ -193,9 +194,13 @@ async def retrieve(
timeout: float | httpx.Timeout | None | NotGiven = not_given,
) -> SenderAPIResponse:
"""
- Retrieve a sender's details by its sender_id.
+ Create a new sender or update an existing one (upsert by sender_id).
Args:
+ sender_id: Sender identifier (unique)
+
+ name: Sender display name
+
extra_headers: Send extra headers
extra_query: Add additional query parameters to the request
@@ -204,21 +209,25 @@ async def retrieve(
timeout: Override the client-level default timeout for this request, in seconds
"""
- if not sender_id:
- raise ValueError(f"Expected a non-empty value for `sender_id` but received {sender_id!r}")
- return await self._get(
- path_template("/api/v1/senders/{sender_id}", sender_id=sender_id),
+ return await self._post(
+ "/api/v1/senders",
+ body=await async_maybe_transform(
+ {
+ "sender_id": sender_id,
+ "name": name,
+ },
+ sender_create_params.SenderCreateParams,
+ ),
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
),
cast_to=SenderAPIResponse,
)
- async def update(
+ async def retrieve(
self,
sender_id: str,
*,
- name: Optional[str] | Omit = omit,
# Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
# The extra values given here take precedence over values defined on the client or passed to this method.
extra_headers: Headers | None = None,
@@ -227,11 +236,9 @@ async def update(
timeout: float | httpx.Timeout | None | NotGiven = not_given,
) -> SenderAPIResponse:
"""
- Update a sender's display name.
+ Retrieve a sender's details by its sender_id.
Args:
- name: New sender display name
-
extra_headers: Send extra headers
extra_query: Add additional query parameters to the request
@@ -242,19 +249,18 @@ async def update(
"""
if not sender_id:
raise ValueError(f"Expected a non-empty value for `sender_id` but received {sender_id!r}")
- return await self._patch(
+ return await self._get(
path_template("/api/v1/senders/{sender_id}", sender_id=sender_id),
- body=await async_maybe_transform({"name": name}, sender_update_params.SenderUpdateParams),
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
),
cast_to=SenderAPIResponse,
)
- async def create_or_update(
+ async def patch(
self,
- *,
sender_id: str,
+ *,
name: Optional[str] | Omit = omit,
# Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
# The extra values given here take precedence over values defined on the client or passed to this method.
@@ -264,12 +270,10 @@ async def create_or_update(
timeout: float | httpx.Timeout | None | NotGiven = not_given,
) -> SenderAPIResponse:
"""
- Create a new sender or update an existing one (upsert by sender_id).
+ Update a sender's display name.
Args:
- sender_id: Sender identifier (unique)
-
- name: Sender display name
+ name: New sender display name
extra_headers: Send extra headers
@@ -279,15 +283,11 @@ async def create_or_update(
timeout: Override the client-level default timeout for this request, in seconds
"""
- return await self._post(
- "/api/v1/senders",
- body=await async_maybe_transform(
- {
- "sender_id": sender_id,
- "name": name,
- },
- sender_create_or_update_params.SenderCreateOrUpdateParams,
- ),
+ if not sender_id:
+ raise ValueError(f"Expected a non-empty value for `sender_id` but received {sender_id!r}")
+ return await self._patch(
+ path_template("/api/v1/senders/{sender_id}", sender_id=sender_id),
+ body=await async_maybe_transform({"name": name}, sender_patch_params.SenderPatchParams),
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
),
@@ -299,14 +299,14 @@ class SendersResourceWithRawResponse:
def __init__(self, senders: SendersResource) -> None:
self._senders = senders
+ self.create = to_raw_response_wrapper(
+ senders.create,
+ )
self.retrieve = to_raw_response_wrapper(
senders.retrieve,
)
- self.update = to_raw_response_wrapper(
- senders.update,
- )
- self.create_or_update = to_raw_response_wrapper(
- senders.create_or_update,
+ self.patch = to_raw_response_wrapper(
+ senders.patch,
)
@@ -314,14 +314,14 @@ class AsyncSendersResourceWithRawResponse:
def __init__(self, senders: AsyncSendersResource) -> None:
self._senders = senders
+ self.create = async_to_raw_response_wrapper(
+ senders.create,
+ )
self.retrieve = async_to_raw_response_wrapper(
senders.retrieve,
)
- self.update = async_to_raw_response_wrapper(
- senders.update,
- )
- self.create_or_update = async_to_raw_response_wrapper(
- senders.create_or_update,
+ self.patch = async_to_raw_response_wrapper(
+ senders.patch,
)
@@ -329,14 +329,14 @@ class SendersResourceWithStreamingResponse:
def __init__(self, senders: SendersResource) -> None:
self._senders = senders
+ self.create = to_streamed_response_wrapper(
+ senders.create,
+ )
self.retrieve = to_streamed_response_wrapper(
senders.retrieve,
)
- self.update = to_streamed_response_wrapper(
- senders.update,
- )
- self.create_or_update = to_streamed_response_wrapper(
- senders.create_or_update,
+ self.patch = to_streamed_response_wrapper(
+ senders.patch,
)
@@ -344,12 +344,12 @@ class AsyncSendersResourceWithStreamingResponse:
def __init__(self, senders: AsyncSendersResource) -> None:
self._senders = senders
+ self.create = async_to_streamed_response_wrapper(
+ senders.create,
+ )
self.retrieve = async_to_streamed_response_wrapper(
senders.retrieve,
)
- self.update = async_to_streamed_response_wrapper(
- senders.update,
- )
- self.create_or_update = async_to_streamed_response_wrapper(
- senders.create_or_update,
+ self.patch = async_to_streamed_response_wrapper(
+ senders.patch,
)
diff --git a/src/everos/resources/v1/settings.py b/src/everos/resources/v1/settings.py
index 5e8b839..4ca55e1 100644
--- a/src/everos/resources/v1/settings.py
+++ b/src/everos/resources/v1/settings.py
@@ -20,6 +20,7 @@
)
from ..._base_client import make_request_options
from ...types.v1.settings_api_response import SettingsAPIResponse
+from ...types.v1.llm_custom_setting_param import LlmCustomSettingParam
__all__ = ["SettingsResource", "AsyncSettingsResource"]
@@ -70,7 +71,7 @@ def update(
*,
boundary_detection_timeout: Optional[int] | Omit = omit,
extraction_mode: Optional[Literal["default", "pro"]] | Omit = omit,
- llm_custom_setting: Optional[setting_update_params.LlmCustomSetting] | Omit = omit,
+ llm_custom_setting: Optional[LlmCustomSettingParam] | Omit = omit,
offline_profile_extraction_interval: Optional[int] | Omit = omit,
timezone: Optional[str] | Omit = omit,
# Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
@@ -169,7 +170,7 @@ async def update(
*,
boundary_detection_timeout: Optional[int] | Omit = omit,
extraction_mode: Optional[Literal["default", "pro"]] | Omit = omit,
- llm_custom_setting: Optional[setting_update_params.LlmCustomSetting] | Omit = omit,
+ llm_custom_setting: Optional[LlmCustomSettingParam] | Omit = omit,
offline_profile_extraction_interval: Optional[int] | Omit = omit,
timezone: Optional[str] | Omit = omit,
# Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
diff --git a/src/everos/resources/v1/tasks.py b/src/everos/resources/v1/tasks.py
new file mode 100644
index 0000000..8ae9b6c
--- /dev/null
+++ b/src/everos/resources/v1/tasks.py
@@ -0,0 +1,174 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+import httpx
+
+from ..._types import Body, Query, Headers, NotGiven, not_given
+from ..._utils import path_template
+from ..._compat import cached_property
+from ..._resource import SyncAPIResource, AsyncAPIResource
+from ..._response import (
+ to_raw_response_wrapper,
+ to_streamed_response_wrapper,
+ async_to_raw_response_wrapper,
+ async_to_streamed_response_wrapper,
+)
+from ..._base_client import make_request_options
+from ...types.v1.get_task_status_response import GetTaskStatusResponse
+
+__all__ = ["TasksResource", "AsyncTasksResource"]
+
+
+class TasksResource(SyncAPIResource):
+ """Async task status tracking"""
+
+ @cached_property
+ def with_raw_response(self) -> TasksResourceWithRawResponse:
+ """
+ This property can be used as a prefix for any HTTP method call to return
+ the raw response object instead of the parsed content.
+
+ For more information, see https://www.github.com/evermemos/everos-python#accessing-raw-response-data-eg-headers
+ """
+ return TasksResourceWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> TasksResourceWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/evermemos/everos-python#with_streaming_response
+ """
+ return TasksResourceWithStreamingResponse(self)
+
+ def retrieve(
+ self,
+ task_id: str,
+ *,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> GetTaskStatusResponse:
+ """Query the processing status of a specific async task by task_id.
+
+ Task status is
+ stored in Redis with a TTL of 1 hour. Internal status 'start' is mapped to
+ 'processing' in the response.
+
+ Args:
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not task_id:
+ raise ValueError(f"Expected a non-empty value for `task_id` but received {task_id!r}")
+ return self._get(
+ path_template("/api/v1/tasks/{task_id}", task_id=task_id),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=GetTaskStatusResponse,
+ )
+
+
+class AsyncTasksResource(AsyncAPIResource):
+ """Async task status tracking"""
+
+ @cached_property
+ def with_raw_response(self) -> AsyncTasksResourceWithRawResponse:
+ """
+ This property can be used as a prefix for any HTTP method call to return
+ the raw response object instead of the parsed content.
+
+ For more information, see https://www.github.com/evermemos/everos-python#accessing-raw-response-data-eg-headers
+ """
+ return AsyncTasksResourceWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> AsyncTasksResourceWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/evermemos/everos-python#with_streaming_response
+ """
+ return AsyncTasksResourceWithStreamingResponse(self)
+
+ async def retrieve(
+ self,
+ task_id: str,
+ *,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> GetTaskStatusResponse:
+ """Query the processing status of a specific async task by task_id.
+
+ Task status is
+ stored in Redis with a TTL of 1 hour. Internal status 'start' is mapped to
+ 'processing' in the response.
+
+ Args:
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not task_id:
+ raise ValueError(f"Expected a non-empty value for `task_id` but received {task_id!r}")
+ return await self._get(
+ path_template("/api/v1/tasks/{task_id}", task_id=task_id),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=GetTaskStatusResponse,
+ )
+
+
+class TasksResourceWithRawResponse:
+ def __init__(self, tasks: TasksResource) -> None:
+ self._tasks = tasks
+
+ self.retrieve = to_raw_response_wrapper(
+ tasks.retrieve,
+ )
+
+
+class AsyncTasksResourceWithRawResponse:
+ def __init__(self, tasks: AsyncTasksResource) -> None:
+ self._tasks = tasks
+
+ self.retrieve = async_to_raw_response_wrapper(
+ tasks.retrieve,
+ )
+
+
+class TasksResourceWithStreamingResponse:
+ def __init__(self, tasks: TasksResource) -> None:
+ self._tasks = tasks
+
+ self.retrieve = to_streamed_response_wrapper(
+ tasks.retrieve,
+ )
+
+
+class AsyncTasksResourceWithStreamingResponse:
+ def __init__(self, tasks: AsyncTasksResource) -> None:
+ self._tasks = tasks
+
+ self.retrieve = async_to_streamed_response_wrapper(
+ tasks.retrieve,
+ )
diff --git a/src/everos/resources/v1/v1.py b/src/everos/resources/v1/v1.py
index 1924c8a..5ef3595 100644
--- a/src/everos/resources/v1/v1.py
+++ b/src/everos/resources/v1/v1.py
@@ -2,8 +2,14 @@
from __future__ import annotations
-import httpx
-
+from .tasks import (
+ TasksResource,
+ AsyncTasksResource,
+ TasksResourceWithRawResponse,
+ AsyncTasksResourceWithRawResponse,
+ TasksResourceWithStreamingResponse,
+ AsyncTasksResourceWithStreamingResponse,
+)
from .groups import (
GroupsResource,
AsyncGroupsResource,
@@ -28,8 +34,6 @@
SendersResourceWithStreamingResponse,
AsyncSendersResourceWithStreamingResponse,
)
-from ..._types import Body, Query, Headers, NotGiven, not_given
-from ..._utils import path_template
from .settings import (
SettingsResource,
AsyncSettingsResource,
@@ -40,13 +44,6 @@
)
from ..._compat import cached_property
from ..._resource import SyncAPIResource, AsyncAPIResource
-from ..._response import (
- to_raw_response_wrapper,
- to_streamed_response_wrapper,
- async_to_raw_response_wrapper,
- async_to_streamed_response_wrapper,
-)
-from ..._base_client import make_request_options
from .memories.memories import (
MemoriesResource,
AsyncMemoriesResource,
@@ -55,38 +52,40 @@
MemoriesResourceWithStreamingResponse,
AsyncMemoriesResourceWithStreamingResponse,
)
-from ...types.v1_query_task_status_response import V1QueryTaskStatusResponse
__all__ = ["V1Resource", "AsyncV1Resource"]
class V1Resource(SyncAPIResource):
- """Async task status tracking"""
-
- @cached_property
- def memories(self) -> MemoriesResource:
- """Memory ingestion, retrieval, search, and deletion"""
- return MemoriesResource(self._client)
-
@cached_property
def groups(self) -> GroupsResource:
"""Group resource CRUD operations"""
return GroupsResource(self._client)
+ @cached_property
+ def settings(self) -> SettingsResource:
+ """Global settings management"""
+ return SettingsResource(self._client)
+
@cached_property
def senders(self) -> SendersResource:
"""Sender resource CRUD operations"""
return SendersResource(self._client)
+ @cached_property
+ def memories(self) -> MemoriesResource:
+ """Memory ingestion, retrieval, search, and deletion"""
+ return MemoriesResource(self._client)
+
@cached_property
def object(self) -> ObjectResource:
"""Multimodal content storage (pre-signed upload)"""
return ObjectResource(self._client)
@cached_property
- def settings(self) -> SettingsResource:
- """Global settings management"""
- return SettingsResource(self._client)
+ def tasks(self) -> TasksResource:
+ """Async task status tracking"""
+ return TasksResource(self._client)
@cached_property
def with_raw_response(self) -> V1ResourceWithRawResponse:
@@ -107,70 +106,37 @@ def with_streaming_response(self) -> V1ResourceWithStreamingResponse:
"""
return V1ResourceWithStreamingResponse(self)
- def query_task_status(
- self,
- task_id: str,
- *,
- # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
- # The extra values given here take precedence over values defined on the client or passed to this method.
- extra_headers: Headers | None = None,
- extra_query: Query | None = None,
- extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
- ) -> V1QueryTaskStatusResponse:
- """Query the processing status of a specific async task by task_id.
-
- Task status is
- stored in Redis with a TTL of 1 hour. Internal status 'start' is mapped to
- 'processing' in the response.
-
- Args:
- extra_headers: Send extra headers
-
- extra_query: Add additional query parameters to the request
-
- extra_body: Add additional JSON properties to the request
-
- timeout: Override the client-level default timeout for this request, in seconds
- """
- if not task_id:
- raise ValueError(f"Expected a non-empty value for `task_id` but received {task_id!r}")
- return self._get(
- path_template("/api/v1/tasks/{task_id}", task_id=task_id),
- options=make_request_options(
- extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
- ),
- cast_to=V1QueryTaskStatusResponse,
- )
-
class AsyncV1Resource(AsyncAPIResource):
- """Async task status tracking"""
-
- @cached_property
- def memories(self) -> AsyncMemoriesResource:
- """Memory ingestion, retrieval, search, and deletion"""
- return AsyncMemoriesResource(self._client)
-
@cached_property
def groups(self) -> AsyncGroupsResource:
"""Group resource CRUD operations"""
return AsyncGroupsResource(self._client)
+ @cached_property
+ def settings(self) -> AsyncSettingsResource:
+ """Global settings management"""
+ return AsyncSettingsResource(self._client)
+
@cached_property
def senders(self) -> AsyncSendersResource:
"""Sender resource CRUD operations"""
return AsyncSendersResource(self._client)
+ @cached_property
+ def memories(self) -> AsyncMemoriesResource:
+ """Memory ingestion, retrieval, search, and deletion"""
+ return AsyncMemoriesResource(self._client)
+
@cached_property
def object(self) -> AsyncObjectResource:
"""Multimodal content storage (pre-signed upload)"""
return AsyncObjectResource(self._client)
@cached_property
- def settings(self) -> AsyncSettingsResource:
- """Global settings management"""
- return AsyncSettingsResource(self._client)
+ def tasks(self) -> AsyncTasksResource:
+ """Async task status tracking"""
+ return AsyncTasksResource(self._client)
@cached_property
def with_raw_response(self) -> AsyncV1ResourceWithRawResponse:
@@ -191,174 +157,142 @@ def with_streaming_response(self) -> AsyncV1ResourceWithStreamingResponse:
"""
return AsyncV1ResourceWithStreamingResponse(self)
- async def query_task_status(
- self,
- task_id: str,
- *,
- # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
- # The extra values given here take precedence over values defined on the client or passed to this method.
- extra_headers: Headers | None = None,
- extra_query: Query | None = None,
- extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
- ) -> V1QueryTaskStatusResponse:
- """Query the processing status of a specific async task by task_id.
-
- Task status is
- stored in Redis with a TTL of 1 hour. Internal status 'start' is mapped to
- 'processing' in the response.
-
- Args:
- extra_headers: Send extra headers
-
- extra_query: Add additional query parameters to the request
-
- extra_body: Add additional JSON properties to the request
-
- timeout: Override the client-level default timeout for this request, in seconds
- """
- if not task_id:
- raise ValueError(f"Expected a non-empty value for `task_id` but received {task_id!r}")
- return await self._get(
- path_template("/api/v1/tasks/{task_id}", task_id=task_id),
- options=make_request_options(
- extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
- ),
- cast_to=V1QueryTaskStatusResponse,
- )
-
class V1ResourceWithRawResponse:
def __init__(self, v1: V1Resource) -> None:
self._v1 = v1
- self.query_task_status = to_raw_response_wrapper(
- v1.query_task_status,
- )
-
- @cached_property
- def memories(self) -> MemoriesResourceWithRawResponse:
- """Memory ingestion, retrieval, search, and deletion"""
- return MemoriesResourceWithRawResponse(self._v1.memories)
-
@cached_property
def groups(self) -> GroupsResourceWithRawResponse:
"""Group resource CRUD operations"""
return GroupsResourceWithRawResponse(self._v1.groups)
+ @cached_property
+ def settings(self) -> SettingsResourceWithRawResponse:
+ """Global settings management"""
+ return SettingsResourceWithRawResponse(self._v1.settings)
+
@cached_property
def senders(self) -> SendersResourceWithRawResponse:
"""Sender resource CRUD operations"""
return SendersResourceWithRawResponse(self._v1.senders)
+ @cached_property
+ def memories(self) -> MemoriesResourceWithRawResponse:
+ """Memory ingestion, retrieval, search, and deletion"""
+ return MemoriesResourceWithRawResponse(self._v1.memories)
+
@cached_property
def object(self) -> ObjectResourceWithRawResponse:
"""Multimodal content storage (pre-signed upload)"""
return ObjectResourceWithRawResponse(self._v1.object)
@cached_property
- def settings(self) -> SettingsResourceWithRawResponse:
- """Global settings management"""
- return SettingsResourceWithRawResponse(self._v1.settings)
+ def tasks(self) -> TasksResourceWithRawResponse:
+ """Async task status tracking"""
+ return TasksResourceWithRawResponse(self._v1.tasks)
class AsyncV1ResourceWithRawResponse:
def __init__(self, v1: AsyncV1Resource) -> None:
self._v1 = v1
- self.query_task_status = async_to_raw_response_wrapper(
- v1.query_task_status,
- )
-
- @cached_property
- def memories(self) -> AsyncMemoriesResourceWithRawResponse:
- """Memory ingestion, retrieval, search, and deletion"""
- return AsyncMemoriesResourceWithRawResponse(self._v1.memories)
-
@cached_property
def groups(self) -> AsyncGroupsResourceWithRawResponse:
"""Group resource CRUD operations"""
return AsyncGroupsResourceWithRawResponse(self._v1.groups)
+ @cached_property
+ def settings(self) -> AsyncSettingsResourceWithRawResponse:
+ """Global settings management"""
+ return AsyncSettingsResourceWithRawResponse(self._v1.settings)
+
@cached_property
def senders(self) -> AsyncSendersResourceWithRawResponse:
"""Sender resource CRUD operations"""
return AsyncSendersResourceWithRawResponse(self._v1.senders)
+ @cached_property
+ def memories(self) -> AsyncMemoriesResourceWithRawResponse:
+ """Memory ingestion, retrieval, search, and deletion"""
+ return AsyncMemoriesResourceWithRawResponse(self._v1.memories)
+
@cached_property
def object(self) -> AsyncObjectResourceWithRawResponse:
"""Multimodal content storage (pre-signed upload)"""
return AsyncObjectResourceWithRawResponse(self._v1.object)
@cached_property
- def settings(self) -> AsyncSettingsResourceWithRawResponse:
- """Global settings management"""
- return AsyncSettingsResourceWithRawResponse(self._v1.settings)
+ def tasks(self) -> AsyncTasksResourceWithRawResponse:
+ """Async task status tracking"""
+ return AsyncTasksResourceWithRawResponse(self._v1.tasks)
class V1ResourceWithStreamingResponse:
def __init__(self, v1: V1Resource) -> None:
self._v1 = v1
- self.query_task_status = to_streamed_response_wrapper(
- v1.query_task_status,
- )
-
- @cached_property
- def memories(self) -> MemoriesResourceWithStreamingResponse:
- """Memory ingestion, retrieval, search, and deletion"""
- return MemoriesResourceWithStreamingResponse(self._v1.memories)
-
@cached_property
def groups(self) -> GroupsResourceWithStreamingResponse:
"""Group resource CRUD operations"""
return GroupsResourceWithStreamingResponse(self._v1.groups)
+ @cached_property
+ def settings(self) -> SettingsResourceWithStreamingResponse:
+ """Global settings management"""
+ return SettingsResourceWithStreamingResponse(self._v1.settings)
+
@cached_property
def senders(self) -> SendersResourceWithStreamingResponse:
"""Sender resource CRUD operations"""
return SendersResourceWithStreamingResponse(self._v1.senders)
+ @cached_property
+ def memories(self) -> MemoriesResourceWithStreamingResponse:
+ """Memory ingestion, retrieval, search, and deletion"""
+ return MemoriesResourceWithStreamingResponse(self._v1.memories)
+
@cached_property
def object(self) -> ObjectResourceWithStreamingResponse:
"""Multimodal content storage (pre-signed upload)"""
return ObjectResourceWithStreamingResponse(self._v1.object)
@cached_property
- def settings(self) -> SettingsResourceWithStreamingResponse:
- """Global settings management"""
- return SettingsResourceWithStreamingResponse(self._v1.settings)
+ def tasks(self) -> TasksResourceWithStreamingResponse:
+ """Async task status tracking"""
+ return TasksResourceWithStreamingResponse(self._v1.tasks)
class AsyncV1ResourceWithStreamingResponse:
def __init__(self, v1: AsyncV1Resource) -> None:
self._v1 = v1
- self.query_task_status = async_to_streamed_response_wrapper(
- v1.query_task_status,
- )
-
- @cached_property
- def memories(self) -> AsyncMemoriesResourceWithStreamingResponse:
- """Memory ingestion, retrieval, search, and deletion"""
- return AsyncMemoriesResourceWithStreamingResponse(self._v1.memories)
-
@cached_property
def groups(self) -> AsyncGroupsResourceWithStreamingResponse:
"""Group resource CRUD operations"""
return AsyncGroupsResourceWithStreamingResponse(self._v1.groups)
+ @cached_property
+ def settings(self) -> AsyncSettingsResourceWithStreamingResponse:
+ """Global settings management"""
+ return AsyncSettingsResourceWithStreamingResponse(self._v1.settings)
+
@cached_property
def senders(self) -> AsyncSendersResourceWithStreamingResponse:
"""Sender resource CRUD operations"""
return AsyncSendersResourceWithStreamingResponse(self._v1.senders)
+ @cached_property
+ def memories(self) -> AsyncMemoriesResourceWithStreamingResponse:
+ """Memory ingestion, retrieval, search, and deletion"""
+ return AsyncMemoriesResourceWithStreamingResponse(self._v1.memories)
+
@cached_property
def object(self) -> AsyncObjectResourceWithStreamingResponse:
"""Multimodal content storage (pre-signed upload)"""
return AsyncObjectResourceWithStreamingResponse(self._v1.object)
@cached_property
- def settings(self) -> AsyncSettingsResourceWithStreamingResponse:
- """Global settings management"""
- return AsyncSettingsResourceWithStreamingResponse(self._v1.settings)
+ def tasks(self) -> AsyncTasksResourceWithStreamingResponse:
+ """Async task status tracking"""
+ return AsyncTasksResourceWithStreamingResponse(self._v1.tasks)
diff --git a/src/everos/types/__init__.py b/src/everos/types/__init__.py
index 1ac372f..f8ee8b1 100644
--- a/src/everos/types/__init__.py
+++ b/src/everos/types/__init__.py
@@ -1,5 +1,3 @@
# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
from __future__ import annotations
-
-from .v1_query_task_status_response import V1QueryTaskStatusResponse as V1QueryTaskStatusResponse
diff --git a/src/everos/types/v1/__init__.py b/src/everos/types/v1/__init__.py
index 1a812ee..5d901dd 100644
--- a/src/everos/types/v1/__init__.py
+++ b/src/everos/types/v1/__init__.py
@@ -2,24 +2,41 @@
from __future__ import annotations
+from .add_result import AddResult as AddResult
from .add_response import AddResponse as AddResponse
+from .episode_item import EpisodeItem as EpisodeItem
+from .flush_result import FlushResult as FlushResult
+from .profile_item import ProfileItem as ProfileItem
from .flush_response import FlushResponse as FlushResponse
+from .group_response import GroupResponse as GroupResponse
+from .raw_message_dto import RawMessageDto as RawMessageDto
+from .sender_response import SenderResponse as SenderResponse
+from .get_mem_response import GetMemResponse as GetMemResponse
+from .object_sign_item import ObjectSignItem as ObjectSignItem
+from .memory_add_params import MemoryAddParams as MemoryAddParams
from .memory_get_params import MemoryGetParams as MemoryGetParams
+from .settings_response import SettingsResponse as SettingsResponse
from .content_item_param import ContentItemParam as ContentItemParam
from .group_api_response import GroupAPIResponse as GroupAPIResponse
-from .group_update_params import GroupUpdateParams as GroupUpdateParams
+from .group_patch_params import GroupPatchParams as GroupPatchParams
+from .message_item_param import MessageItemParam as MessageItemParam
+from .object_sign_params import ObjectSignParams as ObjectSignParams
+from .object_signed_info import ObjectSignedInfo as ObjectSignedInfo
+from .task_status_result import TaskStatusResult as TaskStatusResult
+from .group_create_params import GroupCreateParams as GroupCreateParams
from .memory_flush_params import MemoryFlushParams as MemoryFlushParams
-from .memory_get_response import MemoryGetResponse as MemoryGetResponse
from .sender_api_response import SenderAPIResponse as SenderAPIResponse
-from .memory_create_params import MemoryCreateParams as MemoryCreateParams
+from .sender_patch_params import SenderPatchParams as SenderPatchParams
from .memory_delete_params import MemoryDeleteParams as MemoryDeleteParams
from .memory_search_params import MemorySearchParams as MemorySearchParams
-from .sender_update_params import SenderUpdateParams as SenderUpdateParams
+from .object_sign_response import ObjectSignResponse as ObjectSignResponse
+from .sender_create_params import SenderCreateParams as SenderCreateParams
+from .get_memories_response import GetMemoriesResponse as GetMemoriesResponse
from .setting_update_params import SettingUpdateParams as SettingUpdateParams
from .settings_api_response import SettingsAPIResponse as SettingsAPIResponse
-from .memory_search_response import MemorySearchResponse as MemorySearchResponse
+from .get_task_status_response import GetTaskStatusResponse as GetTaskStatusResponse
+from .llm_custom_setting_param import LlmCustomSettingParam as LlmCustomSettingParam
+from .search_memories_response import SearchMemoriesResponse as SearchMemoriesResponse
from .llm_provider_config_param import LlmProviderConfigParam as LlmProviderConfigParam
-from .group_create_or_update_params import GroupCreateOrUpdateParams as GroupCreateOrUpdateParams
-from .sender_create_or_update_params import SenderCreateOrUpdateParams as SenderCreateOrUpdateParams
-from .object_get_presigned_url_params import ObjectGetPresignedURLParams as ObjectGetPresignedURLParams
-from .object_get_presigned_url_response import ObjectGetPresignedURLResponse as ObjectGetPresignedURLResponse
+from .search_memories_response_data import SearchMemoriesResponseData as SearchMemoriesResponseData
+from .object_sign_item_request_param import ObjectSignItemRequestParam as ObjectSignItemRequestParam
diff --git a/src/everos/types/v1/add_response.py b/src/everos/types/v1/add_response.py
index 2875c75..79d38f4 100644
--- a/src/everos/types/v1/add_response.py
+++ b/src/everos/types/v1/add_response.py
@@ -1,26 +1,12 @@
# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
from typing import Optional
-from typing_extensions import Literal
from ..._models import BaseModel
+from .add_result import AddResult
-__all__ = ["AddResponse", "Data"]
-
-
-class Data(BaseModel):
- message: Optional[str] = None
- """Human-readable status description"""
-
- message_count: Optional[int] = None
- """Number of messages accepted"""
-
- status: Optional[Literal["accumulated", "extracted"]] = None
- """Processing status"""
-
- task_id: Optional[str] = None
- """Task tracking ID"""
+__all__ = ["AddResponse"]
class AddResponse(BaseModel):
- data: Optional[Data] = None
+ data: Optional[AddResult] = None
diff --git a/src/everos/types/v1/add_result.py b/src/everos/types/v1/add_result.py
new file mode 100644
index 0000000..c057b84
--- /dev/null
+++ b/src/everos/types/v1/add_result.py
@@ -0,0 +1,22 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import Optional
+from typing_extensions import Literal
+
+from ..._models import BaseModel
+
+__all__ = ["AddResult"]
+
+
+class AddResult(BaseModel):
+ message: Optional[str] = None
+ """Human-readable status description"""
+
+ message_count: Optional[int] = None
+ """Number of messages accepted"""
+
+ status: Optional[Literal["accumulated", "extracted"]] = None
+ """Processing status"""
+
+ task_id: Optional[str] = None
+ """Task tracking ID"""
diff --git a/src/everos/types/v1/episode_item.py b/src/everos/types/v1/episode_item.py
new file mode 100644
index 0000000..bcf189d
--- /dev/null
+++ b/src/everos/types/v1/episode_item.py
@@ -0,0 +1,51 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import List, Optional
+from datetime import datetime
+
+from ..._models import BaseModel
+
+__all__ = ["EpisodeItem"]
+
+
+class EpisodeItem(BaseModel):
+ """Episodic memory item"""
+
+ id: str
+ """MongoDB ObjectId as string"""
+
+ episode: Optional[str] = None
+ """Full episodic memory text"""
+
+ group_id: Optional[str] = None
+ """Group ID"""
+
+ parent_id: Optional[str] = None
+ """Parent memory ID"""
+
+ parent_type: Optional[str] = None
+ """Parent memory type"""
+
+ participants: Optional[List[str]] = None
+ """Event participant names"""
+
+ sender_ids: Optional[List[str]] = None
+ """Sender IDs of event participants"""
+
+ session_id: Optional[str] = None
+ """Session identifier"""
+
+ subject: Optional[str] = None
+ """Memory subject"""
+
+ summary: Optional[str] = None
+ """Memory summary"""
+
+ timestamp: Optional[datetime] = None
+ """Event occurrence time"""
+
+ type: Optional[str] = None
+ """Episode type"""
+
+ user_id: Optional[str] = None
+ """Owner user ID"""
diff --git a/src/everos/types/v1/flush_response.py b/src/everos/types/v1/flush_response.py
index ee76bac..42f4cbf 100644
--- a/src/everos/types/v1/flush_response.py
+++ b/src/everos/types/v1/flush_response.py
@@ -1,23 +1,12 @@
# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
from typing import Optional
-from typing_extensions import Literal
from ..._models import BaseModel
+from .flush_result import FlushResult
-__all__ = ["FlushResponse", "Data"]
-
-
-class Data(BaseModel):
- message: Optional[str] = None
- """Human-readable status description"""
-
- request_id: Optional[str] = None
- """Request tracking ID (reserved)"""
-
- status: Optional[Literal["extracted", "no_extraction"]] = None
- """Processing status"""
+__all__ = ["FlushResponse"]
class FlushResponse(BaseModel):
- data: Optional[Data] = None
+ data: Optional[FlushResult] = None
diff --git a/src/everos/types/v1/flush_result.py b/src/everos/types/v1/flush_result.py
new file mode 100644
index 0000000..1ecde79
--- /dev/null
+++ b/src/everos/types/v1/flush_result.py
@@ -0,0 +1,19 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import Optional
+from typing_extensions import Literal
+
+from ..._models import BaseModel
+
+__all__ = ["FlushResult"]
+
+
+class FlushResult(BaseModel):
+ message: Optional[str] = None
+ """Human-readable status description"""
+
+ request_id: Optional[str] = None
+ """Request tracking ID (reserved)"""
+
+ status: Optional[Literal["extracted", "no_extraction"]] = None
+ """Processing status"""
diff --git a/src/everos/types/v1/get_mem_response.py b/src/everos/types/v1/get_mem_response.py
new file mode 100644
index 0000000..1220e78
--- /dev/null
+++ b/src/everos/types/v1/get_mem_response.py
@@ -0,0 +1,33 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import List, Optional
+
+from ..._models import BaseModel
+from .episode_item import EpisodeItem
+from .profile_item import ProfileItem
+from .memories.agent_case_item import AgentCaseItem
+from .memories.agent_skill_item import AgentSkillItem
+
+__all__ = ["GetMemResponse"]
+
+
+class GetMemResponse(BaseModel):
+ """Memory get result data"""
+
+ agent_cases: Optional[List[AgentCaseItem]] = None
+ """Agent case items"""
+
+ agent_skills: Optional[List[AgentSkillItem]] = None
+ """Agent skill items"""
+
+ count: Optional[int] = None
+ """Records in current page"""
+
+ episodes: Optional[List[EpisodeItem]] = None
+ """Episodic memory items"""
+
+ profiles: Optional[List[ProfileItem]] = None
+ """Profile items"""
+
+ total_count: Optional[int] = None
+ """Total records matching filters"""
diff --git a/src/everos/types/v1/get_memories_response.py b/src/everos/types/v1/get_memories_response.py
new file mode 100644
index 0000000..d2876ee
--- /dev/null
+++ b/src/everos/types/v1/get_memories_response.py
@@ -0,0 +1,13 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import Optional
+
+from ..._models import BaseModel
+from .get_mem_response import GetMemResponse
+
+__all__ = ["GetMemoriesResponse"]
+
+
+class GetMemoriesResponse(BaseModel):
+ data: Optional[GetMemResponse] = None
+ """Memory get result data"""
diff --git a/src/everos/types/v1/get_task_status_response.py b/src/everos/types/v1/get_task_status_response.py
new file mode 100644
index 0000000..77e01fb
--- /dev/null
+++ b/src/everos/types/v1/get_task_status_response.py
@@ -0,0 +1,10 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from ..._models import BaseModel
+from .task_status_result import TaskStatusResult
+
+__all__ = ["GetTaskStatusResponse"]
+
+
+class GetTaskStatusResponse(BaseModel):
+ data: TaskStatusResult
diff --git a/src/everos/types/v1/group_api_response.py b/src/everos/types/v1/group_api_response.py
index 9493d55..08f1fe1 100644
--- a/src/everos/types/v1/group_api_response.py
+++ b/src/everos/types/v1/group_api_response.py
@@ -3,26 +3,10 @@
from typing import Optional
from ..._models import BaseModel
+from .group_response import GroupResponse
-__all__ = ["GroupAPIResponse", "Data"]
-
-
-class Data(BaseModel):
- created_at: str
- """Creation time (ISO 8601)"""
-
- group_id: str
- """Group identifier"""
-
- updated_at: str
- """Last update time (ISO 8601)"""
-
- description: Optional[str] = None
- """Group description"""
-
- name: Optional[str] = None
- """Group display name"""
+__all__ = ["GroupAPIResponse"]
class GroupAPIResponse(BaseModel):
- data: Optional[Data] = None
+ data: Optional[GroupResponse] = None
diff --git a/src/everos/types/v1/group_create_or_update_params.py b/src/everos/types/v1/group_create_params.py
similarity index 79%
rename from src/everos/types/v1/group_create_or_update_params.py
rename to src/everos/types/v1/group_create_params.py
index 2b98113..0ea652c 100644
--- a/src/everos/types/v1/group_create_or_update_params.py
+++ b/src/everos/types/v1/group_create_params.py
@@ -5,10 +5,10 @@
from typing import Optional
from typing_extensions import Required, TypedDict
-__all__ = ["GroupCreateOrUpdateParams"]
+__all__ = ["GroupCreateParams"]
-class GroupCreateOrUpdateParams(TypedDict, total=False):
+class GroupCreateParams(TypedDict, total=False):
group_id: Required[str]
"""Group identifier (unique)"""
diff --git a/src/everos/types/v1/group_update_params.py b/src/everos/types/v1/group_patch_params.py
similarity index 79%
rename from src/everos/types/v1/group_update_params.py
rename to src/everos/types/v1/group_patch_params.py
index 0627dbd..f319b7e 100644
--- a/src/everos/types/v1/group_update_params.py
+++ b/src/everos/types/v1/group_patch_params.py
@@ -5,10 +5,10 @@
from typing import Optional
from typing_extensions import TypedDict
-__all__ = ["GroupUpdateParams"]
+__all__ = ["GroupPatchParams"]
-class GroupUpdateParams(TypedDict, total=False):
+class GroupPatchParams(TypedDict, total=False):
description: Optional[str]
"""New group description"""
diff --git a/src/everos/types/v1/group_response.py b/src/everos/types/v1/group_response.py
new file mode 100644
index 0000000..65c901a
--- /dev/null
+++ b/src/everos/types/v1/group_response.py
@@ -0,0 +1,24 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import Optional
+
+from ..._models import BaseModel
+
+__all__ = ["GroupResponse"]
+
+
+class GroupResponse(BaseModel):
+ created_at: str
+ """Creation time (ISO 8601)"""
+
+ group_id: str
+ """Group identifier"""
+
+ updated_at: str
+ """Last update time (ISO 8601)"""
+
+ description: Optional[str] = None
+ """Group description"""
+
+ name: Optional[str] = None
+ """Group display name"""
diff --git a/src/everos/types/v1/llm_custom_setting_param.py b/src/everos/types/v1/llm_custom_setting_param.py
new file mode 100644
index 0000000..10d412a
--- /dev/null
+++ b/src/everos/types/v1/llm_custom_setting_param.py
@@ -0,0 +1,21 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Dict, Optional
+from typing_extensions import TypedDict
+
+from .llm_provider_config_param import LlmProviderConfigParam
+
+__all__ = ["LlmCustomSettingParam"]
+
+
+class LlmCustomSettingParam(TypedDict, total=False):
+ boundary: Optional[LlmProviderConfigParam]
+ """LLM config for boundary detection (fast, cheap model recommended)"""
+
+ extra: Optional[Dict[str, object]]
+ """Additional task-specific LLM configurations"""
+
+ extraction: Optional[LlmProviderConfigParam]
+ """LLM config for memory extraction (high quality model recommended)"""
diff --git a/src/everos/types/v1/memories/__init__.py b/src/everos/types/v1/memories/__init__.py
index fa27560..3f1c387 100644
--- a/src/everos/types/v1/memories/__init__.py
+++ b/src/everos/types/v1/memories/__init__.py
@@ -2,7 +2,13 @@
from __future__ import annotations
+from .agent_case_item import AgentCaseItem as AgentCaseItem
+from .tool_call_param import ToolCallParam as ToolCallParam
+from .agent_add_params import AgentAddParams as AgentAddParams
+from .agent_skill_item import AgentSkillItem as AgentSkillItem
+from .group_add_params import GroupAddParams as GroupAddParams
from .agent_flush_params import AgentFlushParams as AgentFlushParams
from .group_flush_params import GroupFlushParams as GroupFlushParams
-from .agent_create_params import AgentCreateParams as AgentCreateParams
-from .group_create_params import GroupCreateParams as GroupCreateParams
+from .agent_message_item_param import AgentMessageItemParam as AgentMessageItemParam
+from .group_message_item_param import GroupMessageItemParam as GroupMessageItemParam
+from .tool_call_function_param import ToolCallFunctionParam as ToolCallFunctionParam
diff --git a/src/everos/types/v1/memories/agent_add_params.py b/src/everos/types/v1/memories/agent_add_params.py
new file mode 100644
index 0000000..040d0f2
--- /dev/null
+++ b/src/everos/types/v1/memories/agent_add_params.py
@@ -0,0 +1,28 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Iterable, Optional
+from typing_extensions import Required, TypedDict
+
+from .agent_message_item_param import AgentMessageItemParam
+
+__all__ = ["AgentAddParams"]
+
+
+class AgentAddParams(TypedDict, total=False):
+ messages: Required[Iterable[AgentMessageItemParam]]
+ """Agent trajectory messages (1-500 items)"""
+
+ user_id: Required[str]
+ """Owner user ID"""
+
+ async_mode: bool
+ """Enable async processing.
+
+ When true, returns 202 with task_id; when false, processes synchronously and
+ returns 200.
+ """
+
+ session_id: Optional[str]
+ """Session identifier"""
diff --git a/src/everos/types/v1/memories/agent_case_item.py b/src/everos/types/v1/memories/agent_case_item.py
new file mode 100644
index 0000000..29869fd
--- /dev/null
+++ b/src/everos/types/v1/memories/agent_case_item.py
@@ -0,0 +1,42 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import Optional
+from datetime import datetime
+
+from ...._models import BaseModel
+
+__all__ = ["AgentCaseItem"]
+
+
+class AgentCaseItem(BaseModel):
+ """Agent case item (task experience)"""
+
+ id: str
+ """MongoDB ObjectId as string"""
+
+ approach: Optional[str] = None
+ """Step-by-step approach with decisions and lessons"""
+
+ group_id: Optional[str] = None
+ """Group ID"""
+
+ parent_id: Optional[str] = None
+ """Parent memory ID"""
+
+ parent_type: Optional[str] = None
+ """Parent memory type"""
+
+ quality_score: Optional[float] = None
+ """Task completion quality score (0.0-1.0)"""
+
+ session_id: Optional[str] = None
+ """Session identifier"""
+
+ task_intent: Optional[str] = None
+ """Rewritten task intent as retrieval key"""
+
+ timestamp: Optional[datetime] = None
+ """Task occurrence time"""
+
+ user_id: Optional[str] = None
+ """User ID"""
diff --git a/src/everos/types/v1/memories/agent_create_params.py b/src/everos/types/v1/memories/agent_message_item_param.py
similarity index 55%
rename from src/everos/types/v1/memories/agent_create_params.py
rename to src/everos/types/v1/memories/agent_message_item_param.py
index 099681c..9d0a17a 100644
--- a/src/everos/types/v1/memories/agent_create_params.py
+++ b/src/everos/types/v1/memories/agent_message_item_param.py
@@ -5,50 +5,13 @@
from typing import Union, Iterable, Optional
from typing_extensions import Literal, Required, TypedDict
+from .tool_call_param import ToolCallParam
from ..content_item_param import ContentItemParam
-__all__ = ["AgentCreateParams", "Message", "MessageToolCall", "MessageToolCallFunction"]
+__all__ = ["AgentMessageItemParam"]
-class AgentCreateParams(TypedDict, total=False):
- messages: Required[Iterable[Message]]
- """Agent trajectory messages (1-500 items)"""
-
- user_id: Required[str]
- """Owner user ID"""
-
- async_mode: bool
- """Enable async processing.
-
- When true, returns 202 with task_id; when false, processes synchronously and
- returns 200.
- """
-
- session_id: Optional[str]
- """Session identifier"""
-
-
-class MessageToolCallFunction(TypedDict, total=False):
- arguments: Required[str]
- """JSON-encoded arguments string"""
-
- name: Required[str]
- """Function/tool name"""
-
-
-class MessageToolCall(TypedDict, total=False):
- """OpenAI-format tool call made by the assistant"""
-
- id: Required[str]
- """Unique tool call ID"""
-
- function: Required[MessageToolCallFunction]
-
- type: str
- """Tool call type"""
-
-
-class Message(TypedDict, total=False):
+class AgentMessageItemParam(TypedDict, total=False):
"""Agent trajectory message.
Supports role='tool' in addition to 'user'/'assistant'. Does not support message_id or sender_name (stripped by Gateway).
@@ -76,5 +39,5 @@ class Message(TypedDict, total=False):
tool_call_id: Optional[str]
"""ID of the tool call this message responds to. Required when role='tool'."""
- tool_calls: Optional[Iterable[MessageToolCall]]
+ tool_calls: Optional[Iterable[ToolCallParam]]
"""Tool calls made by assistant (OpenAI format). Only when role='assistant'."""
diff --git a/src/everos/types/v1/memories/agent_skill_item.py b/src/everos/types/v1/memories/agent_skill_item.py
new file mode 100644
index 0000000..4d3343f
--- /dev/null
+++ b/src/everos/types/v1/memories/agent_skill_item.py
@@ -0,0 +1,41 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import List, Optional
+
+from ...._models import BaseModel
+
+__all__ = ["AgentSkillItem"]
+
+
+class AgentSkillItem(BaseModel):
+ """Agent skill item (reusable skill from clustered cases)"""
+
+ id: str
+ """MongoDB ObjectId as string"""
+
+ cluster_id: Optional[str] = None
+ """MemScene cluster ID"""
+
+ confidence: Optional[float] = None
+ """Confidence score (0.0-1.0)"""
+
+ content: Optional[str] = None
+ """Full skill content"""
+
+ description: Optional[str] = None
+ """What this skill does and when to use it"""
+
+ group_id: Optional[str] = None
+ """Group ID"""
+
+ maturity_score: Optional[float] = None
+ """Maturity score (0.0-1.0)"""
+
+ name: Optional[str] = None
+ """Skill name"""
+
+ source_case_ids: Optional[List[str]] = None
+ """Source AgentCase IDs"""
+
+ user_id: Optional[str] = None
+ """User ID"""
diff --git a/src/everos/types/v1/memories/group_add_params.py b/src/everos/types/v1/memories/group_add_params.py
new file mode 100644
index 0000000..a518ff4
--- /dev/null
+++ b/src/everos/types/v1/memories/group_add_params.py
@@ -0,0 +1,28 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Dict, Iterable, Optional
+from typing_extensions import Required, TypedDict
+
+from .group_message_item_param import GroupMessageItemParam
+
+__all__ = ["GroupAddParams"]
+
+
+class GroupAddParams(TypedDict, total=False):
+ group_id: Required[str]
+ """Group identifier"""
+
+ messages: Required[Iterable[GroupMessageItemParam]]
+ """Batch message array (1-500 items). sender_id is required per message."""
+
+ async_mode: bool
+ """Enable async processing.
+
+ When true, returns 202 with task_id; when false, processes synchronously and
+ returns 200.
+ """
+
+ group_meta: Optional[Dict[str, object]]
+ """Group metadata"""
diff --git a/src/everos/types/v1/memories/group_create_params.py b/src/everos/types/v1/memories/group_message_item_param.py
similarity index 58%
rename from src/everos/types/v1/memories/group_create_params.py
rename to src/everos/types/v1/memories/group_message_item_param.py
index 7115ae4..f7f3225 100644
--- a/src/everos/types/v1/memories/group_create_params.py
+++ b/src/everos/types/v1/memories/group_message_item_param.py
@@ -2,33 +2,15 @@
from __future__ import annotations
-from typing import Dict, Union, Iterable, Optional
+from typing import Union, Iterable, Optional
from typing_extensions import Literal, Required, TypedDict
from ..content_item_param import ContentItemParam
-__all__ = ["GroupCreateParams", "Message"]
+__all__ = ["GroupMessageItemParam"]
-class GroupCreateParams(TypedDict, total=False):
- group_id: Required[str]
- """Group identifier"""
-
- messages: Required[Iterable[Message]]
- """Batch message array (1-500 items). sender_id is required per message."""
-
- async_mode: bool
- """Enable async processing.
-
- When true, returns 202 with task_id; when false, processes synchronously and
- returns 200.
- """
-
- group_meta: Optional[Dict[str, object]]
- """Group metadata"""
-
-
-class Message(TypedDict, total=False):
+class GroupMessageItemParam(TypedDict, total=False):
"""Single message item for group memories.
Each message must include sender_id to identify participants.
diff --git a/src/everos/types/v1/memories/tool_call_function_param.py b/src/everos/types/v1/memories/tool_call_function_param.py
new file mode 100644
index 0000000..7029e8a
--- /dev/null
+++ b/src/everos/types/v1/memories/tool_call_function_param.py
@@ -0,0 +1,15 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing_extensions import Required, TypedDict
+
+__all__ = ["ToolCallFunctionParam"]
+
+
+class ToolCallFunctionParam(TypedDict, total=False):
+ arguments: Required[str]
+ """JSON-encoded arguments string"""
+
+ name: Required[str]
+ """Function/tool name"""
diff --git a/src/everos/types/v1/memories/tool_call_param.py b/src/everos/types/v1/memories/tool_call_param.py
new file mode 100644
index 0000000..e08e0f4
--- /dev/null
+++ b/src/everos/types/v1/memories/tool_call_param.py
@@ -0,0 +1,21 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing_extensions import Required, TypedDict
+
+from .tool_call_function_param import ToolCallFunctionParam
+
+__all__ = ["ToolCallParam"]
+
+
+class ToolCallParam(TypedDict, total=False):
+ """OpenAI-format tool call made by the assistant"""
+
+ id: Required[str]
+ """Unique tool call ID"""
+
+ function: Required[ToolCallFunctionParam]
+
+ type: str
+ """Tool call type"""
diff --git a/src/everos/types/v1/memory_add_params.py b/src/everos/types/v1/memory_add_params.py
new file mode 100644
index 0000000..0879fb3
--- /dev/null
+++ b/src/everos/types/v1/memory_add_params.py
@@ -0,0 +1,28 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Iterable, Optional
+from typing_extensions import Required, TypedDict
+
+from .message_item_param import MessageItemParam
+
+__all__ = ["MemoryAddParams"]
+
+
+class MemoryAddParams(TypedDict, total=False):
+ messages: Required[Iterable[MessageItemParam]]
+ """Batch message array (1-500 items)"""
+
+ user_id: Required[str]
+ """Owner user ID"""
+
+ async_mode: bool
+ """Enable async processing.
+
+ When true, returns 202 with task_id; when false, processes synchronously and
+ returns 200.
+ """
+
+ session_id: Optional[str]
+ """Session identifier"""
diff --git a/src/everos/types/v1/memory_get_response.py b/src/everos/types/v1/memory_get_response.py
deleted file mode 100644
index f93790f..0000000
--- a/src/everos/types/v1/memory_get_response.py
+++ /dev/null
@@ -1,168 +0,0 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
-
-from typing import Dict, List, Optional
-from datetime import datetime
-
-from ..._models import BaseModel
-
-__all__ = ["MemoryGetResponse", "Data", "DataAgentCase", "DataAgentSkill", "DataEpisode", "DataProfile"]
-
-
-class DataAgentCase(BaseModel):
- """Agent case item (task experience)"""
-
- id: str
- """MongoDB ObjectId as string"""
-
- approach: Optional[str] = None
- """Step-by-step approach with decisions and lessons"""
-
- group_id: Optional[str] = None
- """Group ID"""
-
- parent_id: Optional[str] = None
- """Parent memory ID"""
-
- parent_type: Optional[str] = None
- """Parent memory type"""
-
- quality_score: Optional[float] = None
- """Task completion quality score (0.0-1.0)"""
-
- session_id: Optional[str] = None
- """Session identifier"""
-
- task_intent: Optional[str] = None
- """Rewritten task intent as retrieval key"""
-
- timestamp: Optional[datetime] = None
- """Task occurrence time"""
-
- user_id: Optional[str] = None
- """User ID"""
-
-
-class DataAgentSkill(BaseModel):
- """Agent skill item (reusable skill from clustered cases)"""
-
- id: str
- """MongoDB ObjectId as string"""
-
- cluster_id: Optional[str] = None
- """MemScene cluster ID"""
-
- confidence: Optional[float] = None
- """Confidence score (0.0-1.0)"""
-
- content: Optional[str] = None
- """Full skill content"""
-
- description: Optional[str] = None
- """What this skill does and when to use it"""
-
- group_id: Optional[str] = None
- """Group ID"""
-
- maturity_score: Optional[float] = None
- """Maturity score (0.0-1.0)"""
-
- name: Optional[str] = None
- """Skill name"""
-
- source_case_ids: Optional[List[str]] = None
- """Source AgentCase IDs"""
-
- user_id: Optional[str] = None
- """User ID"""
-
-
-class DataEpisode(BaseModel):
- """Episodic memory item"""
-
- id: str
- """MongoDB ObjectId as string"""
-
- episode: Optional[str] = None
- """Full episodic memory text"""
-
- group_id: Optional[str] = None
- """Group ID"""
-
- parent_id: Optional[str] = None
- """Parent memory ID"""
-
- parent_type: Optional[str] = None
- """Parent memory type"""
-
- participants: Optional[List[str]] = None
- """Event participant names"""
-
- sender_ids: Optional[List[str]] = None
- """Sender IDs of event participants"""
-
- session_id: Optional[str] = None
- """Session identifier"""
-
- subject: Optional[str] = None
- """Memory subject"""
-
- summary: Optional[str] = None
- """Memory summary"""
-
- timestamp: Optional[datetime] = None
- """Event occurrence time"""
-
- type: Optional[str] = None
- """Episode type"""
-
- user_id: Optional[str] = None
- """Owner user ID"""
-
-
-class DataProfile(BaseModel):
- """User profile item"""
-
- id: str
- """MongoDB ObjectId as string"""
-
- group_id: Optional[str] = None
- """Group ID"""
-
- memcell_count: Optional[int] = None
- """Number of MemCells"""
-
- profile_data: Optional[Dict[str, object]] = None
- """Profile data (explicit_info, implicit_traits)"""
-
- scenario: Optional[str] = None
- """Scenario type: solo or team"""
-
- user_id: Optional[str] = None
- """User ID"""
-
-
-class Data(BaseModel):
- """Memory get result data"""
-
- agent_cases: Optional[List[DataAgentCase]] = None
- """Agent case items"""
-
- agent_skills: Optional[List[DataAgentSkill]] = None
- """Agent skill items"""
-
- count: Optional[int] = None
- """Records in current page"""
-
- episodes: Optional[List[DataEpisode]] = None
- """Episodic memory items"""
-
- profiles: Optional[List[DataProfile]] = None
- """Profile items"""
-
- total_count: Optional[int] = None
- """Total records matching filters"""
-
-
-class MemoryGetResponse(BaseModel):
- data: Optional[Data] = None
- """Memory get result data"""
diff --git a/src/everos/types/v1/memory_create_params.py b/src/everos/types/v1/message_item_param.py
similarity index 61%
rename from src/everos/types/v1/memory_create_params.py
rename to src/everos/types/v1/message_item_param.py
index ffbfe43..a80287d 100644
--- a/src/everos/types/v1/memory_create_params.py
+++ b/src/everos/types/v1/message_item_param.py
@@ -7,28 +7,10 @@
from .content_item_param import ContentItemParam
-__all__ = ["MemoryCreateParams", "Message"]
+__all__ = ["MessageItemParam"]
-class MemoryCreateParams(TypedDict, total=False):
- messages: Required[Iterable[Message]]
- """Batch message array (1-500 items)"""
-
- user_id: Required[str]
- """Owner user ID"""
-
- async_mode: bool
- """Enable async processing.
-
- When true, returns 202 with task_id; when false, processes synchronously and
- returns 200.
- """
-
- session_id: Optional[str]
- """Session identifier"""
-
-
-class Message(TypedDict, total=False):
+class MessageItemParam(TypedDict, total=False):
"""Single message item for personal memories.
Content accepts a plain string shorthand: "hello" is coerced to [{"type": "text", "text": "hello"}].
diff --git a/src/everos/types/v1/object_get_presigned_url_response.py b/src/everos/types/v1/object_get_presigned_url_response.py
deleted file mode 100644
index 3b413c3..0000000
--- a/src/everos/types/v1/object_get_presigned_url_response.py
+++ /dev/null
@@ -1,80 +0,0 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
-
-from typing import Dict, List, Optional
-
-from pydantic import Field as FieldInfo
-
-from ..._models import BaseModel
-
-__all__ = [
- "ObjectGetPresignedURLResponse",
- "Result",
- "ResultData",
- "ResultDataObjectList",
- "ResultDataObjectListObjectSignedInfo",
-]
-
-
-class ResultDataObjectListObjectSignedInfo(BaseModel):
- fields: Optional[Dict[str, str]] = None
- """
- Form fields required for S3 POST upload (key, policy, x-amz-\\** headers,
- Content-Type)
- """
-
- max_size: Optional[int] = FieldInfo(alias="maxSize", default=None)
- """Maximum file size in bytes. image: 10MB, file: 100MB, video: 500MB"""
-
- url: Optional[str] = None
- """Pre-signed S3 upload URL"""
-
-
-class ResultDataObjectList(BaseModel):
- """Signed object item.
-
- MMS echoes back the request fields alongside generated signing info.
- """
-
- file_id: Optional[str] = FieldInfo(alias="fileId", default=None)
- """Echoed file ID from request"""
-
- file_name: Optional[str] = FieldInfo(alias="fileName", default=None)
- """Echoed file name from request"""
-
- file_type: Optional[str] = FieldInfo(alias="fileType", default=None)
- """Echoed file type from request"""
-
- object_key: Optional[str] = FieldInfo(alias="objectKey", default=None)
- """S3 object key (generated by MMS)"""
-
- object_signed_info: Optional[ResultDataObjectListObjectSignedInfo] = FieldInfo(
- alias="objectSignedInfo", default=None
- )
-
-
-class ResultData(BaseModel):
- """On success: contains objectList. On validation error: string error message."""
-
- object_list: Optional[List[ResultDataObjectList]] = FieldInfo(alias="objectList", default=None)
-
-
-class Result(BaseModel):
- data: Optional[ResultData] = None
- """On success: contains objectList. On validation error: string error message."""
-
-
-class ObjectGetPresignedURLResponse(BaseModel):
- """
- MMS service response format (Gateway transparent proxy, not standard ErrorResponse format)
- """
-
- error: Optional[str] = None
- """Error message. 'OK' on success, error description on failure"""
-
- request_id: Optional[str] = None
- """MMS request tracking ID"""
-
- result: Optional[Result] = None
-
- status: Optional[int] = None
- """MMS status code. 0 = success, 2018 = validation error"""
diff --git a/src/everos/types/v1/object_sign_item.py b/src/everos/types/v1/object_sign_item.py
new file mode 100644
index 0000000..d2769b6
--- /dev/null
+++ b/src/everos/types/v1/object_sign_item.py
@@ -0,0 +1,31 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import Optional
+
+from pydantic import Field as FieldInfo
+
+from ..._models import BaseModel
+from .object_signed_info import ObjectSignedInfo
+
+__all__ = ["ObjectSignItem"]
+
+
+class ObjectSignItem(BaseModel):
+ """Signed object item.
+
+ MMS echoes back the request fields alongside generated signing info.
+ """
+
+ file_id: Optional[str] = FieldInfo(alias="fileId", default=None)
+ """Echoed file ID from request"""
+
+ file_name: Optional[str] = FieldInfo(alias="fileName", default=None)
+ """Echoed file name from request"""
+
+ file_type: Optional[str] = FieldInfo(alias="fileType", default=None)
+ """Echoed file type from request"""
+
+ object_key: Optional[str] = FieldInfo(alias="objectKey", default=None)
+ """S3 object key (generated by MMS)"""
+
+ object_signed_info: Optional[ObjectSignedInfo] = FieldInfo(alias="objectSignedInfo", default=None)
diff --git a/src/everos/types/v1/object_get_presigned_url_params.py b/src/everos/types/v1/object_sign_item_request_param.py
similarity index 67%
rename from src/everos/types/v1/object_get_presigned_url_params.py
rename to src/everos/types/v1/object_sign_item_request_param.py
index 2a5ffa4..33350ac 100644
--- a/src/everos/types/v1/object_get_presigned_url_params.py
+++ b/src/everos/types/v1/object_sign_item_request_param.py
@@ -2,20 +2,14 @@
from __future__ import annotations
-from typing import Iterable
from typing_extensions import Literal, Required, Annotated, TypedDict
from ..._utils import PropertyInfo
-__all__ = ["ObjectGetPresignedURLParams", "ObjectList"]
+__all__ = ["ObjectSignItemRequestParam"]
-class ObjectGetPresignedURLParams(TypedDict, total=False):
- object_list: Required[Annotated[Iterable[ObjectList], PropertyInfo(alias="objectList")]]
- """List of files to sign (supports batch signing)"""
-
-
-class ObjectList(TypedDict, total=False):
+class ObjectSignItemRequestParam(TypedDict, total=False):
"""Single file sign request item"""
file_id: Required[Annotated[str, PropertyInfo(alias="fileId")]]
diff --git a/src/everos/types/v1/object_sign_params.py b/src/everos/types/v1/object_sign_params.py
new file mode 100644
index 0000000..5857440
--- /dev/null
+++ b/src/everos/types/v1/object_sign_params.py
@@ -0,0 +1,16 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Iterable
+from typing_extensions import Required, Annotated, TypedDict
+
+from ..._utils import PropertyInfo
+from .object_sign_item_request_param import ObjectSignItemRequestParam
+
+__all__ = ["ObjectSignParams"]
+
+
+class ObjectSignParams(TypedDict, total=False):
+ object_list: Required[Annotated[Iterable[ObjectSignItemRequestParam], PropertyInfo(alias="objectList")]]
+ """List of files to sign (supports batch signing)"""
diff --git a/src/everos/types/v1/object_sign_response.py b/src/everos/types/v1/object_sign_response.py
new file mode 100644
index 0000000..6dda9b7
--- /dev/null
+++ b/src/everos/types/v1/object_sign_response.py
@@ -0,0 +1,38 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import List, Optional
+
+from pydantic import Field as FieldInfo
+
+from ..._models import BaseModel
+from .object_sign_item import ObjectSignItem
+
+__all__ = ["ObjectSignResponse", "Result", "ResultData"]
+
+
+class ResultData(BaseModel):
+ """On success: contains objectList. On validation error: string error message."""
+
+ object_list: Optional[List[ObjectSignItem]] = FieldInfo(alias="objectList", default=None)
+
+
+class Result(BaseModel):
+ data: Optional[ResultData] = None
+ """On success: contains objectList. On validation error: string error message."""
+
+
+class ObjectSignResponse(BaseModel):
+ """
+ MMS service response format (Gateway transparent proxy, not standard ErrorResponse format)
+ """
+
+ error: Optional[str] = None
+ """Error message. 'OK' on success, error description on failure"""
+
+ request_id: Optional[str] = None
+ """MMS request tracking ID"""
+
+ result: Optional[Result] = None
+
+ status: Optional[int] = None
+ """MMS status code. 0 = success, 2018 = validation error"""
diff --git a/src/everos/types/v1/object_signed_info.py b/src/everos/types/v1/object_signed_info.py
new file mode 100644
index 0000000..3162a70
--- /dev/null
+++ b/src/everos/types/v1/object_signed_info.py
@@ -0,0 +1,23 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import Dict, Optional
+
+from pydantic import Field as FieldInfo
+
+from ..._models import BaseModel
+
+__all__ = ["ObjectSignedInfo"]
+
+
+class ObjectSignedInfo(BaseModel):
+ fields: Optional[Dict[str, str]] = None
+ """
+ Form fields required for S3 POST upload (key, policy, x-amz-\\** headers,
+ Content-Type)
+ """
+
+ max_size: Optional[int] = FieldInfo(alias="maxSize", default=None)
+ """Maximum file size in bytes. image: 10MB, file: 100MB, video: 500MB"""
+
+ url: Optional[str] = None
+ """Pre-signed S3 upload URL"""
diff --git a/src/everos/types/v1/profile_item.py b/src/everos/types/v1/profile_item.py
new file mode 100644
index 0000000..5eb6a05
--- /dev/null
+++ b/src/everos/types/v1/profile_item.py
@@ -0,0 +1,29 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import Dict, Optional
+
+from ..._models import BaseModel
+
+__all__ = ["ProfileItem"]
+
+
+class ProfileItem(BaseModel):
+ """User profile item"""
+
+ id: str
+ """MongoDB ObjectId as string"""
+
+ group_id: Optional[str] = None
+ """Group ID"""
+
+ memcell_count: Optional[int] = None
+ """Number of MemCells"""
+
+ profile_data: Optional[Dict[str, object]] = None
+ """Profile data (explicit_info, implicit_traits)"""
+
+ scenario: Optional[str] = None
+ """Scenario type: solo or team"""
+
+ user_id: Optional[str] = None
+ """User ID"""
diff --git a/src/everos/types/v1/raw_message_dto.py b/src/everos/types/v1/raw_message_dto.py
new file mode 100644
index 0000000..d46ad6a
--- /dev/null
+++ b/src/everos/types/v1/raw_message_dto.py
@@ -0,0 +1,43 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import Dict, List, Optional
+
+from ..._models import BaseModel
+
+__all__ = ["RawMessageDto"]
+
+
+class RawMessageDto(BaseModel):
+ """Raw unprocessed message waiting for memory extraction"""
+
+ id: str
+ """MongoDB ObjectId as string"""
+
+ request_id: str
+ """Request ID"""
+
+ content_items: Optional[List[Dict[str, object]]] = None
+
+ created_at: Optional[str] = None
+ """ISO 8601 format"""
+
+ group_id: Optional[str] = None
+ """Group ID"""
+
+ message_id: Optional[str] = None
+ """Message ID"""
+
+ sender_id: Optional[str] = None
+ """Sender ID"""
+
+ sender_name: Optional[str] = None
+ """Sender name"""
+
+ session_id: Optional[str] = None
+ """Session identifier"""
+
+ timestamp: Optional[str] = None
+ """ISO 8601 format"""
+
+ updated_at: Optional[str] = None
+ """ISO 8601 format"""
diff --git a/src/everos/types/v1/search_memories_response.py b/src/everos/types/v1/search_memories_response.py
new file mode 100644
index 0000000..f19d16b
--- /dev/null
+++ b/src/everos/types/v1/search_memories_response.py
@@ -0,0 +1,13 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import Optional
+
+from ..._models import BaseModel
+from .search_memories_response_data import SearchMemoriesResponseData
+
+__all__ = ["SearchMemoriesResponse"]
+
+
+class SearchMemoriesResponse(BaseModel):
+ data: Optional[SearchMemoriesResponseData] = None
+ """Memory search result data"""
diff --git a/src/everos/types/v1/memory_search_response.py b/src/everos/types/v1/search_memories_response_data.py
similarity index 73%
rename from src/everos/types/v1/memory_search_response.py
rename to src/everos/types/v1/search_memories_response_data.py
index 8f3a590..18c9bea 100644
--- a/src/everos/types/v1/memory_search_response.py
+++ b/src/everos/types/v1/search_memories_response_data.py
@@ -4,22 +4,21 @@
from datetime import datetime
from ..._models import BaseModel
+from .raw_message_dto import RawMessageDto
__all__ = [
- "MemorySearchResponse",
- "Data",
- "DataQuery",
- "DataAgentMemory",
- "DataAgentMemoryCase",
- "DataAgentMemorySkill",
- "DataEpisode",
- "DataEpisodeAtomicFact",
- "DataProfile",
- "DataRawMessage",
+ "SearchMemoriesResponseData",
+ "Query",
+ "AgentMemory",
+ "AgentMemoryCase",
+ "AgentMemorySkill",
+ "Episode",
+ "EpisodeAtomicFact",
+ "Profile",
]
-class DataQuery(BaseModel):
+class Query(BaseModel):
method: str
"""Retrieval method used"""
@@ -30,7 +29,7 @@ class DataQuery(BaseModel):
"""Filters applied"""
-class DataAgentMemoryCase(BaseModel):
+class AgentMemoryCase(BaseModel):
"""Agent case search result with relevance score"""
id: str
@@ -67,7 +66,7 @@ class DataAgentMemoryCase(BaseModel):
"""User ID"""
-class DataAgentMemorySkill(BaseModel):
+class AgentMemorySkill(BaseModel):
"""Agent skill search result with relevance score"""
id: str
@@ -104,17 +103,17 @@ class DataAgentMemorySkill(BaseModel):
"""User ID"""
-class DataAgentMemory(BaseModel):
+class AgentMemory(BaseModel):
"""Agent memory search results (cases + skills)"""
- cases: Optional[List[DataAgentMemoryCase]] = None
+ cases: Optional[List[AgentMemoryCase]] = None
"""Agent case search results"""
- skills: Optional[List[DataAgentMemorySkill]] = None
+ skills: Optional[List[AgentMemorySkill]] = None
"""Agent skill search results"""
-class DataEpisodeAtomicFact(BaseModel):
+class EpisodeAtomicFact(BaseModel):
"""Atomic fact expanded from an episode"""
id: str
@@ -154,13 +153,13 @@ class DataEpisodeAtomicFact(BaseModel):
"""User ID"""
-class DataEpisode(BaseModel):
+class Episode(BaseModel):
"""Episode search result with relevance score"""
id: str
"""MongoDB ObjectId as string"""
- atomic_facts: Optional[List[DataEpisodeAtomicFact]] = None
+ atomic_facts: Optional[List[EpisodeAtomicFact]] = None
"""Atomic facts expanded from this episode"""
episode: Optional[str] = None
@@ -200,7 +199,7 @@ class DataEpisode(BaseModel):
"""Owner user ID"""
-class DataProfile(BaseModel):
+class Profile(BaseModel):
"""Profile search result with relevance score"""
id: str
@@ -225,63 +224,22 @@ class DataProfile(BaseModel):
"""User ID"""
-class DataRawMessage(BaseModel):
- """Raw unprocessed message waiting for memory extraction"""
-
- id: str
- """MongoDB ObjectId as string"""
-
- request_id: str
- """Request ID"""
-
- content_items: Optional[List[Dict[str, object]]] = None
-
- created_at: Optional[str] = None
- """ISO 8601 format"""
-
- group_id: Optional[str] = None
- """Group ID"""
-
- message_id: Optional[str] = None
- """Message ID"""
-
- sender_id: Optional[str] = None
- """Sender ID"""
-
- sender_name: Optional[str] = None
- """Sender name"""
-
- session_id: Optional[str] = None
- """Session identifier"""
-
- timestamp: Optional[str] = None
- """ISO 8601 format"""
-
- updated_at: Optional[str] = None
- """ISO 8601 format"""
-
-
-class Data(BaseModel):
+class SearchMemoriesResponseData(BaseModel):
"""Memory search result data"""
- query: DataQuery
+ query: Query
- agent_memory: Optional[DataAgentMemory] = None
+ agent_memory: Optional[AgentMemory] = None
"""Agent memory search results (cases + skills)"""
- episodes: Optional[List[DataEpisode]] = None
+ episodes: Optional[List[Episode]] = None
"""Episodic memory search results"""
original_data: Optional[Dict[str, object]] = None
"""Original data (if include_original_data=true)"""
- profiles: Optional[List[DataProfile]] = None
+ profiles: Optional[List[Profile]] = None
"""Profile search results"""
- raw_messages: Optional[List[DataRawMessage]] = None
+ raw_messages: Optional[List[RawMessageDto]] = None
"""Raw unprocessed messages (pending)"""
-
-
-class MemorySearchResponse(BaseModel):
- data: Optional[Data] = None
- """Memory search result data"""
diff --git a/src/everos/types/v1/sender_api_response.py b/src/everos/types/v1/sender_api_response.py
index 9581b54..35823ff 100644
--- a/src/everos/types/v1/sender_api_response.py
+++ b/src/everos/types/v1/sender_api_response.py
@@ -3,23 +3,10 @@
from typing import Optional
from ..._models import BaseModel
+from .sender_response import SenderResponse
-__all__ = ["SenderAPIResponse", "Data"]
-
-
-class Data(BaseModel):
- created_at: str
- """Creation time (ISO 8601)"""
-
- sender_id: str
- """Sender identifier"""
-
- updated_at: str
- """Last update time (ISO 8601)"""
-
- name: Optional[str] = None
- """Sender display name"""
+__all__ = ["SenderAPIResponse"]
class SenderAPIResponse(BaseModel):
- data: Optional[Data] = None
+ data: Optional[SenderResponse] = None
diff --git a/src/everos/types/v1/sender_create_or_update_params.py b/src/everos/types/v1/sender_create_params.py
similarity index 76%
rename from src/everos/types/v1/sender_create_or_update_params.py
rename to src/everos/types/v1/sender_create_params.py
index e0439b1..ce10f5b 100644
--- a/src/everos/types/v1/sender_create_or_update_params.py
+++ b/src/everos/types/v1/sender_create_params.py
@@ -5,10 +5,10 @@
from typing import Optional
from typing_extensions import Required, TypedDict
-__all__ = ["SenderCreateOrUpdateParams"]
+__all__ = ["SenderCreateParams"]
-class SenderCreateOrUpdateParams(TypedDict, total=False):
+class SenderCreateParams(TypedDict, total=False):
sender_id: Required[str]
"""Sender identifier (unique)"""
diff --git a/src/everos/types/v1/sender_update_params.py b/src/everos/types/v1/sender_patch_params.py
similarity index 75%
rename from src/everos/types/v1/sender_update_params.py
rename to src/everos/types/v1/sender_patch_params.py
index 9fc7ffc..7fd428e 100644
--- a/src/everos/types/v1/sender_update_params.py
+++ b/src/everos/types/v1/sender_patch_params.py
@@ -5,9 +5,9 @@
from typing import Optional
from typing_extensions import TypedDict
-__all__ = ["SenderUpdateParams"]
+__all__ = ["SenderPatchParams"]
-class SenderUpdateParams(TypedDict, total=False):
+class SenderPatchParams(TypedDict, total=False):
name: Optional[str]
"""New sender display name"""
diff --git a/src/everos/types/v1/sender_response.py b/src/everos/types/v1/sender_response.py
new file mode 100644
index 0000000..6641b8e
--- /dev/null
+++ b/src/everos/types/v1/sender_response.py
@@ -0,0 +1,21 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import Optional
+
+from ..._models import BaseModel
+
+__all__ = ["SenderResponse"]
+
+
+class SenderResponse(BaseModel):
+ created_at: str
+ """Creation time (ISO 8601)"""
+
+ sender_id: str
+ """Sender identifier"""
+
+ updated_at: str
+ """Last update time (ISO 8601)"""
+
+ name: Optional[str] = None
+ """Sender display name"""
diff --git a/src/everos/types/v1/setting_update_params.py b/src/everos/types/v1/setting_update_params.py
index dcdd093..f44ccf2 100644
--- a/src/everos/types/v1/setting_update_params.py
+++ b/src/everos/types/v1/setting_update_params.py
@@ -2,12 +2,12 @@
from __future__ import annotations
-from typing import Dict, Optional
+from typing import Optional
from typing_extensions import Literal, TypedDict
-from .llm_provider_config_param import LlmProviderConfigParam
+from .llm_custom_setting_param import LlmCustomSettingParam
-__all__ = ["SettingUpdateParams", "LlmCustomSetting"]
+__all__ = ["SettingUpdateParams"]
class SettingUpdateParams(TypedDict, total=False):
@@ -17,7 +17,7 @@ class SettingUpdateParams(TypedDict, total=False):
extraction_mode: Optional[Literal["default", "pro"]]
"""Extraction mode"""
- llm_custom_setting: Optional[LlmCustomSetting]
+ llm_custom_setting: Optional[LlmCustomSettingParam]
"""LLM custom settings for algorithm control"""
offline_profile_extraction_interval: Optional[int]
@@ -25,16 +25,3 @@ class SettingUpdateParams(TypedDict, total=False):
timezone: Optional[str]
"""IANA timezone identifier"""
-
-
-class LlmCustomSetting(TypedDict, total=False):
- """LLM custom settings for algorithm control"""
-
- boundary: Optional[LlmProviderConfigParam]
- """LLM config for boundary detection (fast, cheap model recommended)"""
-
- extra: Optional[Dict[str, object]]
- """Additional task-specific LLM configurations"""
-
- extraction: Optional[LlmProviderConfigParam]
- """LLM config for memory extraction (high quality model recommended)"""
diff --git a/src/everos/types/v1/settings_api_response.py b/src/everos/types/v1/settings_api_response.py
index bde4ea6..2e77df5 100644
--- a/src/everos/types/v1/settings_api_response.py
+++ b/src/everos/types/v1/settings_api_response.py
@@ -1,34 +1,12 @@
# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
-from typing import Dict, Optional
+from typing import Optional
from ..._models import BaseModel
+from .settings_response import SettingsResponse
-__all__ = ["SettingsAPIResponse", "Data"]
-
-
-class Data(BaseModel):
- boundary_detection_timeout: int
- """MemCell auto-flush idle timeout in seconds"""
-
- created_at: str
- """Creation time (ISO 8601)"""
-
- extraction_mode: str
- """Extraction mode"""
-
- offline_profile_extraction_interval: int
- """Offline profile extraction interval in seconds"""
-
- timezone: str
- """IANA timezone identifier"""
-
- updated_at: str
- """Last update time (ISO 8601)"""
-
- llm_custom_setting: Optional[Dict[str, object]] = None
- """LLM custom settings (serialized)"""
+__all__ = ["SettingsAPIResponse"]
class SettingsAPIResponse(BaseModel):
- data: Optional[Data] = None
+ data: Optional[SettingsResponse] = None
diff --git a/src/everos/types/v1/settings_response.py b/src/everos/types/v1/settings_response.py
new file mode 100644
index 0000000..7b6ed13
--- /dev/null
+++ b/src/everos/types/v1/settings_response.py
@@ -0,0 +1,30 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import Dict, Optional
+
+from ..._models import BaseModel
+
+__all__ = ["SettingsResponse"]
+
+
+class SettingsResponse(BaseModel):
+ boundary_detection_timeout: int
+ """MemCell auto-flush idle timeout in seconds"""
+
+ created_at: str
+ """Creation time (ISO 8601)"""
+
+ extraction_mode: str
+ """Extraction mode"""
+
+ offline_profile_extraction_interval: int
+ """Offline profile extraction interval in seconds"""
+
+ timezone: str
+ """IANA timezone identifier"""
+
+ updated_at: str
+ """Last update time (ISO 8601)"""
+
+ llm_custom_setting: Optional[Dict[str, object]] = None
+ """LLM custom settings (serialized)"""
diff --git a/src/everos/types/v1_query_task_status_response.py b/src/everos/types/v1/task_status_result.py
similarity index 61%
rename from src/everos/types/v1_query_task_status_response.py
rename to src/everos/types/v1/task_status_result.py
index 6a02074..db8827d 100644
--- a/src/everos/types/v1_query_task_status_response.py
+++ b/src/everos/types/v1/task_status_result.py
@@ -2,18 +2,14 @@
from typing_extensions import Literal
-from .._models import BaseModel
+from ..._models import BaseModel
-__all__ = ["V1QueryTaskStatusResponse", "Data"]
+__all__ = ["TaskStatusResult"]
-class Data(BaseModel):
+class TaskStatusResult(BaseModel):
status: Literal["processing", "success", "failed"]
"""Task status"""
task_id: str
"""Async task tracking ID"""
-
-
-class V1QueryTaskStatusResponse(BaseModel):
- data: Data
diff --git a/tests/api_resources/v1/memories/test_agent.py b/tests/api_resources/v1/memories/test_agent.py
index 5ffc615..329e1c5 100644
--- a/tests/api_resources/v1/memories/test_agent.py
+++ b/tests/api_resources/v1/memories/test_agent.py
@@ -7,7 +7,7 @@
import pytest
-from everos import Everos, AsyncEveros
+from everos import EverOS, AsyncEverOS
from tests.utils import assert_matches_type
from everos.types.v1 import AddResponse, FlushResponse
@@ -19,8 +19,8 @@ class TestAgent:
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
- def test_method_create(self, client: Everos) -> None:
- agent = client.v1.memories.agent.create(
+ def test_method_add(self, client: EverOS) -> None:
+ agent = client.v1.memories.agent.add(
messages=[
{
"role": "user",
@@ -33,8 +33,8 @@ def test_method_create(self, client: Everos) -> None:
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
- def test_method_create_with_all_params(self, client: Everos) -> None:
- agent = client.v1.memories.agent.create(
+ def test_method_add_with_all_params(self, client: EverOS) -> None:
+ agent = client.v1.memories.agent.add(
messages=[
{
"role": "user",
@@ -62,8 +62,8 @@ def test_method_create_with_all_params(self, client: Everos) -> None:
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
- def test_raw_response_create(self, client: Everos) -> None:
- response = client.v1.memories.agent.with_raw_response.create(
+ def test_raw_response_add(self, client: EverOS) -> None:
+ response = client.v1.memories.agent.with_raw_response.add(
messages=[
{
"role": "user",
@@ -80,8 +80,8 @@ def test_raw_response_create(self, client: Everos) -> None:
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
- def test_streaming_response_create(self, client: Everos) -> None:
- with client.v1.memories.agent.with_streaming_response.create(
+ def test_streaming_response_add(self, client: EverOS) -> None:
+ with client.v1.memories.agent.with_streaming_response.add(
messages=[
{
"role": "user",
@@ -100,7 +100,7 @@ def test_streaming_response_create(self, client: Everos) -> None:
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
- def test_method_flush(self, client: Everos) -> None:
+ def test_method_flush(self, client: EverOS) -> None:
agent = client.v1.memories.agent.flush(
user_id="user_id",
)
@@ -108,7 +108,7 @@ def test_method_flush(self, client: Everos) -> None:
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
- def test_method_flush_with_all_params(self, client: Everos) -> None:
+ def test_method_flush_with_all_params(self, client: EverOS) -> None:
agent = client.v1.memories.agent.flush(
user_id="user_id",
session_id="session_id",
@@ -117,7 +117,7 @@ def test_method_flush_with_all_params(self, client: Everos) -> None:
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
- def test_raw_response_flush(self, client: Everos) -> None:
+ def test_raw_response_flush(self, client: EverOS) -> None:
response = client.v1.memories.agent.with_raw_response.flush(
user_id="user_id",
)
@@ -129,7 +129,7 @@ def test_raw_response_flush(self, client: Everos) -> None:
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
- def test_streaming_response_flush(self, client: Everos) -> None:
+ def test_streaming_response_flush(self, client: EverOS) -> None:
with client.v1.memories.agent.with_streaming_response.flush(
user_id="user_id",
) as response:
@@ -149,8 +149,8 @@ class TestAsyncAgent:
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
- async def test_method_create(self, async_client: AsyncEveros) -> None:
- agent = await async_client.v1.memories.agent.create(
+ async def test_method_add(self, async_client: AsyncEverOS) -> None:
+ agent = await async_client.v1.memories.agent.add(
messages=[
{
"role": "user",
@@ -163,8 +163,8 @@ async def test_method_create(self, async_client: AsyncEveros) -> None:
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
- async def test_method_create_with_all_params(self, async_client: AsyncEveros) -> None:
- agent = await async_client.v1.memories.agent.create(
+ async def test_method_add_with_all_params(self, async_client: AsyncEverOS) -> None:
+ agent = await async_client.v1.memories.agent.add(
messages=[
{
"role": "user",
@@ -192,8 +192,8 @@ async def test_method_create_with_all_params(self, async_client: AsyncEveros) ->
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
- async def test_raw_response_create(self, async_client: AsyncEveros) -> None:
- response = await async_client.v1.memories.agent.with_raw_response.create(
+ async def test_raw_response_add(self, async_client: AsyncEverOS) -> None:
+ response = await async_client.v1.memories.agent.with_raw_response.add(
messages=[
{
"role": "user",
@@ -210,8 +210,8 @@ async def test_raw_response_create(self, async_client: AsyncEveros) -> None:
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
- async def test_streaming_response_create(self, async_client: AsyncEveros) -> None:
- async with async_client.v1.memories.agent.with_streaming_response.create(
+ async def test_streaming_response_add(self, async_client: AsyncEverOS) -> None:
+ async with async_client.v1.memories.agent.with_streaming_response.add(
messages=[
{
"role": "user",
@@ -230,7 +230,7 @@ async def test_streaming_response_create(self, async_client: AsyncEveros) -> Non
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
- async def test_method_flush(self, async_client: AsyncEveros) -> None:
+ async def test_method_flush(self, async_client: AsyncEverOS) -> None:
agent = await async_client.v1.memories.agent.flush(
user_id="user_id",
)
@@ -238,7 +238,7 @@ async def test_method_flush(self, async_client: AsyncEveros) -> None:
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
- async def test_method_flush_with_all_params(self, async_client: AsyncEveros) -> None:
+ async def test_method_flush_with_all_params(self, async_client: AsyncEverOS) -> None:
agent = await async_client.v1.memories.agent.flush(
user_id="user_id",
session_id="session_id",
@@ -247,7 +247,7 @@ async def test_method_flush_with_all_params(self, async_client: AsyncEveros) ->
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
- async def test_raw_response_flush(self, async_client: AsyncEveros) -> None:
+ async def test_raw_response_flush(self, async_client: AsyncEverOS) -> None:
response = await async_client.v1.memories.agent.with_raw_response.flush(
user_id="user_id",
)
@@ -259,7 +259,7 @@ async def test_raw_response_flush(self, async_client: AsyncEveros) -> None:
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
- async def test_streaming_response_flush(self, async_client: AsyncEveros) -> None:
+ async def test_streaming_response_flush(self, async_client: AsyncEverOS) -> None:
async with async_client.v1.memories.agent.with_streaming_response.flush(
user_id="user_id",
) as response:
diff --git a/tests/api_resources/v1/memories/test_group.py b/tests/api_resources/v1/memories/test_group.py
index 5fd97f5..73b0d08 100644
--- a/tests/api_resources/v1/memories/test_group.py
+++ b/tests/api_resources/v1/memories/test_group.py
@@ -7,7 +7,7 @@
import pytest
-from everos import Everos, AsyncEveros
+from everos import EverOS, AsyncEverOS
from tests.utils import assert_matches_type
from everos.types.v1 import AddResponse, FlushResponse
@@ -19,8 +19,8 @@ class TestGroup:
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
- def test_method_create(self, client: Everos) -> None:
- group = client.v1.memories.group.create(
+ def test_method_add(self, client: EverOS) -> None:
+ group = client.v1.memories.group.add(
group_id="group_id",
messages=[
{
@@ -35,8 +35,8 @@ def test_method_create(self, client: Everos) -> None:
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
- def test_method_create_with_all_params(self, client: Everos) -> None:
- group = client.v1.memories.group.create(
+ def test_method_add_with_all_params(self, client: EverOS) -> None:
+ group = client.v1.memories.group.add(
group_id="group_id",
messages=[
{
@@ -55,8 +55,8 @@ def test_method_create_with_all_params(self, client: Everos) -> None:
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
- def test_raw_response_create(self, client: Everos) -> None:
- response = client.v1.memories.group.with_raw_response.create(
+ def test_raw_response_add(self, client: EverOS) -> None:
+ response = client.v1.memories.group.with_raw_response.add(
group_id="group_id",
messages=[
{
@@ -75,8 +75,8 @@ def test_raw_response_create(self, client: Everos) -> None:
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
- def test_streaming_response_create(self, client: Everos) -> None:
- with client.v1.memories.group.with_streaming_response.create(
+ def test_streaming_response_add(self, client: EverOS) -> None:
+ with client.v1.memories.group.with_streaming_response.add(
group_id="group_id",
messages=[
{
@@ -97,7 +97,7 @@ def test_streaming_response_create(self, client: Everos) -> None:
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
- def test_method_flush(self, client: Everos) -> None:
+ def test_method_flush(self, client: EverOS) -> None:
group = client.v1.memories.group.flush(
group_id="group_id",
)
@@ -105,7 +105,7 @@ def test_method_flush(self, client: Everos) -> None:
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
- def test_raw_response_flush(self, client: Everos) -> None:
+ def test_raw_response_flush(self, client: EverOS) -> None:
response = client.v1.memories.group.with_raw_response.flush(
group_id="group_id",
)
@@ -117,7 +117,7 @@ def test_raw_response_flush(self, client: Everos) -> None:
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
- def test_streaming_response_flush(self, client: Everos) -> None:
+ def test_streaming_response_flush(self, client: EverOS) -> None:
with client.v1.memories.group.with_streaming_response.flush(
group_id="group_id",
) as response:
@@ -137,8 +137,8 @@ class TestAsyncGroup:
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
- async def test_method_create(self, async_client: AsyncEveros) -> None:
- group = await async_client.v1.memories.group.create(
+ async def test_method_add(self, async_client: AsyncEverOS) -> None:
+ group = await async_client.v1.memories.group.add(
group_id="group_id",
messages=[
{
@@ -153,8 +153,8 @@ async def test_method_create(self, async_client: AsyncEveros) -> None:
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
- async def test_method_create_with_all_params(self, async_client: AsyncEveros) -> None:
- group = await async_client.v1.memories.group.create(
+ async def test_method_add_with_all_params(self, async_client: AsyncEverOS) -> None:
+ group = await async_client.v1.memories.group.add(
group_id="group_id",
messages=[
{
@@ -173,8 +173,8 @@ async def test_method_create_with_all_params(self, async_client: AsyncEveros) ->
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
- async def test_raw_response_create(self, async_client: AsyncEveros) -> None:
- response = await async_client.v1.memories.group.with_raw_response.create(
+ async def test_raw_response_add(self, async_client: AsyncEverOS) -> None:
+ response = await async_client.v1.memories.group.with_raw_response.add(
group_id="group_id",
messages=[
{
@@ -193,8 +193,8 @@ async def test_raw_response_create(self, async_client: AsyncEveros) -> None:
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
- async def test_streaming_response_create(self, async_client: AsyncEveros) -> None:
- async with async_client.v1.memories.group.with_streaming_response.create(
+ async def test_streaming_response_add(self, async_client: AsyncEverOS) -> None:
+ async with async_client.v1.memories.group.with_streaming_response.add(
group_id="group_id",
messages=[
{
@@ -215,7 +215,7 @@ async def test_streaming_response_create(self, async_client: AsyncEveros) -> Non
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
- async def test_method_flush(self, async_client: AsyncEveros) -> None:
+ async def test_method_flush(self, async_client: AsyncEverOS) -> None:
group = await async_client.v1.memories.group.flush(
group_id="group_id",
)
@@ -223,7 +223,7 @@ async def test_method_flush(self, async_client: AsyncEveros) -> None:
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
- async def test_raw_response_flush(self, async_client: AsyncEveros) -> None:
+ async def test_raw_response_flush(self, async_client: AsyncEverOS) -> None:
response = await async_client.v1.memories.group.with_raw_response.flush(
group_id="group_id",
)
@@ -235,7 +235,7 @@ async def test_raw_response_flush(self, async_client: AsyncEveros) -> None:
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
- async def test_streaming_response_flush(self, async_client: AsyncEveros) -> None:
+ async def test_streaming_response_flush(self, async_client: AsyncEverOS) -> None:
async with async_client.v1.memories.group.with_streaming_response.flush(
group_id="group_id",
) as response:
diff --git a/tests/api_resources/v1/test_groups.py b/tests/api_resources/v1/test_groups.py
index 32c00ef..c02783f 100644
--- a/tests/api_resources/v1/test_groups.py
+++ b/tests/api_resources/v1/test_groups.py
@@ -7,7 +7,7 @@
import pytest
-from everos import Everos, AsyncEveros
+from everos import EverOS, AsyncEverOS
from tests.utils import assert_matches_type
from everos.types.v1 import GroupAPIResponse
@@ -19,7 +19,51 @@ class TestGroups:
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
- def test_method_retrieve(self, client: Everos) -> None:
+ def test_method_create(self, client: EverOS) -> None:
+ group = client.v1.groups.create(
+ group_id="group_abc",
+ )
+ assert_matches_type(GroupAPIResponse, group, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_method_create_with_all_params(self, client: EverOS) -> None:
+ group = client.v1.groups.create(
+ group_id="group_abc",
+ description="Weekly sync on Project X",
+ name="Project Discussion",
+ )
+ assert_matches_type(GroupAPIResponse, group, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_raw_response_create(self, client: EverOS) -> None:
+ response = client.v1.groups.with_raw_response.create(
+ group_id="group_abc",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ group = response.parse()
+ assert_matches_type(GroupAPIResponse, group, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_streaming_response_create(self, client: EverOS) -> None:
+ with client.v1.groups.with_streaming_response.create(
+ group_id="group_abc",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ group = response.parse()
+ assert_matches_type(GroupAPIResponse, group, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_method_retrieve(self, client: EverOS) -> None:
group = client.v1.groups.retrieve(
"group_abc",
)
@@ -27,7 +71,7 @@ def test_method_retrieve(self, client: Everos) -> None:
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
- def test_raw_response_retrieve(self, client: Everos) -> None:
+ def test_raw_response_retrieve(self, client: EverOS) -> None:
response = client.v1.groups.with_raw_response.retrieve(
"group_abc",
)
@@ -39,7 +83,7 @@ def test_raw_response_retrieve(self, client: Everos) -> None:
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
- def test_streaming_response_retrieve(self, client: Everos) -> None:
+ def test_streaming_response_retrieve(self, client: EverOS) -> None:
with client.v1.groups.with_streaming_response.retrieve(
"group_abc",
) as response:
@@ -53,7 +97,7 @@ def test_streaming_response_retrieve(self, client: Everos) -> None:
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
- def test_path_params_retrieve(self, client: Everos) -> None:
+ def test_path_params_retrieve(self, client: EverOS) -> None:
with pytest.raises(ValueError, match=r"Expected a non-empty value for `group_id` but received ''"):
client.v1.groups.with_raw_response.retrieve(
"",
@@ -61,16 +105,16 @@ def test_path_params_retrieve(self, client: Everos) -> None:
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
- def test_method_update(self, client: Everos) -> None:
- group = client.v1.groups.update(
+ def test_method_patch(self, client: EverOS) -> None:
+ group = client.v1.groups.patch(
group_id="group_abc",
)
assert_matches_type(GroupAPIResponse, group, path=["response"])
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
- def test_method_update_with_all_params(self, client: Everos) -> None:
- group = client.v1.groups.update(
+ def test_method_patch_with_all_params(self, client: EverOS) -> None:
+ group = client.v1.groups.patch(
group_id="group_abc",
description="description",
name="name",
@@ -79,8 +123,8 @@ def test_method_update_with_all_params(self, client: Everos) -> None:
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
- def test_raw_response_update(self, client: Everos) -> None:
- response = client.v1.groups.with_raw_response.update(
+ def test_raw_response_patch(self, client: EverOS) -> None:
+ response = client.v1.groups.with_raw_response.patch(
group_id="group_abc",
)
@@ -91,8 +135,8 @@ def test_raw_response_update(self, client: Everos) -> None:
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
- def test_streaming_response_update(self, client: Everos) -> None:
- with client.v1.groups.with_streaming_response.update(
+ def test_streaming_response_patch(self, client: EverOS) -> None:
+ with client.v1.groups.with_streaming_response.patch(
group_id="group_abc",
) as response:
assert not response.is_closed
@@ -105,24 +149,30 @@ def test_streaming_response_update(self, client: Everos) -> None:
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
- def test_path_params_update(self, client: Everos) -> None:
+ def test_path_params_patch(self, client: EverOS) -> None:
with pytest.raises(ValueError, match=r"Expected a non-empty value for `group_id` but received ''"):
- client.v1.groups.with_raw_response.update(
+ client.v1.groups.with_raw_response.patch(
group_id="",
)
+
+class TestAsyncGroups:
+ parametrize = pytest.mark.parametrize(
+ "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
+ )
+
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
- def test_method_create_or_update(self, client: Everos) -> None:
- group = client.v1.groups.create_or_update(
+ async def test_method_create(self, async_client: AsyncEverOS) -> None:
+ group = await async_client.v1.groups.create(
group_id="group_abc",
)
assert_matches_type(GroupAPIResponse, group, path=["response"])
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
- def test_method_create_or_update_with_all_params(self, client: Everos) -> None:
- group = client.v1.groups.create_or_update(
+ async def test_method_create_with_all_params(self, async_client: AsyncEverOS) -> None:
+ group = await async_client.v1.groups.create(
group_id="group_abc",
description="Weekly sync on Project X",
name="Project Discussion",
@@ -131,39 +181,33 @@ def test_method_create_or_update_with_all_params(self, client: Everos) -> None:
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
- def test_raw_response_create_or_update(self, client: Everos) -> None:
- response = client.v1.groups.with_raw_response.create_or_update(
+ async def test_raw_response_create(self, async_client: AsyncEverOS) -> None:
+ response = await async_client.v1.groups.with_raw_response.create(
group_id="group_abc",
)
assert response.is_closed is True
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
- group = response.parse()
+ group = await response.parse()
assert_matches_type(GroupAPIResponse, group, path=["response"])
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
- def test_streaming_response_create_or_update(self, client: Everos) -> None:
- with client.v1.groups.with_streaming_response.create_or_update(
+ async def test_streaming_response_create(self, async_client: AsyncEverOS) -> None:
+ async with async_client.v1.groups.with_streaming_response.create(
group_id="group_abc",
) as response:
assert not response.is_closed
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
- group = response.parse()
+ group = await response.parse()
assert_matches_type(GroupAPIResponse, group, path=["response"])
assert cast(Any, response.is_closed) is True
-
-class TestAsyncGroups:
- parametrize = pytest.mark.parametrize(
- "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
- )
-
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
- async def test_method_retrieve(self, async_client: AsyncEveros) -> None:
+ async def test_method_retrieve(self, async_client: AsyncEverOS) -> None:
group = await async_client.v1.groups.retrieve(
"group_abc",
)
@@ -171,7 +215,7 @@ async def test_method_retrieve(self, async_client: AsyncEveros) -> None:
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
- async def test_raw_response_retrieve(self, async_client: AsyncEveros) -> None:
+ async def test_raw_response_retrieve(self, async_client: AsyncEverOS) -> None:
response = await async_client.v1.groups.with_raw_response.retrieve(
"group_abc",
)
@@ -183,7 +227,7 @@ async def test_raw_response_retrieve(self, async_client: AsyncEveros) -> None:
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
- async def test_streaming_response_retrieve(self, async_client: AsyncEveros) -> None:
+ async def test_streaming_response_retrieve(self, async_client: AsyncEverOS) -> None:
async with async_client.v1.groups.with_streaming_response.retrieve(
"group_abc",
) as response:
@@ -197,7 +241,7 @@ async def test_streaming_response_retrieve(self, async_client: AsyncEveros) -> N
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
- async def test_path_params_retrieve(self, async_client: AsyncEveros) -> None:
+ async def test_path_params_retrieve(self, async_client: AsyncEverOS) -> None:
with pytest.raises(ValueError, match=r"Expected a non-empty value for `group_id` but received ''"):
await async_client.v1.groups.with_raw_response.retrieve(
"",
@@ -205,16 +249,16 @@ async def test_path_params_retrieve(self, async_client: AsyncEveros) -> None:
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
- async def test_method_update(self, async_client: AsyncEveros) -> None:
- group = await async_client.v1.groups.update(
+ async def test_method_patch(self, async_client: AsyncEverOS) -> None:
+ group = await async_client.v1.groups.patch(
group_id="group_abc",
)
assert_matches_type(GroupAPIResponse, group, path=["response"])
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
- async def test_method_update_with_all_params(self, async_client: AsyncEveros) -> None:
- group = await async_client.v1.groups.update(
+ async def test_method_patch_with_all_params(self, async_client: AsyncEverOS) -> None:
+ group = await async_client.v1.groups.patch(
group_id="group_abc",
description="description",
name="name",
@@ -223,8 +267,8 @@ async def test_method_update_with_all_params(self, async_client: AsyncEveros) ->
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
- async def test_raw_response_update(self, async_client: AsyncEveros) -> None:
- response = await async_client.v1.groups.with_raw_response.update(
+ async def test_raw_response_patch(self, async_client: AsyncEverOS) -> None:
+ response = await async_client.v1.groups.with_raw_response.patch(
group_id="group_abc",
)
@@ -235,8 +279,8 @@ async def test_raw_response_update(self, async_client: AsyncEveros) -> None:
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
- async def test_streaming_response_update(self, async_client: AsyncEveros) -> None:
- async with async_client.v1.groups.with_streaming_response.update(
+ async def test_streaming_response_patch(self, async_client: AsyncEverOS) -> None:
+ async with async_client.v1.groups.with_streaming_response.patch(
group_id="group_abc",
) as response:
assert not response.is_closed
@@ -249,52 +293,8 @@ async def test_streaming_response_update(self, async_client: AsyncEveros) -> Non
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
- async def test_path_params_update(self, async_client: AsyncEveros) -> None:
+ async def test_path_params_patch(self, async_client: AsyncEverOS) -> None:
with pytest.raises(ValueError, match=r"Expected a non-empty value for `group_id` but received ''"):
- await async_client.v1.groups.with_raw_response.update(
+ await async_client.v1.groups.with_raw_response.patch(
group_id="",
)
-
- @pytest.mark.skip(reason="Mock server tests are disabled")
- @parametrize
- async def test_method_create_or_update(self, async_client: AsyncEveros) -> None:
- group = await async_client.v1.groups.create_or_update(
- group_id="group_abc",
- )
- assert_matches_type(GroupAPIResponse, group, path=["response"])
-
- @pytest.mark.skip(reason="Mock server tests are disabled")
- @parametrize
- async def test_method_create_or_update_with_all_params(self, async_client: AsyncEveros) -> None:
- group = await async_client.v1.groups.create_or_update(
- group_id="group_abc",
- description="Weekly sync on Project X",
- name="Project Discussion",
- )
- assert_matches_type(GroupAPIResponse, group, path=["response"])
-
- @pytest.mark.skip(reason="Mock server tests are disabled")
- @parametrize
- async def test_raw_response_create_or_update(self, async_client: AsyncEveros) -> None:
- response = await async_client.v1.groups.with_raw_response.create_or_update(
- group_id="group_abc",
- )
-
- assert response.is_closed is True
- assert response.http_request.headers.get("X-Stainless-Lang") == "python"
- group = await response.parse()
- assert_matches_type(GroupAPIResponse, group, path=["response"])
-
- @pytest.mark.skip(reason="Mock server tests are disabled")
- @parametrize
- async def test_streaming_response_create_or_update(self, async_client: AsyncEveros) -> None:
- async with async_client.v1.groups.with_streaming_response.create_or_update(
- group_id="group_abc",
- ) as response:
- assert not response.is_closed
- assert response.http_request.headers.get("X-Stainless-Lang") == "python"
-
- group = await response.parse()
- assert_matches_type(GroupAPIResponse, group, path=["response"])
-
- assert cast(Any, response.is_closed) is True
diff --git a/tests/api_resources/v1/test_memories.py b/tests/api_resources/v1/test_memories.py
index 37f5e8c..aafc0fb 100644
--- a/tests/api_resources/v1/test_memories.py
+++ b/tests/api_resources/v1/test_memories.py
@@ -7,13 +7,13 @@
import pytest
-from everos import Everos, AsyncEveros
+from everos import EverOS, AsyncEverOS
from tests.utils import assert_matches_type
from everos.types.v1 import (
AddResponse,
FlushResponse,
- MemoryGetResponse,
- MemorySearchResponse,
+ GetMemoriesResponse,
+ SearchMemoriesResponse,
)
base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010")
@@ -24,8 +24,48 @@ class TestMemories:
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
- def test_method_create(self, client: Everos) -> None:
- memory = client.v1.memories.create(
+ def test_method_delete(self, client: EverOS) -> None:
+ memory = client.v1.memories.delete()
+ assert memory is None
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_method_delete_with_all_params(self, client: EverOS) -> None:
+ memory = client.v1.memories.delete(
+ group_id="group_id",
+ memory_id="memory_id",
+ sender_id="sender_id",
+ session_id="session_id",
+ user_id="user_id",
+ )
+ assert memory is None
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_raw_response_delete(self, client: EverOS) -> None:
+ response = client.v1.memories.with_raw_response.delete()
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ memory = response.parse()
+ assert memory is None
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_streaming_response_delete(self, client: EverOS) -> None:
+ with client.v1.memories.with_streaming_response.delete() as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ memory = response.parse()
+ assert memory is None
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_method_add(self, client: EverOS) -> None:
+ memory = client.v1.memories.add(
messages=[
{
"content": "x",
@@ -39,8 +79,8 @@ def test_method_create(self, client: Everos) -> None:
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
- def test_method_create_with_all_params(self, client: Everos) -> None:
- memory = client.v1.memories.create(
+ def test_method_add_with_all_params(self, client: EverOS) -> None:
+ memory = client.v1.memories.add(
messages=[
{
"content": "x",
@@ -57,8 +97,8 @@ def test_method_create_with_all_params(self, client: Everos) -> None:
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
- def test_raw_response_create(self, client: Everos) -> None:
- response = client.v1.memories.with_raw_response.create(
+ def test_raw_response_add(self, client: EverOS) -> None:
+ response = client.v1.memories.with_raw_response.add(
messages=[
{
"content": "x",
@@ -76,8 +116,8 @@ def test_raw_response_create(self, client: Everos) -> None:
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
- def test_streaming_response_create(self, client: Everos) -> None:
- with client.v1.memories.with_streaming_response.create(
+ def test_streaming_response_add(self, client: EverOS) -> None:
+ with client.v1.memories.with_streaming_response.add(
messages=[
{
"content": "x",
@@ -97,47 +137,7 @@ def test_streaming_response_create(self, client: Everos) -> None:
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
- def test_method_delete(self, client: Everos) -> None:
- memory = client.v1.memories.delete()
- assert memory is None
-
- @pytest.mark.skip(reason="Mock server tests are disabled")
- @parametrize
- def test_method_delete_with_all_params(self, client: Everos) -> None:
- memory = client.v1.memories.delete(
- group_id="group_id",
- memory_id="memory_id",
- sender_id="sender_id",
- session_id="session_id",
- user_id="user_id",
- )
- assert memory is None
-
- @pytest.mark.skip(reason="Mock server tests are disabled")
- @parametrize
- def test_raw_response_delete(self, client: Everos) -> None:
- response = client.v1.memories.with_raw_response.delete()
-
- assert response.is_closed is True
- assert response.http_request.headers.get("X-Stainless-Lang") == "python"
- memory = response.parse()
- assert memory is None
-
- @pytest.mark.skip(reason="Mock server tests are disabled")
- @parametrize
- def test_streaming_response_delete(self, client: Everos) -> None:
- with client.v1.memories.with_streaming_response.delete() as response:
- assert not response.is_closed
- assert response.http_request.headers.get("X-Stainless-Lang") == "python"
-
- memory = response.parse()
- assert memory is None
-
- assert cast(Any, response.is_closed) is True
-
- @pytest.mark.skip(reason="Mock server tests are disabled")
- @parametrize
- def test_method_flush(self, client: Everos) -> None:
+ def test_method_flush(self, client: EverOS) -> None:
memory = client.v1.memories.flush(
user_id="user_id",
)
@@ -145,7 +145,7 @@ def test_method_flush(self, client: Everos) -> None:
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
- def test_method_flush_with_all_params(self, client: Everos) -> None:
+ def test_method_flush_with_all_params(self, client: EverOS) -> None:
memory = client.v1.memories.flush(
user_id="user_id",
session_id="session_id",
@@ -154,7 +154,7 @@ def test_method_flush_with_all_params(self, client: Everos) -> None:
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
- def test_raw_response_flush(self, client: Everos) -> None:
+ def test_raw_response_flush(self, client: EverOS) -> None:
response = client.v1.memories.with_raw_response.flush(
user_id="user_id",
)
@@ -166,7 +166,7 @@ def test_raw_response_flush(self, client: Everos) -> None:
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
- def test_streaming_response_flush(self, client: Everos) -> None:
+ def test_streaming_response_flush(self, client: EverOS) -> None:
with client.v1.memories.with_streaming_response.flush(
user_id="user_id",
) as response:
@@ -180,16 +180,16 @@ def test_streaming_response_flush(self, client: Everos) -> None:
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
- def test_method_get(self, client: Everos) -> None:
+ def test_method_get(self, client: EverOS) -> None:
memory = client.v1.memories.get(
filters={"foo": "bar"},
memory_type="episodic_memory",
)
- assert_matches_type(MemoryGetResponse, memory, path=["response"])
+ assert_matches_type(GetMemoriesResponse, memory, path=["response"])
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
- def test_method_get_with_all_params(self, client: Everos) -> None:
+ def test_method_get_with_all_params(self, client: EverOS) -> None:
memory = client.v1.memories.get(
filters={"foo": "bar"},
memory_type="episodic_memory",
@@ -198,11 +198,11 @@ def test_method_get_with_all_params(self, client: Everos) -> None:
rank_by="rank_by",
rank_order="asc",
)
- assert_matches_type(MemoryGetResponse, memory, path=["response"])
+ assert_matches_type(GetMemoriesResponse, memory, path=["response"])
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
- def test_raw_response_get(self, client: Everos) -> None:
+ def test_raw_response_get(self, client: EverOS) -> None:
response = client.v1.memories.with_raw_response.get(
filters={"foo": "bar"},
memory_type="episodic_memory",
@@ -211,11 +211,11 @@ def test_raw_response_get(self, client: Everos) -> None:
assert response.is_closed is True
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
memory = response.parse()
- assert_matches_type(MemoryGetResponse, memory, path=["response"])
+ assert_matches_type(GetMemoriesResponse, memory, path=["response"])
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
- def test_streaming_response_get(self, client: Everos) -> None:
+ def test_streaming_response_get(self, client: EverOS) -> None:
with client.v1.memories.with_streaming_response.get(
filters={"foo": "bar"},
memory_type="episodic_memory",
@@ -224,22 +224,22 @@ def test_streaming_response_get(self, client: Everos) -> None:
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
memory = response.parse()
- assert_matches_type(MemoryGetResponse, memory, path=["response"])
+ assert_matches_type(GetMemoriesResponse, memory, path=["response"])
assert cast(Any, response.is_closed) is True
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
- def test_method_search(self, client: Everos) -> None:
+ def test_method_search(self, client: EverOS) -> None:
memory = client.v1.memories.search(
filters={"foo": "bar"},
query="What did Alice say about the project?",
)
- assert_matches_type(MemorySearchResponse, memory, path=["response"])
+ assert_matches_type(SearchMemoriesResponse, memory, path=["response"])
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
- def test_method_search_with_all_params(self, client: Everos) -> None:
+ def test_method_search_with_all_params(self, client: EverOS) -> None:
memory = client.v1.memories.search(
filters={"foo": "bar"},
query="What did Alice say about the project?",
@@ -249,11 +249,11 @@ def test_method_search_with_all_params(self, client: Everos) -> None:
radius=0,
top_k=-1,
)
- assert_matches_type(MemorySearchResponse, memory, path=["response"])
+ assert_matches_type(SearchMemoriesResponse, memory, path=["response"])
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
- def test_raw_response_search(self, client: Everos) -> None:
+ def test_raw_response_search(self, client: EverOS) -> None:
response = client.v1.memories.with_raw_response.search(
filters={"foo": "bar"},
query="What did Alice say about the project?",
@@ -262,11 +262,11 @@ def test_raw_response_search(self, client: Everos) -> None:
assert response.is_closed is True
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
memory = response.parse()
- assert_matches_type(MemorySearchResponse, memory, path=["response"])
+ assert_matches_type(SearchMemoriesResponse, memory, path=["response"])
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
- def test_streaming_response_search(self, client: Everos) -> None:
+ def test_streaming_response_search(self, client: EverOS) -> None:
with client.v1.memories.with_streaming_response.search(
filters={"foo": "bar"},
query="What did Alice say about the project?",
@@ -275,7 +275,7 @@ def test_streaming_response_search(self, client: Everos) -> None:
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
memory = response.parse()
- assert_matches_type(MemorySearchResponse, memory, path=["response"])
+ assert_matches_type(SearchMemoriesResponse, memory, path=["response"])
assert cast(Any, response.is_closed) is True
@@ -287,8 +287,48 @@ class TestAsyncMemories:
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
- async def test_method_create(self, async_client: AsyncEveros) -> None:
- memory = await async_client.v1.memories.create(
+ async def test_method_delete(self, async_client: AsyncEverOS) -> None:
+ memory = await async_client.v1.memories.delete()
+ assert memory is None
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_method_delete_with_all_params(self, async_client: AsyncEverOS) -> None:
+ memory = await async_client.v1.memories.delete(
+ group_id="group_id",
+ memory_id="memory_id",
+ sender_id="sender_id",
+ session_id="session_id",
+ user_id="user_id",
+ )
+ assert memory is None
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_raw_response_delete(self, async_client: AsyncEverOS) -> None:
+ response = await async_client.v1.memories.with_raw_response.delete()
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ memory = await response.parse()
+ assert memory is None
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_streaming_response_delete(self, async_client: AsyncEverOS) -> None:
+ async with async_client.v1.memories.with_streaming_response.delete() as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ memory = await response.parse()
+ assert memory is None
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_method_add(self, async_client: AsyncEverOS) -> None:
+ memory = await async_client.v1.memories.add(
messages=[
{
"content": "x",
@@ -302,8 +342,8 @@ async def test_method_create(self, async_client: AsyncEveros) -> None:
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
- async def test_method_create_with_all_params(self, async_client: AsyncEveros) -> None:
- memory = await async_client.v1.memories.create(
+ async def test_method_add_with_all_params(self, async_client: AsyncEverOS) -> None:
+ memory = await async_client.v1.memories.add(
messages=[
{
"content": "x",
@@ -320,8 +360,8 @@ async def test_method_create_with_all_params(self, async_client: AsyncEveros) ->
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
- async def test_raw_response_create(self, async_client: AsyncEveros) -> None:
- response = await async_client.v1.memories.with_raw_response.create(
+ async def test_raw_response_add(self, async_client: AsyncEverOS) -> None:
+ response = await async_client.v1.memories.with_raw_response.add(
messages=[
{
"content": "x",
@@ -339,8 +379,8 @@ async def test_raw_response_create(self, async_client: AsyncEveros) -> None:
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
- async def test_streaming_response_create(self, async_client: AsyncEveros) -> None:
- async with async_client.v1.memories.with_streaming_response.create(
+ async def test_streaming_response_add(self, async_client: AsyncEverOS) -> None:
+ async with async_client.v1.memories.with_streaming_response.add(
messages=[
{
"content": "x",
@@ -360,47 +400,7 @@ async def test_streaming_response_create(self, async_client: AsyncEveros) -> Non
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
- async def test_method_delete(self, async_client: AsyncEveros) -> None:
- memory = await async_client.v1.memories.delete()
- assert memory is None
-
- @pytest.mark.skip(reason="Mock server tests are disabled")
- @parametrize
- async def test_method_delete_with_all_params(self, async_client: AsyncEveros) -> None:
- memory = await async_client.v1.memories.delete(
- group_id="group_id",
- memory_id="memory_id",
- sender_id="sender_id",
- session_id="session_id",
- user_id="user_id",
- )
- assert memory is None
-
- @pytest.mark.skip(reason="Mock server tests are disabled")
- @parametrize
- async def test_raw_response_delete(self, async_client: AsyncEveros) -> None:
- response = await async_client.v1.memories.with_raw_response.delete()
-
- assert response.is_closed is True
- assert response.http_request.headers.get("X-Stainless-Lang") == "python"
- memory = await response.parse()
- assert memory is None
-
- @pytest.mark.skip(reason="Mock server tests are disabled")
- @parametrize
- async def test_streaming_response_delete(self, async_client: AsyncEveros) -> None:
- async with async_client.v1.memories.with_streaming_response.delete() as response:
- assert not response.is_closed
- assert response.http_request.headers.get("X-Stainless-Lang") == "python"
-
- memory = await response.parse()
- assert memory is None
-
- assert cast(Any, response.is_closed) is True
-
- @pytest.mark.skip(reason="Mock server tests are disabled")
- @parametrize
- async def test_method_flush(self, async_client: AsyncEveros) -> None:
+ async def test_method_flush(self, async_client: AsyncEverOS) -> None:
memory = await async_client.v1.memories.flush(
user_id="user_id",
)
@@ -408,7 +408,7 @@ async def test_method_flush(self, async_client: AsyncEveros) -> None:
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
- async def test_method_flush_with_all_params(self, async_client: AsyncEveros) -> None:
+ async def test_method_flush_with_all_params(self, async_client: AsyncEverOS) -> None:
memory = await async_client.v1.memories.flush(
user_id="user_id",
session_id="session_id",
@@ -417,7 +417,7 @@ async def test_method_flush_with_all_params(self, async_client: AsyncEveros) ->
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
- async def test_raw_response_flush(self, async_client: AsyncEveros) -> None:
+ async def test_raw_response_flush(self, async_client: AsyncEverOS) -> None:
response = await async_client.v1.memories.with_raw_response.flush(
user_id="user_id",
)
@@ -429,7 +429,7 @@ async def test_raw_response_flush(self, async_client: AsyncEveros) -> None:
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
- async def test_streaming_response_flush(self, async_client: AsyncEveros) -> None:
+ async def test_streaming_response_flush(self, async_client: AsyncEverOS) -> None:
async with async_client.v1.memories.with_streaming_response.flush(
user_id="user_id",
) as response:
@@ -443,16 +443,16 @@ async def test_streaming_response_flush(self, async_client: AsyncEveros) -> None
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
- async def test_method_get(self, async_client: AsyncEveros) -> None:
+ async def test_method_get(self, async_client: AsyncEverOS) -> None:
memory = await async_client.v1.memories.get(
filters={"foo": "bar"},
memory_type="episodic_memory",
)
- assert_matches_type(MemoryGetResponse, memory, path=["response"])
+ assert_matches_type(GetMemoriesResponse, memory, path=["response"])
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
- async def test_method_get_with_all_params(self, async_client: AsyncEveros) -> None:
+ async def test_method_get_with_all_params(self, async_client: AsyncEverOS) -> None:
memory = await async_client.v1.memories.get(
filters={"foo": "bar"},
memory_type="episodic_memory",
@@ -461,11 +461,11 @@ async def test_method_get_with_all_params(self, async_client: AsyncEveros) -> No
rank_by="rank_by",
rank_order="asc",
)
- assert_matches_type(MemoryGetResponse, memory, path=["response"])
+ assert_matches_type(GetMemoriesResponse, memory, path=["response"])
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
- async def test_raw_response_get(self, async_client: AsyncEveros) -> None:
+ async def test_raw_response_get(self, async_client: AsyncEverOS) -> None:
response = await async_client.v1.memories.with_raw_response.get(
filters={"foo": "bar"},
memory_type="episodic_memory",
@@ -474,11 +474,11 @@ async def test_raw_response_get(self, async_client: AsyncEveros) -> None:
assert response.is_closed is True
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
memory = await response.parse()
- assert_matches_type(MemoryGetResponse, memory, path=["response"])
+ assert_matches_type(GetMemoriesResponse, memory, path=["response"])
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
- async def test_streaming_response_get(self, async_client: AsyncEveros) -> None:
+ async def test_streaming_response_get(self, async_client: AsyncEverOS) -> None:
async with async_client.v1.memories.with_streaming_response.get(
filters={"foo": "bar"},
memory_type="episodic_memory",
@@ -487,22 +487,22 @@ async def test_streaming_response_get(self, async_client: AsyncEveros) -> None:
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
memory = await response.parse()
- assert_matches_type(MemoryGetResponse, memory, path=["response"])
+ assert_matches_type(GetMemoriesResponse, memory, path=["response"])
assert cast(Any, response.is_closed) is True
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
- async def test_method_search(self, async_client: AsyncEveros) -> None:
+ async def test_method_search(self, async_client: AsyncEverOS) -> None:
memory = await async_client.v1.memories.search(
filters={"foo": "bar"},
query="What did Alice say about the project?",
)
- assert_matches_type(MemorySearchResponse, memory, path=["response"])
+ assert_matches_type(SearchMemoriesResponse, memory, path=["response"])
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
- async def test_method_search_with_all_params(self, async_client: AsyncEveros) -> None:
+ async def test_method_search_with_all_params(self, async_client: AsyncEverOS) -> None:
memory = await async_client.v1.memories.search(
filters={"foo": "bar"},
query="What did Alice say about the project?",
@@ -512,11 +512,11 @@ async def test_method_search_with_all_params(self, async_client: AsyncEveros) ->
radius=0,
top_k=-1,
)
- assert_matches_type(MemorySearchResponse, memory, path=["response"])
+ assert_matches_type(SearchMemoriesResponse, memory, path=["response"])
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
- async def test_raw_response_search(self, async_client: AsyncEveros) -> None:
+ async def test_raw_response_search(self, async_client: AsyncEverOS) -> None:
response = await async_client.v1.memories.with_raw_response.search(
filters={"foo": "bar"},
query="What did Alice say about the project?",
@@ -525,11 +525,11 @@ async def test_raw_response_search(self, async_client: AsyncEveros) -> None:
assert response.is_closed is True
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
memory = await response.parse()
- assert_matches_type(MemorySearchResponse, memory, path=["response"])
+ assert_matches_type(SearchMemoriesResponse, memory, path=["response"])
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
- async def test_streaming_response_search(self, async_client: AsyncEveros) -> None:
+ async def test_streaming_response_search(self, async_client: AsyncEverOS) -> None:
async with async_client.v1.memories.with_streaming_response.search(
filters={"foo": "bar"},
query="What did Alice say about the project?",
@@ -538,6 +538,6 @@ async def test_streaming_response_search(self, async_client: AsyncEveros) -> Non
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
memory = await response.parse()
- assert_matches_type(MemorySearchResponse, memory, path=["response"])
+ assert_matches_type(SearchMemoriesResponse, memory, path=["response"])
assert cast(Any, response.is_closed) is True
diff --git a/tests/api_resources/v1/test_object.py b/tests/api_resources/v1/test_object.py
index 0f43a2c..583717b 100644
--- a/tests/api_resources/v1/test_object.py
+++ b/tests/api_resources/v1/test_object.py
@@ -7,9 +7,9 @@
import pytest
-from everos import Everos, AsyncEveros
+from everos import EverOS, AsyncEverOS
from tests.utils import assert_matches_type
-from everos.types.v1 import ObjectGetPresignedURLResponse
+from everos.types.v1 import ObjectSignResponse
base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010")
@@ -19,8 +19,8 @@ class TestObject:
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
- def test_method_get_presigned_url(self, client: Everos) -> None:
- object_ = client.v1.object.get_presigned_url(
+ def test_method_sign(self, client: EverOS) -> None:
+ object_ = client.v1.object.sign(
object_list=[
{
"file_id": "file_abc123",
@@ -29,12 +29,12 @@ def test_method_get_presigned_url(self, client: Everos) -> None:
}
],
)
- assert_matches_type(ObjectGetPresignedURLResponse, object_, path=["response"])
+ assert_matches_type(ObjectSignResponse, object_, path=["response"])
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
- def test_raw_response_get_presigned_url(self, client: Everos) -> None:
- response = client.v1.object.with_raw_response.get_presigned_url(
+ def test_raw_response_sign(self, client: EverOS) -> None:
+ response = client.v1.object.with_raw_response.sign(
object_list=[
{
"file_id": "file_abc123",
@@ -47,12 +47,12 @@ def test_raw_response_get_presigned_url(self, client: Everos) -> None:
assert response.is_closed is True
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
object_ = response.parse()
- assert_matches_type(ObjectGetPresignedURLResponse, object_, path=["response"])
+ assert_matches_type(ObjectSignResponse, object_, path=["response"])
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
- def test_streaming_response_get_presigned_url(self, client: Everos) -> None:
- with client.v1.object.with_streaming_response.get_presigned_url(
+ def test_streaming_response_sign(self, client: EverOS) -> None:
+ with client.v1.object.with_streaming_response.sign(
object_list=[
{
"file_id": "file_abc123",
@@ -65,7 +65,7 @@ def test_streaming_response_get_presigned_url(self, client: Everos) -> None:
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
object_ = response.parse()
- assert_matches_type(ObjectGetPresignedURLResponse, object_, path=["response"])
+ assert_matches_type(ObjectSignResponse, object_, path=["response"])
assert cast(Any, response.is_closed) is True
@@ -77,8 +77,8 @@ class TestAsyncObject:
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
- async def test_method_get_presigned_url(self, async_client: AsyncEveros) -> None:
- object_ = await async_client.v1.object.get_presigned_url(
+ async def test_method_sign(self, async_client: AsyncEverOS) -> None:
+ object_ = await async_client.v1.object.sign(
object_list=[
{
"file_id": "file_abc123",
@@ -87,12 +87,12 @@ async def test_method_get_presigned_url(self, async_client: AsyncEveros) -> None
}
],
)
- assert_matches_type(ObjectGetPresignedURLResponse, object_, path=["response"])
+ assert_matches_type(ObjectSignResponse, object_, path=["response"])
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
- async def test_raw_response_get_presigned_url(self, async_client: AsyncEveros) -> None:
- response = await async_client.v1.object.with_raw_response.get_presigned_url(
+ async def test_raw_response_sign(self, async_client: AsyncEverOS) -> None:
+ response = await async_client.v1.object.with_raw_response.sign(
object_list=[
{
"file_id": "file_abc123",
@@ -105,12 +105,12 @@ async def test_raw_response_get_presigned_url(self, async_client: AsyncEveros) -
assert response.is_closed is True
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
object_ = await response.parse()
- assert_matches_type(ObjectGetPresignedURLResponse, object_, path=["response"])
+ assert_matches_type(ObjectSignResponse, object_, path=["response"])
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
- async def test_streaming_response_get_presigned_url(self, async_client: AsyncEveros) -> None:
- async with async_client.v1.object.with_streaming_response.get_presigned_url(
+ async def test_streaming_response_sign(self, async_client: AsyncEverOS) -> None:
+ async with async_client.v1.object.with_streaming_response.sign(
object_list=[
{
"file_id": "file_abc123",
@@ -123,6 +123,6 @@ async def test_streaming_response_get_presigned_url(self, async_client: AsyncEve
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
object_ = await response.parse()
- assert_matches_type(ObjectGetPresignedURLResponse, object_, path=["response"])
+ assert_matches_type(ObjectSignResponse, object_, path=["response"])
assert cast(Any, response.is_closed) is True
diff --git a/tests/api_resources/v1/test_senders.py b/tests/api_resources/v1/test_senders.py
index 3303b98..f17fa02 100644
--- a/tests/api_resources/v1/test_senders.py
+++ b/tests/api_resources/v1/test_senders.py
@@ -7,7 +7,7 @@
import pytest
-from everos import Everos, AsyncEveros
+from everos import EverOS, AsyncEverOS
from tests.utils import assert_matches_type
from everos.types.v1 import SenderAPIResponse
@@ -19,7 +19,50 @@ class TestSenders:
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
- def test_method_retrieve(self, client: Everos) -> None:
+ def test_method_create(self, client: EverOS) -> None:
+ sender = client.v1.senders.create(
+ sender_id="user_123",
+ )
+ assert_matches_type(SenderAPIResponse, sender, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_method_create_with_all_params(self, client: EverOS) -> None:
+ sender = client.v1.senders.create(
+ sender_id="user_123",
+ name="Alice",
+ )
+ assert_matches_type(SenderAPIResponse, sender, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_raw_response_create(self, client: EverOS) -> None:
+ response = client.v1.senders.with_raw_response.create(
+ sender_id="user_123",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ sender = response.parse()
+ assert_matches_type(SenderAPIResponse, sender, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_streaming_response_create(self, client: EverOS) -> None:
+ with client.v1.senders.with_streaming_response.create(
+ sender_id="user_123",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ sender = response.parse()
+ assert_matches_type(SenderAPIResponse, sender, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_method_retrieve(self, client: EverOS) -> None:
sender = client.v1.senders.retrieve(
"user_123",
)
@@ -27,7 +70,7 @@ def test_method_retrieve(self, client: Everos) -> None:
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
- def test_raw_response_retrieve(self, client: Everos) -> None:
+ def test_raw_response_retrieve(self, client: EverOS) -> None:
response = client.v1.senders.with_raw_response.retrieve(
"user_123",
)
@@ -39,7 +82,7 @@ def test_raw_response_retrieve(self, client: Everos) -> None:
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
- def test_streaming_response_retrieve(self, client: Everos) -> None:
+ def test_streaming_response_retrieve(self, client: EverOS) -> None:
with client.v1.senders.with_streaming_response.retrieve(
"user_123",
) as response:
@@ -53,7 +96,7 @@ def test_streaming_response_retrieve(self, client: Everos) -> None:
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
- def test_path_params_retrieve(self, client: Everos) -> None:
+ def test_path_params_retrieve(self, client: EverOS) -> None:
with pytest.raises(ValueError, match=r"Expected a non-empty value for `sender_id` but received ''"):
client.v1.senders.with_raw_response.retrieve(
"",
@@ -61,16 +104,16 @@ def test_path_params_retrieve(self, client: Everos) -> None:
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
- def test_method_update(self, client: Everos) -> None:
- sender = client.v1.senders.update(
+ def test_method_patch(self, client: EverOS) -> None:
+ sender = client.v1.senders.patch(
sender_id="user_123",
)
assert_matches_type(SenderAPIResponse, sender, path=["response"])
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
- def test_method_update_with_all_params(self, client: Everos) -> None:
- sender = client.v1.senders.update(
+ def test_method_patch_with_all_params(self, client: EverOS) -> None:
+ sender = client.v1.senders.patch(
sender_id="user_123",
name="name",
)
@@ -78,8 +121,8 @@ def test_method_update_with_all_params(self, client: Everos) -> None:
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
- def test_raw_response_update(self, client: Everos) -> None:
- response = client.v1.senders.with_raw_response.update(
+ def test_raw_response_patch(self, client: EverOS) -> None:
+ response = client.v1.senders.with_raw_response.patch(
sender_id="user_123",
)
@@ -90,8 +133,8 @@ def test_raw_response_update(self, client: Everos) -> None:
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
- def test_streaming_response_update(self, client: Everos) -> None:
- with client.v1.senders.with_streaming_response.update(
+ def test_streaming_response_patch(self, client: EverOS) -> None:
+ with client.v1.senders.with_streaming_response.patch(
sender_id="user_123",
) as response:
assert not response.is_closed
@@ -104,24 +147,30 @@ def test_streaming_response_update(self, client: Everos) -> None:
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
- def test_path_params_update(self, client: Everos) -> None:
+ def test_path_params_patch(self, client: EverOS) -> None:
with pytest.raises(ValueError, match=r"Expected a non-empty value for `sender_id` but received ''"):
- client.v1.senders.with_raw_response.update(
+ client.v1.senders.with_raw_response.patch(
sender_id="",
)
+
+class TestAsyncSenders:
+ parametrize = pytest.mark.parametrize(
+ "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
+ )
+
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
- def test_method_create_or_update(self, client: Everos) -> None:
- sender = client.v1.senders.create_or_update(
+ async def test_method_create(self, async_client: AsyncEverOS) -> None:
+ sender = await async_client.v1.senders.create(
sender_id="user_123",
)
assert_matches_type(SenderAPIResponse, sender, path=["response"])
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
- def test_method_create_or_update_with_all_params(self, client: Everos) -> None:
- sender = client.v1.senders.create_or_update(
+ async def test_method_create_with_all_params(self, async_client: AsyncEverOS) -> None:
+ sender = await async_client.v1.senders.create(
sender_id="user_123",
name="Alice",
)
@@ -129,39 +178,33 @@ def test_method_create_or_update_with_all_params(self, client: Everos) -> None:
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
- def test_raw_response_create_or_update(self, client: Everos) -> None:
- response = client.v1.senders.with_raw_response.create_or_update(
+ async def test_raw_response_create(self, async_client: AsyncEverOS) -> None:
+ response = await async_client.v1.senders.with_raw_response.create(
sender_id="user_123",
)
assert response.is_closed is True
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
- sender = response.parse()
+ sender = await response.parse()
assert_matches_type(SenderAPIResponse, sender, path=["response"])
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
- def test_streaming_response_create_or_update(self, client: Everos) -> None:
- with client.v1.senders.with_streaming_response.create_or_update(
+ async def test_streaming_response_create(self, async_client: AsyncEverOS) -> None:
+ async with async_client.v1.senders.with_streaming_response.create(
sender_id="user_123",
) as response:
assert not response.is_closed
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
- sender = response.parse()
+ sender = await response.parse()
assert_matches_type(SenderAPIResponse, sender, path=["response"])
assert cast(Any, response.is_closed) is True
-
-class TestAsyncSenders:
- parametrize = pytest.mark.parametrize(
- "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
- )
-
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
- async def test_method_retrieve(self, async_client: AsyncEveros) -> None:
+ async def test_method_retrieve(self, async_client: AsyncEverOS) -> None:
sender = await async_client.v1.senders.retrieve(
"user_123",
)
@@ -169,7 +212,7 @@ async def test_method_retrieve(self, async_client: AsyncEveros) -> None:
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
- async def test_raw_response_retrieve(self, async_client: AsyncEveros) -> None:
+ async def test_raw_response_retrieve(self, async_client: AsyncEverOS) -> None:
response = await async_client.v1.senders.with_raw_response.retrieve(
"user_123",
)
@@ -181,7 +224,7 @@ async def test_raw_response_retrieve(self, async_client: AsyncEveros) -> None:
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
- async def test_streaming_response_retrieve(self, async_client: AsyncEveros) -> None:
+ async def test_streaming_response_retrieve(self, async_client: AsyncEverOS) -> None:
async with async_client.v1.senders.with_streaming_response.retrieve(
"user_123",
) as response:
@@ -195,7 +238,7 @@ async def test_streaming_response_retrieve(self, async_client: AsyncEveros) -> N
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
- async def test_path_params_retrieve(self, async_client: AsyncEveros) -> None:
+ async def test_path_params_retrieve(self, async_client: AsyncEverOS) -> None:
with pytest.raises(ValueError, match=r"Expected a non-empty value for `sender_id` but received ''"):
await async_client.v1.senders.with_raw_response.retrieve(
"",
@@ -203,16 +246,16 @@ async def test_path_params_retrieve(self, async_client: AsyncEveros) -> None:
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
- async def test_method_update(self, async_client: AsyncEveros) -> None:
- sender = await async_client.v1.senders.update(
+ async def test_method_patch(self, async_client: AsyncEverOS) -> None:
+ sender = await async_client.v1.senders.patch(
sender_id="user_123",
)
assert_matches_type(SenderAPIResponse, sender, path=["response"])
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
- async def test_method_update_with_all_params(self, async_client: AsyncEveros) -> None:
- sender = await async_client.v1.senders.update(
+ async def test_method_patch_with_all_params(self, async_client: AsyncEverOS) -> None:
+ sender = await async_client.v1.senders.patch(
sender_id="user_123",
name="name",
)
@@ -220,8 +263,8 @@ async def test_method_update_with_all_params(self, async_client: AsyncEveros) ->
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
- async def test_raw_response_update(self, async_client: AsyncEveros) -> None:
- response = await async_client.v1.senders.with_raw_response.update(
+ async def test_raw_response_patch(self, async_client: AsyncEverOS) -> None:
+ response = await async_client.v1.senders.with_raw_response.patch(
sender_id="user_123",
)
@@ -232,8 +275,8 @@ async def test_raw_response_update(self, async_client: AsyncEveros) -> None:
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
- async def test_streaming_response_update(self, async_client: AsyncEveros) -> None:
- async with async_client.v1.senders.with_streaming_response.update(
+ async def test_streaming_response_patch(self, async_client: AsyncEverOS) -> None:
+ async with async_client.v1.senders.with_streaming_response.patch(
sender_id="user_123",
) as response:
assert not response.is_closed
@@ -246,51 +289,8 @@ async def test_streaming_response_update(self, async_client: AsyncEveros) -> Non
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
- async def test_path_params_update(self, async_client: AsyncEveros) -> None:
+ async def test_path_params_patch(self, async_client: AsyncEverOS) -> None:
with pytest.raises(ValueError, match=r"Expected a non-empty value for `sender_id` but received ''"):
- await async_client.v1.senders.with_raw_response.update(
+ await async_client.v1.senders.with_raw_response.patch(
sender_id="",
)
-
- @pytest.mark.skip(reason="Mock server tests are disabled")
- @parametrize
- async def test_method_create_or_update(self, async_client: AsyncEveros) -> None:
- sender = await async_client.v1.senders.create_or_update(
- sender_id="user_123",
- )
- assert_matches_type(SenderAPIResponse, sender, path=["response"])
-
- @pytest.mark.skip(reason="Mock server tests are disabled")
- @parametrize
- async def test_method_create_or_update_with_all_params(self, async_client: AsyncEveros) -> None:
- sender = await async_client.v1.senders.create_or_update(
- sender_id="user_123",
- name="Alice",
- )
- assert_matches_type(SenderAPIResponse, sender, path=["response"])
-
- @pytest.mark.skip(reason="Mock server tests are disabled")
- @parametrize
- async def test_raw_response_create_or_update(self, async_client: AsyncEveros) -> None:
- response = await async_client.v1.senders.with_raw_response.create_or_update(
- sender_id="user_123",
- )
-
- assert response.is_closed is True
- assert response.http_request.headers.get("X-Stainless-Lang") == "python"
- sender = await response.parse()
- assert_matches_type(SenderAPIResponse, sender, path=["response"])
-
- @pytest.mark.skip(reason="Mock server tests are disabled")
- @parametrize
- async def test_streaming_response_create_or_update(self, async_client: AsyncEveros) -> None:
- async with async_client.v1.senders.with_streaming_response.create_or_update(
- sender_id="user_123",
- ) as response:
- assert not response.is_closed
- assert response.http_request.headers.get("X-Stainless-Lang") == "python"
-
- sender = await response.parse()
- assert_matches_type(SenderAPIResponse, sender, path=["response"])
-
- assert cast(Any, response.is_closed) is True
diff --git a/tests/api_resources/v1/test_settings.py b/tests/api_resources/v1/test_settings.py
index 66d7770..d5fe710 100644
--- a/tests/api_resources/v1/test_settings.py
+++ b/tests/api_resources/v1/test_settings.py
@@ -7,7 +7,7 @@
import pytest
-from everos import Everos, AsyncEveros
+from everos import EverOS, AsyncEverOS
from tests.utils import assert_matches_type
from everos.types.v1 import SettingsAPIResponse
@@ -19,13 +19,13 @@ class TestSettings:
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
- def test_method_retrieve(self, client: Everos) -> None:
+ def test_method_retrieve(self, client: EverOS) -> None:
setting = client.v1.settings.retrieve()
assert_matches_type(SettingsAPIResponse, setting, path=["response"])
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
- def test_raw_response_retrieve(self, client: Everos) -> None:
+ def test_raw_response_retrieve(self, client: EverOS) -> None:
response = client.v1.settings.with_raw_response.retrieve()
assert response.is_closed is True
@@ -35,7 +35,7 @@ def test_raw_response_retrieve(self, client: Everos) -> None:
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
- def test_streaming_response_retrieve(self, client: Everos) -> None:
+ def test_streaming_response_retrieve(self, client: EverOS) -> None:
with client.v1.settings.with_streaming_response.retrieve() as response:
assert not response.is_closed
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
@@ -47,13 +47,13 @@ def test_streaming_response_retrieve(self, client: Everos) -> None:
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
- def test_method_update(self, client: Everos) -> None:
+ def test_method_update(self, client: EverOS) -> None:
setting = client.v1.settings.update()
assert_matches_type(SettingsAPIResponse, setting, path=["response"])
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
- def test_method_update_with_all_params(self, client: Everos) -> None:
+ def test_method_update_with_all_params(self, client: EverOS) -> None:
setting = client.v1.settings.update(
boundary_detection_timeout=3600,
extraction_mode="default",
@@ -77,7 +77,7 @@ def test_method_update_with_all_params(self, client: Everos) -> None:
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
- def test_raw_response_update(self, client: Everos) -> None:
+ def test_raw_response_update(self, client: EverOS) -> None:
response = client.v1.settings.with_raw_response.update()
assert response.is_closed is True
@@ -87,7 +87,7 @@ def test_raw_response_update(self, client: Everos) -> None:
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
- def test_streaming_response_update(self, client: Everos) -> None:
+ def test_streaming_response_update(self, client: EverOS) -> None:
with client.v1.settings.with_streaming_response.update() as response:
assert not response.is_closed
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
@@ -105,13 +105,13 @@ class TestAsyncSettings:
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
- async def test_method_retrieve(self, async_client: AsyncEveros) -> None:
+ async def test_method_retrieve(self, async_client: AsyncEverOS) -> None:
setting = await async_client.v1.settings.retrieve()
assert_matches_type(SettingsAPIResponse, setting, path=["response"])
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
- async def test_raw_response_retrieve(self, async_client: AsyncEveros) -> None:
+ async def test_raw_response_retrieve(self, async_client: AsyncEverOS) -> None:
response = await async_client.v1.settings.with_raw_response.retrieve()
assert response.is_closed is True
@@ -121,7 +121,7 @@ async def test_raw_response_retrieve(self, async_client: AsyncEveros) -> None:
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
- async def test_streaming_response_retrieve(self, async_client: AsyncEveros) -> None:
+ async def test_streaming_response_retrieve(self, async_client: AsyncEverOS) -> None:
async with async_client.v1.settings.with_streaming_response.retrieve() as response:
assert not response.is_closed
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
@@ -133,13 +133,13 @@ async def test_streaming_response_retrieve(self, async_client: AsyncEveros) -> N
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
- async def test_method_update(self, async_client: AsyncEveros) -> None:
+ async def test_method_update(self, async_client: AsyncEverOS) -> None:
setting = await async_client.v1.settings.update()
assert_matches_type(SettingsAPIResponse, setting, path=["response"])
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
- async def test_method_update_with_all_params(self, async_client: AsyncEveros) -> None:
+ async def test_method_update_with_all_params(self, async_client: AsyncEverOS) -> None:
setting = await async_client.v1.settings.update(
boundary_detection_timeout=3600,
extraction_mode="default",
@@ -163,7 +163,7 @@ async def test_method_update_with_all_params(self, async_client: AsyncEveros) ->
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
- async def test_raw_response_update(self, async_client: AsyncEveros) -> None:
+ async def test_raw_response_update(self, async_client: AsyncEverOS) -> None:
response = await async_client.v1.settings.with_raw_response.update()
assert response.is_closed is True
@@ -173,7 +173,7 @@ async def test_raw_response_update(self, async_client: AsyncEveros) -> None:
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
- async def test_streaming_response_update(self, async_client: AsyncEveros) -> None:
+ async def test_streaming_response_update(self, async_client: AsyncEverOS) -> None:
async with async_client.v1.settings.with_streaming_response.update() as response:
assert not response.is_closed
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
diff --git a/tests/api_resources/test_v1.py b/tests/api_resources/v1/test_tasks.py
similarity index 54%
rename from tests/api_resources/test_v1.py
rename to tests/api_resources/v1/test_tasks.py
index 57747f7..85f1217 100644
--- a/tests/api_resources/test_v1.py
+++ b/tests/api_resources/v1/test_tasks.py
@@ -7,102 +7,102 @@
import pytest
-from everos import Everos, AsyncEveros
+from everos import EverOS, AsyncEverOS
from tests.utils import assert_matches_type
-from everos.types import V1QueryTaskStatusResponse
+from everos.types.v1 import GetTaskStatusResponse
base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010")
-class TestV1:
+class TestTasks:
parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"])
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
- def test_method_query_task_status(self, client: Everos) -> None:
- v1 = client.v1.query_task_status(
+ def test_method_retrieve(self, client: EverOS) -> None:
+ task = client.v1.tasks.retrieve(
"task_id",
)
- assert_matches_type(V1QueryTaskStatusResponse, v1, path=["response"])
+ assert_matches_type(GetTaskStatusResponse, task, path=["response"])
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
- def test_raw_response_query_task_status(self, client: Everos) -> None:
- response = client.v1.with_raw_response.query_task_status(
+ def test_raw_response_retrieve(self, client: EverOS) -> None:
+ response = client.v1.tasks.with_raw_response.retrieve(
"task_id",
)
assert response.is_closed is True
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
- v1 = response.parse()
- assert_matches_type(V1QueryTaskStatusResponse, v1, path=["response"])
+ task = response.parse()
+ assert_matches_type(GetTaskStatusResponse, task, path=["response"])
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
- def test_streaming_response_query_task_status(self, client: Everos) -> None:
- with client.v1.with_streaming_response.query_task_status(
+ def test_streaming_response_retrieve(self, client: EverOS) -> None:
+ with client.v1.tasks.with_streaming_response.retrieve(
"task_id",
) as response:
assert not response.is_closed
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
- v1 = response.parse()
- assert_matches_type(V1QueryTaskStatusResponse, v1, path=["response"])
+ task = response.parse()
+ assert_matches_type(GetTaskStatusResponse, task, path=["response"])
assert cast(Any, response.is_closed) is True
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
- def test_path_params_query_task_status(self, client: Everos) -> None:
+ def test_path_params_retrieve(self, client: EverOS) -> None:
with pytest.raises(ValueError, match=r"Expected a non-empty value for `task_id` but received ''"):
- client.v1.with_raw_response.query_task_status(
+ client.v1.tasks.with_raw_response.retrieve(
"",
)
-class TestAsyncV1:
+class TestAsyncTasks:
parametrize = pytest.mark.parametrize(
"async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
)
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
- async def test_method_query_task_status(self, async_client: AsyncEveros) -> None:
- v1 = await async_client.v1.query_task_status(
+ async def test_method_retrieve(self, async_client: AsyncEverOS) -> None:
+ task = await async_client.v1.tasks.retrieve(
"task_id",
)
- assert_matches_type(V1QueryTaskStatusResponse, v1, path=["response"])
+ assert_matches_type(GetTaskStatusResponse, task, path=["response"])
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
- async def test_raw_response_query_task_status(self, async_client: AsyncEveros) -> None:
- response = await async_client.v1.with_raw_response.query_task_status(
+ async def test_raw_response_retrieve(self, async_client: AsyncEverOS) -> None:
+ response = await async_client.v1.tasks.with_raw_response.retrieve(
"task_id",
)
assert response.is_closed is True
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
- v1 = await response.parse()
- assert_matches_type(V1QueryTaskStatusResponse, v1, path=["response"])
+ task = await response.parse()
+ assert_matches_type(GetTaskStatusResponse, task, path=["response"])
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
- async def test_streaming_response_query_task_status(self, async_client: AsyncEveros) -> None:
- async with async_client.v1.with_streaming_response.query_task_status(
+ async def test_streaming_response_retrieve(self, async_client: AsyncEverOS) -> None:
+ async with async_client.v1.tasks.with_streaming_response.retrieve(
"task_id",
) as response:
assert not response.is_closed
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
- v1 = await response.parse()
- assert_matches_type(V1QueryTaskStatusResponse, v1, path=["response"])
+ task = await response.parse()
+ assert_matches_type(GetTaskStatusResponse, task, path=["response"])
assert cast(Any, response.is_closed) is True
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
- async def test_path_params_query_task_status(self, async_client: AsyncEveros) -> None:
+ async def test_path_params_retrieve(self, async_client: AsyncEverOS) -> None:
with pytest.raises(ValueError, match=r"Expected a non-empty value for `task_id` but received ''"):
- await async_client.v1.with_raw_response.query_task_status(
+ await async_client.v1.tasks.with_raw_response.retrieve(
"",
)
diff --git a/tests/conftest.py b/tests/conftest.py
index 1ff0005..b50badd 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -10,7 +10,7 @@
import pytest
from pytest_asyncio import is_async_test
-from everos import Everos, AsyncEveros, DefaultAioHttpClient
+from everos import EverOS, AsyncEverOS, DefaultAioHttpClient
from everos._utils import is_dict
if TYPE_CHECKING:
@@ -49,17 +49,17 @@ def pytest_collection_modifyitems(items: list[pytest.Function]) -> None:
@pytest.fixture(scope="session")
-def client(request: FixtureRequest) -> Iterator[Everos]:
+def client(request: FixtureRequest) -> Iterator[EverOS]:
strict = getattr(request, "param", True)
if not isinstance(strict, bool):
raise TypeError(f"Unexpected fixture parameter type {type(strict)}, expected {bool}")
- with Everos(base_url=base_url, api_key=api_key, _strict_response_validation=strict) as client:
+ with EverOS(base_url=base_url, api_key=api_key, _strict_response_validation=strict) as client:
yield client
@pytest.fixture(scope="session")
-async def async_client(request: FixtureRequest) -> AsyncIterator[AsyncEveros]:
+async def async_client(request: FixtureRequest) -> AsyncIterator[AsyncEverOS]:
param = getattr(request, "param", True)
# defaults
@@ -78,7 +78,7 @@ async def async_client(request: FixtureRequest) -> AsyncIterator[AsyncEveros]:
else:
raise TypeError(f"Unexpected fixture parameter type {type(param)}, expected bool or dict")
- async with AsyncEveros(
+ async with AsyncEverOS(
base_url=base_url, api_key=api_key, _strict_response_validation=strict, http_client=http_client
) as client:
yield client
diff --git a/tests/lib/__init__.py b/tests/lib/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/tests/lib/conftest.py b/tests/lib/conftest.py
new file mode 100644
index 0000000..6202e41
--- /dev/null
+++ b/tests/lib/conftest.py
@@ -0,0 +1,9 @@
+"""Add src/ to sys.path so EverOS package is importable in all lib tests."""
+from __future__ import annotations
+
+import sys
+from pathlib import Path
+
+src = str(Path(__file__).parent.parent.parent / "src")
+if src not in sys.path:
+ sys.path.insert(0, src)
diff --git a/tests/lib/test_detect.py b/tests/lib/test_detect.py
new file mode 100644
index 0000000..6b5bfee
--- /dev/null
+++ b/tests/lib/test_detect.py
@@ -0,0 +1,197 @@
+"""Unit tests for everos.lib._detect — URI detection and message scanning."""
+from __future__ import annotations
+
+from typing import Any
+from pathlib import Path
+
+import pytest
+
+from everos.lib._detect import (
+ _is_http_uri,
+ scan_messages,
+ _is_local_file,
+)
+
+# ── _is_http_uri ─────────────────────────────────────────────────────────────
+
+
+def test_is_http_uri_http() -> None:
+ assert _is_http_uri("http://example.com/file.jpg") is True
+
+
+def test_is_http_uri_https() -> None:
+ assert _is_http_uri("https://example.com/image.png") is True
+
+
+def test_is_http_uri_local_path() -> None:
+ assert _is_http_uri("./photo.jpg") is False
+
+
+def test_is_http_uri_object_key() -> None:
+ assert _is_http_uri("uploads/2026/abc/photo.jpg") is False
+
+
+def test_is_http_uri_ftp() -> None:
+ assert _is_http_uri("ftp://example.com/file") is False
+
+
+# ── _is_local_file ────────────────────────────────────────────────────────────
+
+
+def test_is_local_file_existing(tmp_path: Path) -> None:
+ f = tmp_path / "photo.jpg"
+ f.write_bytes(b"data")
+ assert _is_local_file(str(f)) is True
+
+
+def test_is_local_file_nonexistent() -> None:
+ assert _is_local_file("/nonexistent/path/file.jpg") is False
+
+
+def test_is_local_file_directory(tmp_path: Path) -> None:
+ assert _is_local_file(str(tmp_path)) is False
+
+
+def test_is_local_file_protocol_prefix() -> None:
+ # Anything with "://" in it should not be treated as a local file
+ assert _is_local_file("s3://bucket/key") is False
+ assert _is_local_file("minio://bucket/key.jpg") is False
+
+
+def test_is_local_file_tilde_expansion(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
+ f = tmp_path / "notes.txt"
+ f.write_bytes(b"hello")
+ monkeypatch.setenv("HOME", str(tmp_path))
+ assert _is_local_file("~/notes.txt") is True
+
+
+def test_is_local_file_object_key_not_local() -> None:
+ # A MinIO object_key like "uploads/uuid/file.jpg" won't exist on disk
+ assert _is_local_file("uploads/abc123-uuid/photo.png") is False
+
+
+# ── scan_messages ─────────────────────────────────────────────────────────────
+
+
+def test_scan_messages_empty() -> None:
+ assert scan_messages([]) == []
+
+
+def test_scan_messages_text_only() -> None:
+ msgs = [{"role": "user", "content": [{"type": "text", "text": "hello"}]}]
+ assert scan_messages(msgs) == []
+
+
+def test_scan_messages_str_content_skipped() -> None:
+ # str shorthand for content → pure text, skip
+ msgs = [{"role": "user", "content": "plain text message"}]
+ assert scan_messages(msgs) == []
+
+
+def test_scan_messages_none_content_skipped() -> None:
+ msgs = [{"role": "user"}]
+ assert scan_messages(msgs) == []
+
+
+def test_scan_messages_empty_uri_skipped() -> None:
+ msgs = [{"role": "user", "content": [{"type": "image", "uri": ""}]}]
+ assert scan_messages(msgs) == []
+
+
+def test_scan_messages_none_uri_skipped() -> None:
+ msgs = [{"role": "user", "content": [{"type": "image"}]}]
+ assert scan_messages(msgs) == []
+
+
+def test_scan_messages_object_key_passthrough() -> None:
+ # Already an object_key — not a local file, not http → passthrough
+ msgs = [{"role": "user", "content": [{"type": "image", "uri": "uploads/uuid/photo.jpg"}]}]
+ assert scan_messages(msgs) == []
+
+
+def test_scan_messages_http_url(tmp_path: Path) -> None: # noqa: ARG001
+ msgs = [
+ {
+ "role": "user",
+ "content": [
+ {"type": "text", "text": "see image"},
+ {"type": "image", "uri": "https://example.com/photo.jpg", "name": "photo.jpg"},
+ ],
+ }
+ ]
+ tasks = scan_messages(msgs)
+ assert len(tasks) == 1
+ t = tasks[0]
+ assert t.msg_idx == 0
+ assert t.content_idx == 1
+ assert t.uri == "https://example.com/photo.jpg"
+ assert t.uri_type == "http"
+ assert t.content_type == "image"
+ assert t.file_type == "image"
+ assert t.file_name == "photo.jpg"
+
+
+def test_scan_messages_local_file(tmp_path: Path) -> None:
+ f = tmp_path / "doc.pdf"
+ f.write_bytes(b"pdf")
+ msgs = [{"role": "user", "content": [{"type": "pdf", "uri": str(f), "ext": "pdf"}]}]
+ tasks = scan_messages(msgs)
+ assert len(tasks) == 1
+ t = tasks[0]
+ assert t.uri_type == "local"
+ assert t.content_type == "pdf"
+ assert t.file_type == "file"
+ assert t.file_ext == "pdf"
+
+
+def test_scan_messages_multiple_messages_and_items(tmp_path: Path) -> None:
+ f = tmp_path / "audio.mp3"
+ f.write_bytes(b"mp3")
+
+ msgs = [
+ {
+ "role": "user",
+ "content": [
+ {"type": "text", "text": "hello"},
+ {"type": "audio", "uri": str(f)},
+ ],
+ },
+ {
+ "role": "assistant",
+ "content": [
+ {"type": "image", "uri": "https://example.com/img.png"},
+ {"type": "doc", "uri": "uploads/existing-key/file.docx"}, # passthrough
+ ],
+ },
+ ]
+ tasks = scan_messages(msgs)
+ assert len(tasks) == 2
+
+ audio_task = tasks[0]
+ assert audio_task.msg_idx == 0
+ assert audio_task.content_idx == 1
+ assert audio_task.uri_type == "local"
+ assert audio_task.file_type == "file"
+
+ img_task = tasks[1]
+ assert img_task.msg_idx == 1
+ assert img_task.content_idx == 0
+ assert img_task.uri_type == "http"
+ assert img_task.file_type == "image"
+
+
+def test_scan_messages_content_type_mapping() -> None:
+ """All non-image content types map to file_type='file'."""
+ for ct in ("audio", "doc", "pdf", "html", "email"):
+ msgs = [{"role": "user", "content": [{"type": ct, "uri": "https://example.com/x"}]}]
+ tasks = scan_messages(msgs)
+ assert len(tasks) == 1
+ assert tasks[0].file_type == "file", f"Expected file_type='file' for type={ct}"
+
+
+def test_scan_messages_does_not_mutate() -> None:
+ """scan_messages must not mutate the input messages."""
+ msgs: list[dict[str, Any]] = [{"role": "user", "content": [{"type": "image", "uri": "https://example.com/x.jpg"}]}]
+ original_uri = msgs[0]["content"][0]["uri"]
+ scan_messages(msgs)
+ assert msgs[0]["content"][0]["uri"] == original_uri
diff --git a/tests/lib/test_files.py b/tests/lib/test_files.py
new file mode 100644
index 0000000..c076ec0
--- /dev/null
+++ b/tests/lib/test_files.py
@@ -0,0 +1,278 @@
+"""Unit tests for everos.lib._files — FileInput, ResolvedFile, resolve_file."""
+from __future__ import annotations
+
+import os
+import tempfile
+from pathlib import Path
+from unittest.mock import AsyncMock, MagicMock, patch
+
+import pytest
+
+from everos.lib._files import (
+ FileInput,
+ ResolvedFile,
+ resolve_file,
+ async_resolve_file,
+)
+from everos.lib._errors import FileResolveError
+
+# ── FileInput validation ──────────────────────────────────────────────────────
+
+
+def test_file_input_path_only(tmp_path: Path) -> None:
+ fi = FileInput(path=str(tmp_path / "file.txt"))
+ assert fi.path is not None
+ assert fi.url is None
+
+
+def test_file_input_url_only() -> None:
+ fi = FileInput(url="https://example.com/file.jpg")
+ assert fi.url is not None
+ assert fi.path is None
+
+
+def test_file_input_both_raises() -> None:
+ with pytest.raises(ValueError, match="only one"):
+ FileInput(path="./file.jpg", url="https://example.com/file.jpg")
+
+
+def test_file_input_neither_raises() -> None:
+ with pytest.raises(ValueError, match="requires exactly one"):
+ FileInput()
+
+
+# ── ResolvedFile ──────────────────────────────────────────────────────────────
+
+
+def test_resolved_file_cleanup_temp(tmp_path: Path) -> None:
+ f = tmp_path / "temp.bin"
+ f.write_bytes(b"data")
+ rf = ResolvedFile(file_path=f, content_type="application/octet-stream", filename="temp.bin", size=4, is_temp=True)
+ assert f.exists()
+ rf.cleanup()
+ assert not f.exists()
+
+
+def test_resolved_file_cleanup_non_temp(tmp_path: Path) -> None:
+ f = tmp_path / "orig.bin"
+ f.write_bytes(b"data")
+ rf = ResolvedFile(file_path=f, content_type="application/octet-stream", filename="orig.bin", size=4, is_temp=False)
+ rf.cleanup()
+ assert f.exists() # should NOT be deleted
+
+
+def test_resolved_file_cleanup_idempotent(tmp_path: Path) -> None:
+ f = tmp_path / "temp2.bin"
+ f.write_bytes(b"x")
+ rf = ResolvedFile(file_path=f, content_type="application/octet-stream", filename="temp2.bin", size=1, is_temp=True)
+ rf.cleanup()
+ rf.cleanup() # second call must not raise
+
+
+def test_resolved_file_open(tmp_path: Path) -> None:
+ f = tmp_path / "data.bin"
+ f.write_bytes(b"hello world")
+ rf = ResolvedFile(file_path=f, content_type="application/octet-stream", filename="data.bin", size=11, is_temp=False)
+ with rf.open() as fh:
+ assert fh.read() == b"hello world"
+
+
+# ── resolve_file (local path) ─────────────────────────────────────────────────
+
+
+def test_resolve_file_local_existing(tmp_path: Path) -> None:
+ f = tmp_path / "photo.jpg"
+ f.write_bytes(b"\xff\xd8\xff" * 10)
+ fi = FileInput(path=str(f))
+ rf = resolve_file(fi)
+ assert rf.filename == "photo.jpg"
+ assert rf.size == 30
+ assert rf.is_temp is False
+ assert rf.file_path == f
+
+
+def test_resolve_file_local_missing() -> None:
+ fi = FileInput(path="/nonexistent/totally/missing.jpg")
+ with pytest.raises(FileResolveError, match="not found"):
+ resolve_file(fi)
+
+
+def test_resolve_file_local_directory(tmp_path: Path) -> None:
+ fi = FileInput(path=str(tmp_path))
+ with pytest.raises(FileResolveError, match="Not a file"):
+ resolve_file(fi)
+
+
+def test_resolve_file_local_mime_inference(tmp_path: Path) -> None:
+ for fname, expected_mime in [("photo.jpg", "image/jpeg"), ("doc.pdf", "application/pdf")]:
+ f = tmp_path / fname
+ f.write_bytes(b"data")
+ rf = resolve_file(FileInput(path=str(f)))
+ assert rf.content_type == expected_mime, f"Expected {expected_mime} for {fname}"
+
+
+def test_resolve_file_local_explicit_content_type(tmp_path: Path) -> None:
+ f = tmp_path / "data.bin"
+ f.write_bytes(b"data")
+ rf = resolve_file(FileInput(path=str(f), content_type="image/webp"))
+ assert rf.content_type == "image/webp"
+
+
+def test_resolve_file_local_explicit_filename(tmp_path: Path) -> None:
+ f = tmp_path / "data.bin"
+ f.write_bytes(b"data")
+ rf = resolve_file(FileInput(path=str(f), filename="custom_name.bin"))
+ assert rf.filename == "custom_name.bin"
+
+
+def test_resolve_file_local_tilde(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
+ f = tmp_path / "me.txt"
+ f.write_bytes(b"hi")
+ monkeypatch.setenv("HOME", str(tmp_path))
+ rf = resolve_file(FileInput(path="~/me.txt"))
+ assert rf.size == 2
+
+
+# ── resolve_file (URL download) ───────────────────────────────────────────────
+
+
+def _make_mock_response(content: bytes, content_type: str = "image/jpeg", status: int = 200) -> MagicMock:
+ """Build a mock httpx streaming response."""
+ mock_resp = MagicMock()
+ mock_resp.status_code = status
+ mock_resp.headers = {"content-type": content_type}
+
+ def raise_for_status() -> None:
+ if status >= 400:
+ raise Exception(f"HTTP {status}")
+
+ mock_resp.raise_for_status = raise_for_status
+ mock_resp.iter_bytes = lambda chunk_size=None: iter([content]) # noqa: ARG005 # pyright: ignore[reportUnknownLambdaType]
+ mock_resp.__enter__ = lambda s: s # pyright: ignore[reportUnknownLambdaType]
+ mock_resp.__exit__ = MagicMock(return_value=False)
+ return mock_resp
+
+
+def test_resolve_file_url_downloads_to_temp() -> None:
+ content = b"fake-image-data"
+ fi = FileInput(url="https://example.com/photo.jpg")
+
+ mock_client = MagicMock()
+ mock_client.__enter__ = lambda s: s # pyright: ignore[reportUnknownLambdaType]
+ mock_client.__exit__ = MagicMock(return_value=False)
+ mock_client.stream = MagicMock(return_value=_make_mock_response(content, "image/jpeg"))
+
+ with patch("everos.lib._files.httpx.Client", return_value=mock_client):
+ rf = resolve_file(fi)
+
+ assert rf.is_temp is True
+ assert rf.size == len(content)
+ assert rf.content_type == "image/jpeg"
+ assert rf.filename == "photo.jpg"
+ rf.cleanup()
+
+
+def test_resolve_file_url_filename_fallback() -> None:
+ """URLs with no path segment fall back to 'download'."""
+ content = b"data"
+ fi = FileInput(url="https://example.com/")
+
+ mock_client = MagicMock()
+ mock_client.__enter__ = lambda s: s # pyright: ignore[reportUnknownLambdaType]
+ mock_client.__exit__ = MagicMock(return_value=False)
+ mock_client.stream = MagicMock(return_value=_make_mock_response(content))
+
+ with patch("everos.lib._files.httpx.Client", return_value=mock_client):
+ rf = resolve_file(fi)
+
+ assert rf.filename == "download"
+ rf.cleanup()
+
+
+def test_resolve_file_url_size_limit_exceeded() -> None:
+ big_chunk = b"x" * 1024 # 1 KB
+ fi = FileInput(url="https://example.com/big.bin")
+
+ mock_resp = MagicMock()
+ mock_resp.status_code = 200
+ mock_resp.headers = {"content-type": "application/octet-stream"}
+ mock_resp.raise_for_status = MagicMock()
+ mock_resp.iter_bytes = lambda chunk_size=None: iter([big_chunk, big_chunk]) # noqa: ARG005 # pyright: ignore[reportUnknownLambdaType] # 2 KB total
+ mock_resp.__enter__ = lambda s: s # pyright: ignore[reportUnknownLambdaType]
+ mock_resp.__exit__ = MagicMock(return_value=False)
+
+ mock_client = MagicMock()
+ mock_client.__enter__ = lambda s: s # pyright: ignore[reportUnknownLambdaType]
+ mock_client.__exit__ = MagicMock(return_value=False)
+ mock_client.stream = MagicMock(return_value=mock_resp)
+
+ with patch("everos.lib._files.httpx.Client", return_value=mock_client):
+ with pytest.raises(FileResolveError, match="exceeded"):
+ resolve_file(fi, max_download_size=1500) # limit < 2 KB
+
+
+def test_resolve_file_url_cleans_up_temp_on_error() -> None:
+ fi = FileInput(url="https://example.com/fail.jpg")
+
+ mock_client = MagicMock()
+ mock_client.__enter__ = lambda s: s # pyright: ignore[reportUnknownLambdaType]
+ mock_client.__exit__ = MagicMock(return_value=False)
+ mock_client.stream = MagicMock(side_effect=Exception("connection refused"))
+
+ created_temps: list[str] = []
+ original_mkstemp = tempfile.mkstemp
+
+ def spy_mkstemp(**_kwargs: object) -> tuple[int, str]:
+ fd, path = original_mkstemp(prefix="everos_")
+ created_temps.append(path)
+ return fd, path
+
+ with patch("everos.lib._files.httpx.Client", return_value=mock_client):
+ with patch("everos.lib._files.tempfile.mkstemp", side_effect=spy_mkstemp):
+ with pytest.raises(FileResolveError):
+ resolve_file(fi)
+
+ for p in created_temps:
+ assert not os.path.exists(p), f"Temp file {p} was not cleaned up"
+
+
+# ── async_resolve_file ────────────────────────────────────────────────────────
+
+
+async def test_async_resolve_file_local(tmp_path: Path) -> None:
+ f = tmp_path / "audio.mp3"
+ f.write_bytes(b"\x00" * 100)
+ rf = await async_resolve_file(FileInput(path=str(f)))
+ assert rf.filename == "audio.mp3"
+ assert rf.size == 100
+ assert rf.is_temp is False
+
+
+async def test_async_resolve_file_url() -> None:
+ content = b"async-image-data"
+ fi = FileInput(url="https://example.com/async.jpg")
+
+ # Use MagicMock (not AsyncMock) for the response so headers remains a plain dict
+ mock_resp = MagicMock()
+ mock_resp.status_code = 200
+ mock_resp.headers = {"content-type": "image/jpeg"}
+ mock_resp.raise_for_status = MagicMock()
+
+ async def aiter_bytes(chunk_size: int = 65536) -> None: # noqa: ARG001 # type: ignore[override]
+ yield content # pyright: ignore[reportReturnType]
+
+ mock_resp.aiter_bytes = aiter_bytes
+ mock_resp.__aenter__ = AsyncMock(return_value=mock_resp)
+ mock_resp.__aexit__ = AsyncMock(return_value=False)
+
+ mock_client = MagicMock()
+ mock_client.__aenter__ = AsyncMock(return_value=mock_client)
+ mock_client.__aexit__ = AsyncMock(return_value=False)
+ mock_client.stream = MagicMock(return_value=mock_resp)
+
+ with patch("everos.lib._files.httpx.AsyncClient", return_value=mock_client):
+ rf = await async_resolve_file(fi)
+
+ assert rf.is_temp is True
+ assert rf.size == len(content)
+ rf.cleanup()
diff --git a/tests/lib/test_multimodal.py b/tests/lib/test_multimodal.py
new file mode 100644
index 0000000..90363a1
--- /dev/null
+++ b/tests/lib/test_multimodal.py
@@ -0,0 +1,350 @@
+"""Unit tests for everos.lib._multimodal — orchestration and resource override."""
+from __future__ import annotations
+
+from typing import Any
+from pathlib import Path
+from unittest.mock import AsyncMock, MagicMock, patch
+
+import pytest
+
+from everos.lib._files import ResolvedFile
+from everos.lib._detect import UploadTask
+from everos.lib._errors import UploadError, MultimodalError
+from everos.lib._upload import UploadResult
+from everos.lib._multimodal import (
+ MemoriesResourceWithMultimodal,
+ AsyncMemoriesResourceWithMultimodal,
+ _cleanup,
+ _replace_uris,
+)
+
+# ── Helpers ───────────────────────────────────────────────────────────────────
+
+
+def _make_upload_result(object_key: str = "uploads/uuid/photo.jpg", filename: str = "photo.jpg") -> UploadResult:
+ return UploadResult(object_key=object_key, filename=filename, content_type="image/jpeg", size=10)
+
+
+def _make_task(msg_idx: int = 0, content_idx: int = 1, uri: str = "photo.jpg") -> UploadTask:
+ return UploadTask(
+ msg_idx=msg_idx, content_idx=content_idx, uri=uri,
+ uri_type="local", content_type="image", file_type="image",
+ file_name="photo.jpg", file_ext="jpg",
+ )
+
+
+def _make_add_response() -> MagicMock:
+ resp = MagicMock()
+ resp.status = 0
+ return resp
+
+
+# ── _replace_uris ─────────────────────────────────────────────────────────────
+
+
+def test_replace_uris_basic() -> None:
+ msg_list: list[dict[str, Any]] = [{"role": "user", "content": [
+ {"type": "text", "text": "hello"},
+ {"type": "image", "uri": "./photo.jpg"},
+ ]}]
+ tasks = [_make_task(0, 1, "./photo.jpg")]
+ results = [_make_upload_result("uploads/uuid/photo.jpg", "photo.jpg")]
+
+ _replace_uris(msg_list, tasks, results)
+
+ item = msg_list[0]["content"][1]
+ assert item["uri"] == "uploads/uuid/photo.jpg"
+
+
+def test_replace_uris_auto_fills_name() -> None:
+ msg_list: list[dict[str, Any]] = [{"role": "user", "content": [{"type": "image", "uri": "./photo.jpg"}]}]
+ tasks = [_make_task(0, 0)]
+ results = [_make_upload_result(filename="photo.jpg")]
+
+ _replace_uris(msg_list, tasks, results)
+ assert msg_list[0]["content"][0]["name"] == "photo.jpg"
+
+
+def test_replace_uris_auto_fills_ext() -> None:
+ msg_list: list[dict[str, Any]] = [{"role": "user", "content": [{"type": "image", "uri": "./photo.jpg"}]}]
+ tasks = [_make_task(0, 0)]
+ results = [_make_upload_result(filename="photo.jpg")]
+
+ _replace_uris(msg_list, tasks, results)
+ assert msg_list[0]["content"][0]["ext"] == "jpg"
+
+
+def test_replace_uris_does_not_overwrite_existing_name() -> None:
+ msg_list: list[dict[str, Any]] = [{"role": "user", "content": [{"type": "image", "uri": "./x.jpg", "name": "custom_name.jpg"}]}]
+ tasks = [_make_task(0, 0, "./x.jpg")]
+ results = [_make_upload_result(filename="x.jpg")]
+
+ _replace_uris(msg_list, tasks, results)
+ assert msg_list[0]["content"][0]["name"] == "custom_name.jpg"
+
+
+def test_replace_uris_multiple_items() -> None:
+ msg_list: list[dict[str, Any]] = [{"role": "user", "content": [
+ {"type": "image", "uri": "./a.jpg"},
+ {"type": "doc", "uri": "./b.pdf"},
+ ]}]
+ tasks = [_make_task(0, 0, "./a.jpg"), _make_task(0, 1, "./b.pdf")]
+ results = [
+ _make_upload_result("uploads/uuid/a.jpg", "a.jpg"),
+ _make_upload_result("uploads/uuid/b.pdf", "b.pdf"),
+ ]
+
+ _replace_uris(msg_list, tasks, results)
+ assert msg_list[0]["content"][0]["uri"] == "uploads/uuid/a.jpg"
+ assert msg_list[0]["content"][1]["uri"] == "uploads/uuid/b.pdf"
+
+
+# ── _cleanup ──────────────────────────────────────────────────────────────────
+
+
+def test_cleanup_removes_temp_files(tmp_path: Path) -> None:
+ f = tmp_path / "tmp.bin"
+ f.write_bytes(b"x")
+ rf = ResolvedFile(file_path=f, content_type="application/octet-stream", filename="tmp.bin", size=1, is_temp=True)
+ _cleanup([rf])
+ assert not f.exists()
+
+
+def test_cleanup_skips_non_temp(tmp_path: Path) -> None:
+ f = tmp_path / "orig.bin"
+ f.write_bytes(b"x")
+ rf = ResolvedFile(file_path=f, content_type="application/octet-stream", filename="orig.bin", size=1, is_temp=False)
+ _cleanup([rf])
+ assert f.exists()
+
+
+# ── MemoriesResourceWithMultimodal (sync) ─────────────────────────────────────
+
+
+def _make_sync_resource(_tmp_path: Path) -> tuple[MemoriesResourceWithMultimodal, MagicMock]:
+ """Return a resource instance and the mock super().add response."""
+ mock_client = MagicMock()
+ resource = MemoriesResourceWithMultimodal.__new__(MemoriesResourceWithMultimodal)
+ resource._client = mock_client
+ return resource, mock_client
+
+
+def test_sync_add_passthrough_text_only(tmp_path: Path) -> None:
+ resource, _ = _make_sync_resource(tmp_path)
+ add_response = _make_add_response()
+
+ msgs: list[dict[str, Any]] = [{"role": "user", "content": [{"type": "text", "text": "hello"}]}]
+
+ with patch.object(
+ MemoriesResourceWithMultimodal.__bases__[0], "add", return_value=add_response
+ ) as mock_super:
+ result = resource.add(messages=msgs, user_id="u1") # type: ignore[arg-type]
+
+ mock_super.assert_called_once()
+ assert result is add_response
+
+
+def test_sync_add_local_file_upload(tmp_path: Path) -> None:
+ resource, mock_client = _make_sync_resource(tmp_path)
+
+ f = tmp_path / "photo.jpg"
+ f.write_bytes(b"fake-image" * 10)
+
+ msgs: list[dict[str, Any]] = [{"role": "user", "content": [
+ {"type": "text", "text": "see this"},
+ {"type": "image", "uri": str(f), "name": "photo.jpg", "ext": "jpg"},
+ ]}]
+
+ sign_resp = MagicMock()
+ sign_resp.status = 0
+ sign_resp.error = "OK"
+ signed_item = MagicMock()
+ signed_item.object_key = "uploads/uuid/photo.jpg"
+ signed_item.object_signed_info.url = "https://s3.example.com/"
+ signed_item.object_signed_info.fields = {"policy": "x"}
+ sign_resp.result.data.object_list = [signed_item]
+ mock_client.v1.object.sign = MagicMock(return_value=sign_resp)
+
+ add_response = _make_add_response()
+ captured_msgs: list[Any] = []
+
+ def _super_add(**kwargs: Any) -> MagicMock:
+ captured_msgs.extend(kwargs["messages"])
+ return add_response
+
+ with patch("everos.lib._upload.httpx.post") as mock_s3:
+ mock_s3.return_value = MagicMock(status_code=204)
+ with patch.object(
+ MemoriesResourceWithMultimodal.__bases__[0], "add", side_effect=_super_add
+ ):
+ result = resource.add(messages=msgs, user_id="u1") # type: ignore[arg-type]
+
+ assert result is add_response
+ # URI should have been replaced in the message passed to super().add()
+ assert captured_msgs[0]["content"][1]["uri"] == "uploads/uuid/photo.jpg"
+ # Original msgs must NOT be mutated
+ assert msgs[0]["content"][1]["uri"] == str(f)
+
+
+def test_sync_add_deepcopy_original_not_mutated(tmp_path: Path) -> None:
+ resource, mock_client = _make_sync_resource(tmp_path)
+ f = tmp_path / "img.jpg"
+ f.write_bytes(b"data")
+ original_uri = str(f)
+
+ msgs: list[dict[str, Any]] = [{"role": "user", "content": [{"type": "image", "uri": original_uri}]}]
+
+ sign_resp = MagicMock()
+ sign_resp.status = 0
+ sign_resp.error = "OK"
+ item = MagicMock()
+ item.object_key = "uploads/uuid/img.jpg"
+ item.object_signed_info.url = "https://s3.example.com/"
+ item.object_signed_info.fields = {}
+ sign_resp.result.data.object_list = [item]
+ mock_client.v1.object.sign = MagicMock(return_value=sign_resp)
+
+ with patch("everos.lib._upload.httpx.post", return_value=MagicMock(status_code=204)):
+ with patch.object(MemoriesResourceWithMultimodal.__bases__[0], "add", return_value=_make_add_response()):
+ resource.add(messages=msgs, user_id="u1") # type: ignore[arg-type]
+
+ assert msgs[0]["content"][0]["uri"] == original_uri
+
+
+def test_sync_add_cleanup_on_success(tmp_path: Path) -> None:
+ resource, mock_client = _make_sync_resource(tmp_path)
+ f = tmp_path / "photo.jpg"
+ f.write_bytes(b"data")
+
+ msgs: list[dict[str, Any]] = [{"role": "user", "content": [{"type": "image", "uri": str(f)}]}]
+
+ sign_resp = MagicMock()
+ sign_resp.status = 0
+ sign_resp.error = "OK"
+ item = MagicMock()
+ item.object_key = "uploads/uuid/photo.jpg"
+ item.object_signed_info.url = "https://s3.example.com/"
+ item.object_signed_info.fields = {}
+ sign_resp.result.data.object_list = [item]
+ mock_client.v1.object.sign = MagicMock(return_value=sign_resp)
+
+ with patch("everos.lib._multimodal._cleanup") as mock_cleanup:
+ with patch("everos.lib._upload.httpx.post", return_value=MagicMock(status_code=204)):
+ with patch.object(MemoriesResourceWithMultimodal.__bases__[0], "add", return_value=_make_add_response()):
+ resource.add(messages=msgs, user_id="u1") # type: ignore[arg-type]
+
+ mock_cleanup.assert_called_once()
+
+
+def test_sync_add_cleanup_on_upload_failure(tmp_path: Path) -> None:
+ resource, mock_client = _make_sync_resource(tmp_path)
+ f = tmp_path / "photo.jpg"
+ f.write_bytes(b"data")
+
+ msgs: list[dict[str, Any]] = [{"role": "user", "content": [{"type": "image", "uri": str(f)}]}]
+
+ sign_resp = MagicMock()
+ sign_resp.status = 0
+ sign_resp.error = "OK"
+ item = MagicMock()
+ item.object_key = "uploads/uuid/photo.jpg"
+ item.object_signed_info.url = "https://s3.example.com/"
+ item.object_signed_info.fields = {}
+ sign_resp.result.data.object_list = [item]
+ mock_client.v1.object.sign = MagicMock(return_value=sign_resp)
+
+ with patch("everos.lib._multimodal._cleanup") as mock_cleanup:
+ with patch("everos.lib._upload.httpx.post", return_value=MagicMock(status_code=500)):
+ with patch("everos.lib._upload.time.sleep"):
+ with pytest.raises(MultimodalError):
+ resource.add(messages=msgs, user_id="u1") # type: ignore[arg-type]
+
+ mock_cleanup.assert_called_once()
+
+
+# ── AsyncMemoriesResourceWithMultimodal ───────────────────────────────────────
+
+
+def _make_async_resource(_tmp_path: Path) -> AsyncMemoriesResourceWithMultimodal:
+ mock_client = MagicMock()
+ resource = AsyncMemoriesResourceWithMultimodal.__new__(AsyncMemoriesResourceWithMultimodal)
+ resource._client = mock_client
+ return resource
+
+
+async def test_async_add_passthrough_text_only() -> None:
+ resource = AsyncMemoriesResourceWithMultimodal.__new__(AsyncMemoriesResourceWithMultimodal)
+ resource._client = MagicMock()
+ add_response = _make_add_response()
+
+ msgs: list[dict[str, Any]] = [{"role": "user", "content": [{"type": "text", "text": "hi"}]}]
+
+ with patch.object(
+ AsyncMemoriesResourceWithMultimodal.__bases__[0], "add",
+ new_callable=AsyncMock, return_value=add_response,
+ ) as mock_super:
+ result = await resource.add(messages=msgs, user_id="u1") # type: ignore[arg-type]
+
+ mock_super.assert_called_once()
+ assert result is add_response
+
+
+async def test_async_add_local_file_upload(tmp_path: Path) -> None:
+ resource = _make_async_resource(tmp_path)
+ f = tmp_path / "audio.mp3"
+ f.write_bytes(b"mp3" * 20)
+
+ msgs: list[dict[str, Any]] = [{"role": "user", "content": [{"type": "audio", "uri": str(f)}]}]
+
+ sign_resp = MagicMock()
+ sign_resp.status = 0
+ sign_resp.error = "OK"
+ item = MagicMock()
+ item.object_key = "uploads/uuid/audio.mp3"
+ item.object_signed_info.url = "https://s3.example.com/"
+ item.object_signed_info.fields = {"policy": "y"}
+ sign_resp.result.data.object_list = [item]
+ resource._client.v1.object.sign = AsyncMock(return_value=sign_resp)
+
+ captured_msgs: list[Any] = []
+
+ async def _super_add(**kwargs: Any) -> MagicMock:
+ captured_msgs.extend(kwargs["messages"])
+ return _make_add_response()
+
+ mock_resp = MagicMock()
+ mock_resp.status_code = 200
+ mock_http = AsyncMock()
+ mock_http.post = AsyncMock(return_value=mock_resp)
+ mock_http.__aenter__ = AsyncMock(return_value=mock_http)
+ mock_http.__aexit__ = AsyncMock(return_value=False)
+
+ with patch("everos.lib._upload.httpx.AsyncClient", return_value=mock_http):
+ with patch.object(
+ AsyncMemoriesResourceWithMultimodal.__bases__[0], "add",
+ side_effect=_super_add,
+ ):
+ await resource.add(messages=msgs, user_id="u1") # type: ignore[arg-type]
+
+ assert captured_msgs[0]["content"][0]["uri"] == "uploads/uuid/audio.mp3"
+ # Original untouched
+ assert msgs[0]["content"][0]["uri"] == str(f)
+
+
+async def test_async_add_cleanup_called_on_failure(tmp_path: Path) -> None:
+ resource = _make_async_resource(tmp_path)
+ f = tmp_path / "doc.pdf"
+ f.write_bytes(b"pdf")
+
+ msgs: list[dict[str, Any]] = [{"role": "user", "content": [{"type": "pdf", "uri": str(f)}]}]
+
+ sign_resp = MagicMock()
+ sign_resp.status = 1 # sign failure
+ sign_resp.error = "InternalError"
+ resource._client.v1.object.sign = AsyncMock(return_value=sign_resp)
+
+ with patch("everos.lib._multimodal._cleanup") as mock_cleanup:
+ with pytest.raises((UploadError, MultimodalError)):
+ await resource.add(messages=msgs, user_id="u1") # type: ignore[arg-type]
+
+ mock_cleanup.assert_called_once()
diff --git a/tests/lib/test_upload.py b/tests/lib/test_upload.py
new file mode 100644
index 0000000..61f9995
--- /dev/null
+++ b/tests/lib/test_upload.py
@@ -0,0 +1,333 @@
+"""Unit tests for everos.lib._upload — batch_sign and s3_post_upload."""
+from __future__ import annotations
+
+from typing import Any
+from pathlib import Path
+from unittest.mock import AsyncMock, MagicMock, call, patch
+
+import httpx
+import pytest
+
+from everos.lib._files import ResolvedFile
+from everos.lib._detect import UploadTask
+from everos.lib._errors import UploadError
+from everos.lib._upload import (
+ _RETRY_BACKOFF_S,
+ _MAX_UPLOAD_RETRIES,
+ SignedFile,
+ batch_sign,
+ s3_post_upload,
+ async_batch_sign,
+ async_s3_post_upload,
+)
+
+# ── Helpers ───────────────────────────────────────────────────────────────────
+
+
+def _make_resolved(tmp_path: Path, name: str = "photo.jpg", size: int = 10) -> ResolvedFile:
+ f = tmp_path / name
+ f.write_bytes(b"x" * size)
+ return ResolvedFile(file_path=f, content_type="image/jpeg", filename=name, size=size, is_temp=False)
+
+
+def _make_task(msg_idx: int = 0, content_idx: int = 0, uri: str = "photo.jpg") -> UploadTask:
+ return UploadTask(
+ msg_idx=msg_idx,
+ content_idx=content_idx,
+ uri=uri,
+ uri_type="local",
+ content_type="image",
+ file_type="image",
+ file_name="photo.jpg",
+ file_ext="jpg",
+ )
+
+
+def _make_sign_response(items: list[dict[str, Any]]) -> MagicMock:
+ sign_resp = MagicMock()
+ sign_resp.status = 0
+ sign_resp.error = "OK"
+
+ data = MagicMock()
+ object_items: list[MagicMock] = []
+ for it in items:
+ item = MagicMock()
+ item.object_key = it["object_key"]
+ item.object_signed_info.url = it["url"]
+ item.object_signed_info.fields = it.get("fields", {})
+ object_items.append(item)
+
+ data.object_list = object_items
+ result = MagicMock()
+ result.data = data
+ sign_resp.result = result
+ return sign_resp
+
+
+def _make_signed_file(tmp_path: Path, name: str = "photo.jpg") -> SignedFile:
+ return SignedFile(
+ resolved=_make_resolved(tmp_path, name),
+ task=_make_task(),
+ object_key=f"uploads/uuid/{name}",
+ upload_url="https://s3.example.com/bucket",
+ upload_fields={"key": f"uploads/uuid/{name}", "policy": "abc"},
+ )
+
+
+# ── batch_sign ────────────────────────────────────────────────────────────────
+
+
+def test_batch_sign_success(tmp_path: Path) -> None:
+ resolved = [_make_resolved(tmp_path)]
+ tasks = [_make_task()]
+
+ sign_resp = _make_sign_response([
+ {"object_key": "uploads/uuid/photo.jpg", "url": "https://s3.example.com/", "fields": {"policy": "x"}},
+ ])
+
+ mock_client = MagicMock()
+ mock_client.v1.object.sign = MagicMock(return_value=sign_resp)
+
+ signed = batch_sign(mock_client, tasks, resolved)
+
+ assert len(signed) == 1
+ assert signed[0].object_key == "uploads/uuid/photo.jpg"
+ assert signed[0].upload_url == "https://s3.example.com/"
+ assert signed[0].upload_fields == {"policy": "x"}
+
+
+def test_batch_sign_passes_file_id_uuid() -> None:
+ """file_id should be a full UUID hex string (no truncation)."""
+ resolved = [MagicMock(filename="f.jpg")]
+ tasks = [_make_task()]
+
+ captured: list[dict[str, Any]] = []
+
+ def _sign(object_list: list[dict[str, Any]]) -> MagicMock:
+ captured.extend(object_list)
+ return _make_sign_response([{"object_key": "k", "url": "u"}])
+
+ mock_client = MagicMock()
+ mock_client.v1.object.sign = _sign
+
+ batch_sign(mock_client, tasks, resolved) # type: ignore[arg-type]
+
+ assert len(captured) == 1
+ file_id = captured[0]["file_id"]
+ assert file_id.startswith("sdk-")
+ hex_part = file_id[len("sdk-"):]
+ assert len(hex_part) == 32, f"Expected 32-char hex, got {len(hex_part)}: {hex_part}"
+
+
+def test_batch_sign_api_failure() -> None:
+ sign_resp = MagicMock()
+ sign_resp.status = 1
+ sign_resp.error = "Unauthorized"
+
+ mock_client = MagicMock()
+ mock_client.v1.object.sign = MagicMock(return_value=sign_resp)
+
+ with pytest.raises(UploadError, match="Sign failed"):
+ batch_sign(mock_client, [_make_task()], [MagicMock(filename="f.jpg")]) # type: ignore[arg-type]
+
+
+def test_batch_sign_count_mismatch(tmp_path: Path) -> None:
+ resolved = [_make_resolved(tmp_path, "a.jpg"), _make_resolved(tmp_path, "b.jpg")]
+ tasks = [_make_task(0, 0, "a.jpg"), _make_task(0, 1, "b.jpg")]
+
+ # Returns only 1 item for 2 tasks
+ sign_resp = _make_sign_response([{"object_key": "k1", "url": "u1"}])
+ mock_client = MagicMock()
+ mock_client.v1.object.sign = MagicMock(return_value=sign_resp)
+
+ with pytest.raises(UploadError, match="expected 2"):
+ batch_sign(mock_client, tasks, resolved)
+
+
+# ── s3_post_upload (sync) ─────────────────────────────────────────────────────
+
+
+def _mock_httpx_response(status_code: int) -> MagicMock:
+ resp = MagicMock(spec=httpx.Response)
+ resp.status_code = status_code
+ resp.text = "error text"
+ return resp
+
+
+def test_s3_post_upload_success_204(tmp_path: Path) -> None:
+ signed = _make_signed_file(tmp_path)
+ with patch("everos.lib._upload.httpx.post", return_value=_mock_httpx_response(204)) as mock_post:
+ result = s3_post_upload(signed)
+
+ assert result.object_key == signed.object_key
+ assert result.filename == "photo.jpg"
+ assert mock_post.call_count == 1
+
+
+def test_s3_post_upload_success_200(tmp_path: Path) -> None:
+ signed = _make_signed_file(tmp_path)
+ with patch("everos.lib._upload.httpx.post", return_value=_mock_httpx_response(200)):
+ result = s3_post_upload(signed)
+ assert result.object_key == signed.object_key
+
+
+def test_s3_post_upload_4xx_no_retry(tmp_path: Path) -> None:
+ signed = _make_signed_file(tmp_path)
+ with patch("everos.lib._upload.httpx.post", return_value=_mock_httpx_response(403)) as mock_post:
+ with pytest.raises(UploadError, match="HTTP 403"):
+ s3_post_upload(signed)
+ assert mock_post.call_count == 1 # no retry on 4xx
+
+
+def test_s3_post_upload_5xx_retries_and_fails(tmp_path: Path) -> None:
+ signed = _make_signed_file(tmp_path)
+ with patch("everos.lib._upload.httpx.post", return_value=_mock_httpx_response(503)) as mock_post:
+ with patch("everos.lib._upload.time.sleep"):
+ with pytest.raises(UploadError, match="after 3 attempts"):
+ s3_post_upload(signed)
+ assert mock_post.call_count == _MAX_UPLOAD_RETRIES
+
+
+def test_s3_post_upload_5xx_then_success(tmp_path: Path) -> None:
+ signed = _make_signed_file(tmp_path)
+ responses = [_mock_httpx_response(503), _mock_httpx_response(204)]
+ with patch("everos.lib._upload.httpx.post", side_effect=responses) as mock_post:
+ with patch("everos.lib._upload.time.sleep"):
+ result = s3_post_upload(signed)
+ assert mock_post.call_count == 2
+ assert result.object_key == signed.object_key
+
+
+def test_s3_post_upload_timeout_retries(tmp_path: Path) -> None:
+ signed = _make_signed_file(tmp_path)
+ with patch(
+ "everos.lib._upload.httpx.post",
+ side_effect=httpx.TimeoutException("timeout"),
+ ) as mock_post:
+ with patch("everos.lib._upload.time.sleep"):
+ with pytest.raises(UploadError, match="after 3 attempts"):
+ s3_post_upload(signed)
+ assert mock_post.call_count == _MAX_UPLOAD_RETRIES
+
+
+def test_s3_post_upload_sync_backoff_called(tmp_path: Path) -> None:
+ """Backoff sleep is called between retries (not after last attempt)."""
+ signed = _make_signed_file(tmp_path)
+ with patch("everos.lib._upload.httpx.post", return_value=_mock_httpx_response(503)):
+ with patch("everos.lib._upload.time.sleep") as mock_sleep:
+ with pytest.raises(UploadError):
+ s3_post_upload(signed)
+ # 3 attempts → 2 backoffs
+ assert mock_sleep.call_count == _MAX_UPLOAD_RETRIES - 1
+ mock_sleep.assert_called_with(_RETRY_BACKOFF_S)
+
+
+# ── async_batch_sign ──────────────────────────────────────────────────────────
+
+
+async def test_async_batch_sign_success(tmp_path: Path) -> None:
+ resolved = [_make_resolved(tmp_path)]
+ tasks = [_make_task()]
+
+ sign_resp = _make_sign_response([
+ {"object_key": "uploads/uuid/photo.jpg", "url": "https://s3.example.com/", "fields": {"p": "q"}},
+ ])
+
+ mock_client = MagicMock()
+ mock_client.v1.object.sign = AsyncMock(return_value=sign_resp)
+
+ signed = await async_batch_sign(mock_client, tasks, resolved)
+ assert len(signed) == 1
+ assert signed[0].object_key == "uploads/uuid/photo.jpg"
+
+
+async def test_async_batch_sign_failure() -> None:
+ sign_resp = MagicMock()
+ sign_resp.status = 1
+ sign_resp.error = "Forbidden"
+ mock_client = MagicMock()
+ mock_client.v1.object.sign = AsyncMock(return_value=sign_resp)
+
+ with pytest.raises(UploadError, match="Sign failed"):
+ await async_batch_sign(mock_client, [_make_task()], [MagicMock(filename="x.jpg")]) # type: ignore[arg-type]
+
+
+# ── async_s3_post_upload ──────────────────────────────────────────────────────
+
+
+async def test_async_s3_post_upload_success(tmp_path: Path) -> None:
+ signed = _make_signed_file(tmp_path)
+
+ mock_resp = MagicMock(spec=httpx.Response)
+ mock_resp.status_code = 204
+
+ mock_http = AsyncMock()
+ mock_http.post = AsyncMock(return_value=mock_resp)
+ mock_http.__aenter__ = AsyncMock(return_value=mock_http)
+ mock_http.__aexit__ = AsyncMock(return_value=False)
+
+ with patch("everos.lib._upload.httpx.AsyncClient", return_value=mock_http):
+ result = await async_s3_post_upload(signed)
+
+ assert result.object_key == signed.object_key
+
+
+async def test_async_s3_post_upload_4xx_no_retry(tmp_path: Path) -> None:
+ signed = _make_signed_file(tmp_path)
+
+ mock_resp = MagicMock(spec=httpx.Response)
+ mock_resp.status_code = 403
+ mock_resp.text = "forbidden"
+
+ mock_http = AsyncMock()
+ mock_http.post = AsyncMock(return_value=mock_resp)
+ mock_http.__aenter__ = AsyncMock(return_value=mock_http)
+ mock_http.__aexit__ = AsyncMock(return_value=False)
+
+ with patch("everos.lib._upload.httpx.AsyncClient", return_value=mock_http):
+ with pytest.raises(UploadError, match="HTTP 403"):
+ await async_s3_post_upload(signed)
+
+ assert mock_http.post.call_count == 1
+
+
+async def test_async_s3_post_upload_5xx_retries_all_fail(tmp_path: Path) -> None:
+ # asyncio is imported locally inside the function, so patch via the asyncio module directly
+ signed = _make_signed_file(tmp_path)
+
+ mock_resp = MagicMock(spec=httpx.Response)
+ mock_resp.status_code = 503
+
+ mock_http = AsyncMock()
+ mock_http.post = AsyncMock(return_value=mock_resp)
+ mock_http.__aenter__ = AsyncMock(return_value=mock_http)
+ mock_http.__aexit__ = AsyncMock(return_value=False)
+
+ with patch("everos.lib._upload.httpx.AsyncClient", return_value=mock_http):
+ with patch("everos.lib._upload.asyncio.sleep", new_callable=AsyncMock):
+ with pytest.raises(UploadError, match="after 3 attempts"):
+ await async_s3_post_upload(signed)
+
+ assert mock_http.post.call_count == _MAX_UPLOAD_RETRIES
+
+
+async def test_async_s3_post_upload_backoff_called(tmp_path: Path) -> None:
+ signed = _make_signed_file(tmp_path)
+
+ mock_resp = MagicMock(spec=httpx.Response)
+ mock_resp.status_code = 503
+
+ mock_http = AsyncMock()
+ mock_http.post = AsyncMock(return_value=mock_resp)
+ mock_http.__aenter__ = AsyncMock(return_value=mock_http)
+ mock_http.__aexit__ = AsyncMock(return_value=False)
+
+ with patch("everos.lib._upload.httpx.AsyncClient", return_value=mock_http):
+ with patch("everos.lib._upload.asyncio.sleep", new_callable=AsyncMock) as mock_sleep:
+ with pytest.raises(UploadError):
+ await async_s3_post_upload(signed)
+
+ # Count only calls with our specific backoff value (session-scoped event loop may
+ # call asyncio.sleep internally with other arguments, e.g. 0)
+ backoff_calls = [c for c in mock_sleep.call_args_list if c == call(_RETRY_BACKOFF_S)]
+ assert len(backoff_calls) == _MAX_UPLOAD_RETRIES - 1
diff --git a/tests/test_client.py b/tests/test_client.py
index c99ff5f..f067aa0 100644
--- a/tests/test_client.py
+++ b/tests/test_client.py
@@ -19,11 +19,11 @@
from respx import MockRouter
from pydantic import ValidationError
-from everos import Everos, AsyncEveros, APIResponseValidationError
+from everos import EverOS, AsyncEverOS, APIResponseValidationError
from everos._types import Omit
from everos._utils import asyncify
from everos._models import BaseModel, FinalRequestOptions
-from everos._exceptions import EverosError, APIStatusError, APITimeoutError, APIResponseValidationError
+from everos._exceptions import EverOSError, APIStatusError, APITimeoutError, APIResponseValidationError
from everos._base_client import (
DEFAULT_TIMEOUT,
HTTPX_DEFAULT_TIMEOUT,
@@ -103,7 +103,7 @@ async def _make_async_iterator(iterable: Iterable[T], counter: Optional[Counter]
yield item
-def _get_open_connections(client: Everos | AsyncEveros) -> int:
+def _get_open_connections(client: EverOS | AsyncEverOS) -> int:
transport = client._client._transport
assert isinstance(transport, httpx.HTTPTransport) or isinstance(transport, httpx.AsyncHTTPTransport)
@@ -111,9 +111,9 @@ def _get_open_connections(client: Everos | AsyncEveros) -> int:
return len(pool._requests)
-class TestEveros:
+class TestEverOS:
@pytest.mark.respx(base_url=base_url)
- def test_raw_response(self, respx_mock: MockRouter, client: Everos) -> None:
+ def test_raw_response(self, respx_mock: MockRouter, client: EverOS) -> None:
respx_mock.post("/foo").mock(return_value=httpx.Response(200, json={"foo": "bar"}))
response = client.post("/foo", cast_to=httpx.Response)
@@ -122,7 +122,7 @@ def test_raw_response(self, respx_mock: MockRouter, client: Everos) -> None:
assert response.json() == {"foo": "bar"}
@pytest.mark.respx(base_url=base_url)
- def test_raw_response_for_binary(self, respx_mock: MockRouter, client: Everos) -> None:
+ def test_raw_response_for_binary(self, respx_mock: MockRouter, client: EverOS) -> None:
respx_mock.post("/foo").mock(
return_value=httpx.Response(200, headers={"Content-Type": "application/binary"}, content='{"foo": "bar"}')
)
@@ -132,7 +132,7 @@ def test_raw_response_for_binary(self, respx_mock: MockRouter, client: Everos) -
assert isinstance(response, httpx.Response)
assert response.json() == {"foo": "bar"}
- def test_copy(self, client: Everos) -> None:
+ def test_copy(self, client: EverOS) -> None:
copied = client.copy()
assert id(copied) != id(client)
@@ -140,7 +140,7 @@ def test_copy(self, client: Everos) -> None:
assert copied.api_key == "another My API Key"
assert client.api_key == "My API Key"
- def test_copy_default_options(self, client: Everos) -> None:
+ def test_copy_default_options(self, client: EverOS) -> None:
# options that have a default are overridden correctly
copied = client.copy(max_retries=7)
assert copied.max_retries == 7
@@ -157,7 +157,7 @@ def test_copy_default_options(self, client: Everos) -> None:
assert isinstance(client.timeout, httpx.Timeout)
def test_copy_default_headers(self) -> None:
- client = Everos(
+ client = EverOS(
base_url=base_url, api_key=api_key, _strict_response_validation=True, default_headers={"X-Foo": "bar"}
)
assert client.default_headers["X-Foo"] == "bar"
@@ -192,7 +192,7 @@ def test_copy_default_headers(self) -> None:
client.close()
def test_copy_default_query(self) -> None:
- client = Everos(
+ client = EverOS(
base_url=base_url, api_key=api_key, _strict_response_validation=True, default_query={"foo": "bar"}
)
assert _get_params(client)["foo"] == "bar"
@@ -229,7 +229,7 @@ def test_copy_default_query(self) -> None:
client.close()
- def test_copy_signature(self, client: Everos) -> None:
+ def test_copy_signature(self, client: EverOS) -> None:
# ensure the same parameters that can be passed to the client are defined in the `.copy()` method
init_signature = inspect.signature(
# mypy doesn't like that we access the `__init__` property.
@@ -246,7 +246,7 @@ def test_copy_signature(self, client: Everos) -> None:
assert copy_param is not None, f"copy() signature is missing the {name} param"
@pytest.mark.skipif(sys.version_info >= (3, 10), reason="fails because of a memory leak that started from 3.12")
- def test_copy_build_request(self, client: Everos) -> None:
+ def test_copy_build_request(self, client: EverOS) -> None:
options = FinalRequestOptions(method="get", url="/foo")
def build_request(options: FinalRequestOptions) -> None:
@@ -308,7 +308,7 @@ def add_leak(leaks: list[tracemalloc.StatisticDiff], diff: tracemalloc.Statistic
print(frame)
raise AssertionError()
- def test_request_timeout(self, client: Everos) -> None:
+ def test_request_timeout(self, client: EverOS) -> None:
request = client._build_request(FinalRequestOptions(method="get", url="/foo"))
timeout = httpx.Timeout(**request.extensions["timeout"]) # type: ignore
assert timeout == DEFAULT_TIMEOUT
@@ -318,7 +318,7 @@ def test_request_timeout(self, client: Everos) -> None:
assert timeout == httpx.Timeout(100.0)
def test_client_timeout_option(self) -> None:
- client = Everos(base_url=base_url, api_key=api_key, _strict_response_validation=True, timeout=httpx.Timeout(0))
+ client = EverOS(base_url=base_url, api_key=api_key, _strict_response_validation=True, timeout=httpx.Timeout(0))
request = client._build_request(FinalRequestOptions(method="get", url="/foo"))
timeout = httpx.Timeout(**request.extensions["timeout"]) # type: ignore
@@ -329,7 +329,7 @@ def test_client_timeout_option(self) -> None:
def test_http_client_timeout_option(self) -> None:
# custom timeout given to the httpx client should be used
with httpx.Client(timeout=None) as http_client:
- client = Everos(
+ client = EverOS(
base_url=base_url, api_key=api_key, _strict_response_validation=True, http_client=http_client
)
@@ -341,7 +341,7 @@ def test_http_client_timeout_option(self) -> None:
# no timeout given to the httpx client should not use the httpx default
with httpx.Client() as http_client:
- client = Everos(
+ client = EverOS(
base_url=base_url, api_key=api_key, _strict_response_validation=True, http_client=http_client
)
@@ -353,7 +353,7 @@ def test_http_client_timeout_option(self) -> None:
# explicitly passing the default timeout currently results in it being ignored
with httpx.Client(timeout=HTTPX_DEFAULT_TIMEOUT) as http_client:
- client = Everos(
+ client = EverOS(
base_url=base_url, api_key=api_key, _strict_response_validation=True, http_client=http_client
)
@@ -366,7 +366,7 @@ def test_http_client_timeout_option(self) -> None:
async def test_invalid_http_client(self) -> None:
with pytest.raises(TypeError, match="Invalid `http_client` arg"):
async with httpx.AsyncClient() as http_client:
- Everos(
+ EverOS(
base_url=base_url,
api_key=api_key,
_strict_response_validation=True,
@@ -374,14 +374,14 @@ async def test_invalid_http_client(self) -> None:
)
def test_default_headers_option(self) -> None:
- test_client = Everos(
+ test_client = EverOS(
base_url=base_url, api_key=api_key, _strict_response_validation=True, default_headers={"X-Foo": "bar"}
)
request = test_client._build_request(FinalRequestOptions(method="get", url="/foo"))
assert request.headers.get("x-foo") == "bar"
assert request.headers.get("x-stainless-lang") == "python"
- test_client2 = Everos(
+ test_client2 = EverOS(
base_url=base_url,
api_key=api_key,
_strict_response_validation=True,
@@ -398,17 +398,17 @@ def test_default_headers_option(self) -> None:
test_client2.close()
def test_validate_headers(self) -> None:
- client = Everos(base_url=base_url, api_key=api_key, _strict_response_validation=True)
+ client = EverOS(base_url=base_url, api_key=api_key, _strict_response_validation=True)
request = client._build_request(FinalRequestOptions(method="get", url="/foo"))
assert request.headers.get("Authorization") == f"Bearer {api_key}"
- with pytest.raises(EverosError):
+ with pytest.raises(EverOSError):
with update_env(**{"EVEROS_API_KEY": Omit()}):
- client2 = Everos(base_url=base_url, api_key=None, _strict_response_validation=True)
+ client2 = EverOS(base_url=base_url, api_key=None, _strict_response_validation=True)
_ = client2
def test_default_query_option(self) -> None:
- client = Everos(
+ client = EverOS(
base_url=base_url, api_key=api_key, _strict_response_validation=True, default_query={"query_param": "bar"}
)
request = client._build_request(FinalRequestOptions(method="get", url="/foo"))
@@ -427,7 +427,7 @@ def test_default_query_option(self) -> None:
client.close()
- def test_request_extra_json(self, client: Everos) -> None:
+ def test_request_extra_json(self, client: EverOS) -> None:
request = client._build_request(
FinalRequestOptions(
method="post",
@@ -461,7 +461,7 @@ def test_request_extra_json(self, client: Everos) -> None:
data = json.loads(request.content.decode("utf-8"))
assert data == {"foo": "bar", "baz": None}
- def test_request_extra_headers(self, client: Everos) -> None:
+ def test_request_extra_headers(self, client: EverOS) -> None:
request = client._build_request(
FinalRequestOptions(
method="post",
@@ -483,7 +483,7 @@ def test_request_extra_headers(self, client: Everos) -> None:
)
assert request.headers.get("X-Bar") == "false"
- def test_request_extra_query(self, client: Everos) -> None:
+ def test_request_extra_query(self, client: EverOS) -> None:
request = client._build_request(
FinalRequestOptions(
method="post",
@@ -524,7 +524,7 @@ def test_request_extra_query(self, client: Everos) -> None:
params = dict(request.url.params)
assert params == {"foo": "2"}
- def test_multipart_repeating_array(self, client: Everos) -> None:
+ def test_multipart_repeating_array(self, client: EverOS) -> None:
request = client._build_request(
FinalRequestOptions.construct(
method="post",
@@ -554,7 +554,7 @@ def test_multipart_repeating_array(self, client: Everos) -> None:
]
@pytest.mark.respx(base_url=base_url)
- def test_binary_content_upload(self, respx_mock: MockRouter, client: Everos) -> None:
+ def test_binary_content_upload(self, respx_mock: MockRouter, client: EverOS) -> None:
respx_mock.post("/upload").mock(side_effect=mirror_request_content)
file_content = b"Hello, this is a test file."
@@ -579,7 +579,7 @@ def mock_handler(request: httpx.Request) -> httpx.Response:
assert counter.value == 0, "the request body should not have been read"
return httpx.Response(200, content=request.read())
- with Everos(
+ with EverOS(
base_url=base_url,
api_key=api_key,
_strict_response_validation=True,
@@ -598,7 +598,7 @@ def mock_handler(request: httpx.Request) -> httpx.Response:
assert counter.value == 1
@pytest.mark.respx(base_url=base_url)
- def test_binary_content_upload_with_body_is_deprecated(self, respx_mock: MockRouter, client: Everos) -> None:
+ def test_binary_content_upload_with_body_is_deprecated(self, respx_mock: MockRouter, client: EverOS) -> None:
respx_mock.post("/upload").mock(side_effect=mirror_request_content)
file_content = b"Hello, this is a test file."
@@ -618,7 +618,7 @@ def test_binary_content_upload_with_body_is_deprecated(self, respx_mock: MockRou
assert response.content == file_content
@pytest.mark.respx(base_url=base_url)
- def test_basic_union_response(self, respx_mock: MockRouter, client: Everos) -> None:
+ def test_basic_union_response(self, respx_mock: MockRouter, client: EverOS) -> None:
class Model1(BaseModel):
name: str
@@ -632,7 +632,7 @@ class Model2(BaseModel):
assert response.foo == "bar"
@pytest.mark.respx(base_url=base_url)
- def test_union_response_different_types(self, respx_mock: MockRouter, client: Everos) -> None:
+ def test_union_response_different_types(self, respx_mock: MockRouter, client: EverOS) -> None:
"""Union of objects with the same field name using a different type"""
class Model1(BaseModel):
@@ -654,7 +654,7 @@ class Model2(BaseModel):
assert response.foo == 1
@pytest.mark.respx(base_url=base_url)
- def test_non_application_json_content_type_for_json_data(self, respx_mock: MockRouter, client: Everos) -> None:
+ def test_non_application_json_content_type_for_json_data(self, respx_mock: MockRouter, client: EverOS) -> None:
"""
Response that sets Content-Type to something other than application/json but returns json data
"""
@@ -675,7 +675,7 @@ class Model(BaseModel):
assert response.foo == 2
def test_base_url_setter(self) -> None:
- client = Everos(base_url="https://example.com/from_init", api_key=api_key, _strict_response_validation=True)
+ client = EverOS(base_url="https://example.com/from_init", api_key=api_key, _strict_response_validation=True)
assert client.base_url == "https://example.com/from_init/"
client.base_url = "https://example.com/from_setter" # type: ignore[assignment]
@@ -685,25 +685,15 @@ def test_base_url_setter(self) -> None:
client.close()
def test_base_url_env(self) -> None:
- with update_env(EVEROS_BASE_URL="http://localhost:5000/from/env"):
- client = Everos(api_key=api_key, _strict_response_validation=True)
+ with update_env(EVER_OS_BASE_URL="http://localhost:5000/from/env"):
+ client = EverOS(api_key=api_key, _strict_response_validation=True)
assert client.base_url == "http://localhost:5000/from/env/"
- # explicit environment arg requires explicitness
- with update_env(EVEROS_BASE_URL="http://localhost:5000/from/env"):
- with pytest.raises(ValueError, match=r"you must pass base_url=None"):
- Everos(api_key=api_key, _strict_response_validation=True, environment="production")
-
- client = Everos(base_url=None, api_key=api_key, _strict_response_validation=True, environment="production")
- assert str(client.base_url).startswith("http://localhost:9527")
-
- client.close()
-
@pytest.mark.parametrize(
"client",
[
- Everos(base_url="http://localhost:5000/custom/path/", api_key=api_key, _strict_response_validation=True),
- Everos(
+ EverOS(base_url="http://localhost:5000/custom/path/", api_key=api_key, _strict_response_validation=True),
+ EverOS(
base_url="http://localhost:5000/custom/path/",
api_key=api_key,
_strict_response_validation=True,
@@ -712,7 +702,7 @@ def test_base_url_env(self) -> None:
],
ids=["standard", "custom http client"],
)
- def test_base_url_trailing_slash(self, client: Everos) -> None:
+ def test_base_url_trailing_slash(self, client: EverOS) -> None:
request = client._build_request(
FinalRequestOptions(
method="post",
@@ -726,8 +716,8 @@ def test_base_url_trailing_slash(self, client: Everos) -> None:
@pytest.mark.parametrize(
"client",
[
- Everos(base_url="http://localhost:5000/custom/path/", api_key=api_key, _strict_response_validation=True),
- Everos(
+ EverOS(base_url="http://localhost:5000/custom/path/", api_key=api_key, _strict_response_validation=True),
+ EverOS(
base_url="http://localhost:5000/custom/path/",
api_key=api_key,
_strict_response_validation=True,
@@ -736,7 +726,7 @@ def test_base_url_trailing_slash(self, client: Everos) -> None:
],
ids=["standard", "custom http client"],
)
- def test_base_url_no_trailing_slash(self, client: Everos) -> None:
+ def test_base_url_no_trailing_slash(self, client: EverOS) -> None:
request = client._build_request(
FinalRequestOptions(
method="post",
@@ -750,8 +740,8 @@ def test_base_url_no_trailing_slash(self, client: Everos) -> None:
@pytest.mark.parametrize(
"client",
[
- Everos(base_url="http://localhost:5000/custom/path/", api_key=api_key, _strict_response_validation=True),
- Everos(
+ EverOS(base_url="http://localhost:5000/custom/path/", api_key=api_key, _strict_response_validation=True),
+ EverOS(
base_url="http://localhost:5000/custom/path/",
api_key=api_key,
_strict_response_validation=True,
@@ -760,7 +750,7 @@ def test_base_url_no_trailing_slash(self, client: Everos) -> None:
],
ids=["standard", "custom http client"],
)
- def test_absolute_request_url(self, client: Everos) -> None:
+ def test_absolute_request_url(self, client: EverOS) -> None:
request = client._build_request(
FinalRequestOptions(
method="post",
@@ -772,7 +762,7 @@ def test_absolute_request_url(self, client: Everos) -> None:
client.close()
def test_copied_client_does_not_close_http(self) -> None:
- test_client = Everos(base_url=base_url, api_key=api_key, _strict_response_validation=True)
+ test_client = EverOS(base_url=base_url, api_key=api_key, _strict_response_validation=True)
assert not test_client.is_closed()
copied = test_client.copy()
@@ -783,7 +773,7 @@ def test_copied_client_does_not_close_http(self) -> None:
assert not test_client.is_closed()
def test_client_context_manager(self) -> None:
- test_client = Everos(base_url=base_url, api_key=api_key, _strict_response_validation=True)
+ test_client = EverOS(base_url=base_url, api_key=api_key, _strict_response_validation=True)
with test_client as c2:
assert c2 is test_client
assert not c2.is_closed()
@@ -791,7 +781,7 @@ def test_client_context_manager(self) -> None:
assert test_client.is_closed()
@pytest.mark.respx(base_url=base_url)
- def test_client_response_validation_error(self, respx_mock: MockRouter, client: Everos) -> None:
+ def test_client_response_validation_error(self, respx_mock: MockRouter, client: EverOS) -> None:
class Model(BaseModel):
foo: str
@@ -804,7 +794,7 @@ class Model(BaseModel):
def test_client_max_retries_validation(self) -> None:
with pytest.raises(TypeError, match=r"max_retries cannot be None"):
- Everos(base_url=base_url, api_key=api_key, _strict_response_validation=True, max_retries=cast(Any, None))
+ EverOS(base_url=base_url, api_key=api_key, _strict_response_validation=True, max_retries=cast(Any, None))
@pytest.mark.respx(base_url=base_url)
def test_received_text_for_expected_json(self, respx_mock: MockRouter) -> None:
@@ -813,12 +803,12 @@ class Model(BaseModel):
respx_mock.get("/foo").mock(return_value=httpx.Response(200, text="my-custom-format"))
- strict_client = Everos(base_url=base_url, api_key=api_key, _strict_response_validation=True)
+ strict_client = EverOS(base_url=base_url, api_key=api_key, _strict_response_validation=True)
with pytest.raises(APIResponseValidationError):
strict_client.get("/foo", cast_to=Model)
- non_strict_client = Everos(base_url=base_url, api_key=api_key, _strict_response_validation=False)
+ non_strict_client = EverOS(base_url=base_url, api_key=api_key, _strict_response_validation=False)
response = non_strict_client.get("/foo", cast_to=Model)
assert isinstance(response, str) # type: ignore[unreachable]
@@ -849,7 +839,7 @@ class Model(BaseModel):
)
@mock.patch("time.time", mock.MagicMock(return_value=1696004797))
def test_parse_retry_after_header(
- self, remaining_retries: int, retry_after: str, timeout: float, client: Everos
+ self, remaining_retries: int, retry_after: str, timeout: float, client: EverOS
) -> None:
headers = httpx.Headers({"retry-after": retry_after})
options = FinalRequestOptions(method="get", url="/foo", max_retries=3)
@@ -858,11 +848,11 @@ def test_parse_retry_after_header(
@mock.patch("everos._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout)
@pytest.mark.respx(base_url=base_url)
- def test_retrying_timeout_errors_doesnt_leak(self, respx_mock: MockRouter, client: Everos) -> None:
+ def test_retrying_timeout_errors_doesnt_leak(self, respx_mock: MockRouter, client: EverOS) -> None:
respx_mock.post("/api/v1/memories").mock(side_effect=httpx.TimeoutException("Test timeout error"))
with pytest.raises(APITimeoutError):
- client.v1.memories.with_streaming_response.create(
+ client.v1.memories.with_streaming_response.add(
messages=[
{
"content": "x",
@@ -877,11 +867,11 @@ def test_retrying_timeout_errors_doesnt_leak(self, respx_mock: MockRouter, clien
@mock.patch("everos._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout)
@pytest.mark.respx(base_url=base_url)
- def test_retrying_status_errors_doesnt_leak(self, respx_mock: MockRouter, client: Everos) -> None:
+ def test_retrying_status_errors_doesnt_leak(self, respx_mock: MockRouter, client: EverOS) -> None:
respx_mock.post("/api/v1/memories").mock(return_value=httpx.Response(500))
with pytest.raises(APIStatusError):
- client.v1.memories.with_streaming_response.create(
+ client.v1.memories.with_streaming_response.add(
messages=[
{
"content": "x",
@@ -899,7 +889,7 @@ def test_retrying_status_errors_doesnt_leak(self, respx_mock: MockRouter, client
@pytest.mark.parametrize("failure_mode", ["status", "exception"])
def test_retries_taken(
self,
- client: Everos,
+ client: EverOS,
failures_before_success: int,
failure_mode: Literal["status", "exception"],
respx_mock: MockRouter,
@@ -919,7 +909,7 @@ def retry_handler(_request: httpx.Request) -> httpx.Response:
respx_mock.post("/api/v1/memories").mock(side_effect=retry_handler)
- response = client.v1.memories.with_raw_response.create(
+ response = client.v1.memories.with_raw_response.add(
messages=[
{
"content": "x",
@@ -937,7 +927,7 @@ def retry_handler(_request: httpx.Request) -> httpx.Response:
@mock.patch("everos._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout)
@pytest.mark.respx(base_url=base_url)
def test_omit_retry_count_header(
- self, client: Everos, failures_before_success: int, respx_mock: MockRouter
+ self, client: EverOS, failures_before_success: int, respx_mock: MockRouter
) -> None:
client = client.with_options(max_retries=4)
@@ -952,7 +942,7 @@ def retry_handler(_request: httpx.Request) -> httpx.Response:
respx_mock.post("/api/v1/memories").mock(side_effect=retry_handler)
- response = client.v1.memories.with_raw_response.create(
+ response = client.v1.memories.with_raw_response.add(
messages=[
{
"content": "x",
@@ -970,7 +960,7 @@ def retry_handler(_request: httpx.Request) -> httpx.Response:
@mock.patch("everos._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout)
@pytest.mark.respx(base_url=base_url)
def test_overwrite_retry_count_header(
- self, client: Everos, failures_before_success: int, respx_mock: MockRouter
+ self, client: EverOS, failures_before_success: int, respx_mock: MockRouter
) -> None:
client = client.with_options(max_retries=4)
@@ -985,7 +975,7 @@ def retry_handler(_request: httpx.Request) -> httpx.Response:
respx_mock.post("/api/v1/memories").mock(side_effect=retry_handler)
- response = client.v1.memories.with_raw_response.create(
+ response = client.v1.memories.with_raw_response.add(
messages=[
{
"content": "x",
@@ -1030,7 +1020,7 @@ def test_default_client_creation(self) -> None:
)
@pytest.mark.respx(base_url=base_url)
- def test_follow_redirects(self, respx_mock: MockRouter, client: Everos) -> None:
+ def test_follow_redirects(self, respx_mock: MockRouter, client: EverOS) -> None:
# Test that the default follow_redirects=True allows following redirects
respx_mock.post("/redirect").mock(
return_value=httpx.Response(302, headers={"Location": f"{base_url}/redirected"})
@@ -1042,7 +1032,7 @@ def test_follow_redirects(self, respx_mock: MockRouter, client: Everos) -> None:
assert response.json() == {"status": "ok"}
@pytest.mark.respx(base_url=base_url)
- def test_follow_redirects_disabled(self, respx_mock: MockRouter, client: Everos) -> None:
+ def test_follow_redirects_disabled(self, respx_mock: MockRouter, client: EverOS) -> None:
# Test that follow_redirects=False prevents following redirects
respx_mock.post("/redirect").mock(
return_value=httpx.Response(302, headers={"Location": f"{base_url}/redirected"})
@@ -1055,9 +1045,9 @@ def test_follow_redirects_disabled(self, respx_mock: MockRouter, client: Everos)
assert exc_info.value.response.headers["Location"] == f"{base_url}/redirected"
-class TestAsyncEveros:
+class TestAsyncEverOS:
@pytest.mark.respx(base_url=base_url)
- async def test_raw_response(self, respx_mock: MockRouter, async_client: AsyncEveros) -> None:
+ async def test_raw_response(self, respx_mock: MockRouter, async_client: AsyncEverOS) -> None:
respx_mock.post("/foo").mock(return_value=httpx.Response(200, json={"foo": "bar"}))
response = await async_client.post("/foo", cast_to=httpx.Response)
@@ -1066,7 +1056,7 @@ async def test_raw_response(self, respx_mock: MockRouter, async_client: AsyncEve
assert response.json() == {"foo": "bar"}
@pytest.mark.respx(base_url=base_url)
- async def test_raw_response_for_binary(self, respx_mock: MockRouter, async_client: AsyncEveros) -> None:
+ async def test_raw_response_for_binary(self, respx_mock: MockRouter, async_client: AsyncEverOS) -> None:
respx_mock.post("/foo").mock(
return_value=httpx.Response(200, headers={"Content-Type": "application/binary"}, content='{"foo": "bar"}')
)
@@ -1076,7 +1066,7 @@ async def test_raw_response_for_binary(self, respx_mock: MockRouter, async_clien
assert isinstance(response, httpx.Response)
assert response.json() == {"foo": "bar"}
- def test_copy(self, async_client: AsyncEveros) -> None:
+ def test_copy(self, async_client: AsyncEverOS) -> None:
copied = async_client.copy()
assert id(copied) != id(async_client)
@@ -1084,7 +1074,7 @@ def test_copy(self, async_client: AsyncEveros) -> None:
assert copied.api_key == "another My API Key"
assert async_client.api_key == "My API Key"
- def test_copy_default_options(self, async_client: AsyncEveros) -> None:
+ def test_copy_default_options(self, async_client: AsyncEverOS) -> None:
# options that have a default are overridden correctly
copied = async_client.copy(max_retries=7)
assert copied.max_retries == 7
@@ -1101,7 +1091,7 @@ def test_copy_default_options(self, async_client: AsyncEveros) -> None:
assert isinstance(async_client.timeout, httpx.Timeout)
async def test_copy_default_headers(self) -> None:
- client = AsyncEveros(
+ client = AsyncEverOS(
base_url=base_url, api_key=api_key, _strict_response_validation=True, default_headers={"X-Foo": "bar"}
)
assert client.default_headers["X-Foo"] == "bar"
@@ -1136,7 +1126,7 @@ async def test_copy_default_headers(self) -> None:
await client.close()
async def test_copy_default_query(self) -> None:
- client = AsyncEveros(
+ client = AsyncEverOS(
base_url=base_url, api_key=api_key, _strict_response_validation=True, default_query={"foo": "bar"}
)
assert _get_params(client)["foo"] == "bar"
@@ -1173,7 +1163,7 @@ async def test_copy_default_query(self) -> None:
await client.close()
- def test_copy_signature(self, async_client: AsyncEveros) -> None:
+ def test_copy_signature(self, async_client: AsyncEverOS) -> None:
# ensure the same parameters that can be passed to the client are defined in the `.copy()` method
init_signature = inspect.signature(
# mypy doesn't like that we access the `__init__` property.
@@ -1190,7 +1180,7 @@ def test_copy_signature(self, async_client: AsyncEveros) -> None:
assert copy_param is not None, f"copy() signature is missing the {name} param"
@pytest.mark.skipif(sys.version_info >= (3, 10), reason="fails because of a memory leak that started from 3.12")
- def test_copy_build_request(self, async_client: AsyncEveros) -> None:
+ def test_copy_build_request(self, async_client: AsyncEverOS) -> None:
options = FinalRequestOptions(method="get", url="/foo")
def build_request(options: FinalRequestOptions) -> None:
@@ -1252,7 +1242,7 @@ def add_leak(leaks: list[tracemalloc.StatisticDiff], diff: tracemalloc.Statistic
print(frame)
raise AssertionError()
- async def test_request_timeout(self, async_client: AsyncEveros) -> None:
+ async def test_request_timeout(self, async_client: AsyncEverOS) -> None:
request = async_client._build_request(FinalRequestOptions(method="get", url="/foo"))
timeout = httpx.Timeout(**request.extensions["timeout"]) # type: ignore
assert timeout == DEFAULT_TIMEOUT
@@ -1264,7 +1254,7 @@ async def test_request_timeout(self, async_client: AsyncEveros) -> None:
assert timeout == httpx.Timeout(100.0)
async def test_client_timeout_option(self) -> None:
- client = AsyncEveros(
+ client = AsyncEverOS(
base_url=base_url, api_key=api_key, _strict_response_validation=True, timeout=httpx.Timeout(0)
)
@@ -1277,7 +1267,7 @@ async def test_client_timeout_option(self) -> None:
async def test_http_client_timeout_option(self) -> None:
# custom timeout given to the httpx client should be used
async with httpx.AsyncClient(timeout=None) as http_client:
- client = AsyncEveros(
+ client = AsyncEverOS(
base_url=base_url, api_key=api_key, _strict_response_validation=True, http_client=http_client
)
@@ -1289,7 +1279,7 @@ async def test_http_client_timeout_option(self) -> None:
# no timeout given to the httpx client should not use the httpx default
async with httpx.AsyncClient() as http_client:
- client = AsyncEveros(
+ client = AsyncEverOS(
base_url=base_url, api_key=api_key, _strict_response_validation=True, http_client=http_client
)
@@ -1301,7 +1291,7 @@ async def test_http_client_timeout_option(self) -> None:
# explicitly passing the default timeout currently results in it being ignored
async with httpx.AsyncClient(timeout=HTTPX_DEFAULT_TIMEOUT) as http_client:
- client = AsyncEveros(
+ client = AsyncEverOS(
base_url=base_url, api_key=api_key, _strict_response_validation=True, http_client=http_client
)
@@ -1314,7 +1304,7 @@ async def test_http_client_timeout_option(self) -> None:
def test_invalid_http_client(self) -> None:
with pytest.raises(TypeError, match="Invalid `http_client` arg"):
with httpx.Client() as http_client:
- AsyncEveros(
+ AsyncEverOS(
base_url=base_url,
api_key=api_key,
_strict_response_validation=True,
@@ -1322,14 +1312,14 @@ def test_invalid_http_client(self) -> None:
)
async def test_default_headers_option(self) -> None:
- test_client = AsyncEveros(
+ test_client = AsyncEverOS(
base_url=base_url, api_key=api_key, _strict_response_validation=True, default_headers={"X-Foo": "bar"}
)
request = test_client._build_request(FinalRequestOptions(method="get", url="/foo"))
assert request.headers.get("x-foo") == "bar"
assert request.headers.get("x-stainless-lang") == "python"
- test_client2 = AsyncEveros(
+ test_client2 = AsyncEverOS(
base_url=base_url,
api_key=api_key,
_strict_response_validation=True,
@@ -1346,17 +1336,17 @@ async def test_default_headers_option(self) -> None:
await test_client2.close()
def test_validate_headers(self) -> None:
- client = AsyncEveros(base_url=base_url, api_key=api_key, _strict_response_validation=True)
+ client = AsyncEverOS(base_url=base_url, api_key=api_key, _strict_response_validation=True)
request = client._build_request(FinalRequestOptions(method="get", url="/foo"))
assert request.headers.get("Authorization") == f"Bearer {api_key}"
- with pytest.raises(EverosError):
+ with pytest.raises(EverOSError):
with update_env(**{"EVEROS_API_KEY": Omit()}):
- client2 = AsyncEveros(base_url=base_url, api_key=None, _strict_response_validation=True)
+ client2 = AsyncEverOS(base_url=base_url, api_key=None, _strict_response_validation=True)
_ = client2
async def test_default_query_option(self) -> None:
- client = AsyncEveros(
+ client = AsyncEverOS(
base_url=base_url, api_key=api_key, _strict_response_validation=True, default_query={"query_param": "bar"}
)
request = client._build_request(FinalRequestOptions(method="get", url="/foo"))
@@ -1375,7 +1365,7 @@ async def test_default_query_option(self) -> None:
await client.close()
- def test_request_extra_json(self, client: Everos) -> None:
+ def test_request_extra_json(self, client: EverOS) -> None:
request = client._build_request(
FinalRequestOptions(
method="post",
@@ -1409,7 +1399,7 @@ def test_request_extra_json(self, client: Everos) -> None:
data = json.loads(request.content.decode("utf-8"))
assert data == {"foo": "bar", "baz": None}
- def test_request_extra_headers(self, client: Everos) -> None:
+ def test_request_extra_headers(self, client: EverOS) -> None:
request = client._build_request(
FinalRequestOptions(
method="post",
@@ -1431,7 +1421,7 @@ def test_request_extra_headers(self, client: Everos) -> None:
)
assert request.headers.get("X-Bar") == "false"
- def test_request_extra_query(self, client: Everos) -> None:
+ def test_request_extra_query(self, client: EverOS) -> None:
request = client._build_request(
FinalRequestOptions(
method="post",
@@ -1472,7 +1462,7 @@ def test_request_extra_query(self, client: Everos) -> None:
params = dict(request.url.params)
assert params == {"foo": "2"}
- def test_multipart_repeating_array(self, async_client: AsyncEveros) -> None:
+ def test_multipart_repeating_array(self, async_client: AsyncEverOS) -> None:
request = async_client._build_request(
FinalRequestOptions.construct(
method="post",
@@ -1502,7 +1492,7 @@ def test_multipart_repeating_array(self, async_client: AsyncEveros) -> None:
]
@pytest.mark.respx(base_url=base_url)
- async def test_binary_content_upload(self, respx_mock: MockRouter, async_client: AsyncEveros) -> None:
+ async def test_binary_content_upload(self, respx_mock: MockRouter, async_client: AsyncEverOS) -> None:
respx_mock.post("/upload").mock(side_effect=mirror_request_content)
file_content = b"Hello, this is a test file."
@@ -1527,7 +1517,7 @@ async def mock_handler(request: httpx.Request) -> httpx.Response:
assert counter.value == 0, "the request body should not have been read"
return httpx.Response(200, content=await request.aread())
- async with AsyncEveros(
+ async with AsyncEverOS(
base_url=base_url,
api_key=api_key,
_strict_response_validation=True,
@@ -1547,7 +1537,7 @@ async def mock_handler(request: httpx.Request) -> httpx.Response:
@pytest.mark.respx(base_url=base_url)
async def test_binary_content_upload_with_body_is_deprecated(
- self, respx_mock: MockRouter, async_client: AsyncEveros
+ self, respx_mock: MockRouter, async_client: AsyncEverOS
) -> None:
respx_mock.post("/upload").mock(side_effect=mirror_request_content)
@@ -1568,7 +1558,7 @@ async def test_binary_content_upload_with_body_is_deprecated(
assert response.content == file_content
@pytest.mark.respx(base_url=base_url)
- async def test_basic_union_response(self, respx_mock: MockRouter, async_client: AsyncEveros) -> None:
+ async def test_basic_union_response(self, respx_mock: MockRouter, async_client: AsyncEverOS) -> None:
class Model1(BaseModel):
name: str
@@ -1582,7 +1572,7 @@ class Model2(BaseModel):
assert response.foo == "bar"
@pytest.mark.respx(base_url=base_url)
- async def test_union_response_different_types(self, respx_mock: MockRouter, async_client: AsyncEveros) -> None:
+ async def test_union_response_different_types(self, respx_mock: MockRouter, async_client: AsyncEverOS) -> None:
"""Union of objects with the same field name using a different type"""
class Model1(BaseModel):
@@ -1605,7 +1595,7 @@ class Model2(BaseModel):
@pytest.mark.respx(base_url=base_url)
async def test_non_application_json_content_type_for_json_data(
- self, respx_mock: MockRouter, async_client: AsyncEveros
+ self, respx_mock: MockRouter, async_client: AsyncEverOS
) -> None:
"""
Response that sets Content-Type to something other than application/json but returns json data
@@ -1627,7 +1617,7 @@ class Model(BaseModel):
assert response.foo == 2
async def test_base_url_setter(self) -> None:
- client = AsyncEveros(
+ client = AsyncEverOS(
base_url="https://example.com/from_init", api_key=api_key, _strict_response_validation=True
)
assert client.base_url == "https://example.com/from_init/"
@@ -1639,29 +1629,17 @@ async def test_base_url_setter(self) -> None:
await client.close()
async def test_base_url_env(self) -> None:
- with update_env(EVEROS_BASE_URL="http://localhost:5000/from/env"):
- client = AsyncEveros(api_key=api_key, _strict_response_validation=True)
+ with update_env(EVER_OS_BASE_URL="http://localhost:5000/from/env"):
+ client = AsyncEverOS(api_key=api_key, _strict_response_validation=True)
assert client.base_url == "http://localhost:5000/from/env/"
- # explicit environment arg requires explicitness
- with update_env(EVEROS_BASE_URL="http://localhost:5000/from/env"):
- with pytest.raises(ValueError, match=r"you must pass base_url=None"):
- AsyncEveros(api_key=api_key, _strict_response_validation=True, environment="production")
-
- client = AsyncEveros(
- base_url=None, api_key=api_key, _strict_response_validation=True, environment="production"
- )
- assert str(client.base_url).startswith("http://localhost:9527")
-
- await client.close()
-
@pytest.mark.parametrize(
"client",
[
- AsyncEveros(
+ AsyncEverOS(
base_url="http://localhost:5000/custom/path/", api_key=api_key, _strict_response_validation=True
),
- AsyncEveros(
+ AsyncEverOS(
base_url="http://localhost:5000/custom/path/",
api_key=api_key,
_strict_response_validation=True,
@@ -1670,7 +1648,7 @@ async def test_base_url_env(self) -> None:
],
ids=["standard", "custom http client"],
)
- async def test_base_url_trailing_slash(self, client: AsyncEveros) -> None:
+ async def test_base_url_trailing_slash(self, client: AsyncEverOS) -> None:
request = client._build_request(
FinalRequestOptions(
method="post",
@@ -1684,10 +1662,10 @@ async def test_base_url_trailing_slash(self, client: AsyncEveros) -> None:
@pytest.mark.parametrize(
"client",
[
- AsyncEveros(
+ AsyncEverOS(
base_url="http://localhost:5000/custom/path/", api_key=api_key, _strict_response_validation=True
),
- AsyncEveros(
+ AsyncEverOS(
base_url="http://localhost:5000/custom/path/",
api_key=api_key,
_strict_response_validation=True,
@@ -1696,7 +1674,7 @@ async def test_base_url_trailing_slash(self, client: AsyncEveros) -> None:
],
ids=["standard", "custom http client"],
)
- async def test_base_url_no_trailing_slash(self, client: AsyncEveros) -> None:
+ async def test_base_url_no_trailing_slash(self, client: AsyncEverOS) -> None:
request = client._build_request(
FinalRequestOptions(
method="post",
@@ -1710,10 +1688,10 @@ async def test_base_url_no_trailing_slash(self, client: AsyncEveros) -> None:
@pytest.mark.parametrize(
"client",
[
- AsyncEveros(
+ AsyncEverOS(
base_url="http://localhost:5000/custom/path/", api_key=api_key, _strict_response_validation=True
),
- AsyncEveros(
+ AsyncEverOS(
base_url="http://localhost:5000/custom/path/",
api_key=api_key,
_strict_response_validation=True,
@@ -1722,7 +1700,7 @@ async def test_base_url_no_trailing_slash(self, client: AsyncEveros) -> None:
],
ids=["standard", "custom http client"],
)
- async def test_absolute_request_url(self, client: AsyncEveros) -> None:
+ async def test_absolute_request_url(self, client: AsyncEverOS) -> None:
request = client._build_request(
FinalRequestOptions(
method="post",
@@ -1734,7 +1712,7 @@ async def test_absolute_request_url(self, client: AsyncEveros) -> None:
await client.close()
async def test_copied_client_does_not_close_http(self) -> None:
- test_client = AsyncEveros(base_url=base_url, api_key=api_key, _strict_response_validation=True)
+ test_client = AsyncEverOS(base_url=base_url, api_key=api_key, _strict_response_validation=True)
assert not test_client.is_closed()
copied = test_client.copy()
@@ -1746,7 +1724,7 @@ async def test_copied_client_does_not_close_http(self) -> None:
assert not test_client.is_closed()
async def test_client_context_manager(self) -> None:
- test_client = AsyncEveros(base_url=base_url, api_key=api_key, _strict_response_validation=True)
+ test_client = AsyncEverOS(base_url=base_url, api_key=api_key, _strict_response_validation=True)
async with test_client as c2:
assert c2 is test_client
assert not c2.is_closed()
@@ -1754,7 +1732,7 @@ async def test_client_context_manager(self) -> None:
assert test_client.is_closed()
@pytest.mark.respx(base_url=base_url)
- async def test_client_response_validation_error(self, respx_mock: MockRouter, async_client: AsyncEveros) -> None:
+ async def test_client_response_validation_error(self, respx_mock: MockRouter, async_client: AsyncEverOS) -> None:
class Model(BaseModel):
foo: str
@@ -1767,7 +1745,7 @@ class Model(BaseModel):
async def test_client_max_retries_validation(self) -> None:
with pytest.raises(TypeError, match=r"max_retries cannot be None"):
- AsyncEveros(
+ AsyncEverOS(
base_url=base_url, api_key=api_key, _strict_response_validation=True, max_retries=cast(Any, None)
)
@@ -1778,12 +1756,12 @@ class Model(BaseModel):
respx_mock.get("/foo").mock(return_value=httpx.Response(200, text="my-custom-format"))
- strict_client = AsyncEveros(base_url=base_url, api_key=api_key, _strict_response_validation=True)
+ strict_client = AsyncEverOS(base_url=base_url, api_key=api_key, _strict_response_validation=True)
with pytest.raises(APIResponseValidationError):
await strict_client.get("/foo", cast_to=Model)
- non_strict_client = AsyncEveros(base_url=base_url, api_key=api_key, _strict_response_validation=False)
+ non_strict_client = AsyncEverOS(base_url=base_url, api_key=api_key, _strict_response_validation=False)
response = await non_strict_client.get("/foo", cast_to=Model)
assert isinstance(response, str) # type: ignore[unreachable]
@@ -1814,7 +1792,7 @@ class Model(BaseModel):
)
@mock.patch("time.time", mock.MagicMock(return_value=1696004797))
async def test_parse_retry_after_header(
- self, remaining_retries: int, retry_after: str, timeout: float, async_client: AsyncEveros
+ self, remaining_retries: int, retry_after: str, timeout: float, async_client: AsyncEverOS
) -> None:
headers = httpx.Headers({"retry-after": retry_after})
options = FinalRequestOptions(method="get", url="/foo", max_retries=3)
@@ -1823,11 +1801,11 @@ async def test_parse_retry_after_header(
@mock.patch("everos._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout)
@pytest.mark.respx(base_url=base_url)
- async def test_retrying_timeout_errors_doesnt_leak(self, respx_mock: MockRouter, async_client: AsyncEveros) -> None:
+ async def test_retrying_timeout_errors_doesnt_leak(self, respx_mock: MockRouter, async_client: AsyncEverOS) -> None:
respx_mock.post("/api/v1/memories").mock(side_effect=httpx.TimeoutException("Test timeout error"))
with pytest.raises(APITimeoutError):
- await async_client.v1.memories.with_streaming_response.create(
+ await async_client.v1.memories.with_streaming_response.add(
messages=[
{
"content": "x",
@@ -1842,11 +1820,11 @@ async def test_retrying_timeout_errors_doesnt_leak(self, respx_mock: MockRouter,
@mock.patch("everos._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout)
@pytest.mark.respx(base_url=base_url)
- async def test_retrying_status_errors_doesnt_leak(self, respx_mock: MockRouter, async_client: AsyncEveros) -> None:
+ async def test_retrying_status_errors_doesnt_leak(self, respx_mock: MockRouter, async_client: AsyncEverOS) -> None:
respx_mock.post("/api/v1/memories").mock(return_value=httpx.Response(500))
with pytest.raises(APIStatusError):
- await async_client.v1.memories.with_streaming_response.create(
+ await async_client.v1.memories.with_streaming_response.add(
messages=[
{
"content": "x",
@@ -1864,7 +1842,7 @@ async def test_retrying_status_errors_doesnt_leak(self, respx_mock: MockRouter,
@pytest.mark.parametrize("failure_mode", ["status", "exception"])
async def test_retries_taken(
self,
- async_client: AsyncEveros,
+ async_client: AsyncEverOS,
failures_before_success: int,
failure_mode: Literal["status", "exception"],
respx_mock: MockRouter,
@@ -1884,7 +1862,7 @@ def retry_handler(_request: httpx.Request) -> httpx.Response:
respx_mock.post("/api/v1/memories").mock(side_effect=retry_handler)
- response = await client.v1.memories.with_raw_response.create(
+ response = await client.v1.memories.with_raw_response.add(
messages=[
{
"content": "x",
@@ -1902,7 +1880,7 @@ def retry_handler(_request: httpx.Request) -> httpx.Response:
@mock.patch("everos._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout)
@pytest.mark.respx(base_url=base_url)
async def test_omit_retry_count_header(
- self, async_client: AsyncEveros, failures_before_success: int, respx_mock: MockRouter
+ self, async_client: AsyncEverOS, failures_before_success: int, respx_mock: MockRouter
) -> None:
client = async_client.with_options(max_retries=4)
@@ -1917,7 +1895,7 @@ def retry_handler(_request: httpx.Request) -> httpx.Response:
respx_mock.post("/api/v1/memories").mock(side_effect=retry_handler)
- response = await client.v1.memories.with_raw_response.create(
+ response = await client.v1.memories.with_raw_response.add(
messages=[
{
"content": "x",
@@ -1935,7 +1913,7 @@ def retry_handler(_request: httpx.Request) -> httpx.Response:
@mock.patch("everos._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout)
@pytest.mark.respx(base_url=base_url)
async def test_overwrite_retry_count_header(
- self, async_client: AsyncEveros, failures_before_success: int, respx_mock: MockRouter
+ self, async_client: AsyncEverOS, failures_before_success: int, respx_mock: MockRouter
) -> None:
client = async_client.with_options(max_retries=4)
@@ -1950,7 +1928,7 @@ def retry_handler(_request: httpx.Request) -> httpx.Response:
respx_mock.post("/api/v1/memories").mock(side_effect=retry_handler)
- response = await client.v1.memories.with_raw_response.create(
+ response = await client.v1.memories.with_raw_response.add(
messages=[
{
"content": "x",
@@ -1999,7 +1977,7 @@ async def test_default_client_creation(self) -> None:
)
@pytest.mark.respx(base_url=base_url)
- async def test_follow_redirects(self, respx_mock: MockRouter, async_client: AsyncEveros) -> None:
+ async def test_follow_redirects(self, respx_mock: MockRouter, async_client: AsyncEverOS) -> None:
# Test that the default follow_redirects=True allows following redirects
respx_mock.post("/redirect").mock(
return_value=httpx.Response(302, headers={"Location": f"{base_url}/redirected"})
@@ -2011,7 +1989,7 @@ async def test_follow_redirects(self, respx_mock: MockRouter, async_client: Asyn
assert response.json() == {"status": "ok"}
@pytest.mark.respx(base_url=base_url)
- async def test_follow_redirects_disabled(self, respx_mock: MockRouter, async_client: AsyncEveros) -> None:
+ async def test_follow_redirects_disabled(self, respx_mock: MockRouter, async_client: AsyncEverOS) -> None:
# Test that follow_redirects=False prevents following redirects
respx_mock.post("/redirect").mock(
return_value=httpx.Response(302, headers={"Location": f"{base_url}/redirected"})
diff --git a/tests/test_response.py b/tests/test_response.py
index 66bdc73..cb0786b 100644
--- a/tests/test_response.py
+++ b/tests/test_response.py
@@ -6,7 +6,7 @@
import pytest
import pydantic
-from everos import Everos, BaseModel, AsyncEveros
+from everos import EverOS, BaseModel, AsyncEverOS
from everos._response import (
APIResponse,
BaseAPIResponse,
@@ -56,7 +56,7 @@ def test_extract_response_type_binary_response() -> None:
class PydanticModel(pydantic.BaseModel): ...
-def test_response_parse_mismatched_basemodel(client: Everos) -> None:
+def test_response_parse_mismatched_basemodel(client: EverOS) -> None:
response = APIResponse(
raw=httpx.Response(200, content=b"foo"),
client=client,
@@ -74,7 +74,7 @@ def test_response_parse_mismatched_basemodel(client: Everos) -> None:
@pytest.mark.asyncio
-async def test_async_response_parse_mismatched_basemodel(async_client: AsyncEveros) -> None:
+async def test_async_response_parse_mismatched_basemodel(async_client: AsyncEverOS) -> None:
response = AsyncAPIResponse(
raw=httpx.Response(200, content=b"foo"),
client=async_client,
@@ -91,7 +91,7 @@ async def test_async_response_parse_mismatched_basemodel(async_client: AsyncEver
await response.parse(to=PydanticModel)
-def test_response_parse_custom_stream(client: Everos) -> None:
+def test_response_parse_custom_stream(client: EverOS) -> None:
response = APIResponse(
raw=httpx.Response(200, content=b"foo"),
client=client,
@@ -106,7 +106,7 @@ def test_response_parse_custom_stream(client: Everos) -> None:
@pytest.mark.asyncio
-async def test_async_response_parse_custom_stream(async_client: AsyncEveros) -> None:
+async def test_async_response_parse_custom_stream(async_client: AsyncEverOS) -> None:
response = AsyncAPIResponse(
raw=httpx.Response(200, content=b"foo"),
client=async_client,
@@ -125,7 +125,7 @@ class CustomModel(BaseModel):
bar: int
-def test_response_parse_custom_model(client: Everos) -> None:
+def test_response_parse_custom_model(client: EverOS) -> None:
response = APIResponse(
raw=httpx.Response(200, content=json.dumps({"foo": "hello!", "bar": 2})),
client=client,
@@ -141,7 +141,7 @@ def test_response_parse_custom_model(client: Everos) -> None:
@pytest.mark.asyncio
-async def test_async_response_parse_custom_model(async_client: AsyncEveros) -> None:
+async def test_async_response_parse_custom_model(async_client: AsyncEverOS) -> None:
response = AsyncAPIResponse(
raw=httpx.Response(200, content=json.dumps({"foo": "hello!", "bar": 2})),
client=async_client,
@@ -156,7 +156,7 @@ async def test_async_response_parse_custom_model(async_client: AsyncEveros) -> N
assert obj.bar == 2
-def test_response_parse_annotated_type(client: Everos) -> None:
+def test_response_parse_annotated_type(client: EverOS) -> None:
response = APIResponse(
raw=httpx.Response(200, content=json.dumps({"foo": "hello!", "bar": 2})),
client=client,
@@ -173,7 +173,7 @@ def test_response_parse_annotated_type(client: Everos) -> None:
assert obj.bar == 2
-async def test_async_response_parse_annotated_type(async_client: AsyncEveros) -> None:
+async def test_async_response_parse_annotated_type(async_client: AsyncEverOS) -> None:
response = AsyncAPIResponse(
raw=httpx.Response(200, content=json.dumps({"foo": "hello!", "bar": 2})),
client=async_client,
@@ -201,7 +201,7 @@ async def test_async_response_parse_annotated_type(async_client: AsyncEveros) ->
("FalSe", False),
],
)
-def test_response_parse_bool(client: Everos, content: str, expected: bool) -> None:
+def test_response_parse_bool(client: EverOS, content: str, expected: bool) -> None:
response = APIResponse(
raw=httpx.Response(200, content=content),
client=client,
@@ -226,7 +226,7 @@ def test_response_parse_bool(client: Everos, content: str, expected: bool) -> No
("FalSe", False),
],
)
-async def test_async_response_parse_bool(client: AsyncEveros, content: str, expected: bool) -> None:
+async def test_async_response_parse_bool(client: AsyncEverOS, content: str, expected: bool) -> None:
response = AsyncAPIResponse(
raw=httpx.Response(200, content=content),
client=client,
@@ -245,7 +245,7 @@ class OtherModel(BaseModel):
@pytest.mark.parametrize("client", [False], indirect=True) # loose validation
-def test_response_parse_expect_model_union_non_json_content(client: Everos) -> None:
+def test_response_parse_expect_model_union_non_json_content(client: EverOS) -> None:
response = APIResponse(
raw=httpx.Response(200, content=b"foo", headers={"Content-Type": "application/text"}),
client=client,
@@ -262,7 +262,7 @@ def test_response_parse_expect_model_union_non_json_content(client: Everos) -> N
@pytest.mark.asyncio
@pytest.mark.parametrize("async_client", [False], indirect=True) # loose validation
-async def test_async_response_parse_expect_model_union_non_json_content(async_client: AsyncEveros) -> None:
+async def test_async_response_parse_expect_model_union_non_json_content(async_client: AsyncEverOS) -> None:
response = AsyncAPIResponse(
raw=httpx.Response(200, content=b"foo", headers={"Content-Type": "application/text"}),
client=async_client,
diff --git a/tests/test_streaming.py b/tests/test_streaming.py
index 763de8b..dc7ea45 100644
--- a/tests/test_streaming.py
+++ b/tests/test_streaming.py
@@ -5,13 +5,13 @@
import httpx
import pytest
-from everos import Everos, AsyncEveros
+from everos import EverOS, AsyncEverOS
from everos._streaming import Stream, AsyncStream, ServerSentEvent
@pytest.mark.asyncio
@pytest.mark.parametrize("sync", [True, False], ids=["sync", "async"])
-async def test_basic(sync: bool, client: Everos, async_client: AsyncEveros) -> None:
+async def test_basic(sync: bool, client: EverOS, async_client: AsyncEverOS) -> None:
def body() -> Iterator[bytes]:
yield b"event: completion\n"
yield b'data: {"foo":true}\n'
@@ -28,7 +28,7 @@ def body() -> Iterator[bytes]:
@pytest.mark.asyncio
@pytest.mark.parametrize("sync", [True, False], ids=["sync", "async"])
-async def test_data_missing_event(sync: bool, client: Everos, async_client: AsyncEveros) -> None:
+async def test_data_missing_event(sync: bool, client: EverOS, async_client: AsyncEverOS) -> None:
def body() -> Iterator[bytes]:
yield b'data: {"foo":true}\n'
yield b"\n"
@@ -44,7 +44,7 @@ def body() -> Iterator[bytes]:
@pytest.mark.asyncio
@pytest.mark.parametrize("sync", [True, False], ids=["sync", "async"])
-async def test_event_missing_data(sync: bool, client: Everos, async_client: AsyncEveros) -> None:
+async def test_event_missing_data(sync: bool, client: EverOS, async_client: AsyncEverOS) -> None:
def body() -> Iterator[bytes]:
yield b"event: ping\n"
yield b"\n"
@@ -60,7 +60,7 @@ def body() -> Iterator[bytes]:
@pytest.mark.asyncio
@pytest.mark.parametrize("sync", [True, False], ids=["sync", "async"])
-async def test_multiple_events(sync: bool, client: Everos, async_client: AsyncEveros) -> None:
+async def test_multiple_events(sync: bool, client: EverOS, async_client: AsyncEverOS) -> None:
def body() -> Iterator[bytes]:
yield b"event: ping\n"
yield b"\n"
@@ -82,7 +82,7 @@ def body() -> Iterator[bytes]:
@pytest.mark.asyncio
@pytest.mark.parametrize("sync", [True, False], ids=["sync", "async"])
-async def test_multiple_events_with_data(sync: bool, client: Everos, async_client: AsyncEveros) -> None:
+async def test_multiple_events_with_data(sync: bool, client: EverOS, async_client: AsyncEverOS) -> None:
def body() -> Iterator[bytes]:
yield b"event: ping\n"
yield b'data: {"foo":true}\n'
@@ -106,7 +106,7 @@ def body() -> Iterator[bytes]:
@pytest.mark.asyncio
@pytest.mark.parametrize("sync", [True, False], ids=["sync", "async"])
-async def test_multiple_data_lines_with_empty_line(sync: bool, client: Everos, async_client: AsyncEveros) -> None:
+async def test_multiple_data_lines_with_empty_line(sync: bool, client: EverOS, async_client: AsyncEverOS) -> None:
def body() -> Iterator[bytes]:
yield b"event: ping\n"
yield b"data: {\n"
@@ -128,7 +128,7 @@ def body() -> Iterator[bytes]:
@pytest.mark.asyncio
@pytest.mark.parametrize("sync", [True, False], ids=["sync", "async"])
-async def test_data_json_escaped_double_new_line(sync: bool, client: Everos, async_client: AsyncEveros) -> None:
+async def test_data_json_escaped_double_new_line(sync: bool, client: EverOS, async_client: AsyncEverOS) -> None:
def body() -> Iterator[bytes]:
yield b"event: ping\n"
yield b'data: {"foo": "my long\\n\\ncontent"}'
@@ -145,7 +145,7 @@ def body() -> Iterator[bytes]:
@pytest.mark.asyncio
@pytest.mark.parametrize("sync", [True, False], ids=["sync", "async"])
-async def test_multiple_data_lines(sync: bool, client: Everos, async_client: AsyncEveros) -> None:
+async def test_multiple_data_lines(sync: bool, client: EverOS, async_client: AsyncEverOS) -> None:
def body() -> Iterator[bytes]:
yield b"event: ping\n"
yield b"data: {\n"
@@ -165,8 +165,8 @@ def body() -> Iterator[bytes]:
@pytest.mark.parametrize("sync", [True, False], ids=["sync", "async"])
async def test_special_new_line_character(
sync: bool,
- client: Everos,
- async_client: AsyncEveros,
+ client: EverOS,
+ async_client: AsyncEverOS,
) -> None:
def body() -> Iterator[bytes]:
yield b'data: {"content":" culpa"}\n'
@@ -196,8 +196,8 @@ def body() -> Iterator[bytes]:
@pytest.mark.parametrize("sync", [True, False], ids=["sync", "async"])
async def test_multi_byte_character_multiple_chunks(
sync: bool,
- client: Everos,
- async_client: AsyncEveros,
+ client: EverOS,
+ async_client: AsyncEverOS,
) -> None:
def body() -> Iterator[bytes]:
yield b'data: {"content":"'
@@ -237,8 +237,8 @@ def make_event_iterator(
content: Iterator[bytes],
*,
sync: bool,
- client: Everos,
- async_client: AsyncEveros,
+ client: EverOS,
+ async_client: AsyncEverOS,
) -> Iterator[ServerSentEvent] | AsyncIterator[ServerSentEvent]:
if sync:
return Stream(cast_to=object, client=client, response=httpx.Response(200, content=content))._iter_events()