meshapi 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.
- meshapi/__init__.py +251 -0
- meshapi/_errors.py +94 -0
- meshapi/_http.py +444 -0
- meshapi/_types.py +573 -0
- meshapi/resources/__init__.py +1 -0
- meshapi/resources/batches.py +60 -0
- meshapi/resources/chat.py +63 -0
- meshapi/resources/compare.py +43 -0
- meshapi/resources/embeddings.py +24 -0
- meshapi/resources/files.py +44 -0
- meshapi/resources/models.py +48 -0
- meshapi/resources/responses.py +43 -0
- meshapi/resources/templates.py +60 -0
- meshapi-0.1.0.dist-info/METADATA +519 -0
- meshapi-0.1.0.dist-info/RECORD +16 -0
- meshapi-0.1.0.dist-info/WHEEL +4 -0
|
@@ -0,0 +1,519 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: meshapi
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Official Python SDK for the MeshAPI AI model gateway
|
|
5
|
+
Project-URL: Homepage, https://meshapi.ai
|
|
6
|
+
Project-URL: Documentation, https://developers.meshapi.ai
|
|
7
|
+
Project-URL: Repository, https://github.com/aifiesta/meshapi-python-sdk
|
|
8
|
+
Project-URL: Issues, https://github.com/aifiesta/meshapi-python-sdk/issues
|
|
9
|
+
Project-URL: Changelog, https://github.com/aifiesta/meshapi-python-sdk/blob/main/CHANGELOG.md
|
|
10
|
+
Author-email: MeshAPI <contact@meshapi.ai>
|
|
11
|
+
License: MIT
|
|
12
|
+
Keywords: ai-gateway,anthropic,batches,embeddings,llm,meshapi,openai,openai-compatible,prompt-templates,sdk
|
|
13
|
+
Classifier: Development Status :: 4 - Beta
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
16
|
+
Classifier: Operating System :: OS Independent
|
|
17
|
+
Classifier: Programming Language :: Python :: 3
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
23
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
24
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
25
|
+
Classifier: Typing :: Typed
|
|
26
|
+
Requires-Python: >=3.9
|
|
27
|
+
Requires-Dist: httpx>=0.27.0
|
|
28
|
+
Requires-Dist: pydantic>=2.0.0
|
|
29
|
+
Provides-Extra: dev
|
|
30
|
+
Requires-Dist: pytest-asyncio>=0.23.0; extra == 'dev'
|
|
31
|
+
Requires-Dist: pytest>=8.0.0; extra == 'dev'
|
|
32
|
+
Requires-Dist: respx>=0.21.0; extra == 'dev'
|
|
33
|
+
Description-Content-Type: text/markdown
|
|
34
|
+
|
|
35
|
+
# meshapi
|
|
36
|
+
|
|
37
|
+
Official Python SDK for [Mesh API](https://meshapi.ai), an AI model gateway that gives you instant access to 300+ LLMs through a single OpenAI-compatible API.
|
|
38
|
+
|
|
39
|
+
Code once with the chat completions signature you already know. Switch between OpenAI, Anthropic, Google, Meta, Mistral, DeepSeek, xAI, Alibaba and the rest by changing a model string. Streaming, tool calling, vision, embeddings, multi-model compare, batch jobs and prompt templates from a single client.
|
|
40
|
+
|
|
41
|
+
```python
|
|
42
|
+
from meshapi import MeshAPI, ChatCompletionParams, ChatMessage
|
|
43
|
+
|
|
44
|
+
client = MeshAPI(base_url="https://api.meshapi.ai", token="rsk_...")
|
|
45
|
+
|
|
46
|
+
reply = client.chat.completions.create(
|
|
47
|
+
ChatCompletionParams(
|
|
48
|
+
model="anthropic/claude-sonnet-4.5",
|
|
49
|
+
messages=[ChatMessage(role="user", content="Write a haiku about Python.")],
|
|
50
|
+
)
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
print(reply.choices[0].message.content)
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Python 3.9+. Built on `httpx` and Pydantic v2. Sync and async clients with first-class type hints.
|
|
57
|
+
|
|
58
|
+
## Install
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
pip install meshapi
|
|
62
|
+
# or
|
|
63
|
+
uv add meshapi
|
|
64
|
+
# or
|
|
65
|
+
poetry add meshapi
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Get a key at [meshapi.ai](https://meshapi.ai). Data-plane keys are prefixed `rsk_`.
|
|
69
|
+
|
|
70
|
+
## What you get
|
|
71
|
+
|
|
72
|
+
| | |
|
|
73
|
+
|---|---|
|
|
74
|
+
| **One Universal API** | Code once. A single `chat.completions.create` call works across 300+ base models. |
|
|
75
|
+
| **Sync and async** | Pick `MeshAPI` for scripts, `AsyncMeshAPI` for servers. Same surface, same params. |
|
|
76
|
+
| **Streaming + tool calling** | SSE streaming via `Iterator` / `AsyncIterator`, function calling with structured tool definitions, vision and audio content parts. |
|
|
77
|
+
| **Reasoning models** | First-class `responses` API with `reasoning.effort` and `max_output_tokens` for o-series and similar models. |
|
|
78
|
+
| **Embeddings** | Drop-in OpenAI-compatible embeddings endpoint. |
|
|
79
|
+
| **Multi-model compare** | Fire one prompt at N models in parallel and stream their replies side by side. |
|
|
80
|
+
| **Batches + Files** | Async bulk inference jobs at discounted rates with file upload, download and lifecycle management. |
|
|
81
|
+
| **Prompt templates** | Server-stored prompts with `{{variable}}` slots. Update prompts without redeploying. |
|
|
82
|
+
| **Provider fallbacks** | If a provider experiences downtime, the gateway falls back to another supported model so your inference stays up. |
|
|
83
|
+
| **Built-in rate limiting** | Per-key RPM and RPD limits to prevent runaway costs. HTTP 429 with `retry_after` surfaced as `MeshAPIError.retry_after_seconds`. |
|
|
84
|
+
| **Unified billing** | One account balance covers every model. No juggling subscriptions. |
|
|
85
|
+
| **Structured errors** | `MeshAPIError` with `error_code`, `status`, `request_id`, `retry_after_seconds`, and provider error details. |
|
|
86
|
+
| **Type-safe** | Every request and response is a Pydantic v2 model. Autocomplete in your editor, validation at the boundary. |
|
|
87
|
+
|
|
88
|
+
## Configuration
|
|
89
|
+
|
|
90
|
+
```python
|
|
91
|
+
client = MeshAPI(
|
|
92
|
+
base_url="https://api.meshapi.ai", # required
|
|
93
|
+
token="rsk_...", # required: data-plane key or Supabase JWT
|
|
94
|
+
timeout=60.0, # default 60s
|
|
95
|
+
max_retries=3, # default 3, set 0 to disable
|
|
96
|
+
httpx_client=None, # optional: inject a custom httpx.Client
|
|
97
|
+
)
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
`AsyncMeshAPI` takes the same arguments (with `async_httpx_client` instead of `httpx_client`) and supports `async with` for clean shutdown.
|
|
101
|
+
|
|
102
|
+
Two auth realms. Use one client per realm.
|
|
103
|
+
|
|
104
|
+
| Realm | Token | Resources |
|
|
105
|
+
|---|---|---|
|
|
106
|
+
| **Data-plane** | `rsk_<ULID>` | `chat`, `responses`, `embeddings`, `compare`, `files`, `batches` |
|
|
107
|
+
| **Control-plane** | Supabase JWT | `templates`, `models` |
|
|
108
|
+
|
|
109
|
+
`models` accepts either token type.
|
|
110
|
+
|
|
111
|
+
## Chat completions
|
|
112
|
+
|
|
113
|
+
```python
|
|
114
|
+
from meshapi import MeshAPI, ChatCompletionParams, ChatMessage
|
|
115
|
+
|
|
116
|
+
client = MeshAPI(base_url="https://api.meshapi.ai", token="rsk_...")
|
|
117
|
+
|
|
118
|
+
reply = client.chat.completions.create(
|
|
119
|
+
ChatCompletionParams(
|
|
120
|
+
model="openai/gpt-4o-mini",
|
|
121
|
+
messages=[
|
|
122
|
+
ChatMessage(role="system", content="You are a concise assistant."),
|
|
123
|
+
ChatMessage(role="user", content="What is the capital of France?"),
|
|
124
|
+
],
|
|
125
|
+
temperature=0.7,
|
|
126
|
+
max_tokens=256,
|
|
127
|
+
)
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
print(reply.choices[0].message.content)
|
|
131
|
+
print(f"Tokens: {reply.usage.total_tokens}")
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
### Async
|
|
135
|
+
|
|
136
|
+
```python
|
|
137
|
+
import asyncio
|
|
138
|
+
from meshapi import AsyncMeshAPI, ChatCompletionParams, ChatMessage
|
|
139
|
+
|
|
140
|
+
async def main():
|
|
141
|
+
async with AsyncMeshAPI(base_url="https://api.meshapi.ai", token="rsk_...") as client:
|
|
142
|
+
reply = await client.chat.completions.create(
|
|
143
|
+
ChatCompletionParams(
|
|
144
|
+
model="openai/gpt-4o-mini",
|
|
145
|
+
messages=[ChatMessage(role="user", content="Hello!")],
|
|
146
|
+
)
|
|
147
|
+
)
|
|
148
|
+
print(reply.choices[0].message.content)
|
|
149
|
+
|
|
150
|
+
asyncio.run(main())
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
### Streaming
|
|
154
|
+
|
|
155
|
+
`stream()` is a separate method from `create()`. It returns an iterator (sync) or async iterator (async) of chunks.
|
|
156
|
+
|
|
157
|
+
```python
|
|
158
|
+
for chunk in client.chat.completions.stream(
|
|
159
|
+
ChatCompletionParams(
|
|
160
|
+
model="openai/gpt-4o-mini",
|
|
161
|
+
messages=[ChatMessage(role="user", content="Write a haiku about Python.")],
|
|
162
|
+
)
|
|
163
|
+
):
|
|
164
|
+
if chunk.choices and chunk.choices[0].delta:
|
|
165
|
+
print(chunk.choices[0].delta.content or "", end="", flush=True)
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
Async streaming:
|
|
169
|
+
|
|
170
|
+
```python
|
|
171
|
+
async for chunk in client.chat.completions.stream(params):
|
|
172
|
+
if chunk.choices and chunk.choices[0].delta:
|
|
173
|
+
print(chunk.choices[0].delta.content or "", end="", flush=True)
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
### Tool calling
|
|
177
|
+
|
|
178
|
+
```python
|
|
179
|
+
from meshapi import ChatCompletionParams, ChatMessage, Tool, ToolFunction
|
|
180
|
+
|
|
181
|
+
params = ChatCompletionParams(
|
|
182
|
+
model="openai/gpt-4o",
|
|
183
|
+
messages=[ChatMessage(role="user", content="What is the weather in Paris?")],
|
|
184
|
+
tools=[
|
|
185
|
+
Tool(
|
|
186
|
+
type="function",
|
|
187
|
+
function=ToolFunction(
|
|
188
|
+
name="get_weather",
|
|
189
|
+
description="Get current weather for a city",
|
|
190
|
+
parameters={
|
|
191
|
+
"type": "object",
|
|
192
|
+
"properties": {"city": {"type": "string"}},
|
|
193
|
+
"required": ["city"],
|
|
194
|
+
},
|
|
195
|
+
),
|
|
196
|
+
)
|
|
197
|
+
],
|
|
198
|
+
tool_choice="auto",
|
|
199
|
+
)
|
|
200
|
+
|
|
201
|
+
for chunk in client.chat.completions.stream(params):
|
|
202
|
+
delta = chunk.choices[0].delta if chunk.choices else None
|
|
203
|
+
if delta and delta.tool_calls:
|
|
204
|
+
print("tool call:", delta.tool_calls)
|
|
205
|
+
elif delta and delta.content:
|
|
206
|
+
print(delta.content, end="", flush=True)
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
## Responses API (reasoning models)
|
|
210
|
+
|
|
211
|
+
```python
|
|
212
|
+
from meshapi import ResponsesParams
|
|
213
|
+
|
|
214
|
+
reply = client.responses.create(
|
|
215
|
+
ResponsesParams(
|
|
216
|
+
model="openai/o4-mini",
|
|
217
|
+
input="Explain the halting problem in two sentences.",
|
|
218
|
+
reasoning={"effort": "medium"},
|
|
219
|
+
max_output_tokens=512,
|
|
220
|
+
)
|
|
221
|
+
)
|
|
222
|
+
|
|
223
|
+
print(reply.output)
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
Streaming works the same way via `client.responses.stream(params)`.
|
|
227
|
+
|
|
228
|
+
## Embeddings
|
|
229
|
+
|
|
230
|
+
```python
|
|
231
|
+
from meshapi import EmbeddingsParams
|
|
232
|
+
|
|
233
|
+
result = client.embeddings.create(
|
|
234
|
+
EmbeddingsParams(
|
|
235
|
+
model="openai/text-embedding-3-small",
|
|
236
|
+
input=["hello world", "goodbye world"],
|
|
237
|
+
)
|
|
238
|
+
)
|
|
239
|
+
|
|
240
|
+
print(len(result.data[0].embedding))
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
## Compare (multi-model fanout)
|
|
244
|
+
|
|
245
|
+
Fire one prompt at several models and stream their replies in parallel.
|
|
246
|
+
|
|
247
|
+
```python
|
|
248
|
+
from meshapi import CompareParams, ChatMessage
|
|
249
|
+
|
|
250
|
+
stream = client.compare.stream(
|
|
251
|
+
CompareParams(
|
|
252
|
+
models=[
|
|
253
|
+
"openai/gpt-4o-mini",
|
|
254
|
+
"anthropic/claude-sonnet-4.5",
|
|
255
|
+
"google/gemini-2.5-flash",
|
|
256
|
+
],
|
|
257
|
+
messages=[ChatMessage(role="user", content="Summarize this paragraph in one sentence: ...")],
|
|
258
|
+
stream=True,
|
|
259
|
+
)
|
|
260
|
+
)
|
|
261
|
+
|
|
262
|
+
for event in stream:
|
|
263
|
+
if event.event == "delta":
|
|
264
|
+
print(event.data)
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
Use `client.compare.create(params)` for a non-streaming side-by-side response with an optional judge-model comparison via `comparison_model`.
|
|
268
|
+
|
|
269
|
+
## Files and Batches
|
|
270
|
+
|
|
271
|
+
Upload a list of requests, kick off a batch, poll until done. Batch jobs run at discounted pricing.
|
|
272
|
+
|
|
273
|
+
```python
|
|
274
|
+
from meshapi import (
|
|
275
|
+
UploadBatchFileParams,
|
|
276
|
+
BatchRequestItem,
|
|
277
|
+
CreateBatchParams,
|
|
278
|
+
)
|
|
279
|
+
|
|
280
|
+
# 1. Upload the batch input
|
|
281
|
+
file = client.files.upload(
|
|
282
|
+
UploadBatchFileParams(
|
|
283
|
+
purpose="batch",
|
|
284
|
+
requests=[
|
|
285
|
+
BatchRequestItem(
|
|
286
|
+
custom_id="req-1",
|
|
287
|
+
body={
|
|
288
|
+
"model": "openai/gpt-4o-mini",
|
|
289
|
+
"messages": [{"role": "user", "content": "Say hi."}],
|
|
290
|
+
},
|
|
291
|
+
),
|
|
292
|
+
BatchRequestItem(
|
|
293
|
+
custom_id="req-2",
|
|
294
|
+
body={
|
|
295
|
+
"model": "openai/gpt-4o-mini",
|
|
296
|
+
"messages": [{"role": "user", "content": "Say bye."}],
|
|
297
|
+
},
|
|
298
|
+
),
|
|
299
|
+
],
|
|
300
|
+
)
|
|
301
|
+
)
|
|
302
|
+
|
|
303
|
+
# 2. Create the batch
|
|
304
|
+
batch = client.batches.create(
|
|
305
|
+
CreateBatchParams(
|
|
306
|
+
input_file_id=file.id,
|
|
307
|
+
endpoint="/v1/chat/completions",
|
|
308
|
+
completion_window="24h",
|
|
309
|
+
)
|
|
310
|
+
)
|
|
311
|
+
|
|
312
|
+
# 3. Poll later
|
|
313
|
+
status = client.batches.get(batch.id)
|
|
314
|
+
if status.status == "completed" and status.output_file_id:
|
|
315
|
+
output_bytes = client.files.content(status.output_file_id)
|
|
316
|
+
# output_bytes is JSONL
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
## Models
|
|
320
|
+
|
|
321
|
+
```python
|
|
322
|
+
all_models = client.models.list()
|
|
323
|
+
free = client.models.free()
|
|
324
|
+
paid = client.models.paid()
|
|
325
|
+
|
|
326
|
+
for m in paid[:5]:
|
|
327
|
+
print(
|
|
328
|
+
f"{m.id}: prompt ${m.pricing.prompt_usd_per_1k}/1k, "
|
|
329
|
+
f"completion ${m.pricing.completion_usd_per_1k}/1k"
|
|
330
|
+
)
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
Free models (`is_free=True`) cost $0 for both prompt and completion, useful for testing and light tasks. Paid models charge per token against your account balance.
|
|
334
|
+
|
|
335
|
+
## Prompt templates
|
|
336
|
+
|
|
337
|
+
Server-stored prompts with `{{variable}}` interpolation. Reference them by name from `chat.completions` to skip re-sending system prompts every request.
|
|
338
|
+
|
|
339
|
+
```python
|
|
340
|
+
from meshapi import MeshAPI, CreateTemplateParams, ChatCompletionParams, ChatMessage
|
|
341
|
+
|
|
342
|
+
# Manage templates with a control-plane JWT
|
|
343
|
+
ctrl = MeshAPI(base_url="https://api.meshapi.ai", token=supabase_session_jwt)
|
|
344
|
+
|
|
345
|
+
ctrl.templates.create(
|
|
346
|
+
CreateTemplateParams(
|
|
347
|
+
name="support-agent",
|
|
348
|
+
system="You are a support agent for {{company}}. Be concise and friendly.",
|
|
349
|
+
model="openai/gpt-4o-mini",
|
|
350
|
+
variables=["company"],
|
|
351
|
+
)
|
|
352
|
+
)
|
|
353
|
+
|
|
354
|
+
# Use the template with a data-plane rsk_ key
|
|
355
|
+
client = MeshAPI(base_url="https://api.meshapi.ai", token="rsk_...")
|
|
356
|
+
|
|
357
|
+
reply = client.chat.completions.create(
|
|
358
|
+
ChatCompletionParams(
|
|
359
|
+
messages=[ChatMessage(role="user", content="How do I reset my password?")],
|
|
360
|
+
template="support-agent",
|
|
361
|
+
variables={"company": "Acme Corp"},
|
|
362
|
+
)
|
|
363
|
+
)
|
|
364
|
+
```
|
|
365
|
+
|
|
366
|
+
CRUD:
|
|
367
|
+
|
|
368
|
+
```python
|
|
369
|
+
from meshapi import UpdateTemplateParams
|
|
370
|
+
|
|
371
|
+
templates = ctrl.templates.list()
|
|
372
|
+
t = ctrl.templates.get(template_id)
|
|
373
|
+
ctrl.templates.update(template_id, UpdateTemplateParams(model="openai/gpt-4o"))
|
|
374
|
+
ctrl.templates.delete(template_id)
|
|
375
|
+
```
|
|
376
|
+
|
|
377
|
+
## Error handling
|
|
378
|
+
|
|
379
|
+
```python
|
|
380
|
+
from meshapi import MeshAPIError
|
|
381
|
+
|
|
382
|
+
try:
|
|
383
|
+
client.chat.completions.create(params)
|
|
384
|
+
except MeshAPIError as e:
|
|
385
|
+
print(f"[{e.status}] {e.error_code}: {e}")
|
|
386
|
+
print("Request ID:", e.request_id)
|
|
387
|
+
|
|
388
|
+
if e.error_code == "rate_limit_exceeded":
|
|
389
|
+
print(f"Retry after {e.retry_after_seconds}s")
|
|
390
|
+
elif e.error_code == "spend_limit_exceeded":
|
|
391
|
+
print("Account balance exhausted. Top up to continue.")
|
|
392
|
+
elif e.error_code == "unauthorized":
|
|
393
|
+
print("Invalid API key.")
|
|
394
|
+
elif e.error_code == "model_not_found":
|
|
395
|
+
print("Model not supported.")
|
|
396
|
+
elif e.error_code == "upstream_error":
|
|
397
|
+
print("Provider error:", e.provider_error)
|
|
398
|
+
elif e.error_code == "validation_error":
|
|
399
|
+
print("Invalid request:", e.details)
|
|
400
|
+
```
|
|
401
|
+
|
|
402
|
+
| Code | HTTP | Meaning |
|
|
403
|
+
|---|---|---|
|
|
404
|
+
| `unauthorized` | 401 | Invalid or missing key |
|
|
405
|
+
| `forbidden` | 403 | Key suspended |
|
|
406
|
+
| `not_found` / `model_not_found` | 404 | Resource or model not found |
|
|
407
|
+
| `spend_limit_exceeded` | 402 | Account balance at zero. Top up to continue. |
|
|
408
|
+
| `validation_error` / `unprocessable_entity` | 422 | Bad request body |
|
|
409
|
+
| `rate_limit_exceeded` | 429 | RPM or RPD limit hit |
|
|
410
|
+
| `upstream_error` / `gateway_timeout` / `internal_error` | 500 | Upstream or server error |
|
|
411
|
+
| `parse_error` | n/a | SDK could not parse response body |
|
|
412
|
+
| `stream_interrupted` | n/a | Mid-stream connection dropped |
|
|
413
|
+
|
|
414
|
+
Mid-stream errors (sent as SSE frames before `[DONE]`) raise the same `MeshAPIError` from inside the iterator.
|
|
415
|
+
|
|
416
|
+
## Retry and backoff
|
|
417
|
+
|
|
418
|
+
The client automatically retries `GET` and non-streaming `POST` / `PATCH` requests on status codes `429`, `502`, `503`, `504` with exponential backoff (default: 3 retries, base delay 500 ms, max 30 s, with jitter). The `Retry-After` header is respected on 429 responses.
|
|
419
|
+
|
|
420
|
+
```python
|
|
421
|
+
client = MeshAPI(
|
|
422
|
+
base_url="https://api.meshapi.ai",
|
|
423
|
+
token="rsk_...",
|
|
424
|
+
max_retries=5, # 0 to disable
|
|
425
|
+
timeout=30.0,
|
|
426
|
+
)
|
|
427
|
+
```
|
|
428
|
+
|
|
429
|
+
## Streaming failure recovery
|
|
430
|
+
|
|
431
|
+
**Streams do not retry.** If a connection drops mid-stream, a `MeshAPIError` with `error_code="stream_interrupted"` is raised. Catch it and restart a new request:
|
|
432
|
+
|
|
433
|
+
```python
|
|
434
|
+
try:
|
|
435
|
+
for chunk in client.chat.completions.stream(params):
|
|
436
|
+
process(chunk)
|
|
437
|
+
except MeshAPIError as e:
|
|
438
|
+
if e.error_code == "stream_interrupted":
|
|
439
|
+
# restart from scratch
|
|
440
|
+
...
|
|
441
|
+
```
|
|
442
|
+
|
|
443
|
+
## Type hints
|
|
444
|
+
|
|
445
|
+
Every request and response is a Pydantic v2 model. Import what you need:
|
|
446
|
+
|
|
447
|
+
```python
|
|
448
|
+
from meshapi import (
|
|
449
|
+
MeshAPI,
|
|
450
|
+
AsyncMeshAPI,
|
|
451
|
+
MeshAPIConfig,
|
|
452
|
+
MeshAPIError,
|
|
453
|
+
# chat
|
|
454
|
+
ChatCompletionParams,
|
|
455
|
+
ChatCompletionResponse,
|
|
456
|
+
ChatCompletionChunk,
|
|
457
|
+
ChatMessage,
|
|
458
|
+
Tool,
|
|
459
|
+
ToolFunction,
|
|
460
|
+
ToolCall,
|
|
461
|
+
# responses
|
|
462
|
+
ResponsesParams,
|
|
463
|
+
ResponsesResponse,
|
|
464
|
+
# embeddings
|
|
465
|
+
EmbeddingsParams,
|
|
466
|
+
EmbeddingsResponse,
|
|
467
|
+
# compare
|
|
468
|
+
CompareParams,
|
|
469
|
+
CompareStreamEvent,
|
|
470
|
+
# batches + files
|
|
471
|
+
UploadBatchFileParams,
|
|
472
|
+
BatchRequestItem,
|
|
473
|
+
CreateBatchParams,
|
|
474
|
+
BatchObject,
|
|
475
|
+
FileObject,
|
|
476
|
+
# models
|
|
477
|
+
ModelInfo,
|
|
478
|
+
ModelPricing,
|
|
479
|
+
# templates
|
|
480
|
+
CreateTemplateParams,
|
|
481
|
+
UpdateTemplateParams,
|
|
482
|
+
TemplateSummary,
|
|
483
|
+
)
|
|
484
|
+
```
|
|
485
|
+
|
|
486
|
+
## Versioning
|
|
487
|
+
|
|
488
|
+
This SDK follows [SemVer 2.0](https://semver.org/). Pre-1.0 releases may have breaking changes between minor versions.
|
|
489
|
+
|
|
490
|
+
```python
|
|
491
|
+
import meshapi
|
|
492
|
+
print(meshapi.__version__) # "0.1.0"
|
|
493
|
+
```
|
|
494
|
+
|
|
495
|
+
## About Mesh API
|
|
496
|
+
|
|
497
|
+
[Mesh API](https://meshapi.ai) is an AI model gateway that gives you instant access to a massive variety of LLMs through a single, unified API. Enjoy the developer experience you already know, upgraded with universal model access.
|
|
498
|
+
|
|
499
|
+
| | |
|
|
500
|
+
|---|---|
|
|
501
|
+
| **One Universal API** | A single `chat.completions.create` request works across 300+ base models. |
|
|
502
|
+
| **Unified Billing** | Deposit funds into one account and consume any model. No juggling provider subscriptions. |
|
|
503
|
+
| **Free Tier** | Free models (`is_free=True`) cost $0 for both prompt and completion. Test and ship light workloads without funding. |
|
|
504
|
+
| **Provider Fallbacks** | If a model or provider goes down, the gateway routes to another supported model so your inference stays up. |
|
|
505
|
+
| **Built-in Rate Limiting** | Robust per-key limits prevent runaway costs. |
|
|
506
|
+
| **Prompt Templates** | Manage, version and share prompts via a secure templating system. |
|
|
507
|
+
|
|
508
|
+
Documentation lives at [developers.meshapi.ai](https://developers.meshapi.ai).
|
|
509
|
+
|
|
510
|
+
Built by the founders of [TagMango](https://tagmango.com) (YC W20) and [AI Fiesta](https://aifiesta.ai) (1M+ users).
|
|
511
|
+
|
|
512
|
+
## Related
|
|
513
|
+
|
|
514
|
+
- [`meshapi-node-sdk`](https://github.com/aifiesta/meshapi-node-sdk): official TypeScript SDK with the same surface
|
|
515
|
+
- [`meshapi-code`](https://github.com/aifiesta/meshapi-code): terminal chat REPL with tool calling
|
|
516
|
+
|
|
517
|
+
## License
|
|
518
|
+
|
|
519
|
+
[MIT](LICENSE)
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
meshapi/__init__.py,sha256=KhsGNxDxtLe6RTZ5m6UGIOXSMAOibQsqqoIRZPzq0Vo,6734
|
|
2
|
+
meshapi/_errors.py,sha256=JPqUyteKbqWaWF2tIognpEmj-y_cuhXF_Z7rRtLxwNw,3001
|
|
3
|
+
meshapi/_http.py,sha256=erMcpjLE-u4jl6ecB-FU_i0zJO-0bFhH86LPyQAMAHY,15341
|
|
4
|
+
meshapi/_types.py,sha256=HJP4V1a07stX0GHME_XUI-YbMrx1ywVP3dKpZau6nW0,16981
|
|
5
|
+
meshapi/resources/__init__.py,sha256=CRcYDTGDql0LqcJyWH1zHmglD3W6L8_Sm3w48XgzNJs,20
|
|
6
|
+
meshapi/resources/batches.py,sha256=E0Z9clWVIjqclnHnvPE4aoEVlLx35UYXJ79Uo7hzcvU,2274
|
|
7
|
+
meshapi/resources/chat.py,sha256=EX5yVMrHbU3aip7fILmjl_f9argAgT-L9hC3qeN2M5k,2389
|
|
8
|
+
meshapi/resources/compare.py,sha256=8EoCT_h1GsVmbKlG_tAEvpoZxxP-yb_qZ7AcSFenvuU,1691
|
|
9
|
+
meshapi/resources/embeddings.py,sha256=QiJTZnm20YxvZCpfCcY3H8IZx5GHkb1Rg7Pog0zxVrc,858
|
|
10
|
+
meshapi/resources/files.py,sha256=kyhBGsnhzFm8_Lm7D-xWYEumsQt1bryAlk525EdaKyQ,1582
|
|
11
|
+
meshapi/resources/models.py,sha256=oW5M1hNnTxR5VDN8Gs635_VQukzEudYWCAkHwYCv42Y,1709
|
|
12
|
+
meshapi/resources/responses.py,sha256=yeBo8GzGNkpcdl5Zh-1HJK-NDXFWkhxSDyAC_M4-ncE,1716
|
|
13
|
+
meshapi/resources/templates.py,sha256=TCFiKTrFEbW1LJ_l1oQdsskUFI_DPaEFv78NrMa7G5o,2367
|
|
14
|
+
meshapi-0.1.0.dist-info/METADATA,sha256=PQ4ncmVDhQQnlQZR4HLrS4WkPf8nt53noQLQHytOkzg,17018
|
|
15
|
+
meshapi-0.1.0.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
|
|
16
|
+
meshapi-0.1.0.dist-info/RECORD,,
|