clark-platform-client 0.1.0__py3-none-any.whl
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.
- clark_platform/__init__.py +61 -0
- clark_platform/_payloads.py +123 -0
- clark_platform/async_client.py +380 -0
- clark_platform/client.py +425 -0
- clark_platform/errors.py +61 -0
- clark_platform/models.py +588 -0
- clark_platform/streaming.py +69 -0
- clark_platform_client-0.1.0.dist-info/METADATA +241 -0
- clark_platform_client-0.1.0.dist-info/RECORD +10 -0
- clark_platform_client-0.1.0.dist-info/WHEEL +4 -0
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
"""Typed sync/async Python client for the Clark Platform API.
|
|
2
|
+
|
|
3
|
+
See https://www.clarkchat.com and ``platform/clients/API_CONTRACT.md`` in the
|
|
4
|
+
Clark monorepo for the full wire contract this client implements.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from .async_client import AsyncClarkClient
|
|
8
|
+
from .client import ClarkClient
|
|
9
|
+
from .errors import ClarkApiError
|
|
10
|
+
from .models import (
|
|
11
|
+
Artifact,
|
|
12
|
+
Capabilities,
|
|
13
|
+
ChatChoice,
|
|
14
|
+
ChatCompletionChunk,
|
|
15
|
+
ChatCompletionObject,
|
|
16
|
+
ChatMessage,
|
|
17
|
+
ClarkMetadata,
|
|
18
|
+
Cost,
|
|
19
|
+
MemoryList,
|
|
20
|
+
MemoryRecord,
|
|
21
|
+
ModelInfo,
|
|
22
|
+
ModelList,
|
|
23
|
+
ModelOption,
|
|
24
|
+
OutputMessage,
|
|
25
|
+
Pricing,
|
|
26
|
+
ResponseError,
|
|
27
|
+
ResponseEvent,
|
|
28
|
+
ResponseEventList,
|
|
29
|
+
ResponseObject,
|
|
30
|
+
ResponseStreamEvent,
|
|
31
|
+
Usage,
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
__version__ = "0.1.0"
|
|
35
|
+
|
|
36
|
+
__all__ = [
|
|
37
|
+
"ClarkClient",
|
|
38
|
+
"AsyncClarkClient",
|
|
39
|
+
"ClarkApiError",
|
|
40
|
+
"ModelInfo",
|
|
41
|
+
"ModelOption",
|
|
42
|
+
"ModelList",
|
|
43
|
+
"Pricing",
|
|
44
|
+
"Capabilities",
|
|
45
|
+
"ResponseObject",
|
|
46
|
+
"ResponseStreamEvent",
|
|
47
|
+
"ResponseEvent",
|
|
48
|
+
"ResponseEventList",
|
|
49
|
+
"OutputMessage",
|
|
50
|
+
"Artifact",
|
|
51
|
+
"ClarkMetadata",
|
|
52
|
+
"ResponseError",
|
|
53
|
+
"Cost",
|
|
54
|
+
"Usage",
|
|
55
|
+
"ChatCompletionObject",
|
|
56
|
+
"ChatCompletionChunk",
|
|
57
|
+
"ChatChoice",
|
|
58
|
+
"ChatMessage",
|
|
59
|
+
"MemoryRecord",
|
|
60
|
+
"MemoryList",
|
|
61
|
+
]
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
"""Internal helpers for building request payloads.
|
|
2
|
+
|
|
3
|
+
Shared by both the sync and async clients so the two stay in lockstep.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
from typing import Any, Dict, List, Optional, Union
|
|
9
|
+
|
|
10
|
+
DEFAULT_BASE_URL = "https://www.clarkchat.com"
|
|
11
|
+
|
|
12
|
+
# Non-background responses/chat-completions calls can legitimately take up to
|
|
13
|
+
# ~120s server-side (CLARK_PLATFORM_RESPONSE_WAIT_MS default), so the client
|
|
14
|
+
# default timeout must comfortably exceed that.
|
|
15
|
+
DEFAULT_TIMEOUT = 150.0
|
|
16
|
+
|
|
17
|
+
ResponseInput = Union[str, List[Dict[str, Any]]]
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def omit_none(d: Dict[str, Any]) -> Dict[str, Any]:
|
|
21
|
+
return {k: v for k, v in d.items() if v is not None}
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def build_responses_payload(
|
|
25
|
+
*,
|
|
26
|
+
model: str,
|
|
27
|
+
input: ResponseInput, # noqa: A002 - mirrors wire field name
|
|
28
|
+
tier_model_id: Optional[str] = None,
|
|
29
|
+
conversation_id: Optional[str] = None,
|
|
30
|
+
memory_scope: Optional[str] = None,
|
|
31
|
+
previous_response_id: Optional[str] = None,
|
|
32
|
+
background: Optional[bool] = None,
|
|
33
|
+
metadata: Optional[Dict[str, Any]] = None,
|
|
34
|
+
stream: Optional[bool] = None,
|
|
35
|
+
) -> Dict[str, Any]:
|
|
36
|
+
return omit_none(
|
|
37
|
+
{
|
|
38
|
+
"model": model,
|
|
39
|
+
"tier_model_id": tier_model_id,
|
|
40
|
+
"input": input,
|
|
41
|
+
"conversation_id": conversation_id,
|
|
42
|
+
"memory_scope": memory_scope,
|
|
43
|
+
"previous_response_id": previous_response_id,
|
|
44
|
+
"background": background,
|
|
45
|
+
"metadata": metadata,
|
|
46
|
+
"stream": stream,
|
|
47
|
+
}
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def build_chat_completions_payload(
|
|
52
|
+
*,
|
|
53
|
+
model: str,
|
|
54
|
+
messages: List[Dict[str, Any]],
|
|
55
|
+
tier_model_id: Optional[str] = None,
|
|
56
|
+
conversation_id: Optional[str] = None,
|
|
57
|
+
memory_scope: Optional[str] = None,
|
|
58
|
+
previous_response_id: Optional[str] = None,
|
|
59
|
+
stream: Optional[bool] = None,
|
|
60
|
+
stream_options: Optional[Dict[str, Any]] = None,
|
|
61
|
+
metadata: Optional[Dict[str, Any]] = None,
|
|
62
|
+
tools: Optional[List[Dict[str, Any]]] = None,
|
|
63
|
+
tool_choice: Optional[Any] = None,
|
|
64
|
+
temperature: Optional[float] = None,
|
|
65
|
+
top_p: Optional[float] = None,
|
|
66
|
+
max_tokens: Optional[int] = None,
|
|
67
|
+
stop: Optional[Union[str, List[str]]] = None,
|
|
68
|
+
parallel_tool_calls: Optional[bool] = None,
|
|
69
|
+
reasoning_effort: Optional[str] = None,
|
|
70
|
+
) -> Dict[str, Any]:
|
|
71
|
+
return omit_none(
|
|
72
|
+
{
|
|
73
|
+
"model": model,
|
|
74
|
+
"tier_model_id": tier_model_id,
|
|
75
|
+
"messages": messages,
|
|
76
|
+
"conversation_id": conversation_id,
|
|
77
|
+
"memory_scope": memory_scope,
|
|
78
|
+
"previous_response_id": previous_response_id,
|
|
79
|
+
"stream": stream,
|
|
80
|
+
"stream_options": stream_options,
|
|
81
|
+
"metadata": metadata,
|
|
82
|
+
"tools": tools,
|
|
83
|
+
"tool_choice": tool_choice,
|
|
84
|
+
"temperature": temperature,
|
|
85
|
+
"top_p": top_p,
|
|
86
|
+
"max_tokens": max_tokens,
|
|
87
|
+
"stop": stop,
|
|
88
|
+
"parallel_tool_calls": parallel_tool_calls,
|
|
89
|
+
"reasoning_effort": reasoning_effort,
|
|
90
|
+
}
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def build_events_params(
|
|
95
|
+
*,
|
|
96
|
+
after_seq: Optional[int] = None,
|
|
97
|
+
limit: Optional[int] = None,
|
|
98
|
+
types: Optional[List[str]] = None,
|
|
99
|
+
) -> Dict[str, Any]:
|
|
100
|
+
params: Dict[str, Any] = {}
|
|
101
|
+
if after_seq is not None:
|
|
102
|
+
params["after_seq"] = after_seq
|
|
103
|
+
if limit is not None:
|
|
104
|
+
params["limit"] = limit
|
|
105
|
+
if types:
|
|
106
|
+
params["types"] = ",".join(types)
|
|
107
|
+
return params
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def build_memories_params(
|
|
111
|
+
*,
|
|
112
|
+
q: Optional[str] = None,
|
|
113
|
+
tags: Optional[List[str]] = None,
|
|
114
|
+
conversation_id: Optional[str] = None,
|
|
115
|
+
) -> Dict[str, Any]:
|
|
116
|
+
params: Dict[str, Any] = {}
|
|
117
|
+
if q is not None:
|
|
118
|
+
params["q"] = q
|
|
119
|
+
if tags:
|
|
120
|
+
params["tags"] = ",".join(tags)
|
|
121
|
+
if conversation_id is not None:
|
|
122
|
+
params["conversation_id"] = conversation_id
|
|
123
|
+
return params
|
|
@@ -0,0 +1,380 @@
|
|
|
1
|
+
"""Asynchronous client for the Clark Platform API.
|
|
2
|
+
|
|
3
|
+
Mirrors :mod:`clark_platform.client` method-for-method; see its docstrings
|
|
4
|
+
for behavioral details not repeated here.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import json
|
|
10
|
+
from typing import Any, AsyncIterator, Dict, List, Optional, Union
|
|
11
|
+
|
|
12
|
+
import httpx
|
|
13
|
+
|
|
14
|
+
from ._payloads import (
|
|
15
|
+
DEFAULT_BASE_URL,
|
|
16
|
+
DEFAULT_TIMEOUT,
|
|
17
|
+
ResponseInput,
|
|
18
|
+
build_chat_completions_payload,
|
|
19
|
+
build_events_params,
|
|
20
|
+
build_memories_params,
|
|
21
|
+
build_responses_payload,
|
|
22
|
+
)
|
|
23
|
+
from .errors import ClarkApiError
|
|
24
|
+
from .models import (
|
|
25
|
+
ChatCompletionChunk,
|
|
26
|
+
ChatCompletionObject,
|
|
27
|
+
MemoryList,
|
|
28
|
+
ModelList,
|
|
29
|
+
ResponseEventList,
|
|
30
|
+
ResponseObject,
|
|
31
|
+
ResponseStreamEvent,
|
|
32
|
+
)
|
|
33
|
+
from .streaming import DONE_SENTINEL, aiter_sse_events
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class AsyncClarkClient:
|
|
37
|
+
"""Async client for the Clark Platform API.
|
|
38
|
+
|
|
39
|
+
Example::
|
|
40
|
+
|
|
41
|
+
from clark_platform import AsyncClarkClient
|
|
42
|
+
|
|
43
|
+
async with AsyncClarkClient(api_key="clk_live_...") as client:
|
|
44
|
+
response = await client.responses.create(model="clark", input="hello")
|
|
45
|
+
print(response.output_text)
|
|
46
|
+
"""
|
|
47
|
+
|
|
48
|
+
def __init__(
|
|
49
|
+
self,
|
|
50
|
+
api_key: Optional[str] = None,
|
|
51
|
+
*,
|
|
52
|
+
base_url: str = DEFAULT_BASE_URL,
|
|
53
|
+
timeout: float = DEFAULT_TIMEOUT,
|
|
54
|
+
http_client: Optional[httpx.AsyncClient] = None,
|
|
55
|
+
) -> None:
|
|
56
|
+
self.api_key = api_key
|
|
57
|
+
self.base_url = base_url.rstrip("/")
|
|
58
|
+
self._owns_http_client = http_client is None
|
|
59
|
+
self._http = http_client or httpx.AsyncClient(timeout=timeout)
|
|
60
|
+
|
|
61
|
+
self.models = AsyncModelsResource(self)
|
|
62
|
+
self.responses = AsyncResponsesResource(self)
|
|
63
|
+
self.chat = AsyncChatResource(self)
|
|
64
|
+
self.memories = AsyncMemoriesResource(self)
|
|
65
|
+
|
|
66
|
+
async def close(self) -> None:
|
|
67
|
+
if self._owns_http_client:
|
|
68
|
+
await self._http.aclose()
|
|
69
|
+
|
|
70
|
+
async def __aenter__(self) -> "AsyncClarkClient":
|
|
71
|
+
return self
|
|
72
|
+
|
|
73
|
+
async def __aexit__(self, *exc_info: Any) -> None:
|
|
74
|
+
await self.close()
|
|
75
|
+
|
|
76
|
+
# -- internal helpers ---------------------------------------------------
|
|
77
|
+
|
|
78
|
+
def _headers(self) -> Dict[str, str]:
|
|
79
|
+
headers = {"Content-Type": "application/json", "Accept": "application/json"}
|
|
80
|
+
if self.api_key:
|
|
81
|
+
headers["Authorization"] = f"Bearer {self.api_key}"
|
|
82
|
+
return headers
|
|
83
|
+
|
|
84
|
+
def _url(self, path: str) -> str:
|
|
85
|
+
return f"{self.base_url}{path}"
|
|
86
|
+
|
|
87
|
+
@staticmethod
|
|
88
|
+
def _error_from_response(response: httpx.Response) -> ClarkApiError:
|
|
89
|
+
try:
|
|
90
|
+
body = response.json()
|
|
91
|
+
except ValueError:
|
|
92
|
+
return ClarkApiError.from_unparsable_body(response.status_code, response.text)
|
|
93
|
+
return ClarkApiError.from_error_body(response.status_code, body)
|
|
94
|
+
|
|
95
|
+
async def request_json(
|
|
96
|
+
self,
|
|
97
|
+
method: str,
|
|
98
|
+
path: str,
|
|
99
|
+
*,
|
|
100
|
+
params: Optional[Dict[str, Any]] = None,
|
|
101
|
+
json_body: Optional[Dict[str, Any]] = None,
|
|
102
|
+
) -> Dict[str, Any]:
|
|
103
|
+
response = await self._http.request(
|
|
104
|
+
method, self._url(path), params=params, json=json_body, headers=self._headers()
|
|
105
|
+
)
|
|
106
|
+
if response.status_code >= 400:
|
|
107
|
+
raise self._error_from_response(response)
|
|
108
|
+
try:
|
|
109
|
+
return response.json()
|
|
110
|
+
except ValueError as exc:
|
|
111
|
+
raise ClarkApiError(
|
|
112
|
+
f"Failed to parse JSON response body: {exc}",
|
|
113
|
+
status_code=response.status_code,
|
|
114
|
+
) from exc
|
|
115
|
+
|
|
116
|
+
async def stream_lines(
|
|
117
|
+
self,
|
|
118
|
+
method: str,
|
|
119
|
+
path: str,
|
|
120
|
+
*,
|
|
121
|
+
json_body: Optional[Dict[str, Any]] = None,
|
|
122
|
+
) -> AsyncIterator[str]:
|
|
123
|
+
async with self._http.stream(
|
|
124
|
+
method, self._url(path), json=json_body, headers=self._headers()
|
|
125
|
+
) as response:
|
|
126
|
+
if response.status_code >= 400:
|
|
127
|
+
await response.aread()
|
|
128
|
+
raise self._error_from_response(response)
|
|
129
|
+
async for line in response.aiter_lines():
|
|
130
|
+
yield line
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
class AsyncModelsResource:
|
|
134
|
+
def __init__(self, client: AsyncClarkClient) -> None:
|
|
135
|
+
self._client = client
|
|
136
|
+
|
|
137
|
+
async def list(self) -> ModelList:
|
|
138
|
+
body = await self._client.request_json("GET", "/v1/models")
|
|
139
|
+
return ModelList.from_dict(body)
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
class AsyncResponsesResource:
|
|
143
|
+
def __init__(self, client: AsyncClarkClient) -> None:
|
|
144
|
+
self._client = client
|
|
145
|
+
|
|
146
|
+
async def create(
|
|
147
|
+
self,
|
|
148
|
+
*,
|
|
149
|
+
model: str,
|
|
150
|
+
input: ResponseInput, # noqa: A002
|
|
151
|
+
tier_model_id: Optional[str] = None,
|
|
152
|
+
conversation_id: Optional[str] = None,
|
|
153
|
+
memory_scope: Optional[str] = None,
|
|
154
|
+
previous_response_id: Optional[str] = None,
|
|
155
|
+
background: Optional[bool] = None,
|
|
156
|
+
metadata: Optional[Dict[str, Any]] = None,
|
|
157
|
+
) -> ResponseObject:
|
|
158
|
+
payload = build_responses_payload(
|
|
159
|
+
model=model,
|
|
160
|
+
input=input,
|
|
161
|
+
tier_model_id=tier_model_id,
|
|
162
|
+
conversation_id=conversation_id,
|
|
163
|
+
memory_scope=memory_scope,
|
|
164
|
+
previous_response_id=previous_response_id,
|
|
165
|
+
background=background,
|
|
166
|
+
metadata=metadata,
|
|
167
|
+
stream=False,
|
|
168
|
+
)
|
|
169
|
+
body = await self._client.request_json("POST", "/v1/responses", json_body=payload)
|
|
170
|
+
return ResponseObject.from_dict(body)
|
|
171
|
+
|
|
172
|
+
async def stream(
|
|
173
|
+
self,
|
|
174
|
+
*,
|
|
175
|
+
model: str,
|
|
176
|
+
input: ResponseInput, # noqa: A002
|
|
177
|
+
tier_model_id: Optional[str] = None,
|
|
178
|
+
conversation_id: Optional[str] = None,
|
|
179
|
+
memory_scope: Optional[str] = None,
|
|
180
|
+
previous_response_id: Optional[str] = None,
|
|
181
|
+
metadata: Optional[Dict[str, Any]] = None,
|
|
182
|
+
) -> AsyncIterator[ResponseStreamEvent]:
|
|
183
|
+
payload = build_responses_payload(
|
|
184
|
+
model=model,
|
|
185
|
+
input=input,
|
|
186
|
+
tier_model_id=tier_model_id,
|
|
187
|
+
conversation_id=conversation_id,
|
|
188
|
+
memory_scope=memory_scope,
|
|
189
|
+
previous_response_id=previous_response_id,
|
|
190
|
+
metadata=metadata,
|
|
191
|
+
stream=True,
|
|
192
|
+
)
|
|
193
|
+
lines = self._client.stream_lines("POST", "/v1/responses", json_body=payload)
|
|
194
|
+
async for event_name, data in aiter_sse_events(lines):
|
|
195
|
+
if data == DONE_SENTINEL:
|
|
196
|
+
return
|
|
197
|
+
obj = json.loads(data)
|
|
198
|
+
yield ResponseStreamEvent.from_dict(obj, event_name)
|
|
199
|
+
|
|
200
|
+
async def get(self, response_id: str) -> ResponseObject:
|
|
201
|
+
body = await self._client.request_json("GET", f"/v1/responses/{response_id}")
|
|
202
|
+
return ResponseObject.from_dict(body)
|
|
203
|
+
|
|
204
|
+
async def list_events(
|
|
205
|
+
self,
|
|
206
|
+
response_id: str,
|
|
207
|
+
*,
|
|
208
|
+
after_seq: Optional[int] = None,
|
|
209
|
+
limit: Optional[int] = None,
|
|
210
|
+
types: Optional[List[str]] = None,
|
|
211
|
+
) -> ResponseEventList:
|
|
212
|
+
params = build_events_params(after_seq=after_seq, limit=limit, types=types)
|
|
213
|
+
body = await self._client.request_json(
|
|
214
|
+
"GET", f"/v1/responses/{response_id}/events", params=params
|
|
215
|
+
)
|
|
216
|
+
return ResponseEventList.from_dict(body)
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
class AsyncChatCompletionsResource:
|
|
220
|
+
def __init__(self, client: AsyncClarkClient) -> None:
|
|
221
|
+
self._client = client
|
|
222
|
+
|
|
223
|
+
async def create(
|
|
224
|
+
self,
|
|
225
|
+
*,
|
|
226
|
+
model: str,
|
|
227
|
+
messages: List[Dict[str, Any]],
|
|
228
|
+
tier_model_id: Optional[str] = None,
|
|
229
|
+
conversation_id: Optional[str] = None,
|
|
230
|
+
memory_scope: Optional[str] = None,
|
|
231
|
+
previous_response_id: Optional[str] = None,
|
|
232
|
+
stream_options: Optional[Dict[str, Any]] = None,
|
|
233
|
+
metadata: Optional[Dict[str, Any]] = None,
|
|
234
|
+
temperature: Optional[float] = None,
|
|
235
|
+
top_p: Optional[float] = None,
|
|
236
|
+
max_tokens: Optional[int] = None,
|
|
237
|
+
stop: Optional[Union[str, List[str]]] = None,
|
|
238
|
+
) -> ChatCompletionObject:
|
|
239
|
+
payload = build_chat_completions_payload(
|
|
240
|
+
model=model,
|
|
241
|
+
messages=messages,
|
|
242
|
+
tier_model_id=tier_model_id,
|
|
243
|
+
conversation_id=conversation_id,
|
|
244
|
+
memory_scope=memory_scope,
|
|
245
|
+
previous_response_id=previous_response_id,
|
|
246
|
+
stream=False,
|
|
247
|
+
stream_options=stream_options,
|
|
248
|
+
metadata=metadata,
|
|
249
|
+
temperature=temperature,
|
|
250
|
+
top_p=top_p,
|
|
251
|
+
max_tokens=max_tokens,
|
|
252
|
+
stop=stop,
|
|
253
|
+
)
|
|
254
|
+
body = await self._client.request_json("POST", "/v1/chat/completions", json_body=payload)
|
|
255
|
+
return ChatCompletionObject.from_dict(body)
|
|
256
|
+
|
|
257
|
+
async def stream(
|
|
258
|
+
self,
|
|
259
|
+
*,
|
|
260
|
+
model: str,
|
|
261
|
+
messages: List[Dict[str, Any]],
|
|
262
|
+
tier_model_id: Optional[str] = None,
|
|
263
|
+
conversation_id: Optional[str] = None,
|
|
264
|
+
memory_scope: Optional[str] = None,
|
|
265
|
+
previous_response_id: Optional[str] = None,
|
|
266
|
+
stream_options: Optional[Dict[str, Any]] = None,
|
|
267
|
+
metadata: Optional[Dict[str, Any]] = None,
|
|
268
|
+
temperature: Optional[float] = None,
|
|
269
|
+
top_p: Optional[float] = None,
|
|
270
|
+
max_tokens: Optional[int] = None,
|
|
271
|
+
stop: Optional[Union[str, List[str]]] = None,
|
|
272
|
+
) -> AsyncIterator[ChatCompletionChunk]:
|
|
273
|
+
payload = build_chat_completions_payload(
|
|
274
|
+
model=model,
|
|
275
|
+
messages=messages,
|
|
276
|
+
tier_model_id=tier_model_id,
|
|
277
|
+
conversation_id=conversation_id,
|
|
278
|
+
memory_scope=memory_scope,
|
|
279
|
+
previous_response_id=previous_response_id,
|
|
280
|
+
stream=True,
|
|
281
|
+
stream_options=stream_options,
|
|
282
|
+
metadata=metadata,
|
|
283
|
+
temperature=temperature,
|
|
284
|
+
top_p=top_p,
|
|
285
|
+
max_tokens=max_tokens,
|
|
286
|
+
stop=stop,
|
|
287
|
+
)
|
|
288
|
+
lines = self._client.stream_lines("POST", "/v1/chat/completions", json_body=payload)
|
|
289
|
+
async for _event_name, data in aiter_sse_events(lines):
|
|
290
|
+
if data == DONE_SENTINEL:
|
|
291
|
+
return
|
|
292
|
+
obj = json.loads(data)
|
|
293
|
+
yield ChatCompletionChunk.from_dict(obj)
|
|
294
|
+
|
|
295
|
+
async def create_passthrough(
|
|
296
|
+
self,
|
|
297
|
+
*,
|
|
298
|
+
model: str,
|
|
299
|
+
messages: List[Dict[str, Any]],
|
|
300
|
+
tools: Optional[List[Dict[str, Any]]] = None,
|
|
301
|
+
tool_choice: Optional[Any] = None,
|
|
302
|
+
temperature: Optional[float] = None,
|
|
303
|
+
top_p: Optional[float] = None,
|
|
304
|
+
max_tokens: Optional[int] = None,
|
|
305
|
+
stop: Optional[Union[str, List[str]]] = None,
|
|
306
|
+
parallel_tool_calls: Optional[bool] = None,
|
|
307
|
+
reasoning_effort: Optional[str] = None,
|
|
308
|
+
metadata: Optional[Dict[str, Any]] = None,
|
|
309
|
+
) -> Dict[str, Any]:
|
|
310
|
+
payload = build_chat_completions_payload(
|
|
311
|
+
model=model,
|
|
312
|
+
messages=messages,
|
|
313
|
+
stream=False,
|
|
314
|
+
tools=tools,
|
|
315
|
+
tool_choice=tool_choice,
|
|
316
|
+
temperature=temperature,
|
|
317
|
+
top_p=top_p,
|
|
318
|
+
max_tokens=max_tokens,
|
|
319
|
+
stop=stop,
|
|
320
|
+
parallel_tool_calls=parallel_tool_calls,
|
|
321
|
+
reasoning_effort=reasoning_effort,
|
|
322
|
+
metadata=metadata,
|
|
323
|
+
)
|
|
324
|
+
return await self._client.request_json("POST", "/v1/chat/completions", json_body=payload)
|
|
325
|
+
|
|
326
|
+
async def stream_passthrough(
|
|
327
|
+
self,
|
|
328
|
+
*,
|
|
329
|
+
model: str,
|
|
330
|
+
messages: List[Dict[str, Any]],
|
|
331
|
+
tools: Optional[List[Dict[str, Any]]] = None,
|
|
332
|
+
tool_choice: Optional[Any] = None,
|
|
333
|
+
temperature: Optional[float] = None,
|
|
334
|
+
top_p: Optional[float] = None,
|
|
335
|
+
max_tokens: Optional[int] = None,
|
|
336
|
+
stop: Optional[Union[str, List[str]]] = None,
|
|
337
|
+
parallel_tool_calls: Optional[bool] = None,
|
|
338
|
+
reasoning_effort: Optional[str] = None,
|
|
339
|
+
metadata: Optional[Dict[str, Any]] = None,
|
|
340
|
+
) -> AsyncIterator[Dict[str, Any]]:
|
|
341
|
+
payload = build_chat_completions_payload(
|
|
342
|
+
model=model,
|
|
343
|
+
messages=messages,
|
|
344
|
+
stream=True,
|
|
345
|
+
tools=tools,
|
|
346
|
+
tool_choice=tool_choice,
|
|
347
|
+
temperature=temperature,
|
|
348
|
+
top_p=top_p,
|
|
349
|
+
max_tokens=max_tokens,
|
|
350
|
+
stop=stop,
|
|
351
|
+
parallel_tool_calls=parallel_tool_calls,
|
|
352
|
+
reasoning_effort=reasoning_effort,
|
|
353
|
+
metadata=metadata,
|
|
354
|
+
)
|
|
355
|
+
lines = self._client.stream_lines("POST", "/v1/chat/completions", json_body=payload)
|
|
356
|
+
async for _event_name, data in aiter_sse_events(lines):
|
|
357
|
+
if data == DONE_SENTINEL:
|
|
358
|
+
return
|
|
359
|
+
yield json.loads(data)
|
|
360
|
+
|
|
361
|
+
|
|
362
|
+
class AsyncChatResource:
|
|
363
|
+
def __init__(self, client: AsyncClarkClient) -> None:
|
|
364
|
+
self.completions = AsyncChatCompletionsResource(client)
|
|
365
|
+
|
|
366
|
+
|
|
367
|
+
class AsyncMemoriesResource:
|
|
368
|
+
def __init__(self, client: AsyncClarkClient) -> None:
|
|
369
|
+
self._client = client
|
|
370
|
+
|
|
371
|
+
async def list(
|
|
372
|
+
self,
|
|
373
|
+
*,
|
|
374
|
+
q: Optional[str] = None,
|
|
375
|
+
tags: Optional[List[str]] = None,
|
|
376
|
+
conversation_id: Optional[str] = None,
|
|
377
|
+
) -> MemoryList:
|
|
378
|
+
params = build_memories_params(q=q, tags=tags, conversation_id=conversation_id)
|
|
379
|
+
body = await self._client.request_json("GET", "/v1/memories", params=params)
|
|
380
|
+
return MemoryList.from_dict(body)
|