universal-mcp 0.1.8rc4__py3-none-any.whl → 0.1.9rc1__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.
Files changed (38) hide show
  1. universal_mcp/applications/__init__.py +7 -2
  2. universal_mcp/applications/application.py +162 -114
  3. universal_mcp/applications/cal_com_v2/README.md +175 -0
  4. universal_mcp/applications/cal_com_v2/__init__.py +0 -0
  5. universal_mcp/applications/cal_com_v2/app.py +4735 -0
  6. universal_mcp/applications/clickup/README.md +160 -0
  7. universal_mcp/applications/clickup/__init__.py +0 -0
  8. universal_mcp/applications/clickup/app.py +4359 -0
  9. universal_mcp/applications/hashnode/app.py +77 -0
  10. universal_mcp/applications/hashnode/prompt.md +21 -0
  11. universal_mcp/applications/mailchimp/README.md +306 -0
  12. universal_mcp/applications/mailchimp/__init__.py +0 -0
  13. universal_mcp/applications/mailchimp/app.py +8883 -0
  14. universal_mcp/applications/markitdown/app.py +2 -2
  15. universal_mcp/applications/perplexity/app.py +0 -1
  16. universal_mcp/applications/replicate/README.md +53 -0
  17. universal_mcp/applications/replicate/app.py +969 -0
  18. universal_mcp/applications/retell_ai/README.md +46 -0
  19. universal_mcp/applications/retell_ai/__init__.py +0 -0
  20. universal_mcp/applications/retell_ai/app.py +316 -0
  21. universal_mcp/applications/rocketlane/README.md +42 -0
  22. universal_mcp/applications/rocketlane/__init__.py +0 -0
  23. universal_mcp/applications/rocketlane/app.py +180 -0
  24. universal_mcp/applications/spotify/README.md +116 -0
  25. universal_mcp/applications/spotify/__init__.py +0 -0
  26. universal_mcp/applications/spotify/app.py +2231 -0
  27. universal_mcp/applications/supabase/README.md +112 -0
  28. universal_mcp/applications/supabase/__init__.py +0 -0
  29. universal_mcp/applications/supabase/app.py +2644 -0
  30. universal_mcp/integrations/integration.py +1 -1
  31. universal_mcp/servers/server.py +3 -3
  32. universal_mcp/stores/store.py +7 -0
  33. universal_mcp/tools/tools.py +2 -2
  34. universal_mcp/utils/docstring_parser.py +171 -104
  35. {universal_mcp-0.1.8rc4.dist-info → universal_mcp-0.1.9rc1.dist-info}/METADATA +2 -1
  36. {universal_mcp-0.1.8rc4.dist-info → universal_mcp-0.1.9rc1.dist-info}/RECORD +38 -13
  37. {universal_mcp-0.1.8rc4.dist-info → universal_mcp-0.1.9rc1.dist-info}/WHEEL +0 -0
  38. {universal_mcp-0.1.8rc4.dist-info → universal_mcp-0.1.9rc1.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,969 @@
1
+ from typing import Any, Literal
2
+
3
+ from loguru import logger
4
+
5
+ # Import the base APIApplication class
6
+ from universal_mcp.applications.application import APIApplication
7
+
8
+ # Import the Integration class
9
+ from universal_mcp.integrations import Integration
10
+
11
+
12
+ class ReplicateApp(APIApplication):
13
+ """
14
+ Application for interacting with the Replicate HTTP API.
15
+
16
+ Exposes Replicate operations as tools, allowing interaction with models,
17
+ predictions, trainings, deployments, etc.
18
+ """
19
+
20
+ def __init__(self, integration: Integration = None) -> None:
21
+ """
22
+ Initializes the ReplicateApp.
23
+
24
+ Args:
25
+ integration: The integration object providing authentication credentials.
26
+ Expected to provide a Replicate API token as 'api_key'.
27
+ """
28
+ # Call the parent constructor with the application name and integration
29
+ super().__init__(name="replicate", integration=integration)
30
+
31
+ # Set the base URL for the Replicate API (from the OpenAPI schema's servers section)
32
+ self.base_url = "https://api.replicate.com/v1"
33
+ logger.debug(f"ReplicateApp initialized with base_url: {self.base_url}")
34
+
35
+ # --- Account Operations ---
36
+
37
+ def account_get(self) -> dict[str, Any]:
38
+ """
39
+ Gets information about the authenticated account.
40
+
41
+ Args:
42
+ None: This function takes no parameters.
43
+
44
+ Returns:
45
+ Dict[str, Any]: A dictionary containing account details including type, username, name, and other account-related information.
46
+
47
+ Raises:
48
+ HTTPError: When the API request fails or returns a non-200 status code
49
+ RequestException: When there are network connectivity issues or other request-related problems
50
+
51
+ Tags:
52
+ account, get, read, api, authentication, important
53
+ """
54
+ url = f"{self.base_url}/account"
55
+ response = self._get(url)
56
+ response.raise_for_status()
57
+ return response.json()
58
+
59
+ # --- Collection Operations ---
60
+
61
+ def collections_list(self) -> dict[str, Any]:
62
+ """
63
+ Lists collections of models available on Replicate, returning a paginated list of collection objects.
64
+
65
+ Args:
66
+ None: This function takes no arguments other than self
67
+
68
+ Returns:
69
+ A dictionary containing a paginated list of collection objects from the Replicate API
70
+
71
+ Raises:
72
+ HTTPError: When the API request fails or returns a non-200 status code
73
+
74
+ Tags:
75
+ list, collections, read, api, important, fetch, models
76
+ """
77
+ url = f"{self.base_url}/collections"
78
+ response = self._get(url)
79
+ response.raise_for_status()
80
+ return response.json()
81
+
82
+ def collections_get(self, collection_slug: str) -> dict[str, Any]:
83
+ """
84
+ Retrieves detailed information about a specific model collection, with automatic truncation of large model lists to manage response size.
85
+
86
+ Args:
87
+ collection_slug: The unique identifier (slug) for the collection to retrieve (e.g., 'super-resolution')
88
+
89
+ Returns:
90
+ A dictionary containing collection details and its associated models. If the model list exceeds the truncation threshold (15), returns a modified dictionary with the model list replaced by a summary count and message.
91
+
92
+ Raises:
93
+ HTTPError: Raised when the API request fails or returns an error status code
94
+ RequestException: Raised when there's a network-related error during the API request
95
+
96
+ Tags:
97
+ get, read, collections, api, data-retrieval, important, truncation
98
+ """
99
+ url = f"{self.base_url}/collections/{collection_slug}"
100
+ response = self._get(url)
101
+ response.raise_for_status()
102
+ collection_data = response.json()
103
+ if 'models' in collection_data and isinstance(collection_data['models'], list):
104
+ original_model_count = len(collection_data['models'])
105
+
106
+ # Define a threshold for truncation.
107
+ # This is a heuristic value. You might need to adjust this number
108
+ # based on how many model objects typically fit within your LLM's context,
109
+ # considering the size of each model object description.
110
+ TRUNCATION_THRESHOLD = 10 # Example: Truncate if more than 15 models
111
+
112
+ if original_model_count > TRUNCATION_THRESHOLD:
113
+ logger.warning(f"Truncating model list for collection '{collection_slug}'. Found {original_model_count} models, exceeding threshold {TRUNCATION_THRESHOLD}.")
114
+
115
+ # Create a new dictionary with essential collection data
116
+ # and replace the 'models' list with a summary.
117
+ truncated_data = {
118
+ k: v for k, v in collection_data.items()
119
+ if k != 'models' # Exclude the full models list
120
+ }
121
+ # Add information about the models list being truncated
122
+ truncated_data['model_count'] = original_model_count
123
+ truncated_data['models'] = f"List of {original_model_count} models omitted due to size. Use models_get for details on individual models by owner/name."
124
+ # Optionally, include a small number of models as a preview, but this adds size.
125
+ # For maximum reduction, just provide the count and message.
126
+ # truncated_data['models_preview'] = collection_data['models'][:5] # Example: include first 5 models
127
+
128
+ return truncated_data
129
+ else:
130
+ # If the list is not too large, return the full data
131
+ return collection_data
132
+ else:
133
+ # If 'models' key is missing or not a list, return the data as is
134
+ # (This handles cases where the structure is unexpected, or an error occurred before this point)
135
+ return collection_data
136
+
137
+ # --- Deployment Operations ---
138
+
139
+ def deployments_list(self) -> dict[str, Any]:
140
+ """
141
+ Lists all deployments associated with the authenticated account.
142
+
143
+ Args:
144
+ None: This function takes no arguments.
145
+
146
+ Returns:
147
+ Dict[str, Any]: A dictionary containing a paginated list of deployment objects with deployment details and metadata.
148
+
149
+ Raises:
150
+ HTTPError: When the API request fails or returns a non-200 status code
151
+ RequestException: When there are network connectivity issues or API communication errors
152
+
153
+ Tags:
154
+ list, read, deployments, api, management, important
155
+ """
156
+ url = f"{self.base_url}/deployments"
157
+ response = self._get(url)
158
+ response.raise_for_status()
159
+ return response.json()
160
+
161
+ def deployments_create(
162
+ self,
163
+ name: str,
164
+ model: str,
165
+ version: str,
166
+ hardware: str,
167
+ min_instances: int,
168
+ max_instances: int,
169
+ ) -> dict[str, Any]:
170
+ """
171
+ Creates a new model deployment with specified configuration parameters.
172
+
173
+ Args:
174
+ name: The name of the deployment
175
+ model: The full name of the model (e.g., 'stability-ai/sdxl')
176
+ version: The 64-character string ID of the model version
177
+ hardware: The SKU for the hardware (e.g., 'gpu-t4')
178
+ min_instances: The minimum number of instances, ranging from 0 to 5
179
+ max_instances: The maximum number of instances, ranging from 0 to 20
180
+
181
+ Returns:
182
+ Dictionary containing the details of the newly created deployment
183
+
184
+ Raises:
185
+ HTTPError: When the API request fails or returns a non-200 status code
186
+
187
+ Tags:
188
+ create, deployment, configuration, api, infrastructure, scaling, important
189
+ """
190
+ url = f"{self.base_url}/deployments"
191
+ request_body = {
192
+ "name": name,
193
+ "model": model,
194
+ "version": version,
195
+ "hardware": hardware,
196
+ "min_instances": min_instances,
197
+ "max_instances": max_instances,
198
+ }
199
+ response = self._post(url, data=request_body)
200
+ response.raise_for_status()
201
+ return response.json()
202
+
203
+ def deployments_get(self, deployment_owner: str, deployment_name: str) -> dict[str, Any]:
204
+ """
205
+ Retrieves detailed information about a specific deployment by its owner and name.
206
+
207
+ Args:
208
+ deployment_owner: The owner's username or organization name associated with the deployment
209
+ deployment_name: The unique name identifier of the deployment
210
+
211
+ Returns:
212
+ A dictionary containing detailed information about the requested deployment
213
+
214
+ Raises:
215
+ HTTPError: When the HTTP request fails or returns a non-200 status code
216
+
217
+ Tags:
218
+ get, read, deployments, api, fetch, important
219
+ """
220
+ url = f"{self.base_url}/deployments/{deployment_owner}/{deployment_name}"
221
+ response = self._get(url)
222
+ response.raise_for_status()
223
+ return response.json()
224
+
225
+ def deployments_update(
226
+ self,
227
+ deployment_owner: str,
228
+ deployment_name: str,
229
+ hardware: str = None,
230
+ max_instances: int = None,
231
+ min_instances: int = None,
232
+ version: str = None,
233
+ ) -> dict[str, Any]:
234
+ """
235
+ Updates configurable properties of an existing deployment, such as hardware specifications and instance scaling parameters.
236
+
237
+ Args:
238
+ deployment_owner: The owner's username or organization name
239
+ deployment_name: The name of the deployment to be updated
240
+ hardware: Optional. The new SKU for the hardware specification
241
+ max_instances: Optional. The new maximum number of instances for scaling
242
+ min_instances: Optional. The new minimum number of instances for scaling
243
+ version: Optional. The new ID of the model version to deploy
244
+
245
+ Returns:
246
+ Dictionary containing the updated deployment configuration details or a message if no updates were provided
247
+
248
+ Raises:
249
+ HTTPError: Raised when the API request fails or returns an error status code
250
+
251
+ Tags:
252
+ update, deployments, configuration, scaling, management, important
253
+ """
254
+ url = f"{self.base_url}/deployments/{deployment_owner}/{deployment_name}"
255
+ update_data = {
256
+ "hardware": hardware,
257
+ "max_instances": max_instances,
258
+ "min_instances": min_instances,
259
+ "version": version,
260
+ }
261
+ update_data = {k: v for k, v in update_data.items() if v is not None}
262
+ if not update_data:
263
+ return {"message": "No update parameters provided."}
264
+ response = self._patch(url, data=update_data)
265
+ response.raise_for_status()
266
+ return response.json()
267
+
268
+ def deployments_delete(self, deployment_owner: str, deployment_name: str) -> str:
269
+ """
270
+ Deletes a specified deployment associated with a given owner or organization
271
+
272
+ Args:
273
+ deployment_owner: The username or organization name that owns the deployment
274
+ deployment_name: The unique identifier/name of the deployment to be deleted
275
+
276
+ Returns:
277
+ A string containing a success message confirming the deployment deletion
278
+
279
+ Raises:
280
+ HTTPError: If the deletion request fails due to server error, invalid permissions, deployment being in use, or deployment not found
281
+
282
+ Tags:
283
+ delete, deployments, management, important, infrastructure, resource-management
284
+ """
285
+ url = f"{self.base_url}/deployments/{deployment_owner}/{deployment_name}"
286
+ response = self._delete(url)
287
+ response.raise_for_status()
288
+ return f"Deployment '{deployment_owner}/{deployment_name}' deleted successfully." # Assuming 204 No Content
289
+
290
+
291
+ # --- Deployments Predictions Operations ---
292
+ # Note: The 'Prefer: wait' header is not directly supported by default _post.
293
+ # The response will likely be 201 or 202, requiring polling via predictions_get.
294
+
295
+ def deployments_predictions_create(
296
+ self,
297
+ deployment_owner: str,
298
+ deployment_name: str,
299
+ input: dict[str, Any],
300
+ stream: bool = None, # Deprecated according to schema
301
+ webhook: str = None,
302
+ webhook_events_filter: list[Literal["start", "output", "logs", "completed"]] = None,
303
+ ) -> dict[str, Any]:
304
+ """
305
+ Creates an asynchronous prediction using a specified deployment, optionally configuring webhook notifications for status updates.
306
+
307
+ Args:
308
+ deployment_owner: The owner's username or organization name of the deployment
309
+ deployment_name: The name of the deployment to use for prediction
310
+ input: Dictionary containing the model's input data as a JSON-serializable object
311
+ stream: Deprecated parameter for requesting streaming output URL
312
+ webhook: HTTPS URL where prediction status updates will be sent
313
+ webhook_events_filter: List of specific events to trigger webhook notifications ('start', 'output', 'logs', 'completed')
314
+
315
+ Returns:
316
+ Dictionary containing the initial prediction state, including a prediction ID for status polling
317
+
318
+ Raises:
319
+ HTTPError: When the API request fails or returns an error status code
320
+ JSONDecodeError: When the API response contains invalid JSON
321
+
322
+ Tags:
323
+ create, predict, async, deployments, webhook, http-request, important
324
+ """
325
+ url = f"{self.base_url}/deployments/{deployment_owner}/{deployment_name}/predictions"
326
+ request_body = {
327
+ "input": input,
328
+ "stream": stream, # Included for completeness, though deprecated
329
+ "webhook": webhook,
330
+ "webhook_events_filter": webhook_events_filter,
331
+ }
332
+ request_body = {k: v for k, v in request_body.items() if v is not None}
333
+ response = self._post(url, data=request_body)
334
+ response.raise_for_status()
335
+ return response.json()
336
+
337
+
338
+ # --- Hardware Operations ---
339
+
340
+ def hardware_list(self) -> list[dict[str, Any]]:
341
+ """
342
+ Retrieves a list of available hardware options for running models.
343
+
344
+ Returns:
345
+ List[Dict[str, Any]]: A list of dictionaries, where each dictionary represents a hardware option containing 'name' and 'sku' keys.
346
+
347
+ Raises:
348
+ HTTPError: When the API request fails or returns a non-200 status code
349
+
350
+ Tags:
351
+ list, hardware, read, api, configuration, important
352
+ """
353
+ url = f"{self.base_url}/hardware"
354
+ response = self._get(url)
355
+ response.raise_for_status()
356
+ return response.json()
357
+
358
+
359
+ # --- Model Operations ---
360
+
361
+ def models_list(self) -> dict[str, Any]:
362
+ """
363
+ Retrieves a paginated list of publicly available models from the Replicate API.
364
+
365
+ Returns:
366
+ Dict[str, Any]: A dictionary containing the paginated list of model objects with their metadata and configurations.
367
+
368
+ Raises:
369
+ HTTPError: When the API request fails or returns a non-200 status code
370
+ RequestException: When there are network connectivity issues or other request-related problems
371
+
372
+ Tags:
373
+ list, models, read, api, fetch, important
374
+ """
375
+ url = f"{self.base_url}/models"
376
+ response = self._get(url)
377
+ response.raise_for_status()
378
+ return response.json()
379
+
380
+ def models_create(
381
+ self,
382
+ owner: str,
383
+ name: str,
384
+ visibility: Literal["public", "private"],
385
+ hardware: str,
386
+ cover_image_url: str = None,
387
+ description: str = None,
388
+ github_url: str = None,
389
+ license_url: str = None,
390
+ paper_url: str = None,
391
+ ) -> dict[str, Any]:
392
+ """
393
+ Creates a new model in the system with specified parameters and metadata.
394
+
395
+ Args:
396
+ owner: The username or organization name that will own the model
397
+ name: The name of the model (must be unique for the owner)
398
+ visibility: Visibility setting for the model ('public' or 'private')
399
+ hardware: The SKU for the hardware requirements
400
+ cover_image_url: Optional URL for the model's cover image
401
+ description: Optional text description of the model
402
+ github_url: Optional URL for the model's source code repository
403
+ license_url: Optional URL for the model's license documentation
404
+ paper_url: Optional URL for the associated research paper
405
+
406
+ Returns:
407
+ Dictionary containing the details and metadata of the newly created model
408
+
409
+ Raises:
410
+ HTTPError: Raised when the API request fails or returns an error status code
411
+
412
+ Tags:
413
+ create, models, management, api, important
414
+ """
415
+ url = f"{self.base_url}/models"
416
+ request_body = {
417
+ "owner": owner,
418
+ "name": name,
419
+ "visibility": visibility,
420
+ "hardware": hardware,
421
+ "cover_image_url": cover_image_url,
422
+ "description": description,
423
+ "github_url": github_url,
424
+ "license_url": license_url,
425
+ "paper_url": paper_url,
426
+ }
427
+ request_body = {k: v for k, v in request_body.items() if v is not None}
428
+ response = self._post(url, data=request_body)
429
+ response.raise_for_status()
430
+ return response.json()
431
+
432
+ def models_search(self, query: str) -> dict[str, Any]:
433
+ """
434
+ Searches for public models based on a provided query string
435
+
436
+ Args:
437
+ query: A string containing the search query to filter models
438
+
439
+ Returns:
440
+ A dictionary containing paginated search results with matching model objects
441
+
442
+ Raises:
443
+ Exception: When the HTTP request fails or encounters network/server errors
444
+ HTTPStatusError: When the API returns a non-successful status code
445
+
446
+ Tags:
447
+ search, models, query, read, api, http, important
448
+ """
449
+ url = f"{self.base_url}/models"
450
+ headers = self._get_headers()
451
+ headers["Content-Type"] = "text/plain"
452
+ try:
453
+ # Use httpx directly to send raw text body
454
+ response = self.client.post(
455
+ url, content=query, headers=headers, timeout=self.client.timeout
456
+ )
457
+ response.raise_for_status()
458
+ return response.json()
459
+ except Exception as e:
460
+ logger.error(f"Error during models_search: {e}")
461
+ raise
462
+
463
+
464
+ def models_get(self, model_owner: str, model_name: str) -> dict[str, Any]:
465
+ """
466
+ Retrieves detailed information about a specific AI model by its owner and name
467
+
468
+ Args:
469
+ model_owner: The owner's username or organization name who owns the model
470
+ model_name: The unique name identifier of the model
471
+
472
+ Returns:
473
+ A dictionary containing detailed model information, including its latest version and usage examples
474
+
475
+ Raises:
476
+ HTTPError: Raised when the API request fails, such as when the model doesn't exist or there are authentication issues
477
+
478
+ Tags:
479
+ get, read, models, api, metadata, important
480
+ """
481
+ url = f"{self.base_url}/models/{model_owner}/{model_name}"
482
+ response = self._get(url)
483
+ response.raise_for_status()
484
+ return response.json()
485
+
486
+ def models_delete(self, model_owner: str, model_name: str) -> str:
487
+ """
488
+ Deletes a private model from the system, provided it has no existing versions.
489
+
490
+ Args:
491
+ model_owner: The owner's username or organization name of the model
492
+ model_name: The name of the model to be deleted
493
+
494
+ Returns:
495
+ A success message string confirming the model deletion
496
+
497
+ Raises:
498
+ HTTPError: When deletion fails due to model being public, having existing versions, or other API restrictions
499
+
500
+ Tags:
501
+ delete, models, management, important, api, cleanup
502
+ """
503
+ url = f"{self.base_url}/models/{model_owner}/{model_name}"
504
+ response = self._delete(url)
505
+ response.raise_for_status() # Expecting 204 No Content
506
+ return f"Model '{model_owner}/{model_name}' deleted successfully."
507
+
508
+
509
+ # --- Model Examples Operations ---
510
+
511
+ def models_examples_list(self, model_owner: str, model_name: str) -> dict[str, Any]:
512
+ """
513
+ Retrieves a list of example predictions associated with a specific model.
514
+
515
+ Args:
516
+ model_owner: The owner's username or organization name of the model
517
+ model_name: The name of the model to retrieve examples for
518
+
519
+ Returns:
520
+ A dictionary containing a paginated list of example prediction objects for the specified model
521
+
522
+ Raises:
523
+ HTTPError: When the API request fails or returns a non-200 status code
524
+ RequestException: When there are network connectivity issues or API communication errors
525
+
526
+ Tags:
527
+ list, read, models, examples, api, pagination, prediction
528
+ """
529
+ url = f"{self.base_url}/models/{model_owner}/{model_name}/examples"
530
+ response = self._get(url)
531
+ response.raise_for_status()
532
+ return response.json()
533
+
534
+
535
+ # --- Model Predictions Operations ---
536
+ # Note: The 'Prefer: wait' header is not directly supported by default _post.
537
+ # The response will likely be 201 or 202, requiring polling via predictions_get.
538
+
539
+ def models_predictions_create(
540
+ self,
541
+ model_owner: str,
542
+ model_name: str,
543
+ input: dict[str, Any],
544
+ stream: bool = None, # Deprecated according to schema
545
+ webhook: str = None,
546
+ webhook_events_filter: list[Literal["start", "output", "logs", "completed"]] = None,
547
+ ) -> dict[str, Any]:
548
+ """
549
+ Creates an asynchronous prediction request using a specified machine learning model.
550
+
551
+ Args:
552
+ model_owner: The owner's username or organization name
553
+ model_name: The name of the model to use for prediction
554
+ input: Dictionary containing the model's input parameters
555
+ stream: DEPRECATED. Boolean flag to request a streaming output URL
556
+ webhook: HTTPS URL for receiving prediction status updates and results
557
+ webhook_events_filter: List of events to trigger webhook notifications ('start', 'output', 'logs', 'completed')
558
+
559
+ Returns:
560
+ Dictionary containing the initial prediction state and metadata. Includes prediction ID for status polling.
561
+
562
+ Raises:
563
+ HTTPError: Raised when the API request fails or returns an error status code
564
+
565
+ Tags:
566
+ predict, create, async, models, webhook, api, ml, important
567
+ """
568
+ url = f"{self.base_url}/models/{model_owner}/{model_name}/predictions"
569
+ request_body = {
570
+ "input": input,
571
+ "stream": stream, # Included for completeness, though deprecated
572
+ "webhook": webhook,
573
+ "webhook_events_filter": webhook_events_filter,
574
+ }
575
+ request_body = {k: v for k, v in request_body.items() if v is not None}
576
+ response = self._post(url, data=request_body)
577
+ response.raise_for_status()
578
+ return response.json()
579
+
580
+
581
+ # --- Model Readme Operations ---
582
+
583
+ def models_readme_get(self, model_owner: str, model_name: str) -> str:
584
+ """
585
+ Retrieves the README content for a specified model in Markdown format.
586
+
587
+ Args:
588
+ model_owner: The owner's username or organization name
589
+ model_name: The name of the model
590
+
591
+ Returns:
592
+ A string containing the README content in Markdown format
593
+
594
+ Raises:
595
+ HTTPError: When the API request fails or returns a non-200 status code
596
+ RequestException: When there are network connectivity issues or other request-related problems
597
+
598
+ Tags:
599
+ get, read, models, documentation, markdown, api, metadata
600
+ """
601
+ url = f"{self.base_url}/models/{model_owner}/{model_name}/readme"
602
+ response = self._get(url)
603
+ response.raise_for_status()
604
+ return response.text # README is text/plain
605
+
606
+
607
+ # --- Model Versions Operations ---
608
+
609
+ def models_versions_list(self, model_owner: str, model_name: str) -> dict[str, Any]:
610
+ """
611
+ Lists all available versions of a specified model.
612
+
613
+ Args:
614
+ model_owner: The username or organization name that owns the model
615
+ model_name: The name of the model whose versions are to be listed
616
+
617
+ Returns:
618
+ A dictionary containing a paginated list of model version objects with version details
619
+
620
+ Raises:
621
+ HTTPError: When the API request fails or returns a non-200 status code
622
+
623
+ Tags:
624
+ list, read, models, versions, api, management, paginated, important
625
+ """
626
+ url = f"{self.base_url}/models/{model_owner}/{model_name}/versions"
627
+ response = self._get(url)
628
+ response.raise_for_status()
629
+ return response.json()
630
+
631
+ def models_versions_get(self, model_owner: str, model_name: str, version_id: str) -> dict[str, Any]:
632
+ """
633
+ Retrieves detailed information about a specific version of a model by querying the API.
634
+
635
+ Args:
636
+ model_owner: The owner's username or organization name who owns the model
637
+ model_name: The name of the model to query
638
+ version_id: The unique identifier of the specific model version
639
+
640
+ Returns:
641
+ A dictionary containing detailed information about the model version, including its OpenAPI schema and metadata
642
+
643
+ Raises:
644
+ HTTPError: Raised when the API request fails, such as when the model version doesn't exist or there are authentication issues
645
+
646
+ Tags:
647
+ get, read, models, versions, api, metadata, important
648
+ """
649
+ url = f"{self.base_url}/models/{model_owner}/{model_name}/versions/{version_id}"
650
+ response = self._get(url)
651
+ response.raise_for_status()
652
+ return response.json()
653
+
654
+ def models_versions_delete(self, model_owner: str, model_name: str, version_id: str) -> str:
655
+ """
656
+ Deletes a specific version of a model and its associated predictions/output.
657
+
658
+ Args:
659
+ model_owner: The owner's username or organization name
660
+ model_name: The name of the model
661
+ version_id: The ID of the version to be deleted
662
+
663
+ Returns:
664
+ A success message confirming the deletion request was accepted
665
+
666
+ Raises:
667
+ HTTPError: If the deletion request fails due to API restrictions or permissions
668
+
669
+ Tags:
670
+ delete, models, versions, management, important
671
+ """
672
+ url = f"{self.base_url}/models/{model_owner}/{model_name}/versions/{version_id}"
673
+ response = self._delete(url)
674
+ response.raise_for_status() # Expecting 202 Accepted or 204 No Content
675
+ return f"Deletion request for version '{version_id}' of model '{model_owner}/{model_name}' accepted."
676
+
677
+
678
+ # --- Training Operations (via Model Version) ---
679
+
680
+ def trainings_create(
681
+ self,
682
+ model_owner: str,
683
+ model_name: str,
684
+ version_id: str,
685
+ destination: str,
686
+ input: dict[str, Any],
687
+ webhook: str = None,
688
+ webhook_events_filter: list[Literal["start", "output", "logs", "completed"]] = None,
689
+ ) -> dict[str, Any]:
690
+ """
691
+ Initiates a new asynchronous training job for a specific model version, with optional webhook notifications for progress updates.
692
+
693
+ Args:
694
+ model_owner: The owner's username or organization name of the base model
695
+ model_name: The name of the base model
696
+ version_id: The ID of the model version to train from
697
+ destination: The target model identifier string in '{new_owner}/{new_name}' format
698
+ input: Dictionary containing inputs for the training function
699
+ webhook: Optional HTTPS URL for receiving training updates
700
+ webhook_events_filter: Optional list of events to trigger webhooks ('start', 'output', 'logs', 'completed')
701
+
702
+ Returns:
703
+ Dictionary containing the initial state of the training job
704
+
705
+ Raises:
706
+ HTTPError: Raised when the API request fails or returns an error status code
707
+
708
+ Tags:
709
+ training, create, async-job, start, model-management, webhook, important
710
+ """
711
+ url = f"{self.base_url}/models/{model_owner}/{model_name}/versions/{version_id}/trainings"
712
+ request_body = {
713
+ "destination": destination,
714
+ "input": input,
715
+ "webhook": webhook,
716
+ "webhook_events_filter": webhook_events_filter,
717
+ }
718
+ request_body = {k: v for k, v in request_body.items() if v is not None}
719
+ response = self._post(url, data=request_body)
720
+ response.raise_for_status()
721
+ return response.json()
722
+
723
+
724
+ # --- Prediction Operations ---
725
+
726
+ def predictions_list(
727
+ self,
728
+ created_after: str = None, # ISO 8601 date-time
729
+ created_before: str = None, # ISO 8601 date-time
730
+ ) -> dict[str, Any]:
731
+ """
732
+ Lists all predictions created by the authenticated account within an optional time range.
733
+
734
+ Args:
735
+ created_after: ISO 8601 date-time string specifying the lower bound for prediction creation time (inclusive)
736
+ created_before: ISO 8601 date-time string specifying the upper bound for prediction creation time (exclusive)
737
+
738
+ Returns:
739
+ Dictionary containing a paginated list of prediction objects with their associated metadata
740
+
741
+ Raises:
742
+ HTTPError: When the API request fails or returns a non-200 status code
743
+ RequestException: When there are network connectivity issues or invalid request parameters
744
+
745
+ Tags:
746
+ list, read, predictions, query, filter, pagination, important
747
+ """
748
+ url = f"{self.base_url}/predictions"
749
+ query_params = {
750
+ "created_after": created_after,
751
+ "created_before": created_before,
752
+ }
753
+ query_params = {k: v for k, v in query_params.items() if v is not None}
754
+ response = self._get(url, params=query_params)
755
+ response.raise_for_status()
756
+ return response.json()
757
+
758
+ # Note: The 'Prefer: wait' header is not directly supported by default _post.
759
+ # The response will likely be 201 or 202, requiring polling via predictions_get.
760
+
761
+ def predictions_create(
762
+ self,
763
+ version: str,
764
+ input: dict[str, Any],
765
+ stream: bool = None, # Deprecated according to schema
766
+ webhook: str = None,
767
+ webhook_events_filter: list[Literal["start", "output", "logs", "completed"]] = None,
768
+ ) -> dict[str, Any]:
769
+ """
770
+ Creates an asynchronous prediction request using a specified model version.
771
+
772
+ Args:
773
+ version: The ID of the model version to execute the prediction
774
+ input: A dictionary containing the input data for the model prediction
775
+ stream: Deprecated parameter - Previously used for requesting streaming output URL
776
+ webhook: Optional HTTPS URL to receive prediction status updates and results
777
+ webhook_events_filter: Optional list of event types to trigger webhook notifications. Valid events: 'start', 'output', 'logs', 'completed'
778
+
779
+ Returns:
780
+ A dictionary containing the initial prediction state, including a prediction ID for status tracking
781
+
782
+ Raises:
783
+ HTTPError: Raised when the API request fails or returns an error status code
784
+
785
+ Tags:
786
+ predictions, create, async, api, model, important, webhook, ml
787
+ """
788
+ url = f"{self.base_url}/predictions"
789
+ request_body = {
790
+ "version": version,
791
+ "input": input,
792
+ "stream": stream, # Included for completeness, though deprecated
793
+ "webhook": webhook,
794
+ "webhook_events_filter": webhook_events_filter,
795
+ }
796
+ request_body = {k: v for k, v in request_body.items() if v is not None}
797
+ response = self._post(url, data=request_body)
798
+ response.raise_for_status()
799
+ return response.json()
800
+
801
+
802
+ def predictions_get(self, prediction_id: str) -> dict[str, Any]:
803
+ """
804
+ Retrieves the current state and details of a prediction by its ID.
805
+
806
+ Args:
807
+ prediction_id: String identifier of the prediction to retrieve
808
+
809
+ Returns:
810
+ Dictionary containing the prediction's current state and associated details
811
+
812
+ Raises:
813
+ HTTPError: When the API request fails or returns a non-200 status code
814
+ RequestException: When there are network connectivity issues or invalid URL
815
+
816
+ Tags:
817
+ get, read, status, predictions, retrieve, api, important
818
+ """
819
+ url = f"{self.base_url}/predictions/{prediction_id}"
820
+ response = self._get(url)
821
+ response.raise_for_status()
822
+ return response.json()
823
+
824
+ def predictions_cancel(self, prediction_id: str) -> str: # Schema shows no content for 200 success
825
+ """
826
+ Cancels a running prediction job identified by its ID.
827
+
828
+ Args:
829
+ prediction_id: The unique identifier of the prediction job to cancel
830
+
831
+ Returns:
832
+ A string message confirming successful cancellation of the prediction
833
+
834
+ Raises:
835
+ HTTPError: When the API request fails or returns an error status code
836
+ RequestException: When there are network connectivity issues or other request-related problems
837
+
838
+ Tags:
839
+ cancel, predictions, management, api, async-job, important
840
+ """
841
+ url = f"{self.base_url}/predictions/{prediction_id}/cancel"
842
+ response = self._post(url, data={}) # POST with empty body for cancel according to typical patterns
843
+ response.raise_for_status() # Expecting 200 Success or similar
844
+ return f"Prediction '{prediction_id}' cancelled successfully."
845
+
846
+ # --- Training Operations ---
847
+
848
+ def trainings_list(self) -> dict[str, Any]:
849
+ """
850
+ Lists all training jobs created by the authenticated account.
851
+
852
+ Returns:
853
+ Dict[str, Any]: A dictionary containing a paginated list of training objects with their details and metadata.
854
+
855
+ Raises:
856
+ HTTPError: When the API request fails or returns a non-200 status code
857
+ RequestException: When there's a network error or connection issue
858
+
859
+ Tags:
860
+ list, read, trainings, management, important
861
+ """
862
+ url = f"{self.base_url}/trainings"
863
+ response = self._get(url)
864
+ response.raise_for_status()
865
+ return response.json()
866
+
867
+ def trainings_get(self, training_id: str) -> dict[str, Any]:
868
+ """
869
+ Retrieves the current state of a training job by its ID.
870
+
871
+ Args:
872
+ training_id: A string identifier for the training job
873
+
874
+ Returns:
875
+ A dictionary containing the current state and details of the training job
876
+
877
+ Raises:
878
+ HTTPError: When the API request fails or returns a non-200 status code
879
+
880
+ Tags:
881
+ get, read, status, training, api, fetch, monitor, important
882
+ """
883
+ url = f"{self.base_url}/trainings/{training_id}"
884
+ response = self._get(url)
885
+ response.raise_for_status()
886
+ return response.json()
887
+
888
+ def trainings_cancel(self, training_id: str) -> dict[str, Any]:
889
+ """
890
+ Cancels a specific training job in progress.
891
+
892
+ Args:
893
+ training_id: String identifier of the training job to be cancelled
894
+
895
+ Returns:
896
+ Dictionary containing the updated state and details of the cancelled training job
897
+
898
+ Raises:
899
+ HTTPError: When the cancellation request fails or the server returns an error response
900
+ RequestException: When network issues or connection problems occur during the API call
901
+
902
+ Tags:
903
+ cancel, trainings, management, async-job, important
904
+ """
905
+ url = f"{self.base_url}/trainings/{training_id}/cancel"
906
+ response = self._post(url, data={}) # POST with empty body for cancel
907
+ response.raise_for_status()
908
+ return response.json()
909
+
910
+ # --- Webhooks Operations ---
911
+
912
+ def webhooks_default_secret_get(self) -> dict[str, str]:
913
+ """
914
+ Retrieves the signing secret for the default webhook endpoint.
915
+
916
+ Returns:
917
+ Dict[str, str]: A dictionary containing the 'key' field with the signing secret value.
918
+
919
+ Raises:
920
+ HTTPError: If the API request fails or returns a non-200 status code.
921
+ RequestException: If there are network connectivity issues or other request-related problems.
922
+
923
+ Tags:
924
+ get, read, webhooks, security, secret, authentication, api, important
925
+ """
926
+ url = f"{self.base_url}/webhooks/default/secret"
927
+ response = self._get(url)
928
+ response.raise_for_status()
929
+ return response.json()
930
+
931
+
932
+ # --- Required list_tools method ---
933
+
934
+ def list_tools(self):
935
+ """
936
+ Returns a list of methods exposed as tools by this application.
937
+ """
938
+ return [
939
+ self.account_get,
940
+ self.collections_list,
941
+ self.collections_get,
942
+ self.deployments_list,
943
+ self.deployments_create,
944
+ self.deployments_get,
945
+ self.deployments_update,
946
+ self.deployments_delete,
947
+ self.deployments_predictions_create,
948
+ self.hardware_list,
949
+ self.models_list,
950
+ self.models_create,
951
+ self.models_search,
952
+ self.models_get,
953
+ self.models_delete,
954
+ self.models_examples_list,
955
+ self.models_predictions_create,
956
+ self.models_readme_get,
957
+ self.models_versions_list,
958
+ self.models_versions_get,
959
+ self.models_versions_delete,
960
+ self.trainings_create, # Training is started via model version path
961
+ self.predictions_list,
962
+ self.predictions_create,
963
+ self.predictions_get,
964
+ self.predictions_cancel,
965
+ self.trainings_list,
966
+ self.trainings_get,
967
+ self.trainings_cancel,
968
+ self.webhooks_default_secret_get,
969
+ ]