meshapi 0.1.5__tar.gz → 0.1.6__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.
- {meshapi-0.1.5 → meshapi-0.1.6}/CLAUDE.md +35 -0
- {meshapi-0.1.5 → meshapi-0.1.6}/PKG-INFO +1 -1
- {meshapi-0.1.5 → meshapi-0.1.6}/livetests/test_audio.py +17 -1
- meshapi-0.1.6/livetests/test_compare.py +38 -0
- {meshapi-0.1.5 → meshapi-0.1.6}/meshapi/__init__.py +4 -0
- {meshapi-0.1.5 → meshapi-0.1.6}/meshapi/_types.py +18 -0
- {meshapi-0.1.5 → meshapi-0.1.6}/meshapi/resources/audio.py +14 -8
- {meshapi-0.1.5 → meshapi-0.1.6}/pyproject.toml +1 -1
- {meshapi-0.1.5 → meshapi-0.1.6}/.gitignore +0 -0
- {meshapi-0.1.5 → meshapi-0.1.6}/CHANGELOG.md +0 -0
- {meshapi-0.1.5 → meshapi-0.1.6}/README.md +0 -0
- {meshapi-0.1.5 → meshapi-0.1.6}/TESTING.md +0 -0
- {meshapi-0.1.5 → meshapi-0.1.6}/livetests/compare.py +0 -0
- {meshapi-0.1.5 → meshapi-0.1.6}/livetests/config.py +0 -0
- {meshapi-0.1.5 → meshapi-0.1.6}/livetests/conftest.py +0 -0
- {meshapi-0.1.5 → meshapi-0.1.6}/livetests/pytest.ini +0 -0
- {meshapi-0.1.5 → meshapi-0.1.6}/livetests/requirements.txt +0 -0
- {meshapi-0.1.5 → meshapi-0.1.6}/livetests/responses.py +0 -0
- {meshapi-0.1.5 → meshapi-0.1.6}/livetests/test_chat.py +0 -0
- {meshapi-0.1.5 → meshapi-0.1.6}/livetests/test_errors.py +0 -0
- {meshapi-0.1.5 → meshapi-0.1.6}/livetests/test_feature_matrix.py +0 -0
- {meshapi-0.1.5 → meshapi-0.1.6}/livetests/test_inference_resources.py +0 -0
- {meshapi-0.1.5 → meshapi-0.1.6}/livetests/test_models.py +0 -0
- {meshapi-0.1.5 → meshapi-0.1.6}/livetests/test_rag.py +0 -0
- {meshapi-0.1.5 → meshapi-0.1.6}/livetests/test_realtime.py +0 -0
- {meshapi-0.1.5 → meshapi-0.1.6}/livetests/test_stream.py +0 -0
- {meshapi-0.1.5 → meshapi-0.1.6}/livetests/test_structured_output.py +0 -0
- {meshapi-0.1.5 → meshapi-0.1.6}/livetests/test_templates.py +0 -0
- {meshapi-0.1.5 → meshapi-0.1.6}/livetests/test_video.py +0 -0
- {meshapi-0.1.5 → meshapi-0.1.6}/livetests/tool_call.py +0 -0
- {meshapi-0.1.5 → meshapi-0.1.6}/meshapi/_errors.py +0 -0
- {meshapi-0.1.5 → meshapi-0.1.6}/meshapi/_http.py +0 -0
- {meshapi-0.1.5 → meshapi-0.1.6}/meshapi/resources/__init__.py +0 -0
- {meshapi-0.1.5 → meshapi-0.1.6}/meshapi/resources/batches.py +0 -0
- {meshapi-0.1.5 → meshapi-0.1.6}/meshapi/resources/chat.py +0 -0
- {meshapi-0.1.5 → meshapi-0.1.6}/meshapi/resources/compare.py +0 -0
- {meshapi-0.1.5 → meshapi-0.1.6}/meshapi/resources/embeddings.py +0 -0
- {meshapi-0.1.5 → meshapi-0.1.6}/meshapi/resources/images.py +0 -0
- {meshapi-0.1.5 → meshapi-0.1.6}/meshapi/resources/models.py +0 -0
- {meshapi-0.1.5 → meshapi-0.1.6}/meshapi/resources/rag.py +0 -0
- {meshapi-0.1.5 → meshapi-0.1.6}/meshapi/resources/realtime.py +0 -0
- {meshapi-0.1.5 → meshapi-0.1.6}/meshapi/resources/responses.py +0 -0
- {meshapi-0.1.5 → meshapi-0.1.6}/meshapi/resources/templates.py +0 -0
- {meshapi-0.1.5 → meshapi-0.1.6}/meshapi/resources/videos.py +0 -0
|
@@ -162,6 +162,7 @@ pytest test_rag.py::test_rag_upload_embed_search -v
|
|
|
162
162
|
| `test_realtime.py` | WebSocket connect/close, session lifecycle |
|
|
163
163
|
| `test_audio.py` | TTS synthesize, voice listing |
|
|
164
164
|
| `test_video.py` | Video list, generate → retrieve |
|
|
165
|
+
| `test_compare.py` | Non-streaming compare, streaming compare |
|
|
165
166
|
|
|
166
167
|
---
|
|
167
168
|
|
|
@@ -176,6 +177,40 @@ Every SDK change — however small — must include all of the following before
|
|
|
176
177
|
|
|
177
178
|
---
|
|
178
179
|
|
|
180
|
+
---
|
|
181
|
+
|
|
182
|
+
## Release
|
|
183
|
+
|
|
184
|
+
Releases are triggered by pushing a `v*` git tag. The `publish.yml` workflow builds the package with hatch and publishes to PyPI via OIDC (no API token needed).
|
|
185
|
+
|
|
186
|
+
### Release checklist
|
|
187
|
+
|
|
188
|
+
1. **Bump the version** in `pyproject.toml`:
|
|
189
|
+
```toml
|
|
190
|
+
version = "0.1.6"
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
2. **Commit the version bump**:
|
|
194
|
+
```bash
|
|
195
|
+
git add pyproject.toml
|
|
196
|
+
git commit -m "chore: bump version to 0.1.6"
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
3. **Tag and push**:
|
|
200
|
+
```bash
|
|
201
|
+
git tag v0.1.6
|
|
202
|
+
git push origin main
|
|
203
|
+
git push origin v0.1.6
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
4. **Monitor the workflow** at `Actions → Publish to PyPI`.
|
|
207
|
+
|
|
208
|
+
5. **Verify** the new version is live:
|
|
209
|
+
```bash
|
|
210
|
+
pip install meshapi==0.1.6
|
|
211
|
+
python -c "import meshapi; print(meshapi.__version__)"
|
|
212
|
+
```
|
|
213
|
+
|
|
179
214
|
### RAG live test notes
|
|
180
215
|
|
|
181
216
|
`test_rag_upload_embed_search` does the following:
|
|
@@ -4,7 +4,7 @@ from __future__ import annotations
|
|
|
4
4
|
|
|
5
5
|
import os
|
|
6
6
|
|
|
7
|
-
from meshapi import MeshAPI, SpeechParams, ListVoicesParams
|
|
7
|
+
from meshapi import MeshAPI, SpeechParams, ListVoicesParams, TranscriptionParams
|
|
8
8
|
|
|
9
9
|
TTS_MODEL = os.environ.get("MESHAPI_TTS_MODEL", "sarvam/bulbul:v2")
|
|
10
10
|
STT_MODEL = os.environ.get("MESHAPI_STT_MODEL", "sarvam/saaras:v3")
|
|
@@ -18,6 +18,22 @@ def test_audio_synthesize(client: MeshAPI) -> None:
|
|
|
18
18
|
print(f"[PASS] audio.synthesize -> {len(audio_bytes)} bytes")
|
|
19
19
|
|
|
20
20
|
|
|
21
|
+
def test_audio_stt_from_tts(client: MeshAPI) -> None:
|
|
22
|
+
audio_bytes = client.audio.synthesize(
|
|
23
|
+
SpeechParams(input="Hello from MeshAPI audio test.", model=TTS_MODEL)
|
|
24
|
+
)
|
|
25
|
+
assert isinstance(audio_bytes, bytes) and len(audio_bytes) > 0, "TTS step failed"
|
|
26
|
+
|
|
27
|
+
result = client.audio.transcribe(
|
|
28
|
+
audio_bytes,
|
|
29
|
+
TranscriptionParams(model=STT_MODEL),
|
|
30
|
+
filename="tts_output.wav",
|
|
31
|
+
)
|
|
32
|
+
assert result is not None
|
|
33
|
+
assert isinstance(result.text, str) and len(result.text) > 0
|
|
34
|
+
print(f"[PASS] audio.transcribe (via TTS audio) -> {result.text!r}")
|
|
35
|
+
|
|
36
|
+
|
|
21
37
|
def test_audio_list_voices(client: MeshAPI) -> None:
|
|
22
38
|
voices = client.audio.list_voices(ListVoicesParams(page_size=5))
|
|
23
39
|
assert voices is not None
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"""Live tests: Model Compare API."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import pytest
|
|
6
|
+
from meshapi import MeshAPI, CompareParams, ChatMessage
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def test_compare_nonstreaming(client: MeshAPI, model: str, second_model: str) -> None:
|
|
10
|
+
result = client.compare.create(
|
|
11
|
+
CompareParams(
|
|
12
|
+
models=[model, second_model],
|
|
13
|
+
messages=[ChatMessage(role="user", content="What is 2+2? Reply in one word.")],
|
|
14
|
+
skip_comparison=True,
|
|
15
|
+
max_tokens=20,
|
|
16
|
+
)
|
|
17
|
+
)
|
|
18
|
+
assert result.comparison_id, "expected comparison_id"
|
|
19
|
+
assert len(result.results) == 2, f"expected 2 results, got {len(result.results)}"
|
|
20
|
+
found_models = {r.model for r in result.results}
|
|
21
|
+
assert model in found_models, f"expected {model} in results"
|
|
22
|
+
assert second_model in found_models, f"expected {second_model} in results"
|
|
23
|
+
for r in result.results:
|
|
24
|
+
assert r.content or r.error, f"result for {r.model} has neither content nor error"
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def test_compare_streaming(client: MeshAPI, model: str, second_model: str) -> None:
|
|
28
|
+
events = list(
|
|
29
|
+
client.compare.stream(
|
|
30
|
+
CompareParams(
|
|
31
|
+
models=[model, second_model],
|
|
32
|
+
messages=[ChatMessage(role="user", content="Tell me a joke.")],
|
|
33
|
+
skip_comparison=True,
|
|
34
|
+
max_tokens=50,
|
|
35
|
+
)
|
|
36
|
+
)
|
|
37
|
+
)
|
|
38
|
+
assert len(events) > 0, "expected at least one streaming event"
|
|
@@ -16,6 +16,8 @@ from ._types import (
|
|
|
16
16
|
TranscriptionParams,
|
|
17
17
|
TranscriptionResponse,
|
|
18
18
|
TranscriptionTranslateParams,
|
|
19
|
+
Voice,
|
|
20
|
+
VoicesResponse,
|
|
19
21
|
VoiceSettings,
|
|
20
22
|
BulkEmbedResponse,
|
|
21
23
|
BulkEmbedResult,
|
|
@@ -218,6 +220,8 @@ __all__ = [
|
|
|
218
220
|
"TranscriptionTranslateParams",
|
|
219
221
|
"TranscriptionResponse",
|
|
220
222
|
"ListVoicesParams",
|
|
223
|
+
"Voice",
|
|
224
|
+
"VoicesResponse",
|
|
221
225
|
]
|
|
222
226
|
|
|
223
227
|
|
|
@@ -935,6 +935,24 @@ class ListVoicesParams(BaseModel):
|
|
|
935
935
|
voice_ids: Optional[List[str]] = None
|
|
936
936
|
|
|
937
937
|
|
|
938
|
+
class Voice(BaseModel):
|
|
939
|
+
model_config = ConfigDict(extra="ignore")
|
|
940
|
+
voice_id: str
|
|
941
|
+
name: str
|
|
942
|
+
category: str
|
|
943
|
+
description: str
|
|
944
|
+
preview_url: str
|
|
945
|
+
labels: Dict[str, str] = {}
|
|
946
|
+
|
|
947
|
+
|
|
948
|
+
class VoicesResponse(BaseModel):
|
|
949
|
+
model_config = ConfigDict(extra="ignore")
|
|
950
|
+
voices: List[Voice]
|
|
951
|
+
has_more: bool
|
|
952
|
+
total_count: int
|
|
953
|
+
next_page_token: Optional[str] = None
|
|
954
|
+
|
|
955
|
+
|
|
938
956
|
# ---------------------------------------------------------------------------
|
|
939
957
|
|
|
940
958
|
class ApiErrorBody(BaseModel):
|
|
@@ -11,6 +11,8 @@ from .._types import (
|
|
|
11
11
|
TranscriptionParams,
|
|
12
12
|
TranscriptionResponse,
|
|
13
13
|
TranscriptionTranslateParams,
|
|
14
|
+
Voice,
|
|
15
|
+
VoicesResponse,
|
|
14
16
|
)
|
|
15
17
|
|
|
16
18
|
|
|
@@ -62,16 +64,18 @@ class AudioResource:
|
|
|
62
64
|
)
|
|
63
65
|
return TranscriptionResponse.model_validate(data)
|
|
64
66
|
|
|
65
|
-
def list_voices(self, params: Optional[ListVoicesParams] = None) ->
|
|
67
|
+
def list_voices(self, params: Optional[ListVoicesParams] = None) -> VoicesResponse:
|
|
66
68
|
"""GET /v1/audio/voices — list/search voices."""
|
|
67
69
|
query: Dict[str, Any] = {}
|
|
68
70
|
if params is not None:
|
|
69
71
|
query = {k: v for k, v in params.model_dump(exclude_none=True).items()}
|
|
70
|
-
|
|
72
|
+
data = self._http.get("/v1/audio/voices", params=query or None)
|
|
73
|
+
return VoicesResponse.model_validate(data)
|
|
71
74
|
|
|
72
|
-
def get_voice(self, voice_id: str) ->
|
|
75
|
+
def get_voice(self, voice_id: str) -> Voice:
|
|
73
76
|
"""GET /v1/audio/voices/{voice_id}."""
|
|
74
|
-
|
|
77
|
+
data = self._http.get(f"/v1/audio/voices/{voice_id}")
|
|
78
|
+
return Voice.model_validate(data)
|
|
75
79
|
|
|
76
80
|
|
|
77
81
|
class AsyncAudioResource:
|
|
@@ -122,13 +126,15 @@ class AsyncAudioResource:
|
|
|
122
126
|
)
|
|
123
127
|
return TranscriptionResponse.model_validate(data)
|
|
124
128
|
|
|
125
|
-
async def list_voices(self, params: Optional[ListVoicesParams] = None) ->
|
|
129
|
+
async def list_voices(self, params: Optional[ListVoicesParams] = None) -> VoicesResponse:
|
|
126
130
|
"""GET /v1/audio/voices — list/search voices."""
|
|
127
131
|
query: Dict[str, Any] = {}
|
|
128
132
|
if params is not None:
|
|
129
133
|
query = {k: v for k, v in params.model_dump(exclude_none=True).items()}
|
|
130
|
-
|
|
134
|
+
data = await self._http.get("/v1/audio/voices", params=query or None)
|
|
135
|
+
return VoicesResponse.model_validate(data)
|
|
131
136
|
|
|
132
|
-
async def get_voice(self, voice_id: str) ->
|
|
137
|
+
async def get_voice(self, voice_id: str) -> Voice:
|
|
133
138
|
"""GET /v1/audio/voices/{voice_id}."""
|
|
134
|
-
|
|
139
|
+
data = await self._http.get(f"/v1/audio/voices/{voice_id}")
|
|
140
|
+
return Voice.model_validate(data)
|
|
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
|