courier-encode 0.1.1__tar.gz → 0.1.2__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.
- {courier_encode-0.1.1 → courier_encode-0.1.2}/.idea/workspace.xml +4 -5
- {courier_encode-0.1.1 → courier_encode-0.1.2}/PKG-INFO +2 -2
- {courier_encode-0.1.1 → courier_encode-0.1.2}/pyproject.toml +2 -2
- {courier_encode-0.1.1 → courier_encode-0.1.2}/src/encode/_http.py +11 -0
- {courier_encode-0.1.1 → courier_encode-0.1.2}/src/encode/relay.py +2 -2
- {courier_encode-0.1.1 → courier_encode-0.1.2}/tests/test_streaming.py +66 -0
- {courier_encode-0.1.1 → courier_encode-0.1.2}/uv.lock +1 -1
- {courier_encode-0.1.1 → courier_encode-0.1.2}/.gitignore +0 -0
- {courier_encode-0.1.1 → courier_encode-0.1.2}/.idea/.gitignore +0 -0
- {courier_encode-0.1.1 → courier_encode-0.1.2}/.idea/encode.iml +0 -0
- {courier_encode-0.1.1 → courier_encode-0.1.2}/.idea/inspectionProfiles/Project_Default.xml +0 -0
- {courier_encode-0.1.1 → courier_encode-0.1.2}/.idea/inspectionProfiles/profiles_settings.xml +0 -0
- {courier_encode-0.1.1 → courier_encode-0.1.2}/.idea/misc.xml +0 -0
- {courier_encode-0.1.1 → courier_encode-0.1.2}/.idea/modules.xml +0 -0
- {courier_encode-0.1.1 → courier_encode-0.1.2}/.idea/vcs.xml +0 -0
- {courier_encode-0.1.1 → courier_encode-0.1.2}/LICENSE +0 -0
- {courier_encode-0.1.1 → courier_encode-0.1.2}/README.md +0 -0
- {courier_encode-0.1.1 → courier_encode-0.1.2}/docs.md +0 -0
- {courier_encode-0.1.1 → courier_encode-0.1.2}/examples/basic_chat.py +0 -0
- {courier_encode-0.1.1 → courier_encode-0.1.2}/examples/intercept.py +0 -0
- {courier_encode-0.1.1 → courier_encode-0.1.2}/examples/responses_endpoint.py +0 -0
- {courier_encode-0.1.1 → courier_encode-0.1.2}/examples/structured_output.py +0 -0
- {courier_encode-0.1.1 → courier_encode-0.1.2}/examples/tools_loop.py +0 -0
- {courier_encode-0.1.1 → courier_encode-0.1.2}/examples/whisper_transcribe.py +0 -0
- {courier_encode-0.1.1 → courier_encode-0.1.2}/src/encode/__init__.py +0 -0
- {courier_encode-0.1.1 → courier_encode-0.1.2}/src/encode/_config.py +0 -0
- {courier_encode-0.1.1 → courier_encode-0.1.2}/src/encode/_schema.py +0 -0
- {courier_encode-0.1.1 → courier_encode-0.1.2}/src/encode/_streaming.py +0 -0
- {courier_encode-0.1.1 → courier_encode-0.1.2}/src/encode/_version.py +0 -0
- {courier_encode-0.1.1 → courier_encode-0.1.2}/src/encode/client.py +0 -0
- {courier_encode-0.1.1 → courier_encode-0.1.2}/src/encode/errors.py +0 -0
- {courier_encode-0.1.1 → courier_encode-0.1.2}/src/encode/messages.py +0 -0
- {courier_encode-0.1.1 → courier_encode-0.1.2}/src/encode/py.typed +0 -0
- {courier_encode-0.1.1 → courier_encode-0.1.2}/src/encode/responses.py +0 -0
- {courier_encode-0.1.1 → courier_encode-0.1.2}/src/encode/session.py +0 -0
- {courier_encode-0.1.1 → courier_encode-0.1.2}/src/encode/tools.py +0 -0
- {courier_encode-0.1.1 → courier_encode-0.1.2}/src/encode/whisper.py +0 -0
- {courier_encode-0.1.1 → courier_encode-0.1.2}/tests/__init__.py +0 -0
- {courier_encode-0.1.1 → courier_encode-0.1.2}/tests/conftest.py +0 -0
- {courier_encode-0.1.1 → courier_encode-0.1.2}/tests/test_async.py +0 -0
- {courier_encode-0.1.1 → courier_encode-0.1.2}/tests/test_errors.py +0 -0
- {courier_encode-0.1.1 → courier_encode-0.1.2}/tests/test_messages.py +0 -0
- {courier_encode-0.1.1 → courier_encode-0.1.2}/tests/test_messages_obj.py +0 -0
- {courier_encode-0.1.1 → courier_encode-0.1.2}/tests/test_relay_chat.py +0 -0
- {courier_encode-0.1.1 → courier_encode-0.1.2}/tests/test_relay_responses.py +0 -0
- {courier_encode-0.1.1 → courier_encode-0.1.2}/tests/test_response_format.py +0 -0
- {courier_encode-0.1.1 → courier_encode-0.1.2}/tests/test_session.py +0 -0
- {courier_encode-0.1.1 → courier_encode-0.1.2}/tests/test_tool_loop.py +0 -0
- {courier_encode-0.1.1 → courier_encode-0.1.2}/tests/test_tools_schema.py +0 -0
- {courier_encode-0.1.1 → courier_encode-0.1.2}/tests/test_whisper.py +0 -0
|
@@ -5,12 +5,11 @@
|
|
|
5
5
|
</component>
|
|
6
6
|
<component name="ChangeListManager">
|
|
7
7
|
<list default="true" id="9259bfb5-884c-4336-83fd-f640b45e88c5" name="Changes" comment="">
|
|
8
|
-
<change beforePath="$PROJECT_DIR$/
|
|
9
|
-
<change beforePath="$PROJECT_DIR$/src/encode/
|
|
10
|
-
<change beforePath="$PROJECT_DIR$/src/encode/_streaming.py" beforeDir="false" afterPath="$PROJECT_DIR$/src/encode/_streaming.py" afterDir="false" />
|
|
11
|
-
<change beforePath="$PROJECT_DIR$/src/encode/messages.py" beforeDir="false" afterPath="$PROJECT_DIR$/src/encode/messages.py" afterDir="false" />
|
|
8
|
+
<change beforePath="$PROJECT_DIR$/pyproject.toml" beforeDir="false" afterPath="$PROJECT_DIR$/pyproject.toml" afterDir="false" />
|
|
9
|
+
<change beforePath="$PROJECT_DIR$/src/encode/_http.py" beforeDir="false" afterPath="$PROJECT_DIR$/src/encode/_http.py" afterDir="false" />
|
|
12
10
|
<change beforePath="$PROJECT_DIR$/src/encode/relay.py" beforeDir="false" afterPath="$PROJECT_DIR$/src/encode/relay.py" afterDir="false" />
|
|
13
11
|
<change beforePath="$PROJECT_DIR$/tests/test_streaming.py" beforeDir="false" afterPath="$PROJECT_DIR$/tests/test_streaming.py" afterDir="false" />
|
|
12
|
+
<change beforePath="$PROJECT_DIR$/uv.lock" beforeDir="false" afterPath="$PROJECT_DIR$/uv.lock" afterDir="false" />
|
|
14
13
|
</list>
|
|
15
14
|
<option name="SHOW_DIALOG" value="false" />
|
|
16
15
|
<option name="HIGHLIGHT_CONFLICTS" value="true" />
|
|
@@ -74,7 +73,7 @@
|
|
|
74
73
|
<option name="number" value="Default" />
|
|
75
74
|
<option name="presentableId" value="Default" />
|
|
76
75
|
<updated>1778249777914</updated>
|
|
77
|
-
<workItem from="1778249778942" duration="
|
|
76
|
+
<workItem from="1778249778942" duration="11261000" />
|
|
78
77
|
</task>
|
|
79
78
|
<servers />
|
|
80
79
|
</component>
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: courier-encode
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.2
|
|
4
4
|
Summary: Python SDK for OpenAI-compatible inference endpoints (Courier and friends) with auto tool-call loops, structured outputs, and Whisper.
|
|
5
5
|
Project-URL: Homepage, https://getcourier.ai
|
|
6
6
|
Project-URL: Documentation, https://getcourier.ai/docs
|
|
@@ -211,7 +211,7 @@ License-File: LICENSE
|
|
|
211
211
|
Keywords: agents,courier,llm,openai,sdk,tool-calling,whisper
|
|
212
212
|
Classifier: Development Status :: 3 - Alpha
|
|
213
213
|
Classifier: Intended Audience :: Developers
|
|
214
|
-
Classifier: License :: OSI Approved ::
|
|
214
|
+
Classifier: License :: OSI Approved :: Apache Software License
|
|
215
215
|
Classifier: Programming Language :: Python :: 3
|
|
216
216
|
Classifier: Programming Language :: Python :: 3.10
|
|
217
217
|
Classifier: Programming Language :: Python :: 3.11
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "courier-encode"
|
|
7
|
-
version = "0.1.
|
|
7
|
+
version = "0.1.2"
|
|
8
8
|
description = "Python SDK for OpenAI-compatible inference endpoints (Courier and friends) with auto tool-call loops, structured outputs, and Whisper."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.10"
|
|
@@ -14,7 +14,7 @@ keywords = ["openai", "courier", "llm", "sdk", "agents", "tool-calling", "whispe
|
|
|
14
14
|
classifiers = [
|
|
15
15
|
"Development Status :: 3 - Alpha",
|
|
16
16
|
"Intended Audience :: Developers",
|
|
17
|
-
"License :: OSI Approved ::
|
|
17
|
+
"License :: OSI Approved :: Apache Software License",
|
|
18
18
|
"Programming Language :: Python :: 3",
|
|
19
19
|
"Programming Language :: Python :: 3.10",
|
|
20
20
|
"Programming Language :: Python :: 3.11",
|
|
@@ -47,6 +47,17 @@ def parse_body(resp: httpx.Response) -> Any:
|
|
|
47
47
|
def raise_for_status(resp: httpx.Response) -> None:
|
|
48
48
|
if resp.is_success:
|
|
49
49
|
return
|
|
50
|
+
# Idempotent for already-buffered responses; required for sync streaming.
|
|
51
|
+
resp.read()
|
|
52
|
+
body = parse_body(resp)
|
|
53
|
+
raise errors.from_envelope(body, status=resp.status_code)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
async def araise_for_status(resp: httpx.Response) -> None:
|
|
57
|
+
if resp.is_success:
|
|
58
|
+
return
|
|
59
|
+
# Required for responses obtained via async_client.stream(...).
|
|
60
|
+
await resp.aread()
|
|
50
61
|
body = parse_body(resp)
|
|
51
62
|
raise errors.from_envelope(body, status=resp.status_code)
|
|
52
63
|
|
|
@@ -1442,7 +1442,7 @@ async def _stream_chat_async(
|
|
|
1442
1442
|
async with client._http.stream(
|
|
1443
1443
|
"POST", "/v1/chat/completions", json=payload
|
|
1444
1444
|
) as resp:
|
|
1445
|
-
_http.
|
|
1445
|
+
await _http.araise_for_status(resp)
|
|
1446
1446
|
async for ev in aiter_chat_completions(resp):
|
|
1447
1447
|
last_chunk = ev.raw
|
|
1448
1448
|
if ev.type == "content.delta":
|
|
@@ -1616,7 +1616,7 @@ async def _stream_responses_async(
|
|
|
1616
1616
|
async with client._http.stream(
|
|
1617
1617
|
"POST", "/v1/responses", json=payload
|
|
1618
1618
|
) as resp:
|
|
1619
|
-
_http.
|
|
1619
|
+
await _http.araise_for_status(resp)
|
|
1620
1620
|
async for ev in aiter_responses(resp):
|
|
1621
1621
|
if ev.type == "response.output_text.delta":
|
|
1622
1622
|
delta = (ev.data or {}).get("delta", "")
|
|
@@ -270,3 +270,69 @@ def test_responses_stream_with_tools_two_iterations(respx_mock, base_url):
|
|
|
270
270
|
assert "content.delta" in types
|
|
271
271
|
text = "".join(e.data for e in events if e.type == "content.delta")
|
|
272
272
|
assert text == "It's 72F."
|
|
273
|
+
|
|
274
|
+
|
|
275
|
+
# ---------------------------------------------------------------------------
|
|
276
|
+
# Streaming + error envelope (regression: httpx.ResponseNotRead)
|
|
277
|
+
# ---------------------------------------------------------------------------
|
|
278
|
+
|
|
279
|
+
|
|
280
|
+
_AUTH_ENVELOPE = {"error": {"message": "bad key", "type": "auth_error"}}
|
|
281
|
+
|
|
282
|
+
|
|
283
|
+
def test_stream_chat_sync_error_status_parses_envelope(respx_mock, base_url):
|
|
284
|
+
respx_mock.post(f"{base_url}/v1/chat/completions").mock(
|
|
285
|
+
return_value=httpx.Response(401, json=_AUTH_ENVELOPE)
|
|
286
|
+
)
|
|
287
|
+
handle = encode.relay(
|
|
288
|
+
model="m",
|
|
289
|
+
messages=[{"role": "user", "content": "hi"}],
|
|
290
|
+
stream=True,
|
|
291
|
+
)
|
|
292
|
+
with pytest.raises(encode.errors.AuthError):
|
|
293
|
+
list(handle)
|
|
294
|
+
|
|
295
|
+
|
|
296
|
+
def test_stream_responses_sync_error_status_parses_envelope(respx_mock, base_url):
|
|
297
|
+
respx_mock.post(f"{base_url}/v1/responses").mock(
|
|
298
|
+
return_value=httpx.Response(401, json=_AUTH_ENVELOPE)
|
|
299
|
+
)
|
|
300
|
+
handle = encode.relay(
|
|
301
|
+
model="m",
|
|
302
|
+
input="hi",
|
|
303
|
+
stream=True,
|
|
304
|
+
endpoint="responses",
|
|
305
|
+
)
|
|
306
|
+
with pytest.raises(encode.errors.AuthError):
|
|
307
|
+
list(handle)
|
|
308
|
+
|
|
309
|
+
|
|
310
|
+
@pytest.mark.asyncio
|
|
311
|
+
async def test_stream_chat_async_error_status_parses_envelope(respx_mock, base_url):
|
|
312
|
+
respx_mock.post(f"{base_url}/v1/chat/completions").mock(
|
|
313
|
+
return_value=httpx.Response(401, json=_AUTH_ENVELOPE)
|
|
314
|
+
)
|
|
315
|
+
handle = encode.relay_async(
|
|
316
|
+
model="m",
|
|
317
|
+
messages=[{"role": "user", "content": "hi"}],
|
|
318
|
+
stream=True,
|
|
319
|
+
)
|
|
320
|
+
with pytest.raises(encode.errors.AuthError):
|
|
321
|
+
async for _ in handle:
|
|
322
|
+
pass
|
|
323
|
+
|
|
324
|
+
|
|
325
|
+
@pytest.mark.asyncio
|
|
326
|
+
async def test_stream_responses_async_error_status_parses_envelope(respx_mock, base_url):
|
|
327
|
+
respx_mock.post(f"{base_url}/v1/responses").mock(
|
|
328
|
+
return_value=httpx.Response(401, json=_AUTH_ENVELOPE)
|
|
329
|
+
)
|
|
330
|
+
handle = encode.relay_async(
|
|
331
|
+
model="m",
|
|
332
|
+
input="hi",
|
|
333
|
+
stream=True,
|
|
334
|
+
endpoint="responses",
|
|
335
|
+
)
|
|
336
|
+
with pytest.raises(encode.errors.AuthError):
|
|
337
|
+
async for _ in handle:
|
|
338
|
+
pass
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{courier_encode-0.1.1 → courier_encode-0.1.2}/.idea/inspectionProfiles/profiles_settings.xml
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|