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.
- wyseos_sdk-0.2.0/LICENSE +21 -0
- wyseos_sdk-0.2.0/PKG-INFO +185 -0
- wyseos_sdk-0.2.0/README.md +143 -0
- wyseos_sdk-0.2.0/pyproject.toml +37 -0
- wyseos_sdk-0.2.0/setup.cfg +4 -0
- wyseos_sdk-0.2.0/wyseos/__init__.py +0 -0
- wyseos_sdk-0.2.0/wyseos/mate/__init__.py +22 -0
- wyseos_sdk-0.2.0/wyseos/mate/client.py +156 -0
- wyseos_sdk-0.2.0/wyseos/mate/config.py +100 -0
- wyseos_sdk-0.2.0/wyseos/mate/constants.py +106 -0
- wyseos_sdk-0.2.0/wyseos/mate/errors.py +161 -0
- wyseos_sdk-0.2.0/wyseos/mate/models.py +341 -0
- wyseos_sdk-0.2.0/wyseos/mate/plan.py +310 -0
- wyseos_sdk-0.2.0/wyseos/mate/services/__init__.py +17 -0
- wyseos_sdk-0.2.0/wyseos/mate/services/agent.py +92 -0
- wyseos_sdk-0.2.0/wyseos/mate/services/browser.py +145 -0
- wyseos_sdk-0.2.0/wyseos/mate/services/session.py +194 -0
- wyseos_sdk-0.2.0/wyseos/mate/services/team.py +91 -0
- wyseos_sdk-0.2.0/wyseos/mate/services/user.py +68 -0
- wyseos_sdk-0.2.0/wyseos/mate/websocket.py +372 -0
- wyseos_sdk-0.2.0/wyseos_sdk.egg-info/PKG-INFO +185 -0
- wyseos_sdk-0.2.0/wyseos_sdk.egg-info/SOURCES.txt +23 -0
- wyseos_sdk-0.2.0/wyseos_sdk.egg-info/dependency_links.txt +1 -0
- wyseos_sdk-0.2.0/wyseos_sdk.egg-info/requires.txt +4 -0
- wyseos_sdk-0.2.0/wyseos_sdk.egg-info/top_level.txt +1 -0
wyseos_sdk-0.2.0/LICENSE
ADDED
|
@@ -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
|
+
[](https://github.com/WyseOS/wyseos-sdk-python/actions/workflows/python-sdk-ci.yml)
|
|
46
|
+
[](https://www.python.org/downloads/)
|
|
47
|
+
[](https://pypi.org/project/wyseos-sdk/)
|
|
48
|
+
[](./README.md)
|
|
49
|
+
[](./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
|
+
[](https://github.com/WyseOS/wyseos-sdk-python/actions/workflows/python-sdk-ci.yml)
|
|
4
|
+
[](https://www.python.org/downloads/)
|
|
5
|
+
[](https://pypi.org/project/wyseos-sdk/)
|
|
6
|
+
[](./README.md)
|
|
7
|
+
[](./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*"]
|
|
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)
|