acontext 0.0.1.dev3__tar.gz → 0.0.1.dev4__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {acontext-0.0.1.dev3 → acontext-0.0.1.dev4}/PKG-INFO +1 -1
- {acontext-0.0.1.dev3 → acontext-0.0.1.dev4}/pyproject.toml +6 -2
- {acontext-0.0.1.dev3 → acontext-0.0.1.dev4}/src/acontext/__init__.py +19 -1
- acontext-0.0.1.dev4/src/acontext/async_client.py +206 -0
- {acontext-0.0.1.dev3 → acontext-0.0.1.dev4}/src/acontext/client.py +7 -11
- acontext-0.0.1.dev4/src/acontext/client_types.py +36 -0
- {acontext-0.0.1.dev3 → acontext-0.0.1.dev4}/src/acontext/errors.py +2 -2
- {acontext-0.0.1.dev3 → acontext-0.0.1.dev4}/src/acontext/messages.py +2 -10
- {acontext-0.0.1.dev3 → acontext-0.0.1.dev4}/src/acontext/resources/__init__.py +9 -0
- acontext-0.0.1.dev4/src/acontext/resources/async_blocks.py +163 -0
- acontext-0.0.1.dev4/src/acontext/resources/async_disks.py +195 -0
- acontext-0.0.1.dev4/src/acontext/resources/async_sessions.py +272 -0
- acontext-0.0.1.dev4/src/acontext/resources/async_spaces.py +90 -0
- {acontext-0.0.1.dev3 → acontext-0.0.1.dev4}/src/acontext/resources/blocks.py +3 -3
- {acontext-0.0.1.dev3 → acontext-0.0.1.dev4}/src/acontext/resources/disks.py +4 -4
- {acontext-0.0.1.dev3 → acontext-0.0.1.dev4}/src/acontext/resources/sessions.py +44 -28
- {acontext-0.0.1.dev3 → acontext-0.0.1.dev4}/src/acontext/resources/spaces.py +3 -3
- {acontext-0.0.1.dev3 → acontext-0.0.1.dev4}/src/acontext/uploads.py +5 -5
- acontext-0.0.1.dev3/src/acontext/client_types.py +0 -21
- {acontext-0.0.1.dev3 → acontext-0.0.1.dev4}/README.md +0 -0
- {acontext-0.0.1.dev3 → acontext-0.0.1.dev4}/src/acontext/_constants.py +0 -0
- {acontext-0.0.1.dev3 → acontext-0.0.1.dev4}/src/acontext/_utils.py +0 -0
- {acontext-0.0.1.dev3 → acontext-0.0.1.dev4}/src/acontext/py.typed +0 -0
- {acontext-0.0.1.dev3 → acontext-0.0.1.dev4}/src/acontext/types/__init__.py +0 -0
- {acontext-0.0.1.dev3 → acontext-0.0.1.dev4}/src/acontext/types/block.py +0 -0
- {acontext-0.0.1.dev3 → acontext-0.0.1.dev4}/src/acontext/types/disk.py +0 -0
- {acontext-0.0.1.dev3 → acontext-0.0.1.dev4}/src/acontext/types/session.py +0 -0
- {acontext-0.0.1.dev3 → acontext-0.0.1.dev4}/src/acontext/types/space.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "acontext"
|
|
3
|
-
version = "0.0.1.
|
|
3
|
+
version = "0.0.1.dev4"
|
|
4
4
|
description = "Python SDK for the Acontext API"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
requires-python = ">=3.10"
|
|
@@ -18,7 +18,11 @@ Repository = "https://github.com/memodb-io/Acontext"
|
|
|
18
18
|
Issues = "https://github.com/memodb-io/Acontext/issues"
|
|
19
19
|
|
|
20
20
|
[dependency-groups]
|
|
21
|
-
dev = [
|
|
21
|
+
dev = [
|
|
22
|
+
"pytest",
|
|
23
|
+
"ruff",
|
|
24
|
+
"pytest-asyncio"
|
|
25
|
+
]
|
|
22
26
|
|
|
23
27
|
[build-system]
|
|
24
28
|
requires = ["uv_build>=0.9.2,<0.10.0"]
|
|
@@ -4,12 +4,25 @@ Python SDK for the Acontext API.
|
|
|
4
4
|
|
|
5
5
|
from importlib import metadata as _metadata
|
|
6
6
|
|
|
7
|
+
from .async_client import AcontextAsyncClient
|
|
7
8
|
from .client import AcontextClient, FileUpload, MessagePart
|
|
8
9
|
from .messages import AcontextMessage
|
|
9
|
-
from .resources import
|
|
10
|
+
from .resources import (
|
|
11
|
+
AsyncBlocksAPI,
|
|
12
|
+
AsyncDiskArtifactsAPI,
|
|
13
|
+
AsyncDisksAPI,
|
|
14
|
+
AsyncSessionsAPI,
|
|
15
|
+
AsyncSpacesAPI,
|
|
16
|
+
BlocksAPI,
|
|
17
|
+
DiskArtifactsAPI,
|
|
18
|
+
DisksAPI,
|
|
19
|
+
SessionsAPI,
|
|
20
|
+
SpacesAPI,
|
|
21
|
+
)
|
|
10
22
|
|
|
11
23
|
__all__ = [
|
|
12
24
|
"AcontextClient",
|
|
25
|
+
"AcontextAsyncClient",
|
|
13
26
|
"FileUpload",
|
|
14
27
|
"MessagePart",
|
|
15
28
|
"AcontextMessage",
|
|
@@ -18,6 +31,11 @@ __all__ = [
|
|
|
18
31
|
"BlocksAPI",
|
|
19
32
|
"SessionsAPI",
|
|
20
33
|
"SpacesAPI",
|
|
34
|
+
"AsyncDisksAPI",
|
|
35
|
+
"AsyncDiskArtifactsAPI",
|
|
36
|
+
"AsyncBlocksAPI",
|
|
37
|
+
"AsyncSessionsAPI",
|
|
38
|
+
"AsyncSpacesAPI",
|
|
21
39
|
"__version__",
|
|
22
40
|
]
|
|
23
41
|
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
"""
|
|
2
|
+
High-level asynchronous client for the Acontext API.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import os
|
|
6
|
+
from collections.abc import Mapping
|
|
7
|
+
from typing import Any, BinaryIO
|
|
8
|
+
|
|
9
|
+
import httpx
|
|
10
|
+
|
|
11
|
+
from ._constants import DEFAULT_BASE_URL, DEFAULT_USER_AGENT
|
|
12
|
+
from .errors import APIError, TransportError
|
|
13
|
+
from .messages import MessagePart as MessagePart
|
|
14
|
+
from .uploads import FileUpload as FileUpload
|
|
15
|
+
from .resources.async_disks import AsyncDisksAPI as AsyncDisksAPI
|
|
16
|
+
from .resources.async_blocks import AsyncBlocksAPI as AsyncBlocksAPI
|
|
17
|
+
from .resources.async_sessions import AsyncSessionsAPI as AsyncSessionsAPI
|
|
18
|
+
from .resources.async_spaces import AsyncSpacesAPI as AsyncSpacesAPI
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class AcontextAsyncClient:
|
|
22
|
+
|
|
23
|
+
def __init__(
|
|
24
|
+
self,
|
|
25
|
+
*,
|
|
26
|
+
api_key: str | None = None,
|
|
27
|
+
base_url: str | None = None,
|
|
28
|
+
timeout: float | httpx.Timeout | None = 10.0,
|
|
29
|
+
user_agent: str | None = None,
|
|
30
|
+
client: httpx.AsyncClient | None = None,
|
|
31
|
+
) -> None:
|
|
32
|
+
"""
|
|
33
|
+
Initialize the Acontext async client.
|
|
34
|
+
|
|
35
|
+
Args:
|
|
36
|
+
api_key: API key for authentication. Can also be set via ACONTEXT_API_KEY env var.
|
|
37
|
+
base_url: Base URL for the API. Defaults to DEFAULT_BASE_URL. Can also be set via ACONTEXT_BASE_URL env var.
|
|
38
|
+
timeout: Request timeout in seconds. Defaults to 10.0. Can also be set via ACONTEXT_TIMEOUT env var.
|
|
39
|
+
Can also be an httpx.Timeout object.
|
|
40
|
+
user_agent: Custom user agent string. Can also be set via ACONTEXT_USER_AGENT env var.
|
|
41
|
+
client: Optional httpx.AsyncClient instance to reuse. If provided, headers and base_url
|
|
42
|
+
will be merged with the client configuration.
|
|
43
|
+
"""
|
|
44
|
+
# Priority: explicit parameters > environment variables > defaults
|
|
45
|
+
# Load api_key from parameter or environment variable
|
|
46
|
+
api_key = api_key or os.getenv("ACONTEXT_API_KEY")
|
|
47
|
+
if not api_key or not api_key.strip():
|
|
48
|
+
raise ValueError(
|
|
49
|
+
"api_key is required. Provide it either as a parameter (api_key='...') "
|
|
50
|
+
"or set the ACONTEXT_API_KEY environment variable."
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
# Load other parameters from environment variables if not provided
|
|
54
|
+
if base_url is None:
|
|
55
|
+
base_url = os.getenv("ACONTEXT_BASE_URL", DEFAULT_BASE_URL)
|
|
56
|
+
base_url = base_url.rstrip("/")
|
|
57
|
+
|
|
58
|
+
if user_agent is None:
|
|
59
|
+
user_agent = os.getenv("ACONTEXT_USER_AGENT", DEFAULT_USER_AGENT)
|
|
60
|
+
|
|
61
|
+
# Handle timeout: support both float and httpx.Timeout
|
|
62
|
+
if timeout is None:
|
|
63
|
+
timeout_str = os.getenv("ACONTEXT_TIMEOUT")
|
|
64
|
+
if timeout_str:
|
|
65
|
+
try:
|
|
66
|
+
timeout = float(timeout_str)
|
|
67
|
+
except ValueError:
|
|
68
|
+
timeout = 10.0
|
|
69
|
+
else:
|
|
70
|
+
timeout = 10.0
|
|
71
|
+
|
|
72
|
+
# Determine actual timeout value
|
|
73
|
+
actual_timeout: float | httpx.Timeout
|
|
74
|
+
if isinstance(timeout, httpx.Timeout):
|
|
75
|
+
actual_timeout = timeout
|
|
76
|
+
else:
|
|
77
|
+
actual_timeout = float(timeout)
|
|
78
|
+
|
|
79
|
+
headers = {
|
|
80
|
+
"Authorization": f"Bearer {api_key}",
|
|
81
|
+
"Accept": "application/json",
|
|
82
|
+
"User-Agent": user_agent,
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
if client is not None:
|
|
86
|
+
self._client = client
|
|
87
|
+
self._owns_client = False
|
|
88
|
+
if client.base_url == httpx.URL():
|
|
89
|
+
client.base_url = httpx.URL(base_url)
|
|
90
|
+
for name, value in headers.items():
|
|
91
|
+
if name not in client.headers:
|
|
92
|
+
client.headers[name] = value
|
|
93
|
+
self._base_url = str(client.base_url) or base_url
|
|
94
|
+
else:
|
|
95
|
+
self._client = httpx.AsyncClient(
|
|
96
|
+
base_url=base_url,
|
|
97
|
+
headers=headers,
|
|
98
|
+
timeout=actual_timeout,
|
|
99
|
+
)
|
|
100
|
+
self._owns_client = True
|
|
101
|
+
self._base_url = base_url
|
|
102
|
+
|
|
103
|
+
self._timeout = actual_timeout
|
|
104
|
+
|
|
105
|
+
self.spaces = AsyncSpacesAPI(self)
|
|
106
|
+
self.sessions = AsyncSessionsAPI(self)
|
|
107
|
+
self.disks = AsyncDisksAPI(self)
|
|
108
|
+
self.artifacts = self.disks.artifacts
|
|
109
|
+
self.blocks = AsyncBlocksAPI(self)
|
|
110
|
+
|
|
111
|
+
@property
|
|
112
|
+
def base_url(self) -> str:
|
|
113
|
+
return self._base_url
|
|
114
|
+
|
|
115
|
+
async def aclose(self) -> None:
|
|
116
|
+
"""Close the async client."""
|
|
117
|
+
if self._owns_client:
|
|
118
|
+
await self._client.aclose()
|
|
119
|
+
|
|
120
|
+
async def __aenter__(self) -> "AcontextAsyncClient":
|
|
121
|
+
return self
|
|
122
|
+
|
|
123
|
+
async def __aexit__(self, exc_type, exc, tb) -> None: # noqa: D401 - standard context manager protocol
|
|
124
|
+
await self.aclose()
|
|
125
|
+
|
|
126
|
+
# ------------------------------------------------------------------
|
|
127
|
+
# HTTP plumbing shared by resource clients
|
|
128
|
+
# ------------------------------------------------------------------
|
|
129
|
+
async def request(
|
|
130
|
+
self,
|
|
131
|
+
method: str,
|
|
132
|
+
path: str,
|
|
133
|
+
*,
|
|
134
|
+
params: Mapping[str, Any] | None = None,
|
|
135
|
+
json_data: Mapping[str, Any] | None = None,
|
|
136
|
+
data: Mapping[str, Any] | None = None,
|
|
137
|
+
files: Mapping[str, tuple[str, BinaryIO, str | None]] | None = None,
|
|
138
|
+
unwrap: bool = True,
|
|
139
|
+
) -> Any:
|
|
140
|
+
try:
|
|
141
|
+
response = await self._client.request(
|
|
142
|
+
method=method,
|
|
143
|
+
url=path,
|
|
144
|
+
params=params,
|
|
145
|
+
json=json_data,
|
|
146
|
+
data=data,
|
|
147
|
+
files=files,
|
|
148
|
+
timeout=self._timeout,
|
|
149
|
+
)
|
|
150
|
+
except httpx.HTTPError as exc: # pragma: no cover - passthrough to caller
|
|
151
|
+
raise TransportError(str(exc)) from exc
|
|
152
|
+
|
|
153
|
+
return self._handle_response(response, unwrap=unwrap)
|
|
154
|
+
|
|
155
|
+
@staticmethod
|
|
156
|
+
def _handle_response(response: httpx.Response, *, unwrap: bool) -> Any:
|
|
157
|
+
content_type = response.headers.get("content-type", "")
|
|
158
|
+
|
|
159
|
+
parsed: Mapping[str, Any] | None = None
|
|
160
|
+
if "application/json" in content_type:
|
|
161
|
+
try:
|
|
162
|
+
parsed = response.json()
|
|
163
|
+
except ValueError:
|
|
164
|
+
parsed = None
|
|
165
|
+
else:
|
|
166
|
+
parsed = None
|
|
167
|
+
|
|
168
|
+
if response.status_code >= 400:
|
|
169
|
+
message = response.reason_phrase
|
|
170
|
+
payload: Mapping[str, Any] | None = parsed
|
|
171
|
+
code: int | None = None
|
|
172
|
+
error: str | None = None
|
|
173
|
+
if payload and isinstance(payload, Mapping):
|
|
174
|
+
message = str(payload.get("msg") or payload.get("message") or message)
|
|
175
|
+
error = payload.get("error")
|
|
176
|
+
try:
|
|
177
|
+
code_val = payload.get("code")
|
|
178
|
+
if isinstance(code_val, int):
|
|
179
|
+
code = code_val
|
|
180
|
+
except Exception: # pragma: no cover - defensive
|
|
181
|
+
code = None
|
|
182
|
+
raise APIError(
|
|
183
|
+
status_code=response.status_code,
|
|
184
|
+
code=code,
|
|
185
|
+
message=message,
|
|
186
|
+
error=error,
|
|
187
|
+
payload=payload,
|
|
188
|
+
)
|
|
189
|
+
|
|
190
|
+
if parsed is None:
|
|
191
|
+
if unwrap:
|
|
192
|
+
return response.text
|
|
193
|
+
return {"code": response.status_code, "data": response.text, "msg": response.reason_phrase}
|
|
194
|
+
|
|
195
|
+
app_code = parsed.get("code")
|
|
196
|
+
if isinstance(app_code, int) and app_code >= 400:
|
|
197
|
+
raise APIError(
|
|
198
|
+
status_code=response.status_code,
|
|
199
|
+
code=app_code,
|
|
200
|
+
message=str(parsed.get("msg") or response.reason_phrase),
|
|
201
|
+
error=parsed.get("error"),
|
|
202
|
+
payload=parsed,
|
|
203
|
+
)
|
|
204
|
+
|
|
205
|
+
return parsed.get("data") if unwrap else parsed
|
|
206
|
+
|
|
@@ -3,7 +3,7 @@ High-level synchronous client for the Acontext API.
|
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
5
|
import os
|
|
6
|
-
from collections.abc import Mapping
|
|
6
|
+
from collections.abc import Mapping
|
|
7
7
|
from typing import Any, BinaryIO
|
|
8
8
|
|
|
9
9
|
import httpx
|
|
@@ -70,6 +70,7 @@ class AcontextClient:
|
|
|
70
70
|
timeout = 10.0
|
|
71
71
|
|
|
72
72
|
# Determine actual timeout value
|
|
73
|
+
actual_timeout: float | httpx.Timeout
|
|
73
74
|
if isinstance(timeout, httpx.Timeout):
|
|
74
75
|
actual_timeout = timeout
|
|
75
76
|
else:
|
|
@@ -130,8 +131,8 @@ class AcontextClient:
|
|
|
130
131
|
path: str,
|
|
131
132
|
*,
|
|
132
133
|
params: Mapping[str, Any] | None = None,
|
|
133
|
-
json_data: Mapping[str, Any] |
|
|
134
|
-
data: Mapping[str, Any] |
|
|
134
|
+
json_data: Mapping[str, Any] | None = None,
|
|
135
|
+
data: Mapping[str, Any] | None = None,
|
|
135
136
|
files: Mapping[str, tuple[str, BinaryIO, str | None]] | None = None,
|
|
136
137
|
unwrap: bool = True,
|
|
137
138
|
) -> Any:
|
|
@@ -154,10 +155,10 @@ class AcontextClient:
|
|
|
154
155
|
def _handle_response(response: httpx.Response, *, unwrap: bool) -> Any:
|
|
155
156
|
content_type = response.headers.get("content-type", "")
|
|
156
157
|
|
|
157
|
-
parsed: Mapping[str, Any] |
|
|
158
|
+
parsed: Mapping[str, Any] | None
|
|
158
159
|
if "application/json" in content_type:
|
|
159
160
|
try:
|
|
160
|
-
parsed = response.json()
|
|
161
|
+
parsed = response.json() # dict
|
|
161
162
|
except ValueError:
|
|
162
163
|
parsed = None
|
|
163
164
|
else:
|
|
@@ -165,7 +166,7 @@ class AcontextClient:
|
|
|
165
166
|
|
|
166
167
|
if response.status_code >= 400:
|
|
167
168
|
message = response.reason_phrase
|
|
168
|
-
payload: Mapping[str, Any] |
|
|
169
|
+
payload: Mapping[str, Any] | None = parsed
|
|
169
170
|
code: int | None = None
|
|
170
171
|
error: str | None = None
|
|
171
172
|
if payload and isinstance(payload, Mapping):
|
|
@@ -190,11 +191,6 @@ class AcontextClient:
|
|
|
190
191
|
return response.text
|
|
191
192
|
return {"code": response.status_code, "data": response.text, "msg": response.reason_phrase}
|
|
192
193
|
|
|
193
|
-
if not isinstance(parsed, Mapping):
|
|
194
|
-
if unwrap:
|
|
195
|
-
return parsed
|
|
196
|
-
return parsed
|
|
197
|
-
|
|
198
194
|
app_code = parsed.get("code")
|
|
199
195
|
if isinstance(app_code, int) and app_code >= 400:
|
|
200
196
|
raise APIError(
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Common typing helpers used by resource modules to avoid circular imports.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from collections.abc import Awaitable, Mapping
|
|
6
|
+
from typing import Any, BinaryIO, Protocol
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class RequesterProtocol(Protocol):
|
|
10
|
+
def request(
|
|
11
|
+
self,
|
|
12
|
+
method: str,
|
|
13
|
+
path: str,
|
|
14
|
+
*,
|
|
15
|
+
params: Mapping[str, Any] | None = None,
|
|
16
|
+
json_data: Mapping[str, Any] | None = None,
|
|
17
|
+
data: Mapping[str, Any] | None = None,
|
|
18
|
+
files: Mapping[str, tuple[str, BinaryIO, str | None]] | None = None,
|
|
19
|
+
unwrap: bool = True,
|
|
20
|
+
) -> Any:
|
|
21
|
+
...
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class AsyncRequesterProtocol(Protocol):
|
|
25
|
+
def request(
|
|
26
|
+
self,
|
|
27
|
+
method: str,
|
|
28
|
+
path: str,
|
|
29
|
+
*,
|
|
30
|
+
params: Mapping[str, Any] | None = None,
|
|
31
|
+
json_data: Mapping[str, Any] | None = None,
|
|
32
|
+
data: Mapping[str, Any] | None = None,
|
|
33
|
+
files: Mapping[str, tuple[str, BinaryIO, str | None]] | None = None,
|
|
34
|
+
unwrap: bool = True,
|
|
35
|
+
) -> Awaitable[Any]:
|
|
36
|
+
...
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
Custom exceptions raised by the acontext Python client.
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
|
-
from collections.abc import Mapping
|
|
5
|
+
from collections.abc import Mapping
|
|
6
6
|
from typing import Any
|
|
7
7
|
|
|
8
8
|
|
|
@@ -29,7 +29,7 @@ class APIError(AcontextError):
|
|
|
29
29
|
code: int | None = None,
|
|
30
30
|
message: str | None = None,
|
|
31
31
|
error: str | None = None,
|
|
32
|
-
payload: Mapping[str, Any] |
|
|
32
|
+
payload: Mapping[str, Any] | None = None,
|
|
33
33
|
) -> None:
|
|
34
34
|
self.status_code = status_code
|
|
35
35
|
self.code = code
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
Support for constructing session messages.
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
|
-
from collections.abc import Mapping,
|
|
5
|
+
from collections.abc import Mapping, Sequence
|
|
6
6
|
from dataclasses import dataclass
|
|
7
7
|
from typing import Any, Literal
|
|
8
8
|
|
|
@@ -25,14 +25,6 @@ class MessagePart:
|
|
|
25
25
|
meta: Mapping[str, Any] | None = None
|
|
26
26
|
file_field: str | None = None
|
|
27
27
|
|
|
28
|
-
@classmethod
|
|
29
|
-
def text_part(cls, text: str, *, meta: Mapping[str, Any] | None = None) -> "MessagePart":
|
|
30
|
-
return cls(type="text", text=text, meta=meta)
|
|
31
|
-
|
|
32
|
-
@classmethod
|
|
33
|
-
def file_field_part(cls, file_field: str, *, meta: Mapping[str, Any] | None = None) -> "MessagePart":
|
|
34
|
-
return cls(type="file", file_field=file_field, meta=meta)
|
|
35
|
-
|
|
36
28
|
@dataclass(slots=True)
|
|
37
29
|
class AcontextMessage:
|
|
38
30
|
"""
|
|
@@ -41,7 +33,7 @@ class AcontextMessage:
|
|
|
41
33
|
|
|
42
34
|
role: Literal["user", "assistant", "system"]
|
|
43
35
|
parts: list[MessagePart]
|
|
44
|
-
meta:
|
|
36
|
+
meta: Mapping[str, Any] | None = None
|
|
45
37
|
|
|
46
38
|
|
|
47
39
|
def build_acontext_message(
|
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
"""Resource-specific API helpers for the Acontext client."""
|
|
2
2
|
|
|
3
|
+
from .async_blocks import AsyncBlocksAPI
|
|
4
|
+
from .async_disks import AsyncDisksAPI, AsyncDiskArtifactsAPI
|
|
5
|
+
from .async_sessions import AsyncSessionsAPI
|
|
6
|
+
from .async_spaces import AsyncSpacesAPI
|
|
3
7
|
from .blocks import BlocksAPI
|
|
4
8
|
from .disks import DisksAPI, DiskArtifactsAPI
|
|
5
9
|
from .sessions import SessionsAPI
|
|
@@ -11,4 +15,9 @@ __all__ = [
|
|
|
11
15
|
"BlocksAPI",
|
|
12
16
|
"SessionsAPI",
|
|
13
17
|
"SpacesAPI",
|
|
18
|
+
"AsyncDisksAPI",
|
|
19
|
+
"AsyncDiskArtifactsAPI",
|
|
20
|
+
"AsyncBlocksAPI",
|
|
21
|
+
"AsyncSessionsAPI",
|
|
22
|
+
"AsyncSpacesAPI",
|
|
14
23
|
]
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Block endpoints (async).
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from collections.abc import Mapping
|
|
6
|
+
from typing import Any
|
|
7
|
+
|
|
8
|
+
from ..client_types import AsyncRequesterProtocol
|
|
9
|
+
from ..types.block import Block
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class AsyncBlocksAPI:
|
|
13
|
+
def __init__(self, requester: AsyncRequesterProtocol) -> None:
|
|
14
|
+
self._requester = requester
|
|
15
|
+
|
|
16
|
+
async def list(
|
|
17
|
+
self,
|
|
18
|
+
space_id: str,
|
|
19
|
+
*,
|
|
20
|
+
parent_id: str | None = None,
|
|
21
|
+
block_type: str | None = None,
|
|
22
|
+
) -> list[Block]:
|
|
23
|
+
"""List blocks in a space.
|
|
24
|
+
|
|
25
|
+
Args:
|
|
26
|
+
space_id: The UUID of the space.
|
|
27
|
+
parent_id: Filter blocks by parent ID. Defaults to None.
|
|
28
|
+
block_type: Filter blocks by type (e.g., "page", "folder", "text", "sop"). Defaults to None.
|
|
29
|
+
|
|
30
|
+
Returns:
|
|
31
|
+
List of Block objects.
|
|
32
|
+
"""
|
|
33
|
+
params: dict[str, Any] = {}
|
|
34
|
+
if parent_id is not None:
|
|
35
|
+
params["parent_id"] = parent_id
|
|
36
|
+
if block_type is not None:
|
|
37
|
+
params["type"] = block_type
|
|
38
|
+
data = await self._requester.request("GET", f"/space/{space_id}/block", params=params or None)
|
|
39
|
+
return [Block.model_validate(item) for item in data]
|
|
40
|
+
|
|
41
|
+
async def create(
|
|
42
|
+
self,
|
|
43
|
+
space_id: str,
|
|
44
|
+
*,
|
|
45
|
+
block_type: str,
|
|
46
|
+
parent_id: str | None = None,
|
|
47
|
+
title: str | None = None,
|
|
48
|
+
props: Mapping[str, Any] | None = None,
|
|
49
|
+
) -> Block:
|
|
50
|
+
"""Create a new block in a space.
|
|
51
|
+
|
|
52
|
+
Args:
|
|
53
|
+
space_id: The UUID of the space.
|
|
54
|
+
block_type: The type of block (e.g., "page", "folder", "text", "sop").
|
|
55
|
+
parent_id: Optional parent block ID. Defaults to None.
|
|
56
|
+
title: Optional block title. Defaults to None.
|
|
57
|
+
props: Optional block properties dictionary. Defaults to None.
|
|
58
|
+
|
|
59
|
+
Returns:
|
|
60
|
+
The created Block object.
|
|
61
|
+
"""
|
|
62
|
+
payload: dict[str, Any] = {"type": block_type}
|
|
63
|
+
if parent_id is not None:
|
|
64
|
+
payload["parent_id"] = parent_id
|
|
65
|
+
if title is not None:
|
|
66
|
+
payload["title"] = title
|
|
67
|
+
if props is not None:
|
|
68
|
+
payload["props"] = props
|
|
69
|
+
data = await self._requester.request("POST", f"/space/{space_id}/block", json_data=payload)
|
|
70
|
+
return Block.model_validate(data)
|
|
71
|
+
|
|
72
|
+
async def delete(self, space_id: str, block_id: str) -> None:
|
|
73
|
+
"""Delete a block by its ID.
|
|
74
|
+
|
|
75
|
+
Args:
|
|
76
|
+
space_id: The UUID of the space.
|
|
77
|
+
block_id: The UUID of the block to delete.
|
|
78
|
+
"""
|
|
79
|
+
await self._requester.request("DELETE", f"/space/{space_id}/block/{block_id}")
|
|
80
|
+
|
|
81
|
+
async def get_properties(self, space_id: str, block_id: str) -> Block:
|
|
82
|
+
"""Get block properties.
|
|
83
|
+
|
|
84
|
+
Args:
|
|
85
|
+
space_id: The UUID of the space.
|
|
86
|
+
block_id: The UUID of the block.
|
|
87
|
+
|
|
88
|
+
Returns:
|
|
89
|
+
Block object containing the properties.
|
|
90
|
+
"""
|
|
91
|
+
data = await self._requester.request("GET", f"/space/{space_id}/block/{block_id}/properties")
|
|
92
|
+
return Block.model_validate(data)
|
|
93
|
+
|
|
94
|
+
async def update_properties(
|
|
95
|
+
self,
|
|
96
|
+
space_id: str,
|
|
97
|
+
block_id: str,
|
|
98
|
+
*,
|
|
99
|
+
title: str | None = None,
|
|
100
|
+
props: Mapping[str, Any] | None = None,
|
|
101
|
+
) -> None:
|
|
102
|
+
"""Update block properties.
|
|
103
|
+
|
|
104
|
+
Args:
|
|
105
|
+
space_id: The UUID of the space.
|
|
106
|
+
block_id: The UUID of the block.
|
|
107
|
+
title: Optional block title. Defaults to None.
|
|
108
|
+
props: Optional block properties dictionary. Defaults to None.
|
|
109
|
+
|
|
110
|
+
Raises:
|
|
111
|
+
ValueError: If both title and props are None.
|
|
112
|
+
"""
|
|
113
|
+
payload: dict[str, Any] = {}
|
|
114
|
+
if title is not None:
|
|
115
|
+
payload["title"] = title
|
|
116
|
+
if props is not None:
|
|
117
|
+
payload["props"] = props
|
|
118
|
+
if not payload:
|
|
119
|
+
raise ValueError("title or props must be provided")
|
|
120
|
+
await self._requester.request("PUT", f"/space/{space_id}/block/{block_id}/properties", json_data=payload)
|
|
121
|
+
|
|
122
|
+
async def move(
|
|
123
|
+
self,
|
|
124
|
+
space_id: str,
|
|
125
|
+
block_id: str,
|
|
126
|
+
*,
|
|
127
|
+
parent_id: str | None = None,
|
|
128
|
+
sort: int | None = None,
|
|
129
|
+
) -> None:
|
|
130
|
+
"""Move a block by updating its parent or sort order.
|
|
131
|
+
|
|
132
|
+
Args:
|
|
133
|
+
space_id: The UUID of the space.
|
|
134
|
+
block_id: The UUID of the block to move.
|
|
135
|
+
parent_id: Optional new parent block ID. Defaults to None.
|
|
136
|
+
sort: Optional new sort order. Defaults to None.
|
|
137
|
+
|
|
138
|
+
Raises:
|
|
139
|
+
ValueError: If both parent_id and sort are None.
|
|
140
|
+
"""
|
|
141
|
+
payload: dict[str, Any] = {}
|
|
142
|
+
if parent_id is not None:
|
|
143
|
+
payload["parent_id"] = parent_id
|
|
144
|
+
if sort is not None:
|
|
145
|
+
payload["sort"] = sort
|
|
146
|
+
if not payload:
|
|
147
|
+
raise ValueError("parent_id or sort must be provided")
|
|
148
|
+
await self._requester.request("PUT", f"/space/{space_id}/block/{block_id}/move", json_data=payload)
|
|
149
|
+
|
|
150
|
+
async def update_sort(self, space_id: str, block_id: str, *, sort: int) -> None:
|
|
151
|
+
"""Update block sort order.
|
|
152
|
+
|
|
153
|
+
Args:
|
|
154
|
+
space_id: The UUID of the space.
|
|
155
|
+
block_id: The UUID of the block.
|
|
156
|
+
sort: The new sort order.
|
|
157
|
+
"""
|
|
158
|
+
await self._requester.request(
|
|
159
|
+
"PUT",
|
|
160
|
+
f"/space/{space_id}/block/{block_id}/sort",
|
|
161
|
+
json_data={"sort": sort},
|
|
162
|
+
)
|
|
163
|
+
|