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/_types.py ADDED
@@ -0,0 +1,573 @@
1
+ """Domain types for MeshAPI SDK — all Pydantic v2 models."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Any, Dict, List, Literal, Optional, Union
6
+
7
+ from pydantic import BaseModel, ConfigDict, Field
8
+
9
+ # ---------------------------------------------------------------------------
10
+ # Shared
11
+ # ---------------------------------------------------------------------------
12
+
13
+ ChatRole = Literal["system", "user", "assistant", "tool"]
14
+
15
+
16
+ class ImageDetail(BaseModel):
17
+ model_config = ConfigDict(extra="ignore")
18
+ url: str
19
+ detail: Optional[Literal["auto", "low", "high"]] = None
20
+
21
+
22
+ class ContentPartText(BaseModel):
23
+ model_config = ConfigDict(extra="ignore")
24
+ type: Literal["text"]
25
+ text: str
26
+
27
+
28
+ class ContentPartImage(BaseModel):
29
+ model_config = ConfigDict(extra="ignore")
30
+ type: Literal["image_url"]
31
+ image_url: ImageDetail
32
+
33
+
34
+ class InputAudio(BaseModel):
35
+ model_config = ConfigDict(extra="ignore")
36
+ data: str
37
+ format: Literal["wav", "mp3", "aiff", "aac", "ogg", "flac", "m4a", "pcm16", "pcm24"]
38
+
39
+
40
+ class ContentPartAudio(BaseModel):
41
+ model_config = ConfigDict(extra="ignore")
42
+ type: Literal["input_audio"]
43
+ input_audio: InputAudio
44
+
45
+
46
+ ContentPart = Union[ContentPartText, ContentPartImage, ContentPartAudio]
47
+
48
+
49
+ class ToolCallFunction(BaseModel):
50
+ model_config = ConfigDict(extra="ignore")
51
+ name: str
52
+ arguments: str # JSON-encoded string
53
+
54
+
55
+ class ToolCall(BaseModel):
56
+ model_config = ConfigDict(extra="ignore")
57
+ id: str
58
+ type: Literal["function"]
59
+ function: ToolCallFunction
60
+
61
+
62
+ class ChatMessage(BaseModel):
63
+ model_config = ConfigDict(extra="ignore")
64
+ role: ChatRole
65
+ content: Optional[Union[str, List[ContentPart]]] = None
66
+ name: Optional[str] = None
67
+ tool_call_id: Optional[str] = None
68
+ tool_calls: Optional[List[ToolCall]] = None
69
+
70
+
71
+ class ToolFunction(BaseModel):
72
+ model_config = ConfigDict(extra="ignore")
73
+ name: str
74
+ description: Optional[str] = None
75
+ parameters: Optional[Dict[str, Any]] = None
76
+
77
+
78
+ class Tool(BaseModel):
79
+ model_config = ConfigDict(extra="ignore")
80
+ type: Literal["function"]
81
+ function: ToolFunction
82
+
83
+
84
+ class ToolChoiceFunction(BaseModel):
85
+ model_config = ConfigDict(extra="ignore")
86
+ name: str
87
+
88
+
89
+ class ToolChoiceObject(BaseModel):
90
+ model_config = ConfigDict(extra="ignore")
91
+ type: Literal["function"]
92
+ function: ToolChoiceFunction
93
+
94
+
95
+ ToolChoice = Union[Literal["auto", "none", "required"], ToolChoiceObject]
96
+
97
+
98
+ class AudioOutputOptions(BaseModel):
99
+ model_config = ConfigDict(extra="ignore")
100
+ voice: str = "alloy"
101
+ format: Literal["wav", "mp3", "flac", "opus", "pcm16"] = "wav"
102
+
103
+
104
+ class ImageOptions(BaseModel):
105
+ model_config = ConfigDict(extra="ignore")
106
+ n: int = Field(default=1, ge=1, le=10)
107
+ size: str = "1024x1024"
108
+ quality: str = "high"
109
+ response_format: Literal["url", "b64_json"] = "url"
110
+
111
+
112
+ # ---------------------------------------------------------------------------
113
+ # Chat Completions
114
+ # ---------------------------------------------------------------------------
115
+
116
+
117
+ class ChatCompletionParams(BaseModel):
118
+ """Request body for POST /v1/chat/completions."""
119
+
120
+ model_config = ConfigDict(extra="ignore")
121
+
122
+ messages: List[ChatMessage]
123
+ model: Optional[str] = None
124
+ stream: Optional[bool] = None
125
+
126
+ # MeshAPI extensions
127
+ template: Optional[str] = None
128
+ variables: Optional[Dict[str, str]] = None
129
+ session_id: Optional[str] = None
130
+
131
+ # Inference params
132
+ temperature: Optional[float] = None
133
+ max_tokens: Optional[int] = None
134
+ top_p: Optional[float] = None
135
+ frequency_penalty: Optional[float] = None
136
+ presence_penalty: Optional[float] = None
137
+ stop: Optional[Union[str, List[str]]] = None
138
+ seed: Optional[int] = None
139
+ tools: Optional[List[Tool]] = None
140
+ tool_choice: Optional[ToolChoice] = None
141
+
142
+ # OpenRouter-specific
143
+ transforms: Optional[List[str]] = None
144
+ models: Optional[List[str]] = None
145
+
146
+ user: Optional[str] = None
147
+ modality: Optional[Literal["text", "image"]] = None
148
+ image: Optional[ImageOptions] = None
149
+ async_mode: Optional[bool] = None
150
+ modalities: Optional[List[Literal["text", "audio"]]] = None
151
+ audio: Optional[AudioOutputOptions] = None
152
+
153
+
154
+ class UsageInfo(BaseModel):
155
+ model_config = ConfigDict(extra="ignore")
156
+ prompt_tokens: int
157
+ completion_tokens: int
158
+ total_tokens: int
159
+ prompt_tokens_details: Optional[Dict[str, Any]] = None
160
+ completion_tokens_details: Optional[Dict[str, Any]] = None
161
+ classifier_prompt_tokens: Optional[int] = None
162
+ classifier_completion_tokens: Optional[int] = None
163
+ classifier_tokens: Optional[int] = None
164
+
165
+
166
+ class ChatCompletionMessage(BaseModel):
167
+ model_config = ConfigDict(extra="ignore")
168
+ role: str
169
+ content: Optional[Union[str, List[ContentPart]]] = None
170
+ tool_calls: Optional[List[ToolCall]] = None
171
+ audio: Optional[Dict[str, Any]] = None
172
+
173
+
174
+ class ChatCompletionChoice(BaseModel):
175
+ model_config = ConfigDict(extra="ignore")
176
+ index: int
177
+ message: Optional[ChatCompletionMessage] = None
178
+ finish_reason: Optional[str] = None
179
+ logprobs: Optional[Any] = None
180
+
181
+
182
+ class ChatCompletionResponse(BaseModel):
183
+ model_config = ConfigDict(extra="ignore")
184
+ id: str
185
+ object: str
186
+ created: int
187
+ model: str
188
+ choices: List[ChatCompletionChoice]
189
+ usage: Optional[UsageInfo] = None
190
+ system_fingerprint: Optional[str] = None
191
+
192
+
193
+ class ChatCompletionChunkDelta(BaseModel):
194
+ model_config = ConfigDict(extra="ignore")
195
+ role: Optional[str] = None
196
+ content: Optional[str] = None
197
+ tool_calls: Optional[List[ToolCall]] = None
198
+ audio: Optional[Dict[str, Any]] = None
199
+
200
+
201
+ class ChatCompletionChunkChoice(BaseModel):
202
+ model_config = ConfigDict(extra="ignore")
203
+ index: int
204
+ delta: Optional[ChatCompletionChunkDelta] = None
205
+ finish_reason: Optional[str] = None
206
+
207
+
208
+ class ChatCompletionChunk(BaseModel):
209
+ model_config = ConfigDict(extra="ignore")
210
+ id: str
211
+ object: str
212
+ created: int
213
+ model: str
214
+ choices: List[ChatCompletionChunkChoice]
215
+ usage: Optional[UsageInfo] = None
216
+ cost: Optional[str] = None
217
+
218
+
219
+ # ---------------------------------------------------------------------------
220
+ # Models
221
+ # ---------------------------------------------------------------------------
222
+
223
+
224
+ class ModelPricing(BaseModel):
225
+ model_config = ConfigDict(extra="ignore")
226
+ prompt_usd_per_1k: Optional[str] = None
227
+ completion_usd_per_1k: Optional[str] = None
228
+ image_usd_per_image: Optional[str] = None
229
+ discount_pct: Optional[str] = None
230
+ prompt_usd_per_1k_discounted: Optional[str] = None
231
+ completion_usd_per_1k_discounted: Optional[str] = None
232
+
233
+
234
+ class ModelInfo(BaseModel):
235
+ model_config = ConfigDict(extra="ignore")
236
+ id: str
237
+ name: str
238
+ context_length: Optional[int] = None
239
+ is_free: bool
240
+ pricing: Optional[ModelPricing] = None
241
+ description: Optional[str] = None
242
+ supports_thinking: Optional[bool] = None
243
+ supports_completions_api: Optional[bool] = None
244
+ supports_responses_api: Optional[bool] = None
245
+ model_type: Optional[str] = None
246
+ input_modalities: Optional[List[str]] = None
247
+ output_modalities: Optional[List[str]] = None
248
+
249
+
250
+ class ListModelsParams(BaseModel):
251
+ model_config = ConfigDict(extra="ignore")
252
+ free: Optional[bool] = None
253
+
254
+
255
+ # ---------------------------------------------------------------------------
256
+ # Templates
257
+ # ---------------------------------------------------------------------------
258
+
259
+
260
+ class TemplateMessage(BaseModel):
261
+ model_config = ConfigDict(extra="ignore")
262
+ role: str
263
+ content: str
264
+
265
+
266
+ class CreateTemplateParams(BaseModel):
267
+ model_config = ConfigDict(extra="ignore")
268
+ name: str
269
+ description: Optional[str] = None
270
+ system: Optional[str] = None
271
+ messages: Optional[List[Dict[str, Any]]] = None
272
+ model: Optional[str] = None
273
+ params: Optional[Dict[str, Any]] = None
274
+ variables: Optional[List[str]] = None
275
+
276
+
277
+ class UpdateTemplateParams(BaseModel):
278
+ model_config = ConfigDict(extra="ignore")
279
+ name: Optional[str] = None
280
+ description: Optional[str] = None
281
+ system: Optional[str] = None
282
+ messages: Optional[List[Dict[str, Any]]] = None
283
+ model: Optional[str] = None
284
+ params: Optional[Dict[str, Any]] = None
285
+ variables: Optional[List[str]] = None
286
+
287
+
288
+ class TemplateSummary(BaseModel):
289
+ model_config = ConfigDict(extra="ignore")
290
+ id: str
291
+ name: str
292
+ owner: Optional[str] = None
293
+ is_global: bool
294
+ description: Optional[str] = None
295
+ system: Optional[str] = None
296
+ messages: Optional[List[Dict[str, Any]]] = None
297
+ model: Optional[str] = None
298
+ params: Optional[Dict[str, Any]] = None
299
+ variables: Optional[List[str]] = None
300
+ created_at: str
301
+ updated_at: str
302
+
303
+
304
+ # ---------------------------------------------------------------------------
305
+ # Embeddings
306
+ # ---------------------------------------------------------------------------
307
+
308
+
309
+ class ProviderPreferences(BaseModel):
310
+ model_config = ConfigDict(extra="ignore")
311
+ order: Optional[List[str]] = None
312
+ allow_fallbacks: Optional[bool] = None
313
+ require_parameters: Optional[bool] = None
314
+ data_collection: Optional[Literal["allow", "deny"]] = None
315
+
316
+
317
+ class EmbeddingsParams(BaseModel):
318
+ model_config = ConfigDict(extra="ignore")
319
+ model: Optional[str] = None
320
+ input: Union[str, List[str], List[int], List[List[int]]]
321
+ dimensions: Optional[int] = Field(default=None, ge=1)
322
+ encoding_format: Optional[Literal["float", "base64"]] = None
323
+ input_type: Optional[str] = None
324
+ provider: Optional[Union[str, ProviderPreferences]] = None
325
+ user: Optional[str] = Field(default=None, max_length=256)
326
+
327
+
328
+ class EmbeddingItem(BaseModel):
329
+ model_config = ConfigDict(extra="ignore")
330
+ object: str
331
+ index: int
332
+ embedding: Union[List[float], str]
333
+
334
+
335
+ class EmbeddingsUsage(BaseModel):
336
+ model_config = ConfigDict(extra="ignore")
337
+ prompt_tokens: int
338
+ total_tokens: int
339
+
340
+
341
+ class EmbeddingsResponse(BaseModel):
342
+ model_config = ConfigDict(extra="ignore")
343
+ object: str
344
+ data: List[EmbeddingItem]
345
+ model: str
346
+ usage: Optional[EmbeddingsUsage] = None
347
+
348
+
349
+ # ---------------------------------------------------------------------------
350
+ # Responses
351
+ # ---------------------------------------------------------------------------
352
+
353
+
354
+ class BuiltinTool(BaseModel):
355
+ model_config = ConfigDict(extra="allow")
356
+ type: Literal[
357
+ "image_generation",
358
+ "web_search_preview",
359
+ "web_search_preview_2025_03_11",
360
+ "file_search",
361
+ "computer_use_preview",
362
+ "code_interpreter",
363
+ ]
364
+
365
+
366
+ class ResponsesFunctionTool(BaseModel):
367
+ model_config = ConfigDict(extra="ignore")
368
+ type: Literal["function"] = "function"
369
+ name: str
370
+ description: Optional[str] = None
371
+ parameters: Optional[Dict[str, Any]] = None
372
+ strict: Optional[bool] = None
373
+
374
+
375
+ class ResponsesParams(BaseModel):
376
+ model_config = ConfigDict(extra="ignore")
377
+ model: Optional[str] = None
378
+ input: Union[str, List[Any]]
379
+ template: Optional[str] = None
380
+ variables: Optional[Dict[str, str]] = None
381
+ session_id: Optional[str] = None
382
+ stream: bool = False
383
+ max_output_tokens: Optional[int] = Field(default=None, ge=1)
384
+ temperature: Optional[float] = Field(default=None, ge=0.0, le=2.0)
385
+ top_p: Optional[float] = Field(default=None, ge=0.0, le=1.0)
386
+ seed: Optional[int] = None
387
+ reasoning: Optional[Dict[str, Any]] = None
388
+ tools: Optional[List[Union[ResponsesFunctionTool, BuiltinTool]]] = None
389
+ tool_choice: Optional[Union[str, Dict[str, Any]]] = None
390
+ response_format: Optional[Dict[str, Any]] = None
391
+ plugins: Optional[List[Any]] = None
392
+ user: Optional[str] = Field(default=None, max_length=256)
393
+
394
+
395
+ class ResponsesUsage(BaseModel):
396
+ model_config = ConfigDict(extra="ignore")
397
+ input_tokens: Optional[int] = None
398
+ output_tokens: Optional[int] = None
399
+ total_tokens: Optional[int] = None
400
+ prompt_tokens: Optional[int] = None
401
+ completion_tokens: Optional[int] = None
402
+ prompt_tokens_details: Optional[Dict[str, Any]] = None
403
+ completion_tokens_details: Optional[Dict[str, Any]] = None
404
+ classifier_tokens: Optional[int] = None
405
+
406
+
407
+ class ResponsesResponse(BaseModel):
408
+ model_config = ConfigDict(extra="allow")
409
+ id: Optional[str] = None
410
+ object: Optional[str] = None
411
+ model: Optional[str] = None
412
+ output: Optional[List[Any]] = None
413
+ usage: Optional[ResponsesUsage] = None
414
+ status: Optional[str] = None
415
+
416
+
417
+ class ResponsesStreamEvent(BaseModel):
418
+ model_config = ConfigDict(extra="allow")
419
+ type: Optional[str] = None
420
+ response: Optional[Dict[str, Any]] = None
421
+ usage: Optional[ResponsesUsage] = None
422
+
423
+
424
+ # ---------------------------------------------------------------------------
425
+ # Compare
426
+ # ---------------------------------------------------------------------------
427
+
428
+
429
+ class ModelOverride(BaseModel):
430
+ model_config = ConfigDict(extra="ignore")
431
+ model: str
432
+ temperature: Optional[float] = Field(default=None, ge=0.0, le=2.0)
433
+ max_tokens: Optional[int] = Field(default=None, ge=1)
434
+ system_prompt: Optional[str] = None
435
+
436
+
437
+ class CompareParams(BaseModel):
438
+ model_config = ConfigDict(extra="ignore")
439
+ models: List[str]
440
+ messages: List[ChatMessage]
441
+ model_overrides: Optional[List[ModelOverride]] = None
442
+ comparison_model: Optional[str] = None
443
+ comparison_instructions: Optional[str] = None
444
+ temperature: Optional[float] = Field(default=None, ge=0.0, le=2.0)
445
+ max_tokens: Optional[int] = Field(default=None, ge=1)
446
+ stream: bool = False
447
+ template: Optional[str] = None
448
+ variables: Optional[Dict[str, str]] = None
449
+ skip_comparison: bool = False
450
+
451
+
452
+ class TokenUsage(BaseModel):
453
+ model_config = ConfigDict(extra="ignore")
454
+ prompt_tokens: Optional[int] = None
455
+ completion_tokens: Optional[int] = None
456
+ total_tokens: Optional[int] = None
457
+
458
+
459
+ class ModelCompareResult(BaseModel):
460
+ model_config = ConfigDict(extra="ignore")
461
+ model: str
462
+ response_body: Optional[Dict[str, Any]] = None
463
+ content: Optional[str] = None
464
+ latency_ms: int
465
+ error: Optional[str] = None
466
+ error_code: Optional[str] = None
467
+ usage: Optional[TokenUsage] = None
468
+ request_id: str
469
+
470
+
471
+ class CompareResponse(BaseModel):
472
+ model_config = ConfigDict(extra="ignore")
473
+ comparison_id: str
474
+ object: str
475
+ created: int
476
+ models: List[str]
477
+ results: List[ModelCompareResult]
478
+ comparison: Optional[str] = None
479
+ comparison_model: Optional[str] = None
480
+ comparison_usage: Optional[TokenUsage] = None
481
+ comparison_fallback_used: bool = False
482
+ total_latency_ms: int
483
+ partial: bool = False
484
+ skip_comparison: bool = False
485
+
486
+
487
+ class CompareStreamEvent(BaseModel):
488
+ model_config = ConfigDict(extra="allow")
489
+ event: Optional[str] = None
490
+ data: Optional[Dict[str, Any]] = None
491
+
492
+
493
+ # ---------------------------------------------------------------------------
494
+ # Files / Batches
495
+ # ---------------------------------------------------------------------------
496
+
497
+
498
+ class BatchRequestItem(BaseModel):
499
+ model_config = ConfigDict(extra="ignore")
500
+ custom_id: str
501
+ method: str = "POST"
502
+ url: str = "/v1/chat/completions"
503
+ body: Dict[str, Any]
504
+
505
+
506
+ class UploadBatchFileParams(BaseModel):
507
+ model_config = ConfigDict(extra="ignore")
508
+ purpose: str = "batch"
509
+ requests: List[BatchRequestItem]
510
+
511
+
512
+ class FileObject(BaseModel):
513
+ model_config = ConfigDict(extra="allow")
514
+ id: str
515
+ object: Optional[str] = None
516
+ bytes: Optional[int] = None
517
+ created_at: Optional[int] = None
518
+ filename: Optional[str] = None
519
+ purpose: Optional[str] = None
520
+ status: Optional[str] = None
521
+ status_details: Optional[Any] = None
522
+
523
+
524
+ class CreateBatchParams(BaseModel):
525
+ model_config = ConfigDict(extra="ignore")
526
+ input_file_id: str
527
+ endpoint: str
528
+ completion_window: str
529
+ metadata: Optional[Dict[str, Any]] = None
530
+
531
+
532
+ class BatchObject(BaseModel):
533
+ model_config = ConfigDict(extra="allow")
534
+ id: str
535
+ object: Optional[str] = None
536
+ endpoint: Optional[str] = None
537
+ input_file_id: Optional[str] = None
538
+ output_file_id: Optional[str] = None
539
+ status: Optional[str] = None
540
+ model: Optional[str] = None
541
+ provider: Optional[str] = None
542
+ created_at: Optional[int] = None
543
+ completed_at: Optional[int] = None
544
+ usage_synced: Optional[bool] = None
545
+
546
+
547
+ class BatchListResponse(BaseModel):
548
+ model_config = ConfigDict(extra="allow")
549
+ object: str
550
+ data: List[BatchObject]
551
+ has_more: bool
552
+ first_id: Optional[str] = None
553
+ last_id: Optional[str] = None
554
+
555
+
556
+ # ---------------------------------------------------------------------------
557
+ # Error wire format
558
+ # ---------------------------------------------------------------------------
559
+
560
+
561
+ class ApiErrorBody(BaseModel):
562
+ model_config = ConfigDict(extra="ignore")
563
+ code: str
564
+ message: str
565
+ details: Optional[List[Any]] = None
566
+ provider_error: Optional[Dict[str, Any]] = None
567
+ retry_after_seconds: Optional[int] = None
568
+
569
+
570
+ class ApiErrorEnvelope(BaseModel):
571
+ model_config = ConfigDict(extra="ignore")
572
+ error: ApiErrorBody
573
+ request_id: str
@@ -0,0 +1 @@
1
+ # resources package
@@ -0,0 +1,60 @@
1
+ """Batch resource — /v1/batches endpoints."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from .._http import AsyncHttpClient, SyncHttpClient
6
+ from .._types import BatchListResponse, BatchObject, CreateBatchParams
7
+
8
+
9
+ class BatchesResource:
10
+ def __init__(self, http: SyncHttpClient) -> None:
11
+ self._http = http
12
+
13
+ def create(self, params: CreateBatchParams) -> BatchObject:
14
+ data = self._http.post("/v1/batches", params.model_dump(exclude_none=True))
15
+ return BatchObject.model_validate(data)
16
+
17
+ def list(self, *, after: str | None = None, limit: int | None = None) -> BatchListResponse:
18
+ query = {}
19
+ if after is not None:
20
+ query["after"] = after
21
+ if limit is not None:
22
+ query["limit"] = str(limit)
23
+ data = self._http.get("/v1/batches", params=query or None)
24
+ return BatchListResponse.model_validate(data)
25
+
26
+ def get(self, batch_id: str) -> BatchObject:
27
+ data = self._http.get(f"/v1/batches/{batch_id}")
28
+ return BatchObject.model_validate(data)
29
+
30
+ def cancel(self, batch_id: str) -> BatchObject:
31
+ data = self._http.post(f"/v1/batches/{batch_id}/cancel", {})
32
+ return BatchObject.model_validate(data)
33
+
34
+
35
+ class AsyncBatchesResource:
36
+ def __init__(self, http: AsyncHttpClient) -> None:
37
+ self._http = http
38
+
39
+ async def create(self, params: CreateBatchParams) -> BatchObject:
40
+ data = await self._http.post("/v1/batches", params.model_dump(exclude_none=True))
41
+ return BatchObject.model_validate(data)
42
+
43
+ async def list(
44
+ self, *, after: str | None = None, limit: int | None = None
45
+ ) -> BatchListResponse:
46
+ query = {}
47
+ if after is not None:
48
+ query["after"] = after
49
+ if limit is not None:
50
+ query["limit"] = str(limit)
51
+ data = await self._http.get("/v1/batches", params=query or None)
52
+ return BatchListResponse.model_validate(data)
53
+
54
+ async def get(self, batch_id: str) -> BatchObject:
55
+ data = await self._http.get(f"/v1/batches/{batch_id}")
56
+ return BatchObject.model_validate(data)
57
+
58
+ async def cancel(self, batch_id: str) -> BatchObject:
59
+ data = await self._http.post(f"/v1/batches/{batch_id}/cancel", {})
60
+ return BatchObject.model_validate(data)
@@ -0,0 +1,63 @@
1
+ """Chat completions resource — POST /v1/chat/completions."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import AsyncIterator, Iterator
6
+
7
+ from .._http import AsyncHttpClient, SyncHttpClient
8
+ from .._types import ChatCompletionChunk, ChatCompletionParams, ChatCompletionResponse
9
+
10
+
11
+ class CompletionsResource:
12
+ def __init__(self, http: SyncHttpClient) -> None:
13
+ self._http = http
14
+
15
+ def create(self, params: ChatCompletionParams) -> ChatCompletionResponse:
16
+ """Non-streaming completion. Returns the full response."""
17
+ body = params.model_dump(exclude_none=True)
18
+ body["stream"] = False
19
+ data = self._http.post("/v1/chat/completions", body)
20
+ return ChatCompletionResponse.model_validate(data)
21
+
22
+ def stream(self, params: ChatCompletionParams) -> Iterator[ChatCompletionChunk]:
23
+ """Streaming completion. Returns an iterator of SSE chunks.
24
+
25
+ Streams do NOT retry on failure. Catch MeshAPIApiError and
26
+ restart a new request if reconnection is needed.
27
+ """
28
+ body = params.model_dump(exclude_none=True)
29
+ body["stream"] = True
30
+ yield from self._http.stream("/v1/chat/completions", body)
31
+
32
+
33
+ class ChatResource:
34
+ def __init__(self, http: SyncHttpClient) -> None:
35
+ self.completions = CompletionsResource(http)
36
+
37
+
38
+ class AsyncCompletionsResource:
39
+ def __init__(self, http: AsyncHttpClient) -> None:
40
+ self._http = http
41
+
42
+ async def create(self, params: ChatCompletionParams) -> ChatCompletionResponse:
43
+ """Non-streaming completion."""
44
+ body = params.model_dump(exclude_none=True)
45
+ body["stream"] = False
46
+ data = await self._http.post("/v1/chat/completions", body)
47
+ return ChatCompletionResponse.model_validate(data)
48
+
49
+ async def stream(self, params: ChatCompletionParams) -> AsyncIterator[ChatCompletionChunk]:
50
+ """Streaming completion. Returns an async iterator of SSE chunks.
51
+
52
+ Streams do NOT retry on failure. Catch MeshAPIApiError and
53
+ restart a new request if reconnection is needed.
54
+ """
55
+ body = params.model_dump(exclude_none=True)
56
+ body["stream"] = True
57
+ async for chunk in self._http.stream("/v1/chat/completions", body):
58
+ yield chunk
59
+
60
+
61
+ class AsyncChatResource:
62
+ def __init__(self, http: AsyncHttpClient) -> None:
63
+ self.completions = AsyncCompletionsResource(http)