synapse-sdk 1.0.0a11__py3-none-any.whl → 2026.1.1b2__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.
Potentially problematic release.
This version of synapse-sdk might be problematic. Click here for more details.
- synapse_sdk/__init__.py +24 -0
- synapse_sdk/cli/__init__.py +9 -8
- synapse_sdk/cli/agent/__init__.py +25 -0
- synapse_sdk/cli/agent/config.py +104 -0
- synapse_sdk/cli/agent/select.py +197 -0
- synapse_sdk/cli/auth.py +104 -0
- synapse_sdk/cli/main.py +1025 -0
- synapse_sdk/cli/plugin/__init__.py +58 -0
- synapse_sdk/cli/plugin/create.py +566 -0
- synapse_sdk/cli/plugin/job.py +196 -0
- synapse_sdk/cli/plugin/publish.py +322 -0
- synapse_sdk/cli/plugin/run.py +131 -0
- synapse_sdk/cli/plugin/test.py +200 -0
- synapse_sdk/clients/README.md +239 -0
- synapse_sdk/clients/__init__.py +5 -0
- synapse_sdk/clients/_template.py +266 -0
- synapse_sdk/clients/agent/__init__.py +84 -29
- synapse_sdk/clients/agent/async_ray.py +289 -0
- synapse_sdk/clients/agent/container.py +83 -0
- synapse_sdk/clients/agent/plugin.py +101 -0
- synapse_sdk/clients/agent/ray.py +296 -39
- synapse_sdk/clients/backend/__init__.py +152 -12
- synapse_sdk/clients/backend/annotation.py +164 -22
- synapse_sdk/clients/backend/core.py +101 -0
- synapse_sdk/clients/backend/data_collection.py +292 -0
- synapse_sdk/clients/backend/hitl.py +87 -0
- synapse_sdk/clients/backend/integration.py +374 -46
- synapse_sdk/clients/backend/ml.py +134 -22
- synapse_sdk/clients/backend/models.py +247 -0
- synapse_sdk/clients/base.py +538 -59
- synapse_sdk/clients/exceptions.py +35 -7
- synapse_sdk/clients/pipeline/__init__.py +5 -0
- synapse_sdk/clients/pipeline/client.py +636 -0
- synapse_sdk/clients/protocols.py +178 -0
- synapse_sdk/clients/utils.py +86 -8
- synapse_sdk/clients/validation.py +58 -0
- synapse_sdk/enums.py +76 -0
- synapse_sdk/exceptions.py +168 -0
- synapse_sdk/integrations/__init__.py +74 -0
- synapse_sdk/integrations/_base.py +119 -0
- synapse_sdk/integrations/_context.py +53 -0
- synapse_sdk/integrations/ultralytics/__init__.py +78 -0
- synapse_sdk/integrations/ultralytics/_callbacks.py +126 -0
- synapse_sdk/integrations/ultralytics/_patches.py +124 -0
- synapse_sdk/loggers.py +476 -95
- synapse_sdk/mcp/MCP.md +69 -0
- synapse_sdk/mcp/__init__.py +48 -0
- synapse_sdk/mcp/__main__.py +6 -0
- synapse_sdk/mcp/config.py +349 -0
- synapse_sdk/mcp/prompts/__init__.py +4 -0
- synapse_sdk/mcp/resources/__init__.py +4 -0
- synapse_sdk/mcp/server.py +1352 -0
- synapse_sdk/mcp/tools/__init__.py +6 -0
- synapse_sdk/plugins/__init__.py +133 -9
- synapse_sdk/plugins/action.py +229 -0
- synapse_sdk/plugins/actions/__init__.py +82 -0
- synapse_sdk/plugins/actions/dataset/__init__.py +37 -0
- synapse_sdk/plugins/actions/dataset/action.py +471 -0
- synapse_sdk/plugins/actions/export/__init__.py +55 -0
- synapse_sdk/plugins/actions/export/action.py +183 -0
- synapse_sdk/plugins/actions/export/context.py +59 -0
- synapse_sdk/plugins/actions/inference/__init__.py +84 -0
- synapse_sdk/plugins/actions/inference/action.py +285 -0
- synapse_sdk/plugins/actions/inference/context.py +81 -0
- synapse_sdk/plugins/actions/inference/deployment.py +322 -0
- synapse_sdk/plugins/actions/inference/serve.py +252 -0
- synapse_sdk/plugins/actions/train/__init__.py +54 -0
- synapse_sdk/plugins/actions/train/action.py +326 -0
- synapse_sdk/plugins/actions/train/context.py +57 -0
- synapse_sdk/plugins/actions/upload/__init__.py +49 -0
- synapse_sdk/plugins/actions/upload/action.py +165 -0
- synapse_sdk/plugins/actions/upload/context.py +61 -0
- synapse_sdk/plugins/config.py +98 -0
- synapse_sdk/plugins/context/__init__.py +109 -0
- synapse_sdk/plugins/context/env.py +113 -0
- synapse_sdk/plugins/datasets/__init__.py +113 -0
- synapse_sdk/plugins/datasets/converters/__init__.py +76 -0
- synapse_sdk/plugins/datasets/converters/base.py +347 -0
- synapse_sdk/plugins/datasets/converters/yolo/__init__.py +9 -0
- synapse_sdk/plugins/datasets/converters/yolo/from_dm.py +468 -0
- synapse_sdk/plugins/datasets/converters/yolo/to_dm.py +381 -0
- synapse_sdk/plugins/datasets/formats/__init__.py +82 -0
- synapse_sdk/plugins/datasets/formats/dm.py +351 -0
- synapse_sdk/plugins/datasets/formats/yolo.py +240 -0
- synapse_sdk/plugins/decorators.py +83 -0
- synapse_sdk/plugins/discovery.py +790 -0
- synapse_sdk/plugins/docs/ACTION_DEV_GUIDE.md +933 -0
- synapse_sdk/plugins/docs/ARCHITECTURE.md +1225 -0
- synapse_sdk/plugins/docs/LOGGING_SYSTEM.md +683 -0
- synapse_sdk/plugins/docs/OVERVIEW.md +531 -0
- synapse_sdk/plugins/docs/PIPELINE_GUIDE.md +145 -0
- synapse_sdk/plugins/docs/README.md +513 -0
- synapse_sdk/plugins/docs/STEP.md +656 -0
- synapse_sdk/plugins/enums.py +70 -10
- synapse_sdk/plugins/errors.py +92 -0
- synapse_sdk/plugins/executors/__init__.py +43 -0
- synapse_sdk/plugins/executors/local.py +99 -0
- synapse_sdk/plugins/executors/ray/__init__.py +18 -0
- synapse_sdk/plugins/executors/ray/base.py +282 -0
- synapse_sdk/plugins/executors/ray/job.py +298 -0
- synapse_sdk/plugins/executors/ray/jobs_api.py +511 -0
- synapse_sdk/plugins/executors/ray/packaging.py +137 -0
- synapse_sdk/plugins/executors/ray/pipeline.py +792 -0
- synapse_sdk/plugins/executors/ray/task.py +257 -0
- synapse_sdk/plugins/models/__init__.py +26 -0
- synapse_sdk/plugins/models/logger.py +173 -0
- synapse_sdk/plugins/models/pipeline.py +25 -0
- synapse_sdk/plugins/pipelines/__init__.py +81 -0
- synapse_sdk/plugins/pipelines/action_pipeline.py +417 -0
- synapse_sdk/plugins/pipelines/context.py +107 -0
- synapse_sdk/plugins/pipelines/display.py +311 -0
- synapse_sdk/plugins/runner.py +114 -0
- synapse_sdk/plugins/schemas/__init__.py +19 -0
- synapse_sdk/plugins/schemas/results.py +152 -0
- synapse_sdk/plugins/steps/__init__.py +63 -0
- synapse_sdk/plugins/steps/base.py +128 -0
- synapse_sdk/plugins/steps/context.py +90 -0
- synapse_sdk/plugins/steps/orchestrator.py +128 -0
- synapse_sdk/plugins/steps/registry.py +103 -0
- synapse_sdk/plugins/steps/utils/__init__.py +20 -0
- synapse_sdk/plugins/steps/utils/logging.py +85 -0
- synapse_sdk/plugins/steps/utils/timing.py +71 -0
- synapse_sdk/plugins/steps/utils/validation.py +68 -0
- synapse_sdk/plugins/templates/__init__.py +50 -0
- synapse_sdk/plugins/templates/base/.gitignore.j2 +26 -0
- synapse_sdk/plugins/templates/base/.synapseignore.j2 +11 -0
- synapse_sdk/plugins/templates/base/README.md.j2 +26 -0
- synapse_sdk/plugins/templates/base/plugin/__init__.py.j2 +1 -0
- synapse_sdk/plugins/templates/base/pyproject.toml.j2 +14 -0
- synapse_sdk/plugins/templates/base/requirements.txt.j2 +1 -0
- synapse_sdk/plugins/templates/custom/plugin/main.py.j2 +18 -0
- synapse_sdk/plugins/templates/data_validation/plugin/validate.py.j2 +32 -0
- synapse_sdk/plugins/templates/export/plugin/export.py.j2 +36 -0
- synapse_sdk/plugins/templates/neural_net/plugin/inference.py.j2 +36 -0
- synapse_sdk/plugins/templates/neural_net/plugin/train.py.j2 +33 -0
- synapse_sdk/plugins/templates/post_annotation/plugin/post_annotate.py.j2 +32 -0
- synapse_sdk/plugins/templates/pre_annotation/plugin/pre_annotate.py.j2 +32 -0
- synapse_sdk/plugins/templates/smart_tool/plugin/auto_label.py.j2 +44 -0
- synapse_sdk/plugins/templates/upload/plugin/upload.py.j2 +35 -0
- synapse_sdk/plugins/testing/__init__.py +25 -0
- synapse_sdk/plugins/testing/sample_actions.py +98 -0
- synapse_sdk/plugins/types.py +206 -0
- synapse_sdk/plugins/upload.py +595 -64
- synapse_sdk/plugins/utils.py +325 -37
- synapse_sdk/shared/__init__.py +25 -0
- synapse_sdk/utils/__init__.py +1 -0
- synapse_sdk/utils/auth.py +74 -0
- synapse_sdk/utils/file/__init__.py +58 -0
- synapse_sdk/utils/file/archive.py +449 -0
- synapse_sdk/utils/file/checksum.py +167 -0
- synapse_sdk/utils/file/download.py +286 -0
- synapse_sdk/utils/file/io.py +129 -0
- synapse_sdk/utils/file/requirements.py +36 -0
- synapse_sdk/utils/network.py +168 -0
- synapse_sdk/utils/storage/__init__.py +238 -0
- synapse_sdk/utils/storage/config.py +188 -0
- synapse_sdk/utils/storage/errors.py +52 -0
- synapse_sdk/utils/storage/providers/__init__.py +13 -0
- synapse_sdk/utils/storage/providers/base.py +76 -0
- synapse_sdk/utils/storage/providers/gcs.py +168 -0
- synapse_sdk/utils/storage/providers/http.py +250 -0
- synapse_sdk/utils/storage/providers/local.py +126 -0
- synapse_sdk/utils/storage/providers/s3.py +177 -0
- synapse_sdk/utils/storage/providers/sftp.py +208 -0
- synapse_sdk/utils/storage/registry.py +125 -0
- synapse_sdk/utils/websocket.py +99 -0
- synapse_sdk-2026.1.1b2.dist-info/METADATA +715 -0
- synapse_sdk-2026.1.1b2.dist-info/RECORD +172 -0
- {synapse_sdk-1.0.0a11.dist-info → synapse_sdk-2026.1.1b2.dist-info}/WHEEL +1 -1
- synapse_sdk-2026.1.1b2.dist-info/licenses/LICENSE +201 -0
- locale/en/LC_MESSAGES/messages.mo +0 -0
- locale/en/LC_MESSAGES/messages.po +0 -39
- locale/ko/LC_MESSAGES/messages.mo +0 -0
- locale/ko/LC_MESSAGES/messages.po +0 -34
- synapse_sdk/cli/create_plugin.py +0 -10
- synapse_sdk/clients/agent/core.py +0 -7
- synapse_sdk/clients/agent/service.py +0 -15
- synapse_sdk/clients/backend/dataset.py +0 -51
- synapse_sdk/clients/ray/__init__.py +0 -6
- synapse_sdk/clients/ray/core.py +0 -22
- synapse_sdk/clients/ray/serve.py +0 -20
- synapse_sdk/i18n.py +0 -35
- synapse_sdk/plugins/categories/__init__.py +0 -0
- synapse_sdk/plugins/categories/base.py +0 -235
- synapse_sdk/plugins/categories/data_validation/__init__.py +0 -0
- synapse_sdk/plugins/categories/data_validation/actions/__init__.py +0 -0
- synapse_sdk/plugins/categories/data_validation/actions/validation.py +0 -10
- synapse_sdk/plugins/categories/data_validation/templates/config.yaml +0 -3
- synapse_sdk/plugins/categories/data_validation/templates/plugin/__init__.py +0 -0
- synapse_sdk/plugins/categories/data_validation/templates/plugin/validation.py +0 -5
- synapse_sdk/plugins/categories/decorators.py +0 -13
- synapse_sdk/plugins/categories/export/__init__.py +0 -0
- synapse_sdk/plugins/categories/export/actions/__init__.py +0 -0
- synapse_sdk/plugins/categories/export/actions/export.py +0 -10
- synapse_sdk/plugins/categories/import/__init__.py +0 -0
- synapse_sdk/plugins/categories/import/actions/__init__.py +0 -0
- synapse_sdk/plugins/categories/import/actions/import.py +0 -10
- synapse_sdk/plugins/categories/neural_net/__init__.py +0 -0
- synapse_sdk/plugins/categories/neural_net/actions/__init__.py +0 -0
- synapse_sdk/plugins/categories/neural_net/actions/deployment.py +0 -45
- synapse_sdk/plugins/categories/neural_net/actions/inference.py +0 -18
- synapse_sdk/plugins/categories/neural_net/actions/test.py +0 -10
- synapse_sdk/plugins/categories/neural_net/actions/train.py +0 -143
- synapse_sdk/plugins/categories/neural_net/templates/config.yaml +0 -12
- synapse_sdk/plugins/categories/neural_net/templates/plugin/__init__.py +0 -0
- synapse_sdk/plugins/categories/neural_net/templates/plugin/inference.py +0 -4
- synapse_sdk/plugins/categories/neural_net/templates/plugin/test.py +0 -2
- synapse_sdk/plugins/categories/neural_net/templates/plugin/train.py +0 -14
- synapse_sdk/plugins/categories/post_annotation/__init__.py +0 -0
- synapse_sdk/plugins/categories/post_annotation/actions/__init__.py +0 -0
- synapse_sdk/plugins/categories/post_annotation/actions/post_annotation.py +0 -10
- synapse_sdk/plugins/categories/post_annotation/templates/config.yaml +0 -3
- synapse_sdk/plugins/categories/post_annotation/templates/plugin/__init__.py +0 -0
- synapse_sdk/plugins/categories/post_annotation/templates/plugin/post_annotation.py +0 -3
- synapse_sdk/plugins/categories/pre_annotation/__init__.py +0 -0
- synapse_sdk/plugins/categories/pre_annotation/actions/__init__.py +0 -0
- synapse_sdk/plugins/categories/pre_annotation/actions/pre_annotation.py +0 -10
- synapse_sdk/plugins/categories/pre_annotation/templates/config.yaml +0 -3
- synapse_sdk/plugins/categories/pre_annotation/templates/plugin/__init__.py +0 -0
- synapse_sdk/plugins/categories/pre_annotation/templates/plugin/pre_annotation.py +0 -3
- synapse_sdk/plugins/categories/registry.py +0 -16
- synapse_sdk/plugins/categories/smart_tool/__init__.py +0 -0
- synapse_sdk/plugins/categories/smart_tool/actions/__init__.py +0 -0
- synapse_sdk/plugins/categories/smart_tool/actions/auto_label.py +0 -37
- synapse_sdk/plugins/categories/smart_tool/templates/config.yaml +0 -7
- synapse_sdk/plugins/categories/smart_tool/templates/plugin/__init__.py +0 -0
- synapse_sdk/plugins/categories/smart_tool/templates/plugin/auto_label.py +0 -11
- synapse_sdk/plugins/categories/templates.py +0 -32
- synapse_sdk/plugins/cli/__init__.py +0 -21
- synapse_sdk/plugins/cli/publish.py +0 -37
- synapse_sdk/plugins/cli/run.py +0 -67
- synapse_sdk/plugins/exceptions.py +0 -22
- synapse_sdk/plugins/models.py +0 -121
- synapse_sdk/plugins/templates/cookiecutter.json +0 -11
- synapse_sdk/plugins/templates/hooks/post_gen_project.py +0 -3
- synapse_sdk/plugins/templates/hooks/pre_prompt.py +0 -21
- synapse_sdk/plugins/templates/synapse-{{cookiecutter.plugin_code}}-plugin/.env +0 -24
- synapse_sdk/plugins/templates/synapse-{{cookiecutter.plugin_code}}-plugin/.env.dist +0 -24
- synapse_sdk/plugins/templates/synapse-{{cookiecutter.plugin_code}}-plugin/.gitignore +0 -27
- synapse_sdk/plugins/templates/synapse-{{cookiecutter.plugin_code}}-plugin/.pre-commit-config.yaml +0 -7
- synapse_sdk/plugins/templates/synapse-{{cookiecutter.plugin_code}}-plugin/README.md +0 -5
- synapse_sdk/plugins/templates/synapse-{{cookiecutter.plugin_code}}-plugin/config.yaml +0 -6
- synapse_sdk/plugins/templates/synapse-{{cookiecutter.plugin_code}}-plugin/main.py +0 -4
- synapse_sdk/plugins/templates/synapse-{{cookiecutter.plugin_code}}-plugin/plugin/__init__.py +0 -0
- synapse_sdk/plugins/templates/synapse-{{cookiecutter.plugin_code}}-plugin/pyproject.toml +0 -13
- synapse_sdk/plugins/templates/synapse-{{cookiecutter.plugin_code}}-plugin/requirements.txt +0 -1
- synapse_sdk/shared/enums.py +0 -8
- synapse_sdk/utils/debug.py +0 -5
- synapse_sdk/utils/file.py +0 -87
- synapse_sdk/utils/module_loading.py +0 -29
- synapse_sdk/utils/pydantic/__init__.py +0 -0
- synapse_sdk/utils/pydantic/config.py +0 -4
- synapse_sdk/utils/pydantic/errors.py +0 -33
- synapse_sdk/utils/pydantic/validators.py +0 -7
- synapse_sdk/utils/storage.py +0 -91
- synapse_sdk/utils/string.py +0 -11
- synapse_sdk-1.0.0a11.dist-info/LICENSE +0 -21
- synapse_sdk-1.0.0a11.dist-info/METADATA +0 -43
- synapse_sdk-1.0.0a11.dist-info/RECORD +0 -111
- {synapse_sdk-1.0.0a11.dist-info → synapse_sdk-2026.1.1b2.dist-info}/entry_points.txt +0 -0
- {synapse_sdk-1.0.0a11.dist-info → synapse_sdk-2026.1.1b2.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
"""Plugin local test command implementation."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from dataclasses import dataclass
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from typing import Any, Literal
|
|
8
|
+
|
|
9
|
+
from rich.console import Console
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@dataclass
|
|
13
|
+
class TestResult:
|
|
14
|
+
"""Result of plugin test execution."""
|
|
15
|
+
|
|
16
|
+
action: str
|
|
17
|
+
plugin: str
|
|
18
|
+
result: Any
|
|
19
|
+
mode: str
|
|
20
|
+
job_id: str | None = None
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def test_plugin(
|
|
24
|
+
action: str,
|
|
25
|
+
console: Console,
|
|
26
|
+
*,
|
|
27
|
+
path: Path,
|
|
28
|
+
params: dict[str, Any] | None = None,
|
|
29
|
+
mode: Literal['local', 'task', 'job'] = 'local',
|
|
30
|
+
ray_address: str = 'auto',
|
|
31
|
+
num_gpus: int | None = None,
|
|
32
|
+
num_cpus: int | None = None,
|
|
33
|
+
job_id: str | None = None,
|
|
34
|
+
env: dict[str, Any] | None = None,
|
|
35
|
+
include_sdk: bool = False,
|
|
36
|
+
) -> TestResult:
|
|
37
|
+
"""Execute plugin action for testing.
|
|
38
|
+
|
|
39
|
+
Args:
|
|
40
|
+
action: Action name to execute (e.g., test, train).
|
|
41
|
+
console: Rich console.
|
|
42
|
+
path: Plugin directory path.
|
|
43
|
+
params: Optional parameters to pass to the action.
|
|
44
|
+
mode: Execution mode:
|
|
45
|
+
- 'local': In-process execution (best for debugging)
|
|
46
|
+
- 'task': Ray Actor execution (no log streaming)
|
|
47
|
+
- 'job': Ray Jobs API with log streaming (recommended for remote)
|
|
48
|
+
ray_address: Ray cluster address (for 'task'/'job' modes).
|
|
49
|
+
num_gpus: Number of GPUs to request.
|
|
50
|
+
num_cpus: Number of CPUs to request.
|
|
51
|
+
job_id: Optional job identifier for tracking.
|
|
52
|
+
env: Environment variables to pass.
|
|
53
|
+
|
|
54
|
+
Returns:
|
|
55
|
+
TestResult with action result.
|
|
56
|
+
|
|
57
|
+
Raises:
|
|
58
|
+
ActionNotFoundError: If action doesn't exist.
|
|
59
|
+
ExecutionError: If execution fails.
|
|
60
|
+
"""
|
|
61
|
+
import sys
|
|
62
|
+
|
|
63
|
+
from synapse_sdk.plugins.discovery import PluginDiscovery
|
|
64
|
+
|
|
65
|
+
# Add plugin directory to sys.path so entrypoints like 'plugin.test.TestAction' can be imported
|
|
66
|
+
path_str = str(path)
|
|
67
|
+
if path_str not in sys.path:
|
|
68
|
+
sys.path.insert(0, path_str)
|
|
69
|
+
|
|
70
|
+
# Discover plugin
|
|
71
|
+
discovery = PluginDiscovery.from_path(path)
|
|
72
|
+
config = discovery.config
|
|
73
|
+
plugin_code = config.code
|
|
74
|
+
|
|
75
|
+
# Merge env: CLI env takes precedence over config env
|
|
76
|
+
merged_env = {**config.env, **(env or {})}
|
|
77
|
+
|
|
78
|
+
console.print(f'[dim]Plugin:[/dim] {plugin_code}')
|
|
79
|
+
console.print(f'[dim]Action:[/dim] {action}')
|
|
80
|
+
console.print(f'[dim]Mode:[/dim] {mode}')
|
|
81
|
+
|
|
82
|
+
params = params or {}
|
|
83
|
+
|
|
84
|
+
if mode == 'local':
|
|
85
|
+
from synapse_sdk.plugins.executors.local import LocalExecutor
|
|
86
|
+
|
|
87
|
+
console.print()
|
|
88
|
+
|
|
89
|
+
# Get action class (local import works since we're in-process)
|
|
90
|
+
action_cls = discovery.get_action_class(action)
|
|
91
|
+
|
|
92
|
+
executor = LocalExecutor(env=merged_env, job_id=job_id)
|
|
93
|
+
result = executor.execute(action_cls, params)
|
|
94
|
+
|
|
95
|
+
return TestResult(
|
|
96
|
+
action=action,
|
|
97
|
+
plugin=plugin_code,
|
|
98
|
+
result=result,
|
|
99
|
+
mode=mode,
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
elif mode == 'task':
|
|
103
|
+
from synapse_sdk.plugins.executors.ray.task import RayActorExecutor
|
|
104
|
+
|
|
105
|
+
if num_gpus is not None:
|
|
106
|
+
console.print(f'[dim]GPUs:[/dim] {num_gpus}')
|
|
107
|
+
if num_cpus is not None:
|
|
108
|
+
console.print(f'[dim]CPUs:[/dim] {num_cpus}')
|
|
109
|
+
console.print()
|
|
110
|
+
|
|
111
|
+
# Get entrypoint string (don't load class locally - it runs on remote Ray worker)
|
|
112
|
+
entrypoint = discovery.get_action_entrypoint(action)
|
|
113
|
+
|
|
114
|
+
executor = RayActorExecutor(
|
|
115
|
+
working_dir=path,
|
|
116
|
+
ray_address=ray_address,
|
|
117
|
+
num_gpus=num_gpus,
|
|
118
|
+
num_cpus=num_cpus,
|
|
119
|
+
env=merged_env,
|
|
120
|
+
job_id=job_id,
|
|
121
|
+
include_sdk=include_sdk,
|
|
122
|
+
package_manager=config.package_manager,
|
|
123
|
+
package_manager_options=config.package_manager_options or None,
|
|
124
|
+
wheels_dir=config.wheels_dir,
|
|
125
|
+
runtime_env=config.runtime_env or None,
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
try:
|
|
129
|
+
result = executor.execute(entrypoint, params)
|
|
130
|
+
finally:
|
|
131
|
+
executor.shutdown()
|
|
132
|
+
|
|
133
|
+
return TestResult(
|
|
134
|
+
action=action,
|
|
135
|
+
plugin=plugin_code,
|
|
136
|
+
result=result,
|
|
137
|
+
mode=mode,
|
|
138
|
+
)
|
|
139
|
+
|
|
140
|
+
else: # mode == 'job' (or 'jobs-api' alias)
|
|
141
|
+
from synapse_sdk.plugins.executors.ray.jobs_api import RayJobsApiExecutor
|
|
142
|
+
|
|
143
|
+
# Derive dashboard address from ray_address
|
|
144
|
+
if ray_address.startswith('ray://'):
|
|
145
|
+
from urllib.parse import urlparse
|
|
146
|
+
|
|
147
|
+
parsed = urlparse(ray_address)
|
|
148
|
+
dashboard_addr = f'http://{parsed.hostname}:8265'
|
|
149
|
+
else:
|
|
150
|
+
dashboard_addr = 'http://localhost:8265'
|
|
151
|
+
|
|
152
|
+
console.print(f'[dim]Dashboard:[/dim] {dashboard_addr}')
|
|
153
|
+
if num_gpus is not None:
|
|
154
|
+
console.print(f'[dim]GPUs:[/dim] {num_gpus}')
|
|
155
|
+
if num_cpus is not None:
|
|
156
|
+
console.print(f'[dim]CPUs:[/dim] {num_cpus}')
|
|
157
|
+
console.print()
|
|
158
|
+
|
|
159
|
+
# Get entrypoint string (don't load class locally - it runs on remote Ray worker)
|
|
160
|
+
entrypoint = discovery.get_action_entrypoint(action)
|
|
161
|
+
|
|
162
|
+
executor = RayJobsApiExecutor(
|
|
163
|
+
dashboard_address=dashboard_addr,
|
|
164
|
+
working_dir=path,
|
|
165
|
+
num_gpus=num_gpus,
|
|
166
|
+
num_cpus=num_cpus,
|
|
167
|
+
env=merged_env,
|
|
168
|
+
include_sdk=include_sdk,
|
|
169
|
+
package_manager=config.package_manager,
|
|
170
|
+
package_manager_options=config.package_manager_options or None,
|
|
171
|
+
)
|
|
172
|
+
|
|
173
|
+
# Submit job via Ray Jobs API
|
|
174
|
+
submitted_job_id = executor.submit(entrypoint, params, job_id=job_id)
|
|
175
|
+
|
|
176
|
+
console.print(f'[dim]Job submitted:[/dim] {submitted_job_id}')
|
|
177
|
+
console.print('[dim]Streaming logs (Ctrl+C to stop watching)...[/dim]')
|
|
178
|
+
console.print()
|
|
179
|
+
|
|
180
|
+
# Stream logs in real-time
|
|
181
|
+
try:
|
|
182
|
+
for log_line in executor.stream_logs(submitted_job_id):
|
|
183
|
+
console.print(log_line, end='')
|
|
184
|
+
except KeyboardInterrupt:
|
|
185
|
+
console.print('\n[dim]Stopped watching logs.[/dim]')
|
|
186
|
+
|
|
187
|
+
# Get final result
|
|
188
|
+
result = executor.get_result(submitted_job_id, timeout=30)
|
|
189
|
+
console.print('\n[dim]Status:[/dim] SUCCEEDED')
|
|
190
|
+
|
|
191
|
+
return TestResult(
|
|
192
|
+
action=action,
|
|
193
|
+
plugin=plugin_code,
|
|
194
|
+
result=result,
|
|
195
|
+
mode=mode,
|
|
196
|
+
job_id=submitted_job_id,
|
|
197
|
+
)
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
__all__ = ['TestResult', 'test_plugin']
|
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
# Synapse SDK Clients
|
|
2
|
+
|
|
3
|
+
HTTP client module providing both synchronous (sync) and asynchronous (async) API clients.
|
|
4
|
+
|
|
5
|
+
## Architecture Overview
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
synapse_sdk/clients/
|
|
9
|
+
├── __init__.py # Public API exports
|
|
10
|
+
├── base.py # BaseClient, AsyncBaseClient
|
|
11
|
+
├── protocols.py # ClientProtocol, AsyncClientProtocol
|
|
12
|
+
├── utils.py # Shared utility functions
|
|
13
|
+
├── validation.py # ValidationMixin
|
|
14
|
+
├── exceptions.py # Exception re-exports
|
|
15
|
+
├── _template.py # New client template
|
|
16
|
+
├── agent/ # Agent API clients
|
|
17
|
+
│ ├── __init__.py # AgentClient, AsyncAgentClient
|
|
18
|
+
│ ├── ray.py # RayClientMixin
|
|
19
|
+
│ ├── async_ray.py # AsyncRayClientMixin
|
|
20
|
+
│ ├── container.py # ContainerClientMixin
|
|
21
|
+
│ └── plugin.py # PluginClientMixin
|
|
22
|
+
└── backend/ # Backend API clients
|
|
23
|
+
├── __init__.py # BackendClient
|
|
24
|
+
├── annotation.py # AnnotationClientMixin
|
|
25
|
+
├── core.py # CoreClientMixin
|
|
26
|
+
├── data_collection.py
|
|
27
|
+
├── hitl.py
|
|
28
|
+
├── integration.py
|
|
29
|
+
├── ml.py
|
|
30
|
+
└── models.py # Pydantic models
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Module Structure
|
|
34
|
+
|
|
35
|
+
### base.py
|
|
36
|
+
|
|
37
|
+
- `BaseClient`: Synchronous HTTP client based on requests
|
|
38
|
+
- `AsyncBaseClient`: Asynchronous HTTP client based on httpx
|
|
39
|
+
- Inherits `ValidationMixin` for Pydantic validation support
|
|
40
|
+
|
|
41
|
+
### protocols.py
|
|
42
|
+
|
|
43
|
+
- `ClientProtocol`: Synchronous client protocol
|
|
44
|
+
- `AsyncClientProtocol`: Asynchronous client protocol
|
|
45
|
+
- `@runtime_checkable` decorator enables runtime type checking
|
|
46
|
+
|
|
47
|
+
### utils.py
|
|
48
|
+
|
|
49
|
+
- `build_url()`: URL composition utility
|
|
50
|
+
- `extract_error_detail()`: Extract error details from response
|
|
51
|
+
- `parse_json_response()`: Parse JSON response
|
|
52
|
+
|
|
53
|
+
### validation.py
|
|
54
|
+
|
|
55
|
+
- `ValidationMixin`: Provides Pydantic validation methods
|
|
56
|
+
- `_validate_response()`: Validate response data
|
|
57
|
+
- `_validate_request()`: Validate request data
|
|
58
|
+
|
|
59
|
+
## Protocol-based Mixin Pattern
|
|
60
|
+
|
|
61
|
+
Use `ClientProtocol` as the `self` type hint in mixin classes:
|
|
62
|
+
|
|
63
|
+
```python
|
|
64
|
+
from typing import TYPE_CHECKING
|
|
65
|
+
|
|
66
|
+
if TYPE_CHECKING:
|
|
67
|
+
from synapse_sdk.clients.protocols import ClientProtocol
|
|
68
|
+
|
|
69
|
+
class MyMixin:
|
|
70
|
+
def get_something(self: ClientProtocol) -> dict:
|
|
71
|
+
# IDE autocompletion supported
|
|
72
|
+
return self._get('something/')
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### Why Use Protocol?
|
|
76
|
+
|
|
77
|
+
1. **IDE Autocompletion**: Using `self: ClientProtocol` instead of `self: BaseClient` allows IDE to recognize methods like `_get`, `_post`, etc.
|
|
78
|
+
2. **Avoid Circular Imports**: Import only within `TYPE_CHECKING` block to prevent runtime circular dependencies
|
|
79
|
+
3. **Flexible Type Checking**: Supports structural typing without actual class inheritance
|
|
80
|
+
|
|
81
|
+
## Exception Hierarchy
|
|
82
|
+
|
|
83
|
+
```
|
|
84
|
+
ClientError (base)
|
|
85
|
+
├── ClientConnectionError # Connection failure
|
|
86
|
+
├── ClientTimeoutError # Timeout
|
|
87
|
+
├── HTTPError # HTTP status code errors
|
|
88
|
+
│ ├── AuthenticationError (401)
|
|
89
|
+
│ ├── AuthorizationError (403)
|
|
90
|
+
│ ├── NotFoundError (404)
|
|
91
|
+
│ ├── ValidationError (400/422)
|
|
92
|
+
│ ├── RateLimitError (429)
|
|
93
|
+
│ └── ServerError (5xx)
|
|
94
|
+
└── StreamError # Streaming errors
|
|
95
|
+
├── StreamLimitExceededError
|
|
96
|
+
└── WebSocketError
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
### Exception Usage Example
|
|
100
|
+
|
|
101
|
+
```python
|
|
102
|
+
from synapse_sdk.exceptions import (
|
|
103
|
+
ClientError,
|
|
104
|
+
NotFoundError,
|
|
105
|
+
AuthenticationError,
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
try:
|
|
109
|
+
result = client.get_resource(123)
|
|
110
|
+
except NotFoundError:
|
|
111
|
+
print("Resource not found")
|
|
112
|
+
except AuthenticationError:
|
|
113
|
+
print("Authentication failed")
|
|
114
|
+
except ClientError as e:
|
|
115
|
+
print(f"Client error: {e.status_code}")
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
## Guide for Adding New Clients
|
|
119
|
+
|
|
120
|
+
### 1. Copy Template
|
|
121
|
+
|
|
122
|
+
Create a new client by referencing `_template.py`:
|
|
123
|
+
|
|
124
|
+
```python
|
|
125
|
+
from synapse_sdk.clients.base import BaseClient
|
|
126
|
+
|
|
127
|
+
class MyApiClient(BaseClient):
|
|
128
|
+
name = 'MyAPI'
|
|
129
|
+
|
|
130
|
+
def __init__(self, base_url: str, api_key: str, **kwargs):
|
|
131
|
+
super().__init__(base_url, **kwargs)
|
|
132
|
+
self.api_key = api_key
|
|
133
|
+
|
|
134
|
+
def _get_headers(self) -> dict[str, str]:
|
|
135
|
+
return {'X-API-Key': self.api_key}
|
|
136
|
+
|
|
137
|
+
def get_resource(self, resource_id: int) -> dict:
|
|
138
|
+
return self._get(f'resources/{resource_id}/')
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
### 2. Use Mixin Pattern (Optional)
|
|
142
|
+
|
|
143
|
+
For large APIs, separate functionality into mixins:
|
|
144
|
+
|
|
145
|
+
```python
|
|
146
|
+
# my_api/users.py
|
|
147
|
+
from typing import TYPE_CHECKING
|
|
148
|
+
|
|
149
|
+
if TYPE_CHECKING:
|
|
150
|
+
from synapse_sdk.clients.protocols import ClientProtocol
|
|
151
|
+
|
|
152
|
+
class UsersMixin:
|
|
153
|
+
def list_users(self: ClientProtocol) -> list[dict]:
|
|
154
|
+
return self._get('users/')
|
|
155
|
+
|
|
156
|
+
def get_user(self: ClientProtocol, user_id: int) -> dict:
|
|
157
|
+
return self._get(f'users/{user_id}/')
|
|
158
|
+
|
|
159
|
+
# my_api/__init__.py
|
|
160
|
+
from synapse_sdk.clients.base import BaseClient
|
|
161
|
+
from my_api.users import UsersMixin
|
|
162
|
+
|
|
163
|
+
class MyApiClient(UsersMixin, BaseClient):
|
|
164
|
+
name = 'MyAPI'
|
|
165
|
+
# ...
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
### 3. Pydantic Model Validation
|
|
169
|
+
|
|
170
|
+
Use Pydantic models for request/response validation:
|
|
171
|
+
|
|
172
|
+
```python
|
|
173
|
+
from pydantic import BaseModel
|
|
174
|
+
|
|
175
|
+
class UserCreate(BaseModel):
|
|
176
|
+
name: str
|
|
177
|
+
email: str
|
|
178
|
+
|
|
179
|
+
class UserResponse(BaseModel):
|
|
180
|
+
id: int
|
|
181
|
+
name: str
|
|
182
|
+
email: str
|
|
183
|
+
|
|
184
|
+
class MyApiClient(BaseClient):
|
|
185
|
+
def create_user(self, data: dict) -> dict:
|
|
186
|
+
return self._post(
|
|
187
|
+
'users/',
|
|
188
|
+
request_model=UserCreate,
|
|
189
|
+
response_model=UserResponse,
|
|
190
|
+
data=data,
|
|
191
|
+
)
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
### 4. Async Client Implementation
|
|
195
|
+
|
|
196
|
+
```python
|
|
197
|
+
from synapse_sdk.clients.base import AsyncBaseClient
|
|
198
|
+
|
|
199
|
+
class AsyncMyApiClient(AsyncBaseClient):
|
|
200
|
+
name = 'MyAPI'
|
|
201
|
+
|
|
202
|
+
def __init__(self, base_url: str, api_key: str, **kwargs):
|
|
203
|
+
super().__init__(base_url, **kwargs)
|
|
204
|
+
self.api_key = api_key
|
|
205
|
+
|
|
206
|
+
def _get_headers(self) -> dict[str, str]:
|
|
207
|
+
return {'X-API-Key': self.api_key}
|
|
208
|
+
|
|
209
|
+
async def get_resource(self, resource_id: int) -> dict:
|
|
210
|
+
return await self._get(f'resources/{resource_id}/')
|
|
211
|
+
|
|
212
|
+
# Usage
|
|
213
|
+
async with AsyncMyApiClient(base_url, api_key) as client:
|
|
214
|
+
result = await client.get_resource(123)
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
## Method Reference
|
|
218
|
+
|
|
219
|
+
### BaseClient / AsyncBaseClient
|
|
220
|
+
|
|
221
|
+
| Method | Description |
|
|
222
|
+
| ---------------------- | ---------------------------- |
|
|
223
|
+
| `_get(path, ...)` | GET request |
|
|
224
|
+
| `_post(path, ...)` | POST request |
|
|
225
|
+
| `_put(path, ...)` | PUT request |
|
|
226
|
+
| `_patch(path, ...)` | PATCH request |
|
|
227
|
+
| `_delete(path, ...)` | DELETE request |
|
|
228
|
+
| `_list(path, ...)` | List with pagination support |
|
|
229
|
+
| `_validate_response()` | Pydantic response validation |
|
|
230
|
+
| `_validate_request()` | Pydantic request validation |
|
|
231
|
+
|
|
232
|
+
### Utility Functions
|
|
233
|
+
|
|
234
|
+
| Function | Description |
|
|
235
|
+
| --------------------------------------- | ------------------------------ |
|
|
236
|
+
| `build_url(base, path, trailing_slash)` | URL composition |
|
|
237
|
+
| `extract_error_detail(response)` | Extract error details |
|
|
238
|
+
| `parse_json_response(response)` | Parse JSON response |
|
|
239
|
+
| `raise_for_status(status_code, detail)` | Raise exception by status code |
|
synapse_sdk/clients/__init__.py
CHANGED
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
"""Template for creating new HTTP clients.
|
|
2
|
+
|
|
3
|
+
This module provides template classes for building new sync and async
|
|
4
|
+
HTTP clients. Copy this file and modify it to create a new client.
|
|
5
|
+
|
|
6
|
+
Usage:
|
|
7
|
+
1. Copy this file to a new module (e.g., my_api/__init__.py)
|
|
8
|
+
2. Rename TemplateClient and AsyncTemplateClient
|
|
9
|
+
3. Implement _get_headers() with your authentication
|
|
10
|
+
4. Add domain-specific methods
|
|
11
|
+
|
|
12
|
+
Example:
|
|
13
|
+
>>> from synapse_sdk.clients.my_api import MyApiClient
|
|
14
|
+
>>> client = MyApiClient('https://api.example.com', api_key='secret')
|
|
15
|
+
>>> users = client.list_users()
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
from __future__ import annotations
|
|
19
|
+
|
|
20
|
+
from typing import TYPE_CHECKING, Any
|
|
21
|
+
|
|
22
|
+
import httpx
|
|
23
|
+
|
|
24
|
+
from synapse_sdk.clients.base import AsyncBaseClient, BaseClient
|
|
25
|
+
|
|
26
|
+
if TYPE_CHECKING:
|
|
27
|
+
from pydantic import BaseModel
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class TemplateClient(BaseClient):
|
|
31
|
+
"""Synchronous HTTP client template.
|
|
32
|
+
|
|
33
|
+
This template demonstrates how to create a new sync client by
|
|
34
|
+
extending BaseClient with custom authentication and methods.
|
|
35
|
+
|
|
36
|
+
Attributes:
|
|
37
|
+
name: Client name for error messages.
|
|
38
|
+
api_key: API key for authentication.
|
|
39
|
+
"""
|
|
40
|
+
|
|
41
|
+
name = 'TemplateAPI'
|
|
42
|
+
|
|
43
|
+
def __init__(
|
|
44
|
+
self,
|
|
45
|
+
base_url: str,
|
|
46
|
+
api_key: str,
|
|
47
|
+
*,
|
|
48
|
+
timeout: dict[str, int] | None = None,
|
|
49
|
+
):
|
|
50
|
+
"""Initialize the template client.
|
|
51
|
+
|
|
52
|
+
Args:
|
|
53
|
+
base_url: The base URL for all API requests.
|
|
54
|
+
api_key: API key for authentication.
|
|
55
|
+
timeout: Optional timeout configuration.
|
|
56
|
+
"""
|
|
57
|
+
super().__init__(base_url, timeout=timeout)
|
|
58
|
+
self.api_key = api_key
|
|
59
|
+
|
|
60
|
+
def _get_headers(self) -> dict[str, str]:
|
|
61
|
+
"""Return authentication headers.
|
|
62
|
+
|
|
63
|
+
Override this method to implement your authentication scheme.
|
|
64
|
+
Common patterns include:
|
|
65
|
+
- API key: {'X-API-Key': self.api_key}
|
|
66
|
+
- Bearer token: {'Authorization': f'Bearer {self.token}'}
|
|
67
|
+
- Basic auth: {'Authorization': f'Basic {base64_credentials}'}
|
|
68
|
+
"""
|
|
69
|
+
return {'X-API-Key': self.api_key}
|
|
70
|
+
|
|
71
|
+
# -------------------------------------------------------------------------
|
|
72
|
+
# Example API Methods
|
|
73
|
+
# -------------------------------------------------------------------------
|
|
74
|
+
|
|
75
|
+
def list_resources(
|
|
76
|
+
self,
|
|
77
|
+
params: dict[str, Any] | None = None,
|
|
78
|
+
*,
|
|
79
|
+
list_all: bool = False,
|
|
80
|
+
) -> dict[str, Any] | tuple[Any, int]:
|
|
81
|
+
"""List resources with optional pagination.
|
|
82
|
+
|
|
83
|
+
Args:
|
|
84
|
+
params: Query parameters for filtering.
|
|
85
|
+
list_all: If True, returns (generator, count).
|
|
86
|
+
|
|
87
|
+
Returns:
|
|
88
|
+
Paginated list or (generator, count).
|
|
89
|
+
"""
|
|
90
|
+
return self._list('resources/', params=params, list_all=list_all)
|
|
91
|
+
|
|
92
|
+
def get_resource(self, resource_id: int) -> dict[str, Any]:
|
|
93
|
+
"""Get a resource by ID.
|
|
94
|
+
|
|
95
|
+
Args:
|
|
96
|
+
resource_id: The resource ID.
|
|
97
|
+
|
|
98
|
+
Returns:
|
|
99
|
+
Resource data.
|
|
100
|
+
"""
|
|
101
|
+
return self._get(f'resources/{resource_id}/')
|
|
102
|
+
|
|
103
|
+
def create_resource(
|
|
104
|
+
self,
|
|
105
|
+
data: dict[str, Any],
|
|
106
|
+
request_model: type[BaseModel] | None = None,
|
|
107
|
+
response_model: type[BaseModel] | None = None,
|
|
108
|
+
) -> dict[str, Any]:
|
|
109
|
+
"""Create a new resource.
|
|
110
|
+
|
|
111
|
+
Args:
|
|
112
|
+
data: Resource data to create.
|
|
113
|
+
request_model: Optional Pydantic model for request validation.
|
|
114
|
+
response_model: Optional Pydantic model for response validation.
|
|
115
|
+
|
|
116
|
+
Returns:
|
|
117
|
+
Created resource data.
|
|
118
|
+
"""
|
|
119
|
+
return self._post(
|
|
120
|
+
'resources/',
|
|
121
|
+
request_model=request_model,
|
|
122
|
+
response_model=response_model,
|
|
123
|
+
data=data,
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
def update_resource(
|
|
127
|
+
self,
|
|
128
|
+
resource_id: int,
|
|
129
|
+
data: dict[str, Any],
|
|
130
|
+
) -> dict[str, Any]:
|
|
131
|
+
"""Update a resource.
|
|
132
|
+
|
|
133
|
+
Args:
|
|
134
|
+
resource_id: The resource ID to update.
|
|
135
|
+
data: Fields to update.
|
|
136
|
+
|
|
137
|
+
Returns:
|
|
138
|
+
Updated resource data.
|
|
139
|
+
"""
|
|
140
|
+
return self._patch(f'resources/{resource_id}/', data=data)
|
|
141
|
+
|
|
142
|
+
def delete_resource(self, resource_id: int) -> None:
|
|
143
|
+
"""Delete a resource.
|
|
144
|
+
|
|
145
|
+
Args:
|
|
146
|
+
resource_id: The resource ID to delete.
|
|
147
|
+
"""
|
|
148
|
+
self._delete(f'resources/{resource_id}/')
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
class AsyncTemplateClient(AsyncBaseClient):
|
|
152
|
+
"""Asynchronous HTTP client template.
|
|
153
|
+
|
|
154
|
+
This template demonstrates how to create a new async client by
|
|
155
|
+
extending AsyncBaseClient with custom authentication and methods.
|
|
156
|
+
|
|
157
|
+
Attributes:
|
|
158
|
+
name: Client name for error messages.
|
|
159
|
+
api_key: API key for authentication.
|
|
160
|
+
"""
|
|
161
|
+
|
|
162
|
+
name = 'TemplateAPI'
|
|
163
|
+
|
|
164
|
+
def __init__(
|
|
165
|
+
self,
|
|
166
|
+
base_url: str,
|
|
167
|
+
api_key: str,
|
|
168
|
+
*,
|
|
169
|
+
timeout: float | httpx.Timeout | None = None,
|
|
170
|
+
):
|
|
171
|
+
"""Initialize the async template client.
|
|
172
|
+
|
|
173
|
+
Args:
|
|
174
|
+
base_url: The base URL for all API requests.
|
|
175
|
+
api_key: API key for authentication.
|
|
176
|
+
timeout: Optional timeout configuration.
|
|
177
|
+
"""
|
|
178
|
+
super().__init__(base_url, timeout=timeout)
|
|
179
|
+
self.api_key = api_key
|
|
180
|
+
|
|
181
|
+
def _get_headers(self) -> dict[str, str]:
|
|
182
|
+
"""Return authentication headers."""
|
|
183
|
+
return {'X-API-Key': self.api_key}
|
|
184
|
+
|
|
185
|
+
# -------------------------------------------------------------------------
|
|
186
|
+
# Example API Methods (Async versions)
|
|
187
|
+
# -------------------------------------------------------------------------
|
|
188
|
+
|
|
189
|
+
async def list_resources(
|
|
190
|
+
self,
|
|
191
|
+
params: dict[str, Any] | None = None,
|
|
192
|
+
*,
|
|
193
|
+
list_all: bool = False,
|
|
194
|
+
) -> dict[str, Any] | tuple[Any, int]:
|
|
195
|
+
"""List resources with optional pagination.
|
|
196
|
+
|
|
197
|
+
Args:
|
|
198
|
+
params: Query parameters for filtering.
|
|
199
|
+
list_all: If True, returns (generator, count).
|
|
200
|
+
|
|
201
|
+
Returns:
|
|
202
|
+
Paginated list or (generator, count).
|
|
203
|
+
"""
|
|
204
|
+
return await self._list('resources/', params=params, list_all=list_all)
|
|
205
|
+
|
|
206
|
+
async def get_resource(self, resource_id: int) -> dict[str, Any]:
|
|
207
|
+
"""Get a resource by ID.
|
|
208
|
+
|
|
209
|
+
Args:
|
|
210
|
+
resource_id: The resource ID.
|
|
211
|
+
|
|
212
|
+
Returns:
|
|
213
|
+
Resource data.
|
|
214
|
+
"""
|
|
215
|
+
return await self._get(f'resources/{resource_id}/')
|
|
216
|
+
|
|
217
|
+
async def create_resource(
|
|
218
|
+
self,
|
|
219
|
+
data: dict[str, Any],
|
|
220
|
+
request_model: type[BaseModel] | None = None,
|
|
221
|
+
response_model: type[BaseModel] | None = None,
|
|
222
|
+
) -> dict[str, Any]:
|
|
223
|
+
"""Create a new resource.
|
|
224
|
+
|
|
225
|
+
Args:
|
|
226
|
+
data: Resource data to create.
|
|
227
|
+
request_model: Optional Pydantic model for request validation.
|
|
228
|
+
response_model: Optional Pydantic model for response validation.
|
|
229
|
+
|
|
230
|
+
Returns:
|
|
231
|
+
Created resource data.
|
|
232
|
+
"""
|
|
233
|
+
return await self._post(
|
|
234
|
+
'resources/',
|
|
235
|
+
request_model=request_model,
|
|
236
|
+
response_model=response_model,
|
|
237
|
+
json=data,
|
|
238
|
+
)
|
|
239
|
+
|
|
240
|
+
async def update_resource(
|
|
241
|
+
self,
|
|
242
|
+
resource_id: int,
|
|
243
|
+
data: dict[str, Any],
|
|
244
|
+
) -> dict[str, Any]:
|
|
245
|
+
"""Update a resource.
|
|
246
|
+
|
|
247
|
+
Args:
|
|
248
|
+
resource_id: The resource ID to update.
|
|
249
|
+
data: Fields to update.
|
|
250
|
+
|
|
251
|
+
Returns:
|
|
252
|
+
Updated resource data.
|
|
253
|
+
"""
|
|
254
|
+
return await self._patch(f'resources/{resource_id}/', json=data)
|
|
255
|
+
|
|
256
|
+
async def delete_resource(self, resource_id: int) -> None:
|
|
257
|
+
"""Delete a resource.
|
|
258
|
+
|
|
259
|
+
Args:
|
|
260
|
+
resource_id: The resource ID to delete.
|
|
261
|
+
"""
|
|
262
|
+
await self._delete(f'resources/{resource_id}/')
|
|
263
|
+
|
|
264
|
+
|
|
265
|
+
# Note: This file is a template. Do not import or use these classes directly.
|
|
266
|
+
# Copy and modify them for your specific API client implementation.
|