akumi 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- akumi-0.1.0/.github/workflows/publish.yml +39 -0
- akumi-0.1.0/.gitignore +7 -0
- akumi-0.1.0/PKG-INFO +113 -0
- akumi-0.1.0/README.md +101 -0
- akumi-0.1.0/pyproject.toml +32 -0
- akumi-0.1.0/src/akumi/__init__.py +48 -0
- akumi-0.1.0/src/akumi/_async_client.py +33 -0
- akumi-0.1.0/src/akumi/_client.py +33 -0
- akumi-0.1.0/src/akumi/_config.py +16 -0
- akumi-0.1.0/src/akumi/_errors.py +52 -0
- akumi-0.1.0/src/akumi/_sse.py +21 -0
- akumi-0.1.0/src/akumi/_transport.py +219 -0
- akumi-0.1.0/src/akumi/models.py +210 -0
- akumi-0.1.0/src/akumi/resources/auditLogs_async.py +17 -0
- akumi-0.1.0/src/akumi/resources/auditLogs_sync.py +17 -0
- akumi-0.1.0/src/akumi/resources/chat_async.py +21 -0
- akumi-0.1.0/src/akumi/resources/chat_sync.py +20 -0
- akumi-0.1.0/src/akumi/resources/embeddings_async.py +14 -0
- akumi-0.1.0/src/akumi/resources/embeddings_sync.py +14 -0
- akumi-0.1.0/src/akumi/resources/models_async.py +14 -0
- akumi-0.1.0/src/akumi/resources/models_sync.py +14 -0
- akumi-0.1.0/src/akumi/resources/recall_async.py +110 -0
- akumi-0.1.0/src/akumi/resources/recall_sync.py +86 -0
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
name: Publish to PyPI
|
|
2
|
+
|
|
3
|
+
# Fires on the vX.Y.Z tag that the control-plane sdk-python.yml workflow
|
|
4
|
+
# auto-pushes. Sets pyproject.toml to the tag version, builds, and uploads to
|
|
5
|
+
# PyPI via Trusted Publishing (OIDC, no stored token). Skips cleanly until the
|
|
6
|
+
# PYPI_PUBLISH repo variable is set to 'true', so early tags do not fail.
|
|
7
|
+
on:
|
|
8
|
+
push:
|
|
9
|
+
tags: ['v*']
|
|
10
|
+
|
|
11
|
+
jobs:
|
|
12
|
+
publish:
|
|
13
|
+
runs-on: ubuntu-latest
|
|
14
|
+
permissions:
|
|
15
|
+
id-token: write
|
|
16
|
+
steps:
|
|
17
|
+
- name: Checkout
|
|
18
|
+
uses: actions/checkout@v4
|
|
19
|
+
|
|
20
|
+
- name: Setup Python
|
|
21
|
+
uses: actions/setup-python@v5
|
|
22
|
+
with:
|
|
23
|
+
python-version: '3.12'
|
|
24
|
+
|
|
25
|
+
- name: Set version from the tag
|
|
26
|
+
run: sed -i -E "s/^version = \".*\"/version = \"${GITHUB_REF_NAME#v}\"/" pyproject.toml
|
|
27
|
+
|
|
28
|
+
- name: Build wheel and sdist
|
|
29
|
+
run: |
|
|
30
|
+
python -m pip install --upgrade build
|
|
31
|
+
python -m build
|
|
32
|
+
|
|
33
|
+
- name: Publish to PyPI (Trusted Publishing)
|
|
34
|
+
if: vars.PYPI_PUBLISH == 'true'
|
|
35
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
36
|
+
|
|
37
|
+
- name: Publishing skipped
|
|
38
|
+
if: vars.PYPI_PUBLISH != 'true'
|
|
39
|
+
run: echo "PyPI publishing not enabled; set repo variable PYPI_PUBLISH=true after configuring Trusted Publishing."
|
akumi-0.1.0/.gitignore
ADDED
akumi-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: akumi
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Official Python SDK for the Akumi EU-sovereign inference API.
|
|
5
|
+
License-Expression: MIT
|
|
6
|
+
Requires-Python: >=3.9
|
|
7
|
+
Requires-Dist: httpx>=0.27
|
|
8
|
+
Provides-Extra: dev
|
|
9
|
+
Requires-Dist: mypy>=1.11; extra == 'dev'
|
|
10
|
+
Requires-Dist: ruff>=0.6; extra == 'dev'
|
|
11
|
+
Description-Content-Type: text/markdown
|
|
12
|
+
|
|
13
|
+
# Akumi Python SDK
|
|
14
|
+
|
|
15
|
+
The official Python client for [Akumi](https://akumi.cloud), the EU-sovereign,
|
|
16
|
+
OpenAI-compatible inference API. Built on httpx, with both a synchronous and an
|
|
17
|
+
asynchronous client. One `base_url` for every model, governed and metered, with
|
|
18
|
+
your regulated data kept in the EU.
|
|
19
|
+
|
|
20
|
+
- **Drop-in OpenAI-compatible.** Chat completions, embeddings, and models under one key.
|
|
21
|
+
- **EU-sovereign by default.** The egress guard fails closed on non-EU routing.
|
|
22
|
+
- **Governed, not just hosted.** PII firewall, per-request residency, and a metadata-only audit trail on every call.
|
|
23
|
+
- **Sync and async.** Streaming and automatic retries in both clients.
|
|
24
|
+
|
|
25
|
+
## Requirements
|
|
26
|
+
|
|
27
|
+
Python 3.9 or newer.
|
|
28
|
+
|
|
29
|
+
## Install
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
pip install akumi
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Quickstart
|
|
36
|
+
|
|
37
|
+
Create an API key under app.akumi.cloud -> Platform -> API keys:
|
|
38
|
+
|
|
39
|
+
```python
|
|
40
|
+
from akumi import Akumi
|
|
41
|
+
|
|
42
|
+
client = Akumi.from_api_key("mk_...")
|
|
43
|
+
|
|
44
|
+
result = client.chat.create(params={
|
|
45
|
+
"model": "mistral/mistral-large-latest",
|
|
46
|
+
"messages": [
|
|
47
|
+
{"role": "user", "content": "Explain EU data residency in one sentence."},
|
|
48
|
+
],
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
print(result["choices"][0]["message"]["content"])
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## Streaming
|
|
55
|
+
|
|
56
|
+
```python
|
|
57
|
+
for chunk in client.chat.create_streamed(params={
|
|
58
|
+
"model": "mistral/mistral-large-latest",
|
|
59
|
+
"messages": [{"role": "user", "content": "Write a haiku about Frankfurt."}],
|
|
60
|
+
}):
|
|
61
|
+
print(chunk["choices"][0]["delta"].get("content", ""), end="")
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## Async
|
|
65
|
+
|
|
66
|
+
```python
|
|
67
|
+
import asyncio
|
|
68
|
+
from akumi import AsyncAkumi
|
|
69
|
+
|
|
70
|
+
async def main():
|
|
71
|
+
client = AsyncAkumi.from_api_key("mk_...")
|
|
72
|
+
result = await client.chat.create(params={
|
|
73
|
+
"model": "mistral/mistral-large-latest",
|
|
74
|
+
"messages": [{"role": "user", "content": "Hello from the EU."}],
|
|
75
|
+
})
|
|
76
|
+
print(result["choices"][0]["message"]["content"])
|
|
77
|
+
|
|
78
|
+
asyncio.run(main())
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
## Embeddings
|
|
82
|
+
|
|
83
|
+
```python
|
|
84
|
+
embeddings = client.embeddings.create(params={
|
|
85
|
+
"model": "mistral/mistral-embed",
|
|
86
|
+
"input": "The quarterly report is ready for review.",
|
|
87
|
+
})
|
|
88
|
+
|
|
89
|
+
vector = embeddings["data"][0]["embedding"]
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
## More resources
|
|
93
|
+
|
|
94
|
+
- `client.models.list()` lists the models available to your key.
|
|
95
|
+
- `client.memory.forget(...)` and `client.memoryThreads.list()` manage long-term memory and threads.
|
|
96
|
+
- `client.auditLogs.list()` reads your metadata-only audit trail.
|
|
97
|
+
|
|
98
|
+
## Configuration
|
|
99
|
+
|
|
100
|
+
`from_api_key()` connects to `https://api.akumi.cloud/v1` and retries transient
|
|
101
|
+
failures (429, 502, 503, 504). Pass `base_url=` to target another host.
|
|
102
|
+
|
|
103
|
+
## Documentation
|
|
104
|
+
|
|
105
|
+
- Guides: https://akumi.cloud/docs
|
|
106
|
+
- API reference: https://akumi.cloud/docs/api-reference
|
|
107
|
+
|
|
108
|
+
## About
|
|
109
|
+
|
|
110
|
+
Generated from the Akumi OpenAPI specification, so it tracks the API
|
|
111
|
+
automatically. Issues: https://github.com/akumi-cloud/python-sdk.
|
|
112
|
+
|
|
113
|
+
MIT licensed.
|
akumi-0.1.0/README.md
ADDED
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
# Akumi Python SDK
|
|
2
|
+
|
|
3
|
+
The official Python client for [Akumi](https://akumi.cloud), the EU-sovereign,
|
|
4
|
+
OpenAI-compatible inference API. Built on httpx, with both a synchronous and an
|
|
5
|
+
asynchronous client. One `base_url` for every model, governed and metered, with
|
|
6
|
+
your regulated data kept in the EU.
|
|
7
|
+
|
|
8
|
+
- **Drop-in OpenAI-compatible.** Chat completions, embeddings, and models under one key.
|
|
9
|
+
- **EU-sovereign by default.** The egress guard fails closed on non-EU routing.
|
|
10
|
+
- **Governed, not just hosted.** PII firewall, per-request residency, and a metadata-only audit trail on every call.
|
|
11
|
+
- **Sync and async.** Streaming and automatic retries in both clients.
|
|
12
|
+
|
|
13
|
+
## Requirements
|
|
14
|
+
|
|
15
|
+
Python 3.9 or newer.
|
|
16
|
+
|
|
17
|
+
## Install
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
pip install akumi
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Quickstart
|
|
24
|
+
|
|
25
|
+
Create an API key under app.akumi.cloud -> Platform -> API keys:
|
|
26
|
+
|
|
27
|
+
```python
|
|
28
|
+
from akumi import Akumi
|
|
29
|
+
|
|
30
|
+
client = Akumi.from_api_key("mk_...")
|
|
31
|
+
|
|
32
|
+
result = client.chat.create(params={
|
|
33
|
+
"model": "mistral/mistral-large-latest",
|
|
34
|
+
"messages": [
|
|
35
|
+
{"role": "user", "content": "Explain EU data residency in one sentence."},
|
|
36
|
+
],
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
print(result["choices"][0]["message"]["content"])
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Streaming
|
|
43
|
+
|
|
44
|
+
```python
|
|
45
|
+
for chunk in client.chat.create_streamed(params={
|
|
46
|
+
"model": "mistral/mistral-large-latest",
|
|
47
|
+
"messages": [{"role": "user", "content": "Write a haiku about Frankfurt."}],
|
|
48
|
+
}):
|
|
49
|
+
print(chunk["choices"][0]["delta"].get("content", ""), end="")
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## Async
|
|
53
|
+
|
|
54
|
+
```python
|
|
55
|
+
import asyncio
|
|
56
|
+
from akumi import AsyncAkumi
|
|
57
|
+
|
|
58
|
+
async def main():
|
|
59
|
+
client = AsyncAkumi.from_api_key("mk_...")
|
|
60
|
+
result = await client.chat.create(params={
|
|
61
|
+
"model": "mistral/mistral-large-latest",
|
|
62
|
+
"messages": [{"role": "user", "content": "Hello from the EU."}],
|
|
63
|
+
})
|
|
64
|
+
print(result["choices"][0]["message"]["content"])
|
|
65
|
+
|
|
66
|
+
asyncio.run(main())
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Embeddings
|
|
70
|
+
|
|
71
|
+
```python
|
|
72
|
+
embeddings = client.embeddings.create(params={
|
|
73
|
+
"model": "mistral/mistral-embed",
|
|
74
|
+
"input": "The quarterly report is ready for review.",
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
vector = embeddings["data"][0]["embedding"]
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
## More resources
|
|
81
|
+
|
|
82
|
+
- `client.models.list()` lists the models available to your key.
|
|
83
|
+
- `client.memory.forget(...)` and `client.memoryThreads.list()` manage long-term memory and threads.
|
|
84
|
+
- `client.auditLogs.list()` reads your metadata-only audit trail.
|
|
85
|
+
|
|
86
|
+
## Configuration
|
|
87
|
+
|
|
88
|
+
`from_api_key()` connects to `https://api.akumi.cloud/v1` and retries transient
|
|
89
|
+
failures (429, 502, 503, 504). Pass `base_url=` to target another host.
|
|
90
|
+
|
|
91
|
+
## Documentation
|
|
92
|
+
|
|
93
|
+
- Guides: https://akumi.cloud/docs
|
|
94
|
+
- API reference: https://akumi.cloud/docs/api-reference
|
|
95
|
+
|
|
96
|
+
## About
|
|
97
|
+
|
|
98
|
+
Generated from the Akumi OpenAPI specification, so it tracks the API
|
|
99
|
+
automatically. Issues: https://github.com/akumi-cloud/python-sdk.
|
|
100
|
+
|
|
101
|
+
MIT licensed.
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "akumi"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Official Python SDK for the Akumi EU-sovereign inference API."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = "MIT"
|
|
11
|
+
requires-python = ">=3.9"
|
|
12
|
+
dependencies = ["httpx>=0.27"]
|
|
13
|
+
|
|
14
|
+
[project.optional-dependencies]
|
|
15
|
+
dev = ["mypy>=1.11", "ruff>=0.6"]
|
|
16
|
+
|
|
17
|
+
[tool.hatch.build.targets.wheel]
|
|
18
|
+
packages = ["src/akumi"]
|
|
19
|
+
|
|
20
|
+
[tool.ruff]
|
|
21
|
+
target-version = "py39"
|
|
22
|
+
|
|
23
|
+
[tool.ruff.lint]
|
|
24
|
+
# The transport's __enter__/__aenter__ return the concrete transport type rather
|
|
25
|
+
# than Self, which on Python < 3.11 would pull in typing_extensions just for an
|
|
26
|
+
# annotation. PYI034 is a stylistic preference, so ignore it and keep the runtime
|
|
27
|
+
# dependency-free. All other lint rules are auto-fixed in CI (ruff check --fix).
|
|
28
|
+
ignore = ["PYI034"]
|
|
29
|
+
|
|
30
|
+
[tool.mypy]
|
|
31
|
+
python_version = "3.9"
|
|
32
|
+
ignore_missing_imports = true
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# AUTO-GENERATED by akumi/codegen. Do not edit by hand.
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
from ._async_client import AsyncAkumi
|
|
5
|
+
from ._client import Akumi
|
|
6
|
+
from ._config import ClientConfig
|
|
7
|
+
from ._errors import (
|
|
8
|
+
AkumiError,
|
|
9
|
+
ApiError,
|
|
10
|
+
AuthenticationError,
|
|
11
|
+
InvalidRequestError,
|
|
12
|
+
RateLimitError,
|
|
13
|
+
)
|
|
14
|
+
from .models import (
|
|
15
|
+
AuditLogApiResource,
|
|
16
|
+
ChatCompletionsRequest,
|
|
17
|
+
EmbeddingsRequest,
|
|
18
|
+
IngestDocumentRequest,
|
|
19
|
+
RememberFactRequest,
|
|
20
|
+
SearchDocumentsRequest,
|
|
21
|
+
SearchFactsRequest,
|
|
22
|
+
SearchRequest,
|
|
23
|
+
StoreCollectionRequest,
|
|
24
|
+
ThreadMessageViewModel,
|
|
25
|
+
ThreadViewModel,
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
__all__ = [
|
|
29
|
+
"Akumi",
|
|
30
|
+
"AkumiError",
|
|
31
|
+
"ApiError",
|
|
32
|
+
"AsyncAkumi",
|
|
33
|
+
"AuditLogApiResource",
|
|
34
|
+
"AuthenticationError",
|
|
35
|
+
"ChatCompletionsRequest",
|
|
36
|
+
"ClientConfig",
|
|
37
|
+
"EmbeddingsRequest",
|
|
38
|
+
"IngestDocumentRequest",
|
|
39
|
+
"InvalidRequestError",
|
|
40
|
+
"RateLimitError",
|
|
41
|
+
"RememberFactRequest",
|
|
42
|
+
"SearchDocumentsRequest",
|
|
43
|
+
"SearchFactsRequest",
|
|
44
|
+
"SearchRequest",
|
|
45
|
+
"StoreCollectionRequest",
|
|
46
|
+
"ThreadMessageViewModel",
|
|
47
|
+
"ThreadViewModel",
|
|
48
|
+
]
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# AUTO-GENERATED by akumi/codegen. Do not edit by hand.
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
from ._config import ClientConfig
|
|
5
|
+
from ._transport import AsyncTransport
|
|
6
|
+
from .resources.auditLogs_async import AsyncAuditLogsResource
|
|
7
|
+
from .resources.chat_async import AsyncChatResource
|
|
8
|
+
from .resources.embeddings_async import AsyncEmbeddingsResource
|
|
9
|
+
from .resources.models_async import AsyncModelsResource
|
|
10
|
+
from .resources.recall_async import AsyncRecallResource
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class AsyncAkumi:
|
|
14
|
+
def __init__(
|
|
15
|
+
self,
|
|
16
|
+
*,
|
|
17
|
+
api_key: str,
|
|
18
|
+
base_url: str = "https://api.akumi.cloud/v1",
|
|
19
|
+
max_retries: int = 2,
|
|
20
|
+
) -> None:
|
|
21
|
+
config = ClientConfig(
|
|
22
|
+
api_key=api_key, base_url=base_url, max_retries=max_retries
|
|
23
|
+
)
|
|
24
|
+
self._transport = AsyncTransport(config)
|
|
25
|
+
self.recall = AsyncRecallResource(self._transport)
|
|
26
|
+
self.auditLogs = AsyncAuditLogsResource(self._transport)
|
|
27
|
+
self.chat = AsyncChatResource(self._transport)
|
|
28
|
+
self.embeddings = AsyncEmbeddingsResource(self._transport)
|
|
29
|
+
self.models = AsyncModelsResource(self._transport)
|
|
30
|
+
|
|
31
|
+
@classmethod
|
|
32
|
+
def from_api_key(cls, api_key: str) -> AsyncAkumi:
|
|
33
|
+
return cls(api_key=api_key)
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# AUTO-GENERATED by akumi/codegen. Do not edit by hand.
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
from ._config import ClientConfig
|
|
5
|
+
from ._transport import SyncTransport
|
|
6
|
+
from .resources.auditLogs_sync import AuditLogsResource
|
|
7
|
+
from .resources.chat_sync import ChatResource
|
|
8
|
+
from .resources.embeddings_sync import EmbeddingsResource
|
|
9
|
+
from .resources.models_sync import ModelsResource
|
|
10
|
+
from .resources.recall_sync import RecallResource
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class Akumi:
|
|
14
|
+
def __init__(
|
|
15
|
+
self,
|
|
16
|
+
*,
|
|
17
|
+
api_key: str,
|
|
18
|
+
base_url: str = "https://api.akumi.cloud/v1",
|
|
19
|
+
max_retries: int = 2,
|
|
20
|
+
) -> None:
|
|
21
|
+
config = ClientConfig(
|
|
22
|
+
api_key=api_key, base_url=base_url, max_retries=max_retries
|
|
23
|
+
)
|
|
24
|
+
self._transport = SyncTransport(config)
|
|
25
|
+
self.recall = RecallResource(self._transport)
|
|
26
|
+
self.auditLogs = AuditLogsResource(self._transport)
|
|
27
|
+
self.chat = ChatResource(self._transport)
|
|
28
|
+
self.embeddings = EmbeddingsResource(self._transport)
|
|
29
|
+
self.models = ModelsResource(self._transport)
|
|
30
|
+
|
|
31
|
+
@classmethod
|
|
32
|
+
def from_api_key(cls, api_key: str) -> Akumi:
|
|
33
|
+
return cls(api_key=api_key)
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# AUTO-GENERATED runtime, copied by akumi/codegen. Do not edit by hand.
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
from dataclasses import dataclass, field
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
@dataclass(frozen=True)
|
|
8
|
+
class ClientConfig:
|
|
9
|
+
"""Immutable client configuration. The api_key is sent only on the
|
|
10
|
+
Authorization header by the transport and is never logged or placed in a
|
|
11
|
+
query string."""
|
|
12
|
+
|
|
13
|
+
api_key: str
|
|
14
|
+
base_url: str = "https://api.akumi.cloud/v1"
|
|
15
|
+
max_retries: int = 2
|
|
16
|
+
retry_on: tuple[int, ...] = field(default_factory=lambda: (429, 500, 502, 503, 504))
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# AUTO-GENERATED runtime, copied by akumi/codegen. Do not edit by hand.
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
from typing import Any
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class AkumiError(Exception):
|
|
8
|
+
"""Base class for every error raised by the SDK."""
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class ApiError(AkumiError):
|
|
12
|
+
"""An error returned by the API. Carries the HTTP status and decoded body."""
|
|
13
|
+
|
|
14
|
+
def __init__(
|
|
15
|
+
self, message: str, status: int, body: dict[str, Any] | None = None
|
|
16
|
+
) -> None:
|
|
17
|
+
super().__init__(message)
|
|
18
|
+
self.status = status
|
|
19
|
+
self.body: dict[str, Any] = body if body is not None else {}
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class AuthenticationError(ApiError):
|
|
23
|
+
"""Raised on a 401 or 403 response."""
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class RateLimitError(ApiError):
|
|
27
|
+
"""Raised on a 429 response."""
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class InvalidRequestError(ApiError):
|
|
31
|
+
"""Raised on a 4xx response that is not 401, 403, or 429."""
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def _message_for(status: int, body: dict[str, Any]) -> str:
|
|
35
|
+
error = body.get("error")
|
|
36
|
+
if isinstance(error, dict):
|
|
37
|
+
message = error.get("message")
|
|
38
|
+
if isinstance(message, str):
|
|
39
|
+
return message
|
|
40
|
+
return f"HTTP {status}"
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def map_error(status: int, body: dict[str, Any]) -> ApiError:
|
|
44
|
+
"""Map an HTTP status and decoded body onto the matching typed exception."""
|
|
45
|
+
message = _message_for(status, body)
|
|
46
|
+
if status in (401, 403):
|
|
47
|
+
return AuthenticationError(message, status, body)
|
|
48
|
+
if status == 429:
|
|
49
|
+
return RateLimitError(message, status, body)
|
|
50
|
+
if 400 <= status < 500:
|
|
51
|
+
return InvalidRequestError(message, status, body)
|
|
52
|
+
return ApiError(message, status, body)
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# AUTO-GENERATED runtime, copied by akumi/codegen. Do not edit by hand.
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
import json
|
|
5
|
+
from typing import Any
|
|
6
|
+
|
|
7
|
+
DONE_SENTINEL = "[DONE]"
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def parse_sse_line(line: str) -> dict[str, Any] | None:
|
|
11
|
+
"""Decode a single SSE line. Returns the parsed event object, or None when
|
|
12
|
+
the line is not a data line, is blank, or is the [DONE] sentinel."""
|
|
13
|
+
if not line.startswith("data:"):
|
|
14
|
+
return None
|
|
15
|
+
|
|
16
|
+
data = line[len("data:") :].strip()
|
|
17
|
+
if data == "" or data == DONE_SENTINEL:
|
|
18
|
+
return None
|
|
19
|
+
|
|
20
|
+
decoded = json.loads(data)
|
|
21
|
+
return decoded if isinstance(decoded, dict) else None
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
# AUTO-GENERATED runtime, copied by akumi/codegen. Do not edit by hand.
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
import asyncio
|
|
5
|
+
import json
|
|
6
|
+
import time
|
|
7
|
+
from collections.abc import AsyncGenerator, Generator
|
|
8
|
+
from typing import Any
|
|
9
|
+
|
|
10
|
+
import httpx
|
|
11
|
+
|
|
12
|
+
from ._config import ClientConfig
|
|
13
|
+
from ._errors import map_error
|
|
14
|
+
from ._sse import parse_sse_line
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def _build_url(base_url: str, path: str, query: dict[str, Any] | None) -> str:
|
|
18
|
+
url = base_url.rstrip("/") + path
|
|
19
|
+
if query:
|
|
20
|
+
pairs = {key: value for key, value in query.items() if value is not None}
|
|
21
|
+
if pairs:
|
|
22
|
+
url = url + "?" + str(httpx.QueryParams(pairs))
|
|
23
|
+
return url
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def _decode(text: str) -> dict[str, Any]:
|
|
27
|
+
if text == "":
|
|
28
|
+
return {}
|
|
29
|
+
decoded = json.loads(text)
|
|
30
|
+
return decoded if isinstance(decoded, dict) else {}
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def _error_body(text: str) -> dict[str, Any]:
|
|
34
|
+
if text == "":
|
|
35
|
+
return {}
|
|
36
|
+
try:
|
|
37
|
+
decoded = json.loads(text)
|
|
38
|
+
except json.JSONDecodeError:
|
|
39
|
+
return {}
|
|
40
|
+
return decoded if isinstance(decoded, dict) else {}
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
class SyncTransport:
|
|
44
|
+
"""Synchronous HTTP transport: bearer auth, JSON, retries with backoff,
|
|
45
|
+
status-to-exception mapping, and incremental SSE reads. The api_key is sent
|
|
46
|
+
only on the Authorization header and is never logged."""
|
|
47
|
+
|
|
48
|
+
def __init__(self, config: ClientConfig) -> None:
|
|
49
|
+
self._config = config
|
|
50
|
+
self._client = httpx.Client()
|
|
51
|
+
|
|
52
|
+
def close(self) -> None:
|
|
53
|
+
self._client.close()
|
|
54
|
+
|
|
55
|
+
def __enter__(self) -> SyncTransport:
|
|
56
|
+
return self
|
|
57
|
+
|
|
58
|
+
def __exit__(self, exc_type: object, exc_val: object, exc_tb: object) -> None:
|
|
59
|
+
self.close()
|
|
60
|
+
|
|
61
|
+
def request(
|
|
62
|
+
self,
|
|
63
|
+
method: str,
|
|
64
|
+
path: str,
|
|
65
|
+
query: dict[str, Any] | None,
|
|
66
|
+
body: dict[str, Any] | None,
|
|
67
|
+
) -> dict[str, Any]:
|
|
68
|
+
response = self._dispatch(method, path, query, body)
|
|
69
|
+
return _decode(response.text)
|
|
70
|
+
|
|
71
|
+
def stream(
|
|
72
|
+
self,
|
|
73
|
+
method: str,
|
|
74
|
+
path: str,
|
|
75
|
+
body: dict[str, Any] | None,
|
|
76
|
+
) -> Generator[dict[str, Any], None, None]:
|
|
77
|
+
url = _build_url(self._config.base_url, path, None)
|
|
78
|
+
headers = {
|
|
79
|
+
"Authorization": f"Bearer {self._config.api_key}",
|
|
80
|
+
"Accept": "text/event-stream",
|
|
81
|
+
}
|
|
82
|
+
content = json.dumps(body) if body is not None else None
|
|
83
|
+
if content is not None:
|
|
84
|
+
headers["Content-Type"] = "application/json"
|
|
85
|
+
with self._client.stream(
|
|
86
|
+
method, url, headers=headers, content=content
|
|
87
|
+
) as response:
|
|
88
|
+
if response.status_code >= 400:
|
|
89
|
+
response.read()
|
|
90
|
+
raise map_error(response.status_code, _error_body(response.text))
|
|
91
|
+
for line in response.iter_lines():
|
|
92
|
+
event = parse_sse_line(line)
|
|
93
|
+
if event is not None:
|
|
94
|
+
yield event
|
|
95
|
+
|
|
96
|
+
def _dispatch(
|
|
97
|
+
self,
|
|
98
|
+
method: str,
|
|
99
|
+
path: str,
|
|
100
|
+
query: dict[str, Any] | None,
|
|
101
|
+
body: dict[str, Any] | None,
|
|
102
|
+
) -> httpx.Response:
|
|
103
|
+
url = _build_url(self._config.base_url, path, query)
|
|
104
|
+
headers = {
|
|
105
|
+
"Authorization": f"Bearer {self._config.api_key}",
|
|
106
|
+
"Accept": "application/json",
|
|
107
|
+
}
|
|
108
|
+
content = json.dumps(body) if body is not None else None
|
|
109
|
+
if content is not None:
|
|
110
|
+
headers["Content-Type"] = "application/json"
|
|
111
|
+
|
|
112
|
+
attempt = 0
|
|
113
|
+
while True:
|
|
114
|
+
response = self._client.request(
|
|
115
|
+
method, url, headers=headers, content=content
|
|
116
|
+
)
|
|
117
|
+
if response.status_code < 400:
|
|
118
|
+
return response
|
|
119
|
+
|
|
120
|
+
should_retry = (
|
|
121
|
+
attempt < self._config.max_retries
|
|
122
|
+
and response.status_code in self._config.retry_on
|
|
123
|
+
)
|
|
124
|
+
if should_retry:
|
|
125
|
+
attempt += 1
|
|
126
|
+
time.sleep(0.25 * (2 ** (attempt - 1)))
|
|
127
|
+
continue
|
|
128
|
+
|
|
129
|
+
raise map_error(response.status_code, _error_body(response.text))
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
class AsyncTransport:
|
|
133
|
+
"""Asynchronous HTTP transport. Same behavior as SyncTransport over
|
|
134
|
+
httpx.AsyncClient. The api_key is sent only on the Authorization header."""
|
|
135
|
+
|
|
136
|
+
def __init__(self, config: ClientConfig) -> None:
|
|
137
|
+
self._config = config
|
|
138
|
+
self._client = httpx.AsyncClient()
|
|
139
|
+
|
|
140
|
+
async def aclose(self) -> None:
|
|
141
|
+
await self._client.aclose()
|
|
142
|
+
|
|
143
|
+
async def __aenter__(self) -> AsyncTransport:
|
|
144
|
+
return self
|
|
145
|
+
|
|
146
|
+
async def __aexit__(
|
|
147
|
+
self, exc_type: object, exc_val: object, exc_tb: object
|
|
148
|
+
) -> None:
|
|
149
|
+
await self.aclose()
|
|
150
|
+
|
|
151
|
+
async def arequest(
|
|
152
|
+
self,
|
|
153
|
+
method: str,
|
|
154
|
+
path: str,
|
|
155
|
+
query: dict[str, Any] | None,
|
|
156
|
+
body: dict[str, Any] | None,
|
|
157
|
+
) -> dict[str, Any]:
|
|
158
|
+
response = await self._adispatch(method, path, query, body)
|
|
159
|
+
return _decode(response.text)
|
|
160
|
+
|
|
161
|
+
async def astream(
|
|
162
|
+
self,
|
|
163
|
+
method: str,
|
|
164
|
+
path: str,
|
|
165
|
+
body: dict[str, Any] | None,
|
|
166
|
+
) -> AsyncGenerator[dict[str, Any], None]:
|
|
167
|
+
url = _build_url(self._config.base_url, path, None)
|
|
168
|
+
headers = {
|
|
169
|
+
"Authorization": f"Bearer {self._config.api_key}",
|
|
170
|
+
"Accept": "text/event-stream",
|
|
171
|
+
}
|
|
172
|
+
content = json.dumps(body) if body is not None else None
|
|
173
|
+
if content is not None:
|
|
174
|
+
headers["Content-Type"] = "application/json"
|
|
175
|
+
async with self._client.stream(
|
|
176
|
+
method, url, headers=headers, content=content
|
|
177
|
+
) as response:
|
|
178
|
+
if response.status_code >= 400:
|
|
179
|
+
await response.aread()
|
|
180
|
+
raise map_error(response.status_code, _error_body(response.text))
|
|
181
|
+
async for line in response.aiter_lines():
|
|
182
|
+
event = parse_sse_line(line)
|
|
183
|
+
if event is not None:
|
|
184
|
+
yield event
|
|
185
|
+
|
|
186
|
+
async def _adispatch(
|
|
187
|
+
self,
|
|
188
|
+
method: str,
|
|
189
|
+
path: str,
|
|
190
|
+
query: dict[str, Any] | None,
|
|
191
|
+
body: dict[str, Any] | None,
|
|
192
|
+
) -> httpx.Response:
|
|
193
|
+
url = _build_url(self._config.base_url, path, query)
|
|
194
|
+
headers = {
|
|
195
|
+
"Authorization": f"Bearer {self._config.api_key}",
|
|
196
|
+
"Accept": "application/json",
|
|
197
|
+
}
|
|
198
|
+
content = json.dumps(body) if body is not None else None
|
|
199
|
+
if content is not None:
|
|
200
|
+
headers["Content-Type"] = "application/json"
|
|
201
|
+
|
|
202
|
+
attempt = 0
|
|
203
|
+
while True:
|
|
204
|
+
response = await self._client.request(
|
|
205
|
+
method, url, headers=headers, content=content
|
|
206
|
+
)
|
|
207
|
+
if response.status_code < 400:
|
|
208
|
+
return response
|
|
209
|
+
|
|
210
|
+
should_retry = (
|
|
211
|
+
attempt < self._config.max_retries
|
|
212
|
+
and response.status_code in self._config.retry_on
|
|
213
|
+
)
|
|
214
|
+
if should_retry:
|
|
215
|
+
attempt += 1
|
|
216
|
+
await asyncio.sleep(0.25 * (2 ** (attempt - 1)))
|
|
217
|
+
continue
|
|
218
|
+
|
|
219
|
+
raise map_error(response.status_code, _error_body(response.text))
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
# AUTO-GENERATED by akumi/codegen. Do not edit by hand.
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
from dataclasses import dataclass
|
|
5
|
+
from typing import Any
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@dataclass
|
|
9
|
+
class AuditLogApiResource:
|
|
10
|
+
id: str
|
|
11
|
+
component: str
|
|
12
|
+
action: str
|
|
13
|
+
actorId: int | None
|
|
14
|
+
ipAddress: str | None
|
|
15
|
+
userAgent: str | None
|
|
16
|
+
target: Any | None
|
|
17
|
+
metadata: list[Any] | None
|
|
18
|
+
createdAt: str
|
|
19
|
+
|
|
20
|
+
@classmethod
|
|
21
|
+
def from_dict(cls, data: dict[str, Any]) -> AuditLogApiResource:
|
|
22
|
+
return cls(
|
|
23
|
+
id=data["id"],
|
|
24
|
+
component=data["component"],
|
|
25
|
+
action=data["action"],
|
|
26
|
+
actorId=data["actorId"],
|
|
27
|
+
ipAddress=data["ipAddress"],
|
|
28
|
+
userAgent=data["userAgent"],
|
|
29
|
+
target=data["target"],
|
|
30
|
+
metadata=data["metadata"],
|
|
31
|
+
createdAt=data["createdAt"],
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
@dataclass
|
|
36
|
+
class ChatCompletionsRequest:
|
|
37
|
+
model: str
|
|
38
|
+
messages: list[Any]
|
|
39
|
+
temperature: float | None = None
|
|
40
|
+
max_tokens: int | None = None
|
|
41
|
+
stream: bool | None = None
|
|
42
|
+
firewall: bool | None = None
|
|
43
|
+
rag: str | None = None
|
|
44
|
+
user: str | None = None
|
|
45
|
+
thread: str | None = None
|
|
46
|
+
top_p: float | None = None
|
|
47
|
+
presence_penalty: float | None = None
|
|
48
|
+
frequency_penalty: float | None = None
|
|
49
|
+
n: int | None = None
|
|
50
|
+
seed: int | None = None
|
|
51
|
+
logprobs: bool | None = None
|
|
52
|
+
top_logprobs: int | None = None
|
|
53
|
+
max_completion_tokens: int | None = None
|
|
54
|
+
tool_choice: str | None = None
|
|
55
|
+
parallel_tool_calls: bool | None = None
|
|
56
|
+
cache: bool | None = None
|
|
57
|
+
stop: list[str] | None = None
|
|
58
|
+
logit_bias: list[float] | None = None
|
|
59
|
+
response_format: Any | None = None
|
|
60
|
+
tools: list[Any] | None = None
|
|
61
|
+
|
|
62
|
+
@classmethod
|
|
63
|
+
def from_dict(cls, data: dict[str, Any]) -> ChatCompletionsRequest:
|
|
64
|
+
return cls(
|
|
65
|
+
model=data["model"],
|
|
66
|
+
messages=data["messages"],
|
|
67
|
+
temperature=data.get("temperature"),
|
|
68
|
+
max_tokens=data.get("max_tokens"),
|
|
69
|
+
stream=data.get("stream"),
|
|
70
|
+
firewall=data.get("firewall"),
|
|
71
|
+
rag=data.get("rag"),
|
|
72
|
+
user=data.get("user"),
|
|
73
|
+
thread=data.get("thread"),
|
|
74
|
+
top_p=data.get("top_p"),
|
|
75
|
+
presence_penalty=data.get("presence_penalty"),
|
|
76
|
+
frequency_penalty=data.get("frequency_penalty"),
|
|
77
|
+
n=data.get("n"),
|
|
78
|
+
seed=data.get("seed"),
|
|
79
|
+
logprobs=data.get("logprobs"),
|
|
80
|
+
top_logprobs=data.get("top_logprobs"),
|
|
81
|
+
max_completion_tokens=data.get("max_completion_tokens"),
|
|
82
|
+
tool_choice=data.get("tool_choice"),
|
|
83
|
+
parallel_tool_calls=data.get("parallel_tool_calls"),
|
|
84
|
+
cache=data.get("cache"),
|
|
85
|
+
stop=data.get("stop"),
|
|
86
|
+
logit_bias=data.get("logit_bias"),
|
|
87
|
+
response_format=data.get("response_format"),
|
|
88
|
+
tools=data.get("tools"),
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
@dataclass
|
|
93
|
+
class EmbeddingsRequest:
|
|
94
|
+
model: str
|
|
95
|
+
input: list[str]
|
|
96
|
+
encoding_format: str | None = None
|
|
97
|
+
dimensions: int | None = None
|
|
98
|
+
user: str | None = None
|
|
99
|
+
|
|
100
|
+
@classmethod
|
|
101
|
+
def from_dict(cls, data: dict[str, Any]) -> EmbeddingsRequest:
|
|
102
|
+
return cls(
|
|
103
|
+
model=data["model"],
|
|
104
|
+
input=data["input"],
|
|
105
|
+
encoding_format=data.get("encoding_format"),
|
|
106
|
+
dimensions=data.get("dimensions"),
|
|
107
|
+
user=data.get("user"),
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
@dataclass
|
|
112
|
+
class IngestDocumentRequest:
|
|
113
|
+
title: str
|
|
114
|
+
text: str
|
|
115
|
+
source: str | None = None
|
|
116
|
+
collection: str | None = None
|
|
117
|
+
|
|
118
|
+
@classmethod
|
|
119
|
+
def from_dict(cls, data: dict[str, Any]) -> IngestDocumentRequest:
|
|
120
|
+
return cls(
|
|
121
|
+
title=data["title"],
|
|
122
|
+
text=data["text"],
|
|
123
|
+
source=data.get("source"),
|
|
124
|
+
collection=data.get("collection"),
|
|
125
|
+
)
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
@dataclass
|
|
129
|
+
class RememberFactRequest:
|
|
130
|
+
content: str
|
|
131
|
+
user_ref: str
|
|
132
|
+
|
|
133
|
+
@classmethod
|
|
134
|
+
def from_dict(cls, data: dict[str, Any]) -> RememberFactRequest:
|
|
135
|
+
return cls(
|
|
136
|
+
content=data["content"],
|
|
137
|
+
user_ref=data["user_ref"],
|
|
138
|
+
)
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
@dataclass
|
|
142
|
+
class SearchDocumentsRequest:
|
|
143
|
+
query: str
|
|
144
|
+
collection: str
|
|
145
|
+
user_ref: str | None = None
|
|
146
|
+
limit: int | None = None
|
|
147
|
+
|
|
148
|
+
@classmethod
|
|
149
|
+
def from_dict(cls, data: dict[str, Any]) -> SearchDocumentsRequest:
|
|
150
|
+
return cls(
|
|
151
|
+
query=data["query"],
|
|
152
|
+
collection=data["collection"],
|
|
153
|
+
user_ref=data.get("user_ref"),
|
|
154
|
+
limit=data.get("limit"),
|
|
155
|
+
)
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
@dataclass
|
|
159
|
+
class SearchFactsRequest:
|
|
160
|
+
query: str
|
|
161
|
+
user_ref: str | None = None
|
|
162
|
+
limit: int | None = None
|
|
163
|
+
|
|
164
|
+
@classmethod
|
|
165
|
+
def from_dict(cls, data: dict[str, Any]) -> SearchFactsRequest:
|
|
166
|
+
return cls(
|
|
167
|
+
query=data["query"],
|
|
168
|
+
user_ref=data.get("user_ref"),
|
|
169
|
+
limit=data.get("limit"),
|
|
170
|
+
)
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
@dataclass
|
|
174
|
+
class SearchRequest:
|
|
175
|
+
query: str
|
|
176
|
+
user_ref: str | None = None
|
|
177
|
+
limit: int | None = None
|
|
178
|
+
|
|
179
|
+
@classmethod
|
|
180
|
+
def from_dict(cls, data: dict[str, Any]) -> SearchRequest:
|
|
181
|
+
return cls(
|
|
182
|
+
query=data["query"],
|
|
183
|
+
user_ref=data.get("user_ref"),
|
|
184
|
+
limit=data.get("limit"),
|
|
185
|
+
)
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
@dataclass
|
|
189
|
+
class StoreCollectionRequest:
|
|
190
|
+
name: str
|
|
191
|
+
|
|
192
|
+
@classmethod
|
|
193
|
+
def from_dict(cls, data: dict[str, Any]) -> StoreCollectionRequest:
|
|
194
|
+
return cls(
|
|
195
|
+
name=data["name"],
|
|
196
|
+
)
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
@dataclass
|
|
200
|
+
class ThreadMessageViewModel:
|
|
201
|
+
@classmethod
|
|
202
|
+
def from_dict(cls, data: dict[str, Any]) -> ThreadMessageViewModel:
|
|
203
|
+
return cls()
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
@dataclass
|
|
207
|
+
class ThreadViewModel:
|
|
208
|
+
@classmethod
|
|
209
|
+
def from_dict(cls, data: dict[str, Any]) -> ThreadViewModel:
|
|
210
|
+
return cls()
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# AUTO-GENERATED by akumi/codegen. Do not edit by hand.
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
from typing import Any
|
|
5
|
+
|
|
6
|
+
from .._transport import AsyncTransport
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class AsyncAuditLogsResource:
|
|
10
|
+
def __init__(self, transport: AsyncTransport) -> None:
|
|
11
|
+
self._transport = transport
|
|
12
|
+
|
|
13
|
+
async def list(self, query: dict[str, Any] | None = None) -> dict[str, Any]:
|
|
14
|
+
return await self._transport.arequest("GET", "/audit-logs", query, None)
|
|
15
|
+
|
|
16
|
+
async def get(self, uuid: str) -> dict[str, Any]:
|
|
17
|
+
return await self._transport.arequest("GET", f"/audit-logs/{uuid}", None, None)
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# AUTO-GENERATED by akumi/codegen. Do not edit by hand.
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
from typing import Any
|
|
5
|
+
|
|
6
|
+
from .._transport import SyncTransport
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class AuditLogsResource:
|
|
10
|
+
def __init__(self, transport: SyncTransport) -> None:
|
|
11
|
+
self._transport = transport
|
|
12
|
+
|
|
13
|
+
def list(self, query: dict[str, Any] | None = None) -> dict[str, Any]:
|
|
14
|
+
return self._transport.request("GET", "/audit-logs", query, None)
|
|
15
|
+
|
|
16
|
+
def get(self, uuid: str) -> dict[str, Any]:
|
|
17
|
+
return self._transport.request("GET", f"/audit-logs/{uuid}", None, None)
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# AUTO-GENERATED by akumi/codegen. Do not edit by hand.
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
from collections.abc import AsyncIterator
|
|
5
|
+
from typing import Any
|
|
6
|
+
|
|
7
|
+
from .._transport import AsyncTransport
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class AsyncChatResource:
|
|
11
|
+
def __init__(self, transport: AsyncTransport) -> None:
|
|
12
|
+
self._transport = transport
|
|
13
|
+
|
|
14
|
+
async def create(self, params: dict[str, Any] | None = None) -> dict[str, Any]:
|
|
15
|
+
return await self._transport.arequest("POST", "/chat/completions", None, params)
|
|
16
|
+
|
|
17
|
+
async def create_streamed(
|
|
18
|
+
self, params: dict[str, Any] | None = None
|
|
19
|
+
) -> AsyncIterator[dict[str, Any]]:
|
|
20
|
+
async for event in self._transport.astream("POST", "/chat/completions", params):
|
|
21
|
+
yield event
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# AUTO-GENERATED by akumi/codegen. Do not edit by hand.
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
from collections.abc import Iterator
|
|
5
|
+
from typing import Any
|
|
6
|
+
|
|
7
|
+
from .._transport import SyncTransport
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class ChatResource:
|
|
11
|
+
def __init__(self, transport: SyncTransport) -> None:
|
|
12
|
+
self._transport = transport
|
|
13
|
+
|
|
14
|
+
def create(self, params: dict[str, Any] | None = None) -> dict[str, Any]:
|
|
15
|
+
return self._transport.request("POST", "/chat/completions", None, params)
|
|
16
|
+
|
|
17
|
+
def create_streamed(
|
|
18
|
+
self, params: dict[str, Any] | None = None
|
|
19
|
+
) -> Iterator[dict[str, Any]]:
|
|
20
|
+
yield from self._transport.stream("POST", "/chat/completions", params)
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# AUTO-GENERATED by akumi/codegen. Do not edit by hand.
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
from typing import Any
|
|
5
|
+
|
|
6
|
+
from .._transport import AsyncTransport
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class AsyncEmbeddingsResource:
|
|
10
|
+
def __init__(self, transport: AsyncTransport) -> None:
|
|
11
|
+
self._transport = transport
|
|
12
|
+
|
|
13
|
+
async def create(self, params: dict[str, Any] | None = None) -> dict[str, Any]:
|
|
14
|
+
return await self._transport.arequest("POST", "/embeddings", None, params)
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# AUTO-GENERATED by akumi/codegen. Do not edit by hand.
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
from typing import Any
|
|
5
|
+
|
|
6
|
+
from .._transport import SyncTransport
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class EmbeddingsResource:
|
|
10
|
+
def __init__(self, transport: SyncTransport) -> None:
|
|
11
|
+
self._transport = transport
|
|
12
|
+
|
|
13
|
+
def create(self, params: dict[str, Any] | None = None) -> dict[str, Any]:
|
|
14
|
+
return self._transport.request("POST", "/embeddings", None, params)
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# AUTO-GENERATED by akumi/codegen. Do not edit by hand.
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
from typing import Any
|
|
5
|
+
|
|
6
|
+
from .._transport import AsyncTransport
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class AsyncModelsResource:
|
|
10
|
+
def __init__(self, transport: AsyncTransport) -> None:
|
|
11
|
+
self._transport = transport
|
|
12
|
+
|
|
13
|
+
async def list(self) -> dict[str, Any]:
|
|
14
|
+
return await self._transport.arequest("GET", "/models", None, None)
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# AUTO-GENERATED by akumi/codegen. Do not edit by hand.
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
from typing import Any
|
|
5
|
+
|
|
6
|
+
from .._transport import SyncTransport
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class ModelsResource:
|
|
10
|
+
def __init__(self, transport: SyncTransport) -> None:
|
|
11
|
+
self._transport = transport
|
|
12
|
+
|
|
13
|
+
def list(self) -> dict[str, Any]:
|
|
14
|
+
return self._transport.request("GET", "/models", None, None)
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
# AUTO-GENERATED by akumi/codegen. Do not edit by hand.
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
from typing import Any
|
|
5
|
+
|
|
6
|
+
from .._transport import AsyncTransport
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class AsyncRecallResource:
|
|
10
|
+
def __init__(self, transport: AsyncTransport) -> None:
|
|
11
|
+
self._transport = transport
|
|
12
|
+
|
|
13
|
+
async def listThreads(self, query: dict[str, Any] | None = None) -> dict[str, Any]:
|
|
14
|
+
return await self._transport.arequest("GET", "/recall/threads", query, None)
|
|
15
|
+
|
|
16
|
+
async def createThread(self) -> dict[str, Any]:
|
|
17
|
+
return await self._transport.arequest("POST", "/recall/threads", None, None)
|
|
18
|
+
|
|
19
|
+
async def getThread(self, thread: str) -> dict[str, Any]:
|
|
20
|
+
return await self._transport.arequest(
|
|
21
|
+
"GET", f"/recall/threads/{thread}", None, None
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
async def deleteThread(self, thread: str) -> dict[str, Any]:
|
|
25
|
+
return await self._transport.arequest(
|
|
26
|
+
"DELETE", f"/recall/threads/{thread}", None, None
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
async def search(self, params: dict[str, Any] | None = None) -> dict[str, Any]:
|
|
30
|
+
return await self._transport.arequest("POST", "/recall/search", None, params)
|
|
31
|
+
|
|
32
|
+
async def searchFacts(self, params: dict[str, Any] | None = None) -> dict[str, Any]:
|
|
33
|
+
return await self._transport.arequest(
|
|
34
|
+
"POST", "/recall/facts/search", None, params
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
async def listFacts(self, query: dict[str, Any] | None = None) -> dict[str, Any]:
|
|
38
|
+
return await self._transport.arequest("GET", "/recall/facts", query, None)
|
|
39
|
+
|
|
40
|
+
async def rememberFact(
|
|
41
|
+
self, params: dict[str, Any] | None = None
|
|
42
|
+
) -> dict[str, Any]:
|
|
43
|
+
return await self._transport.arequest("POST", "/recall/facts", None, params)
|
|
44
|
+
|
|
45
|
+
async def forgetFact(self, id: str) -> dict[str, Any]:
|
|
46
|
+
return await self._transport.arequest(
|
|
47
|
+
"DELETE", f"/recall/facts/{id}", None, None
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
async def export(self, query: dict[str, Any] | None = None) -> dict[str, Any]:
|
|
51
|
+
return await self._transport.arequest("GET", "/recall/export", query, None)
|
|
52
|
+
|
|
53
|
+
async def erase(self) -> dict[str, Any]:
|
|
54
|
+
return await self._transport.arequest("DELETE", "/recall", None, None)
|
|
55
|
+
|
|
56
|
+
async def searchDocuments(
|
|
57
|
+
self, params: dict[str, Any] | None = None
|
|
58
|
+
) -> dict[str, Any]:
|
|
59
|
+
return await self._transport.arequest(
|
|
60
|
+
"POST", "/recall/documents/search", None, params
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
async def listDocuments(
|
|
64
|
+
self, query: dict[str, Any] | None = None
|
|
65
|
+
) -> dict[str, Any]:
|
|
66
|
+
return await self._transport.arequest("GET", "/recall/documents", query, None)
|
|
67
|
+
|
|
68
|
+
async def ingestDocument(
|
|
69
|
+
self, params: dict[str, Any] | None = None
|
|
70
|
+
) -> dict[str, Any]:
|
|
71
|
+
return await self._transport.arequest("POST", "/recall/documents", None, params)
|
|
72
|
+
|
|
73
|
+
async def getDocument(self, document: str) -> dict[str, Any]:
|
|
74
|
+
return await self._transport.arequest(
|
|
75
|
+
"GET", f"/recall/documents/{document}", None, None
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
async def deleteDocument(self, document: str) -> dict[str, Any]:
|
|
79
|
+
return await self._transport.arequest(
|
|
80
|
+
"DELETE", f"/recall/documents/{document}", None, None
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
async def listCollections(
|
|
84
|
+
self, query: dict[str, Any] | None = None
|
|
85
|
+
) -> dict[str, Any]:
|
|
86
|
+
return await self._transport.arequest("GET", "/recall/collections", query, None)
|
|
87
|
+
|
|
88
|
+
async def createCollection(
|
|
89
|
+
self, params: dict[str, Any] | None = None
|
|
90
|
+
) -> dict[str, Any]:
|
|
91
|
+
return await self._transport.arequest(
|
|
92
|
+
"POST", "/recall/collections", None, params
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
async def getCollection(self, slug: str) -> dict[str, Any]:
|
|
96
|
+
return await self._transport.arequest(
|
|
97
|
+
"GET", f"/recall/collections/{slug}", None, None
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
async def deleteCollection(self, slug: str) -> dict[str, Any]:
|
|
101
|
+
return await self._transport.arequest(
|
|
102
|
+
"DELETE", f"/recall/collections/{slug}", None, None
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
async def updateCollection(
|
|
106
|
+
self, slug: str, params: dict[str, Any] | None = None
|
|
107
|
+
) -> dict[str, Any]:
|
|
108
|
+
return await self._transport.arequest(
|
|
109
|
+
"PATCH", f"/recall/collections/{slug}", None, params
|
|
110
|
+
)
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
# AUTO-GENERATED by akumi/codegen. Do not edit by hand.
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
from typing import Any
|
|
5
|
+
|
|
6
|
+
from .._transport import SyncTransport
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class RecallResource:
|
|
10
|
+
def __init__(self, transport: SyncTransport) -> None:
|
|
11
|
+
self._transport = transport
|
|
12
|
+
|
|
13
|
+
def listThreads(self, query: dict[str, Any] | None = None) -> dict[str, Any]:
|
|
14
|
+
return self._transport.request("GET", "/recall/threads", query, None)
|
|
15
|
+
|
|
16
|
+
def createThread(self) -> dict[str, Any]:
|
|
17
|
+
return self._transport.request("POST", "/recall/threads", None, None)
|
|
18
|
+
|
|
19
|
+
def getThread(self, thread: str) -> dict[str, Any]:
|
|
20
|
+
return self._transport.request("GET", f"/recall/threads/{thread}", None, None)
|
|
21
|
+
|
|
22
|
+
def deleteThread(self, thread: str) -> dict[str, Any]:
|
|
23
|
+
return self._transport.request(
|
|
24
|
+
"DELETE", f"/recall/threads/{thread}", None, None
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
def search(self, params: dict[str, Any] | None = None) -> dict[str, Any]:
|
|
28
|
+
return self._transport.request("POST", "/recall/search", None, params)
|
|
29
|
+
|
|
30
|
+
def searchFacts(self, params: dict[str, Any] | None = None) -> dict[str, Any]:
|
|
31
|
+
return self._transport.request("POST", "/recall/facts/search", None, params)
|
|
32
|
+
|
|
33
|
+
def listFacts(self, query: dict[str, Any] | None = None) -> dict[str, Any]:
|
|
34
|
+
return self._transport.request("GET", "/recall/facts", query, None)
|
|
35
|
+
|
|
36
|
+
def rememberFact(self, params: dict[str, Any] | None = None) -> dict[str, Any]:
|
|
37
|
+
return self._transport.request("POST", "/recall/facts", None, params)
|
|
38
|
+
|
|
39
|
+
def forgetFact(self, id: str) -> dict[str, Any]:
|
|
40
|
+
return self._transport.request("DELETE", f"/recall/facts/{id}", None, None)
|
|
41
|
+
|
|
42
|
+
def export(self, query: dict[str, Any] | None = None) -> dict[str, Any]:
|
|
43
|
+
return self._transport.request("GET", "/recall/export", query, None)
|
|
44
|
+
|
|
45
|
+
def erase(self) -> dict[str, Any]:
|
|
46
|
+
return self._transport.request("DELETE", "/recall", None, None)
|
|
47
|
+
|
|
48
|
+
def searchDocuments(self, params: dict[str, Any] | None = None) -> dict[str, Any]:
|
|
49
|
+
return self._transport.request("POST", "/recall/documents/search", None, params)
|
|
50
|
+
|
|
51
|
+
def listDocuments(self, query: dict[str, Any] | None = None) -> dict[str, Any]:
|
|
52
|
+
return self._transport.request("GET", "/recall/documents", query, None)
|
|
53
|
+
|
|
54
|
+
def ingestDocument(self, params: dict[str, Any] | None = None) -> dict[str, Any]:
|
|
55
|
+
return self._transport.request("POST", "/recall/documents", None, params)
|
|
56
|
+
|
|
57
|
+
def getDocument(self, document: str) -> dict[str, Any]:
|
|
58
|
+
return self._transport.request(
|
|
59
|
+
"GET", f"/recall/documents/{document}", None, None
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
def deleteDocument(self, document: str) -> dict[str, Any]:
|
|
63
|
+
return self._transport.request(
|
|
64
|
+
"DELETE", f"/recall/documents/{document}", None, None
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
def listCollections(self, query: dict[str, Any] | None = None) -> dict[str, Any]:
|
|
68
|
+
return self._transport.request("GET", "/recall/collections", query, None)
|
|
69
|
+
|
|
70
|
+
def createCollection(self, params: dict[str, Any] | None = None) -> dict[str, Any]:
|
|
71
|
+
return self._transport.request("POST", "/recall/collections", None, params)
|
|
72
|
+
|
|
73
|
+
def getCollection(self, slug: str) -> dict[str, Any]:
|
|
74
|
+
return self._transport.request("GET", f"/recall/collections/{slug}", None, None)
|
|
75
|
+
|
|
76
|
+
def deleteCollection(self, slug: str) -> dict[str, Any]:
|
|
77
|
+
return self._transport.request(
|
|
78
|
+
"DELETE", f"/recall/collections/{slug}", None, None
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
def updateCollection(
|
|
82
|
+
self, slug: str, params: dict[str, Any] | None = None
|
|
83
|
+
) -> dict[str, Any]:
|
|
84
|
+
return self._transport.request(
|
|
85
|
+
"PATCH", f"/recall/collections/{slug}", None, params
|
|
86
|
+
)
|