wyseos-sdk 0.2.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 WyseOS
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,185 @@
1
+ Metadata-Version: 2.4
2
+ Name: wyseos-sdk
3
+ Version: 0.2.0
4
+ Summary: Python SDK for WyseOS
5
+ Author-email: Wyse <info@wyseos.com>
6
+ License: MIT License
7
+
8
+ Copyright (c) 2025 WyseOS
9
+
10
+ Permission is hereby granted, free of charge, to any person obtaining a copy
11
+ of this software and associated documentation files (the "Software"), to deal
12
+ in the Software without restriction, including without limitation the rights
13
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14
+ copies of the Software, and to permit persons to whom the Software is
15
+ furnished to do so, subject to the following conditions:
16
+
17
+ The above copyright notice and this permission notice shall be included in all
18
+ copies or substantial portions of the Software.
19
+
20
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26
+ SOFTWARE.
27
+
28
+ Project-URL: Homepage, https://github.com/WyseOS/wyseos-sdk-python
29
+ Project-URL: Repository, https://github.com/WyseOS/wyseos-sdk-python
30
+ Project-URL: Issues, https://github.com/WyseOS/wyseos-sdk-python/issues
31
+ Project-URL: Documentation, https://github.com/WyseOS/wyseos-sdk-python#readme
32
+ Project-URL: PyPI, https://pypi.org/project/wyseos-sdk/
33
+ Keywords: WyseOS,Mate,AI,Agentic Web,Mutlti Agent System
34
+ Requires-Python: >=3.9
35
+ Description-Content-Type: text/markdown
36
+ License-File: LICENSE
37
+ Requires-Dist: requests>=2.31.0
38
+ Requires-Dist: pydantic>=2.0.0
39
+ Requires-Dist: websockets>=11.0.0
40
+ Requires-Dist: PyYAML>=6.0.0
41
+ Dynamic: license-file
42
+
43
+ # WyseOS SDK for Python
44
+
45
+ [![Python SDK CI/CD](https://github.com/WyseOS/wyseos-sdk-python/actions/workflows/python-sdk-ci.yml/badge.svg)](https://github.com/WyseOS/wyseos-sdk-python/actions/workflows/python-sdk-ci.yml)
46
+ [![Python Version](https://img.shields.io/badge/python-3.9%2B-blue)](https://www.python.org/downloads/)
47
+ [![PyPI Package](https://img.shields.io/badge/PyPI-wyseos--sdk-blue)](https://pypi.org/project/wyseos-sdk/)
48
+ [![Documentation](https://img.shields.io/badge/docs-comprehensive-green)](./README.md)
49
+ [![License](https://img.shields.io/badge/license-MIT-green)](./LICENSE)
50
+
51
+ WyseOS SDK Python for interacting with the WyseOS API. Built with modern Python practices, type safety, and real-time support.
52
+
53
+ ## 🚀 Features
54
+
55
+ - **🎯 Type Safe**: Pydantic models and validation
56
+ - **⚡ Real-time**: WebSocket client
57
+ - **🔧 Simple Config**: YAML config file support
58
+ - **🛡️ Robust Errors**: Clear, structured exceptions
59
+
60
+ ## 📦 Installation
61
+
62
+ ```bash
63
+ pip install wyseos-sdk
64
+ ```
65
+
66
+ ## 🚀 Examples
67
+
68
+ To help you get started, we've included a collection of ready-to-run examples in the `examples` directory.
69
+
70
+ ### Getting Started
71
+
72
+ The `examples/getting_started` directory contains a script demonstrating core features of the SDK. To run it:
73
+
74
+ 1. Navigate to `examples/getting_started`.
75
+ 2. Open `mate.yaml` and add your API key.
76
+ 3. Run the script: `python example.py`.
77
+
78
+ For a more detailed walkthrough, check out the **[Quick Start Guide](./examples/quickstart.md)**.
79
+
80
+ ## 📚 Documentation
81
+
82
+ - **[Installation Guide](./installation.md)**
83
+ - **[Quick Start Guide](./examples/quickstart.md)**
84
+
85
+ ## 🔧 Configuration
86
+
87
+ Create `mate.yaml`:
88
+
89
+ ```yaml
90
+ api_key: "your-api-key"
91
+ base_url: "https://api.mate.wyseos.com"
92
+ timeout: 30
93
+ ```
94
+
95
+ Load configuration:
96
+
97
+ ```python
98
+ from wyseos.mate import Client
99
+ from wyseos.mate.config import load_config
100
+
101
+ client = Client(load_config("mate.yaml"))
102
+ ```
103
+
104
+ ## 🌟 Client Services
105
+
106
+ - `client.user` — API key management
107
+ - `client.team` — Team retrieval
108
+ - `client.agent` — Agent retrieval
109
+ - `client.session` — Session create/info/messages
110
+ - `client.browser` — Browser info/pages/release
111
+
112
+ ## 🧩 Models and Pagination
113
+
114
+ - `ListOptions(page_num, page_size)`
115
+ - Most list endpoints return `PaginatedResponse[T]` with `data`, `total`, `page_num`, `page_size`, `total_page`.
116
+
117
+ ## 🔌 WebSocket
118
+
119
+ ```python
120
+ from wyseos.mate.websocket import WebSocketClient, MessageType
121
+
122
+ ws = WebSocketClient(base_url=client.base_url, api_key=client.api_key, session_id="your-session-id")
123
+ ws.set_connect_handler(lambda: print("Connected"))
124
+ ws.set_disconnect_handler(lambda: print("Disconnected"))
125
+ ws.set_message_handler(lambda m: print(m))
126
+ ws.connect("your-session-id")
127
+
128
+ # Start a task
129
+ ws.send_message({
130
+ "type": MessageType.START,
131
+ "data": {
132
+ "messages": [{"type": "task", "content": "Do something"}],
133
+ "attachments": [],
134
+ "team_id": "your-team-id",
135
+ "kb_ids": [],
136
+ },
137
+ })
138
+ ```
139
+
140
+ ## 🛠️ Development
141
+
142
+ ```bash
143
+ # Clone repository
144
+ git clone https://github.com/WyseOS/wyseos-sdk-python
145
+ cd wyseos-sdk-python
146
+
147
+ # Install in development mode
148
+ pip install -e .
149
+
150
+ # Optional development tools
151
+ pip install pytest pytest-cov black isort flake8 mypy
152
+ ```
153
+
154
+ ## 📊 Project Status
155
+
156
+ - Core implementation: ✅
157
+ - Documentation: ✅
158
+ - Tests: 🚧
159
+
160
+ ## 🤝 Contributing
161
+
162
+ 1. Fork
163
+ 2. Create a branch
164
+ 3. Commit
165
+ 4. Push
166
+ 5. Open a PR
167
+
168
+ ## 📄 License
169
+
170
+ MIT License — see `LICENSE`.
171
+
172
+ ## 🆘 Support
173
+
174
+ - Issues: https://github.com/WyseOS/wyseos-sdk-python/issues
175
+ - Email: support@wyseos.com
176
+
177
+ ## 🔗 Links
178
+
179
+ - PyPI: https://pypi.org/project/wyseos-sdk/
180
+ - API Docs: https://docs.wyseos.com
181
+ - Website: https://wyseos.com
182
+
183
+
184
+
185
+ Ready for production. Build with WyseOS SDK Python.
@@ -0,0 +1,143 @@
1
+ # WyseOS SDK for Python
2
+
3
+ [![Python SDK CI/CD](https://github.com/WyseOS/wyseos-sdk-python/actions/workflows/python-sdk-ci.yml/badge.svg)](https://github.com/WyseOS/wyseos-sdk-python/actions/workflows/python-sdk-ci.yml)
4
+ [![Python Version](https://img.shields.io/badge/python-3.9%2B-blue)](https://www.python.org/downloads/)
5
+ [![PyPI Package](https://img.shields.io/badge/PyPI-wyseos--sdk-blue)](https://pypi.org/project/wyseos-sdk/)
6
+ [![Documentation](https://img.shields.io/badge/docs-comprehensive-green)](./README.md)
7
+ [![License](https://img.shields.io/badge/license-MIT-green)](./LICENSE)
8
+
9
+ WyseOS SDK Python for interacting with the WyseOS API. Built with modern Python practices, type safety, and real-time support.
10
+
11
+ ## 🚀 Features
12
+
13
+ - **🎯 Type Safe**: Pydantic models and validation
14
+ - **⚡ Real-time**: WebSocket client
15
+ - **🔧 Simple Config**: YAML config file support
16
+ - **🛡️ Robust Errors**: Clear, structured exceptions
17
+
18
+ ## 📦 Installation
19
+
20
+ ```bash
21
+ pip install wyseos-sdk
22
+ ```
23
+
24
+ ## 🚀 Examples
25
+
26
+ To help you get started, we've included a collection of ready-to-run examples in the `examples` directory.
27
+
28
+ ### Getting Started
29
+
30
+ The `examples/getting_started` directory contains a script demonstrating core features of the SDK. To run it:
31
+
32
+ 1. Navigate to `examples/getting_started`.
33
+ 2. Open `mate.yaml` and add your API key.
34
+ 3. Run the script: `python example.py`.
35
+
36
+ For a more detailed walkthrough, check out the **[Quick Start Guide](./examples/quickstart.md)**.
37
+
38
+ ## 📚 Documentation
39
+
40
+ - **[Installation Guide](./installation.md)**
41
+ - **[Quick Start Guide](./examples/quickstart.md)**
42
+
43
+ ## 🔧 Configuration
44
+
45
+ Create `mate.yaml`:
46
+
47
+ ```yaml
48
+ api_key: "your-api-key"
49
+ base_url: "https://api.mate.wyseos.com"
50
+ timeout: 30
51
+ ```
52
+
53
+ Load configuration:
54
+
55
+ ```python
56
+ from wyseos.mate import Client
57
+ from wyseos.mate.config import load_config
58
+
59
+ client = Client(load_config("mate.yaml"))
60
+ ```
61
+
62
+ ## 🌟 Client Services
63
+
64
+ - `client.user` — API key management
65
+ - `client.team` — Team retrieval
66
+ - `client.agent` — Agent retrieval
67
+ - `client.session` — Session create/info/messages
68
+ - `client.browser` — Browser info/pages/release
69
+
70
+ ## 🧩 Models and Pagination
71
+
72
+ - `ListOptions(page_num, page_size)`
73
+ - Most list endpoints return `PaginatedResponse[T]` with `data`, `total`, `page_num`, `page_size`, `total_page`.
74
+
75
+ ## 🔌 WebSocket
76
+
77
+ ```python
78
+ from wyseos.mate.websocket import WebSocketClient, MessageType
79
+
80
+ ws = WebSocketClient(base_url=client.base_url, api_key=client.api_key, session_id="your-session-id")
81
+ ws.set_connect_handler(lambda: print("Connected"))
82
+ ws.set_disconnect_handler(lambda: print("Disconnected"))
83
+ ws.set_message_handler(lambda m: print(m))
84
+ ws.connect("your-session-id")
85
+
86
+ # Start a task
87
+ ws.send_message({
88
+ "type": MessageType.START,
89
+ "data": {
90
+ "messages": [{"type": "task", "content": "Do something"}],
91
+ "attachments": [],
92
+ "team_id": "your-team-id",
93
+ "kb_ids": [],
94
+ },
95
+ })
96
+ ```
97
+
98
+ ## 🛠️ Development
99
+
100
+ ```bash
101
+ # Clone repository
102
+ git clone https://github.com/WyseOS/wyseos-sdk-python
103
+ cd wyseos-sdk-python
104
+
105
+ # Install in development mode
106
+ pip install -e .
107
+
108
+ # Optional development tools
109
+ pip install pytest pytest-cov black isort flake8 mypy
110
+ ```
111
+
112
+ ## 📊 Project Status
113
+
114
+ - Core implementation: ✅
115
+ - Documentation: ✅
116
+ - Tests: 🚧
117
+
118
+ ## 🤝 Contributing
119
+
120
+ 1. Fork
121
+ 2. Create a branch
122
+ 3. Commit
123
+ 4. Push
124
+ 5. Open a PR
125
+
126
+ ## 📄 License
127
+
128
+ MIT License — see `LICENSE`.
129
+
130
+ ## 🆘 Support
131
+
132
+ - Issues: https://github.com/WyseOS/wyseos-sdk-python/issues
133
+ - Email: support@wyseos.com
134
+
135
+ ## 🔗 Links
136
+
137
+ - PyPI: https://pypi.org/project/wyseos-sdk/
138
+ - API Docs: https://docs.wyseos.com
139
+ - Website: https://wyseos.com
140
+
141
+
142
+
143
+ Ready for production. Build with WyseOS SDK Python.
@@ -0,0 +1,37 @@
1
+ [project]
2
+ name = "wyseos-sdk"
3
+ version = "0.2.0"
4
+ description = "Python SDK for WyseOS"
5
+ authors = [{ name = "Wyse", email = "info@wyseos.com" }]
6
+ requires-python = ">=3.9"
7
+ keywords = ["WyseOS", "Mate", "AI", "Agentic Web", "Mutlti Agent System"]
8
+ dependencies = [
9
+ "requests>=2.31.0",
10
+ "pydantic>=2.0.0",
11
+ "websockets>=11.0.0",
12
+ "PyYAML>=6.0.0",
13
+ ]
14
+
15
+
16
+ [project.urls]
17
+ Homepage = "https://github.com/WyseOS/wyseos-sdk-python"
18
+ Repository = "https://github.com/WyseOS/wyseos-sdk-python"
19
+ Issues = "https://github.com/WyseOS/wyseos-sdk-python/issues"
20
+ Documentation = "https://github.com/WyseOS/wyseos-sdk-python#readme"
21
+ PyPI = "https://pypi.org/project/wyseos-sdk/"
22
+
23
+
24
+ [project.readme]
25
+ file = "README.md"
26
+ content-type = "text/markdown"
27
+
28
+ [project.license]
29
+ file = "LICENSE"
30
+
31
+ [build-system]
32
+ requires = ["setuptools>=61.0"]
33
+ build-backend = "setuptools.build_meta"
34
+
35
+ [tool.setuptools.packages.find]
36
+ where = ["."]
37
+ include = ["wyseos*"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
File without changes
@@ -0,0 +1,22 @@
1
+ """
2
+ WyseOS Mate Python SDK
3
+ """
4
+
5
+ __version__ = "0.2.0"
6
+ __author__ = "Wyse"
7
+ __email__ = "info@wyseos.com"
8
+
9
+ # Import main classes for easy access
10
+ from .client import Client
11
+ from .config import ClientOptions
12
+ from .errors import APIError, ConfigError, NetworkError, ValidationError, WebSocketError
13
+
14
+ __all__ = [
15
+ "Client",
16
+ "ClientOptions",
17
+ "APIError",
18
+ "ValidationError",
19
+ "NetworkError",
20
+ "WebSocketError",
21
+ "ConfigError",
22
+ ]
@@ -0,0 +1,156 @@
1
+ """
2
+ Core API client for the WyseOS Python SDK.
3
+ """
4
+
5
+ from typing import Dict, Optional, Type, TypeVar
6
+ from urllib.parse import urlencode, urljoin
7
+
8
+ import requests
9
+ from pydantic import BaseModel
10
+
11
+ from .config import ClientOptions
12
+ from .constants import (
13
+ CONTENT_TYPE_JSON,
14
+ DEFAULT_BASE_URL,
15
+ DEFAULT_TIMEOUT,
16
+ HEADER_ACCEPT,
17
+ HEADER_API_KEY,
18
+ HEADER_CONTENT_TYPE,
19
+ HEADER_USER_AGENT,
20
+ )
21
+ from .errors import APIError
22
+ from .services.agent import AgentService
23
+ from .services.browser import BrowserService
24
+ from .services.session import SessionService
25
+ from .services.team import TeamService
26
+ from .services.user import UserService
27
+
28
+ T = TypeVar("T", bound=BaseModel)
29
+
30
+
31
+ class Client:
32
+ """Main API client for the WyseOS."""
33
+
34
+ def __init__(self, options: Optional[ClientOptions] = None):
35
+ if options is None:
36
+ options = ClientOptions()
37
+
38
+ self.base_url = options.base_url or DEFAULT_BASE_URL
39
+ self.api_key = options.api_key
40
+ self.timeout = options.timeout or DEFAULT_TIMEOUT
41
+ self.user_agent = "WyseOSPython/0.2.0" # Set user_agent directly
42
+ self.http_client = requests.Session()
43
+
44
+ # Initialize services
45
+ self.user = UserService(self)
46
+ self.team = TeamService(self)
47
+ self.agent = AgentService(self)
48
+ self.session = SessionService(self)
49
+ self.browser = BrowserService(self)
50
+
51
+ def _do_request(
52
+ self, method: str, endpoint: str, body: Optional[Dict] = None
53
+ ) -> requests.Response:
54
+ url = urljoin(self.base_url, endpoint)
55
+
56
+ headers = {
57
+ HEADER_CONTENT_TYPE: CONTENT_TYPE_JSON,
58
+ HEADER_USER_AGENT: self.user_agent,
59
+ HEADER_ACCEPT: CONTENT_TYPE_JSON,
60
+ }
61
+
62
+ if self.api_key:
63
+ headers[HEADER_API_KEY] = self.api_key
64
+
65
+ try:
66
+ response = self.http_client.request(
67
+ method=method, url=url, headers=headers, json=body, timeout=self.timeout
68
+ )
69
+
70
+ if response.status_code != 200:
71
+ raise APIError(
72
+ message="Request failed", status_code=response.status_code
73
+ )
74
+
75
+ return response
76
+
77
+ except requests.exceptions.RequestException as e:
78
+ from .errors import NetworkError
79
+
80
+ raise NetworkError(
81
+ f"Network error during {method} request to {url}: {str(e)}", cause=e
82
+ )
83
+
84
+ def get(
85
+ self,
86
+ endpoint: str,
87
+ result_model: Type[T],
88
+ params: Optional[Dict[str, str]] = None,
89
+ ) -> T:
90
+ if params:
91
+ endpoint = self._build_url(endpoint, params)
92
+ response = self._do_request("GET", endpoint)
93
+ if result_model is dict:
94
+ return response.json()
95
+ return result_model.model_validate(response.json())
96
+
97
+ def get_paginated(
98
+ self,
99
+ endpoint: str,
100
+ result_model: Type[T],
101
+ params: Optional[Dict[str, str]] = None,
102
+ ) -> T:
103
+ if params:
104
+ endpoint = self._build_url(endpoint, params)
105
+
106
+ response = self._do_request("GET", endpoint)
107
+ response_data = response.json()
108
+
109
+ if "code" in response_data and "data" in response_data:
110
+ api_response = response_data
111
+ if api_response.get("code") != 0:
112
+ message = api_response.get("msg", "Unknown error")
113
+ raise APIError(message=message, code=api_response.get("code"))
114
+
115
+ data = api_response.get("data", {})
116
+ return result_model.model_validate(data)
117
+ else:
118
+ return result_model.model_validate(response_data)
119
+
120
+ def post(
121
+ self,
122
+ endpoint: str,
123
+ body: Optional[Dict] = None,
124
+ result_model: Optional[Type[T]] = None,
125
+ ) -> Optional[T]:
126
+ response = self._do_request("POST", endpoint, body)
127
+ if result_model and response.content:
128
+ if result_model is dict:
129
+ return response.json()
130
+ return result_model.model_validate(response.json())
131
+ return None
132
+
133
+ def put(
134
+ self,
135
+ endpoint: str,
136
+ body: Optional[Dict] = None,
137
+ result_model: Optional[Type[T]] = None,
138
+ ) -> Optional[T]:
139
+ response = self._do_request("PUT", endpoint, body)
140
+ if result_model and response.content:
141
+ if result_model is dict:
142
+ return response.json()
143
+ return result_model.model_validate(response.json())
144
+ return None
145
+
146
+ def delete(self, endpoint: str) -> None:
147
+ self._do_request("DELETE", endpoint)
148
+
149
+ def _build_url(self, endpoint: str, params: Dict[str, str]) -> str:
150
+ if not params:
151
+ return endpoint
152
+
153
+ query_string = urlencode(params)
154
+ separator = "&" if "?" in endpoint else "?"
155
+
156
+ return f"{endpoint}{separator}{query_string}"
@@ -0,0 +1,100 @@
1
+ """
2
+ Configuration management for the WyseOS Python SDK.
3
+ """
4
+
5
+ from pathlib import Path
6
+ from typing import Optional, TypeVar, Union
7
+
8
+ try:
9
+ import yaml
10
+ except ImportError:
11
+ raise ImportError(
12
+ "PyYAML is required for configuration file support. "
13
+ "Install it with: pip install pyyaml"
14
+ )
15
+ from pydantic import BaseModel, Field, validator
16
+
17
+ from .constants import (
18
+ DEFAULT_BASE_URL,
19
+ DEFAULT_CONFIG_FILE,
20
+ DEFAULT_TIMEOUT,
21
+ )
22
+ from .errors import ConfigError
23
+
24
+ T = TypeVar("T", bound=BaseModel)
25
+
26
+
27
+ class ClientOptions(BaseModel):
28
+ """Configuration options for the WyseOS client."""
29
+
30
+ api_key: Optional[str] = Field(
31
+ default=None,
32
+ min_length=1,
33
+ )
34
+ base_url: str = Field(
35
+ default=DEFAULT_BASE_URL,
36
+ min_length=1,
37
+ )
38
+ timeout: int = Field(
39
+ default=DEFAULT_TIMEOUT,
40
+ ge=1,
41
+ le=300,
42
+ )
43
+
44
+ class Config:
45
+ extra = "forbid"
46
+ validate_assignment = True
47
+
48
+ @validator("base_url")
49
+ def validate_base_url(cls, v):
50
+ if v is not None:
51
+ if not v.startswith(("http://", "https://")):
52
+ raise ValueError("Base URL must start with http:// or https://")
53
+ if v.endswith("/"):
54
+ v = v.rstrip("/")
55
+ return v
56
+
57
+ @validator("api_key")
58
+ def validate_api_key(cls, v):
59
+ if v is not None:
60
+ if len(v.strip()) == 0:
61
+ raise ValueError("API key cannot be empty")
62
+ return v
63
+
64
+
65
+ def load_config(config_path: Optional[Union[str, Path]] = None) -> ClientOptions:
66
+ if config_path is None:
67
+ config_path = Path.cwd() / DEFAULT_CONFIG_FILE
68
+ else:
69
+ config_path = Path(config_path)
70
+
71
+ if not config_path.exists():
72
+ raise ConfigError(f"Configuration file not found: {config_path}")
73
+
74
+ if not config_path.is_file():
75
+ raise ConfigError(f"Configuration path is not a file: {config_path}")
76
+
77
+ try:
78
+ with open(config_path, "r", encoding="utf-8") as f:
79
+ content = f.read()
80
+
81
+ try:
82
+ config_data = yaml.safe_load(content)
83
+ except yaml.YAMLError as e:
84
+ raise ConfigError(f"Invalid YAML in configuration file: {e}", cause=e)
85
+
86
+ if not isinstance(config_data, dict):
87
+ raise ConfigError("Configuration file must contain a YAML dictionary")
88
+
89
+ # If a 'mate' key exists at the top level, use that as the config dict
90
+ if "mate" in config_data and isinstance(config_data["mate"], dict):
91
+ config_data = config_data["mate"]
92
+
93
+ return ClientOptions(**config_data)
94
+
95
+ except IOError as e:
96
+ raise ConfigError(f"Unable to read configuration file: {e}", cause=e)
97
+ except Exception as e:
98
+ if isinstance(e, ConfigError):
99
+ raise
100
+ raise ConfigError(f"Unexpected error loading configuration: {e}", cause=e)