cosmon-agent-sdk 0.1.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.
- cosmon_agent_sdk-0.1.0/.gitignore +8 -0
- cosmon_agent_sdk-0.1.0/CHANGELOG.md +37 -0
- cosmon_agent_sdk-0.1.0/LICENSE +31 -0
- cosmon_agent_sdk-0.1.0/PKG-INFO +188 -0
- cosmon_agent_sdk-0.1.0/README.md +127 -0
- cosmon_agent_sdk-0.1.0/examples/README.md +35 -0
- cosmon_agent_sdk-0.1.0/examples/async_quickstart.py +24 -0
- cosmon_agent_sdk-0.1.0/examples/attachments.py +30 -0
- cosmon_agent_sdk-0.1.0/examples/batch.py +37 -0
- cosmon_agent_sdk-0.1.0/examples/elicitation.py +48 -0
- cosmon_agent_sdk-0.1.0/examples/handoff.py +46 -0
- cosmon_agent_sdk-0.1.0/examples/playground.py +89 -0
- cosmon_agent_sdk-0.1.0/examples/quickstart.py +21 -0
- cosmon_agent_sdk-0.1.0/examples/sessions.py +98 -0
- cosmon_agent_sdk-0.1.0/examples/streaming.py +28 -0
- cosmon_agent_sdk-0.1.0/examples/structural_study.py +45 -0
- cosmon_agent_sdk-0.1.0/pyproject.toml +73 -0
- cosmon_agent_sdk-0.1.0/src/cosmon_agent_sdk/__init__.py +71 -0
- cosmon_agent_sdk-0.1.0/src/cosmon_agent_sdk/__main__.py +8 -0
- cosmon_agent_sdk-0.1.0/src/cosmon_agent_sdk/async_client.py +683 -0
- cosmon_agent_sdk-0.1.0/src/cosmon_agent_sdk/cli.py +175 -0
- cosmon_agent_sdk-0.1.0/src/cosmon_agent_sdk/client.py +234 -0
- cosmon_agent_sdk-0.1.0/src/cosmon_agent_sdk/discovery.py +69 -0
- cosmon_agent_sdk-0.1.0/src/cosmon_agent_sdk/elicit.py +186 -0
- cosmon_agent_sdk-0.1.0/src/cosmon_agent_sdk/errors.py +53 -0
- cosmon_agent_sdk-0.1.0/src/cosmon_agent_sdk/events.py +40 -0
- cosmon_agent_sdk-0.1.0/src/cosmon_agent_sdk/py.typed +0 -0
- cosmon_agent_sdk-0.1.0/tests/conftest.py +13 -0
- cosmon_agent_sdk-0.1.0/tests/e2e/test_e2e.py +492 -0
- cosmon_agent_sdk-0.1.0/tests/http_harness.py +89 -0
- cosmon_agent_sdk-0.1.0/tests/mcp_test_server.py +219 -0
- cosmon_agent_sdk-0.1.0/tests/test_cli.py +80 -0
- cosmon_agent_sdk-0.1.0/tests/test_discovery.py +45 -0
- cosmon_agent_sdk-0.1.0/tests/test_elicit_window.py +81 -0
- cosmon_agent_sdk-0.1.0/tests/test_mcp_async.py +407 -0
- cosmon_agent_sdk-0.1.0/tests/test_mcp_sync.py +199 -0
- cosmon_agent_sdk-0.1.0/tests/test_sessions.py +261 -0
- cosmon_agent_sdk-0.1.0/tests/test_status.py +53 -0
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to `cosmon-agent-sdk` are documented here. The format follows
|
|
4
|
+
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and the project follows
|
|
5
|
+
[Semantic Versioning](https://semver.org/) (while `0.x`, breaking changes may ride
|
|
6
|
+
minor bumps — see the design doc §6).
|
|
7
|
+
|
|
8
|
+
## [Unreleased]
|
|
9
|
+
|
|
10
|
+
## [0.1.0] — Unreleased
|
|
11
|
+
|
|
12
|
+
Initial release. The SDK is an [MCP](https://modelcontextprotocol.io) client for
|
|
13
|
+
the local MCP server the Cosmon app exposes — the agent is the tool.
|
|
14
|
+
|
|
15
|
+
### Added
|
|
16
|
+
- `Client` / `AsyncClient` — discover the running app (via the descriptor; or
|
|
17
|
+
explicit `endpoint`/`token`), connect to its MCP server, and drive the agent.
|
|
18
|
+
Used as context managers; `AsyncClient` is native async, `Client` is a blocking
|
|
19
|
+
façade over it.
|
|
20
|
+
- `client.run(prompt, agent=, attachments=)` returning a `Run` / `AsyncRun`.
|
|
21
|
+
Consume it either way: `.text()` runs to completion and returns the agent's
|
|
22
|
+
final answer, or iterate it (`for event in run` / `async for`) to stream typed
|
|
23
|
+
`ToolCall` / `TextChunk` events (fed by MCP progress notifications).
|
|
24
|
+
- `client.discover()` → `DiscoverInfo` (login state, agents, model, runtime
|
|
25
|
+
readiness) from the `nexus://status` resource.
|
|
26
|
+
- **Interactive** runs via `on_question` / `on_confirm` handlers, mapped onto MCP
|
|
27
|
+
elicitation (`Question` / `Confirmation`). Supplying handlers declares the
|
|
28
|
+
elicitation capability; omitting both runs the agent unattended (no `ask_user`;
|
|
29
|
+
confirms auto-resolved — approve safe, decline dangerous). Because elicitation
|
|
30
|
+
is answered out of band, `.text()` never stalls or raises mid-run for input.
|
|
31
|
+
- Long client-side `timeout` (default 10 min) so multi-minute runs complete.
|
|
32
|
+
- Typed error family (`CosmonError` and subclasses, incl. `CosmonRunError`).
|
|
33
|
+
`py.typed` shipped.
|
|
34
|
+
- `cosmon` CLI: `doctor`, `run`, `ask` (headless; `--auto-approve`).
|
|
35
|
+
|
|
36
|
+
[Unreleased]: https://github.com/Cosmon-Inc/cosmon
|
|
37
|
+
[0.1.0]: https://github.com/Cosmon-Inc/cosmon
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
Copyright (c) 2026 Cosmon, Inc. All rights reserved.
|
|
2
|
+
|
|
3
|
+
NEXUS AGENT SDK — PROPRIETARY SOFTWARE LICENSE
|
|
4
|
+
|
|
5
|
+
This software and associated documentation files (the "Software") are the
|
|
6
|
+
proprietary and confidential property of Cosmon, Inc. ("Cosmon").
|
|
7
|
+
|
|
8
|
+
1. License grant. Subject to your agreement to Cosmon's Terms of Service and any
|
|
9
|
+
applicable subscription or commercial agreement, Cosmon grants you a limited,
|
|
10
|
+
non-exclusive, non-transferable, revocable license to install and use the
|
|
11
|
+
Software solely to interface with the Cosmon Nexus application that you have
|
|
12
|
+
licensed and are authorized to use.
|
|
13
|
+
|
|
14
|
+
2. Restrictions. You may not, except to the extent permitted by applicable law:
|
|
15
|
+
(a) copy, modify, or create derivative works of the Software; (b) reverse
|
|
16
|
+
engineer, decompile, or disassemble the Software; (c) redistribute, sublicense,
|
|
17
|
+
sell, rent, or lease the Software; or (d) remove or alter any proprietary
|
|
18
|
+
notices.
|
|
19
|
+
|
|
20
|
+
3. Ownership. The Software is licensed, not sold. Cosmon retains all right, title,
|
|
21
|
+
and interest in and to the Software, including all intellectual property rights.
|
|
22
|
+
|
|
23
|
+
4. No warranty. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
24
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
25
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND NONINFRINGEMENT.
|
|
26
|
+
|
|
27
|
+
5. Limitation of liability. IN NO EVENT SHALL COSMON BE LIABLE FOR ANY CLAIM,
|
|
28
|
+
DAMAGES, OR OTHER LIABILITY ARISING FROM OR IN CONNECTION WITH THE SOFTWARE OR
|
|
29
|
+
ITS USE.
|
|
30
|
+
|
|
31
|
+
For licensing inquiries, contact legal@cosmon.com.
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: cosmon-agent-sdk
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Program the Nexus agent in your installed Cosmon app — drive SolidWorks, Abaqus, COMSOL, Fluent and more.
|
|
5
|
+
Project-URL: Homepage, https://cosmon.com
|
|
6
|
+
Project-URL: Documentation, https://docs.cosmon.com/sdk/overview
|
|
7
|
+
Project-URL: Repository, https://github.com/Cosmon-Inc/cosmon
|
|
8
|
+
Author-email: "Cosmon, Inc." <support@cosmon.com>
|
|
9
|
+
License: Copyright (c) 2026 Cosmon, Inc. All rights reserved.
|
|
10
|
+
|
|
11
|
+
NEXUS AGENT SDK — PROPRIETARY SOFTWARE LICENSE
|
|
12
|
+
|
|
13
|
+
This software and associated documentation files (the "Software") are the
|
|
14
|
+
proprietary and confidential property of Cosmon, Inc. ("Cosmon").
|
|
15
|
+
|
|
16
|
+
1. License grant. Subject to your agreement to Cosmon's Terms of Service and any
|
|
17
|
+
applicable subscription or commercial agreement, Cosmon grants you a limited,
|
|
18
|
+
non-exclusive, non-transferable, revocable license to install and use the
|
|
19
|
+
Software solely to interface with the Cosmon Nexus application that you have
|
|
20
|
+
licensed and are authorized to use.
|
|
21
|
+
|
|
22
|
+
2. Restrictions. You may not, except to the extent permitted by applicable law:
|
|
23
|
+
(a) copy, modify, or create derivative works of the Software; (b) reverse
|
|
24
|
+
engineer, decompile, or disassemble the Software; (c) redistribute, sublicense,
|
|
25
|
+
sell, rent, or lease the Software; or (d) remove or alter any proprietary
|
|
26
|
+
notices.
|
|
27
|
+
|
|
28
|
+
3. Ownership. The Software is licensed, not sold. Cosmon retains all right, title,
|
|
29
|
+
and interest in and to the Software, including all intellectual property rights.
|
|
30
|
+
|
|
31
|
+
4. No warranty. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
32
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
33
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND NONINFRINGEMENT.
|
|
34
|
+
|
|
35
|
+
5. Limitation of liability. IN NO EVENT SHALL COSMON BE LIABLE FOR ANY CLAIM,
|
|
36
|
+
DAMAGES, OR OTHER LIABILITY ARISING FROM OR IN CONNECTION WITH THE SOFTWARE OR
|
|
37
|
+
ITS USE.
|
|
38
|
+
|
|
39
|
+
For licensing inquiries, contact legal@cosmon.com.
|
|
40
|
+
License-File: LICENSE
|
|
41
|
+
Keywords: agent,cad,cae,cosmon,mcp,nexus,sdk
|
|
42
|
+
Classifier: Development Status :: 4 - Beta
|
|
43
|
+
Classifier: Intended Audience :: Developers
|
|
44
|
+
Classifier: License :: Other/Proprietary License
|
|
45
|
+
Classifier: Operating System :: OS Independent
|
|
46
|
+
Classifier: Programming Language :: Python :: 3
|
|
47
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
48
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
49
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
50
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
51
|
+
Classifier: Typing :: Typed
|
|
52
|
+
Requires-Python: >=3.10
|
|
53
|
+
Requires-Dist: anyio>=4
|
|
54
|
+
Requires-Dist: httpx>=0.24
|
|
55
|
+
Requires-Dist: mcp<2,>=1.28
|
|
56
|
+
Requires-Dist: pydantic>=2
|
|
57
|
+
Provides-Extra: dev
|
|
58
|
+
Requires-Dist: mypy>=1.8; extra == 'dev'
|
|
59
|
+
Requires-Dist: pytest>=7; extra == 'dev'
|
|
60
|
+
Description-Content-Type: text/markdown
|
|
61
|
+
|
|
62
|
+
# Cosmon Agent SDK (Python)
|
|
63
|
+
|
|
64
|
+
Program the **Nexus agent** in your installed, signed-in Cosmon app — drive
|
|
65
|
+
SolidWorks, Abaqus, COMSOL, Fluent, Ansys Mechanical and more from your own
|
|
66
|
+
Python. Your code brings the *caller*; the installed app brings the *capability*
|
|
67
|
+
(your login, your credits, the CAD software on your machine). **Your code never
|
|
68
|
+
holds a key.**
|
|
69
|
+
|
|
70
|
+
Under the hood the SDK is a small [MCP](https://modelcontextprotocol.io) client:
|
|
71
|
+
it discovers the running app, connects to the local MCP server it exposes, and
|
|
72
|
+
drives the agent through one `run` tool.
|
|
73
|
+
|
|
74
|
+
## Install
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
pip install cosmon-agent-sdk
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Then: open Cosmon, sign in, and turn on **Settings → Developer access**.
|
|
81
|
+
|
|
82
|
+
Check it's working:
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
cosmon doctor
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
## Quickstart
|
|
89
|
+
|
|
90
|
+
`client.run()` returns a `Run`; get the agent's final answer with `.text()`:
|
|
91
|
+
|
|
92
|
+
```python
|
|
93
|
+
from cosmon_agent_sdk import Client
|
|
94
|
+
|
|
95
|
+
with Client() as client:
|
|
96
|
+
answer = client.run("What's the max von Mises in bracket.sldprt?", agent="solidworks").text()
|
|
97
|
+
print(answer)
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
Or **iterate** the run to stream it — it yields typed `ToolCall` / `TextChunk`
|
|
101
|
+
events (`.text()` and iterating are the two ways to consume a run; pick one):
|
|
102
|
+
|
|
103
|
+
```python
|
|
104
|
+
import sys
|
|
105
|
+
from cosmon_agent_sdk import Client, TextChunk, ToolCall
|
|
106
|
+
|
|
107
|
+
with Client() as client:
|
|
108
|
+
for event in client.run("Run a static study on bracket.sldprt", agent="solidworks"):
|
|
109
|
+
if isinstance(event, TextChunk):
|
|
110
|
+
print(event.text, end="", flush=True) # answer, token by token
|
|
111
|
+
elif isinstance(event, ToolCall) and not event.finished:
|
|
112
|
+
print(f"[{event.summary}]", file=sys.stderr) # tool steps
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
## Attended vs unattended
|
|
116
|
+
|
|
117
|
+
Interactivity is set when you build the client, by whether you supply handlers:
|
|
118
|
+
|
|
119
|
+
- **Unattended (default)** — no handlers. The agent is never offered `ask_user`
|
|
120
|
+
(it proceeds on its own) and confirmations are auto-resolved (approve safe,
|
|
121
|
+
**decline dangerous**). `.text()` just runs to completion. This is the headless
|
|
122
|
+
path; put any decisions the agent might otherwise ask about in the prompt.
|
|
123
|
+
- **Interactive** — supply `on_question` and/or `on_confirm`. The app forwards the
|
|
124
|
+
agent's `ask_user` / `confirm_action` to your handlers (over MCP elicitation)
|
|
125
|
+
while `.text()` runs to completion. There's no event loop to drive — your
|
|
126
|
+
handlers are called as needed, so `.text()` never stalls waiting for an answer.
|
|
127
|
+
|
|
128
|
+
```python
|
|
129
|
+
from cosmon_agent_sdk import Client, Question, Confirmation
|
|
130
|
+
|
|
131
|
+
def answer(question: Question) -> str:
|
|
132
|
+
return question.options[0] if question.options else "10mm seed size"
|
|
133
|
+
|
|
134
|
+
def approve(confirmation: Confirmation) -> bool:
|
|
135
|
+
return True # your policy here
|
|
136
|
+
|
|
137
|
+
with Client(on_question=answer, on_confirm=approve) as client:
|
|
138
|
+
print(client.run("Mesh the model and submit the job", agent="abaqus").text())
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
## Long runs
|
|
142
|
+
|
|
143
|
+
The agent can work for many minutes (mesh, solve, post-process). The client sets
|
|
144
|
+
a generous default `timeout` (10 minutes) and you can raise it:
|
|
145
|
+
|
|
146
|
+
```python
|
|
147
|
+
with Client(timeout=1800) as client: # 30 minutes
|
|
148
|
+
client.run("Run a mesh-convergence study on bracket.sldprt", agent="solidworks").text()
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
## Async
|
|
152
|
+
|
|
153
|
+
`AsyncClient` is the async twin (for pipelines, FastAPI, etc.) — same surface,
|
|
154
|
+
`await`ed:
|
|
155
|
+
|
|
156
|
+
```python
|
|
157
|
+
import asyncio
|
|
158
|
+
from cosmon_agent_sdk import AsyncClient
|
|
159
|
+
|
|
160
|
+
async def main() -> None:
|
|
161
|
+
async with AsyncClient() as client:
|
|
162
|
+
print(await client.run("what can you do?", agent="nexus").text())
|
|
163
|
+
|
|
164
|
+
asyncio.run(main())
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
`Client`/`AsyncClient` and `Run`/`AsyncRun` are the sync/async pairs.
|
|
168
|
+
|
|
169
|
+
## Command line
|
|
170
|
+
|
|
171
|
+
```bash
|
|
172
|
+
cosmon doctor # check the connection, step by step
|
|
173
|
+
cosmon run "mesh and solve" --agent abaqus # stream the work (steps to stderr)
|
|
174
|
+
cosmon run "what can you do?" --agent nexus -q # -q: print just the final answer
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
The CLI is headless (it can't answer `ask_user`); `--auto-approve` additionally
|
|
178
|
+
approves confirmation requests, including dangerous ones.
|
|
179
|
+
|
|
180
|
+
## Notes
|
|
181
|
+
|
|
182
|
+
- **Discovery**: the app writes a `mcp-gateway.json` descriptor while Developer
|
|
183
|
+
access is on; the SDK reads it automatically. Override with the
|
|
184
|
+
`COSMON_SDK_DESCRIPTOR` environment variable.
|
|
185
|
+
- **Errors**: connecting / starting a run can raise `CosmonConnectionError`,
|
|
186
|
+
`CosmonAuthError`, `CosmonTimeoutError`, `CosmonProtocolError`; a run that fails
|
|
187
|
+
while running raises `CosmonRunError` from `.text()`.
|
|
188
|
+
- Docs: <https://docs.cosmon.com/>
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
# Cosmon Agent SDK (Python)
|
|
2
|
+
|
|
3
|
+
Program the **Nexus agent** in your installed, signed-in Cosmon app — drive
|
|
4
|
+
SolidWorks, Abaqus, COMSOL, Fluent, Ansys Mechanical and more from your own
|
|
5
|
+
Python. Your code brings the *caller*; the installed app brings the *capability*
|
|
6
|
+
(your login, your credits, the CAD software on your machine). **Your code never
|
|
7
|
+
holds a key.**
|
|
8
|
+
|
|
9
|
+
Under the hood the SDK is a small [MCP](https://modelcontextprotocol.io) client:
|
|
10
|
+
it discovers the running app, connects to the local MCP server it exposes, and
|
|
11
|
+
drives the agent through one `run` tool.
|
|
12
|
+
|
|
13
|
+
## Install
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
pip install cosmon-agent-sdk
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Then: open Cosmon, sign in, and turn on **Settings → Developer access**.
|
|
20
|
+
|
|
21
|
+
Check it's working:
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
cosmon doctor
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Quickstart
|
|
28
|
+
|
|
29
|
+
`client.run()` returns a `Run`; get the agent's final answer with `.text()`:
|
|
30
|
+
|
|
31
|
+
```python
|
|
32
|
+
from cosmon_agent_sdk import Client
|
|
33
|
+
|
|
34
|
+
with Client() as client:
|
|
35
|
+
answer = client.run("What's the max von Mises in bracket.sldprt?", agent="solidworks").text()
|
|
36
|
+
print(answer)
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Or **iterate** the run to stream it — it yields typed `ToolCall` / `TextChunk`
|
|
40
|
+
events (`.text()` and iterating are the two ways to consume a run; pick one):
|
|
41
|
+
|
|
42
|
+
```python
|
|
43
|
+
import sys
|
|
44
|
+
from cosmon_agent_sdk import Client, TextChunk, ToolCall
|
|
45
|
+
|
|
46
|
+
with Client() as client:
|
|
47
|
+
for event in client.run("Run a static study on bracket.sldprt", agent="solidworks"):
|
|
48
|
+
if isinstance(event, TextChunk):
|
|
49
|
+
print(event.text, end="", flush=True) # answer, token by token
|
|
50
|
+
elif isinstance(event, ToolCall) and not event.finished:
|
|
51
|
+
print(f"[{event.summary}]", file=sys.stderr) # tool steps
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## Attended vs unattended
|
|
55
|
+
|
|
56
|
+
Interactivity is set when you build the client, by whether you supply handlers:
|
|
57
|
+
|
|
58
|
+
- **Unattended (default)** — no handlers. The agent is never offered `ask_user`
|
|
59
|
+
(it proceeds on its own) and confirmations are auto-resolved (approve safe,
|
|
60
|
+
**decline dangerous**). `.text()` just runs to completion. This is the headless
|
|
61
|
+
path; put any decisions the agent might otherwise ask about in the prompt.
|
|
62
|
+
- **Interactive** — supply `on_question` and/or `on_confirm`. The app forwards the
|
|
63
|
+
agent's `ask_user` / `confirm_action` to your handlers (over MCP elicitation)
|
|
64
|
+
while `.text()` runs to completion. There's no event loop to drive — your
|
|
65
|
+
handlers are called as needed, so `.text()` never stalls waiting for an answer.
|
|
66
|
+
|
|
67
|
+
```python
|
|
68
|
+
from cosmon_agent_sdk import Client, Question, Confirmation
|
|
69
|
+
|
|
70
|
+
def answer(question: Question) -> str:
|
|
71
|
+
return question.options[0] if question.options else "10mm seed size"
|
|
72
|
+
|
|
73
|
+
def approve(confirmation: Confirmation) -> bool:
|
|
74
|
+
return True # your policy here
|
|
75
|
+
|
|
76
|
+
with Client(on_question=answer, on_confirm=approve) as client:
|
|
77
|
+
print(client.run("Mesh the model and submit the job", agent="abaqus").text())
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
## Long runs
|
|
81
|
+
|
|
82
|
+
The agent can work for many minutes (mesh, solve, post-process). The client sets
|
|
83
|
+
a generous default `timeout` (10 minutes) and you can raise it:
|
|
84
|
+
|
|
85
|
+
```python
|
|
86
|
+
with Client(timeout=1800) as client: # 30 minutes
|
|
87
|
+
client.run("Run a mesh-convergence study on bracket.sldprt", agent="solidworks").text()
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
## Async
|
|
91
|
+
|
|
92
|
+
`AsyncClient` is the async twin (for pipelines, FastAPI, etc.) — same surface,
|
|
93
|
+
`await`ed:
|
|
94
|
+
|
|
95
|
+
```python
|
|
96
|
+
import asyncio
|
|
97
|
+
from cosmon_agent_sdk import AsyncClient
|
|
98
|
+
|
|
99
|
+
async def main() -> None:
|
|
100
|
+
async with AsyncClient() as client:
|
|
101
|
+
print(await client.run("what can you do?", agent="nexus").text())
|
|
102
|
+
|
|
103
|
+
asyncio.run(main())
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
`Client`/`AsyncClient` and `Run`/`AsyncRun` are the sync/async pairs.
|
|
107
|
+
|
|
108
|
+
## Command line
|
|
109
|
+
|
|
110
|
+
```bash
|
|
111
|
+
cosmon doctor # check the connection, step by step
|
|
112
|
+
cosmon run "mesh and solve" --agent abaqus # stream the work (steps to stderr)
|
|
113
|
+
cosmon run "what can you do?" --agent nexus -q # -q: print just the final answer
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
The CLI is headless (it can't answer `ask_user`); `--auto-approve` additionally
|
|
117
|
+
approves confirmation requests, including dangerous ones.
|
|
118
|
+
|
|
119
|
+
## Notes
|
|
120
|
+
|
|
121
|
+
- **Discovery**: the app writes a `mcp-gateway.json` descriptor while Developer
|
|
122
|
+
access is on; the SDK reads it automatically. Override with the
|
|
123
|
+
`COSMON_SDK_DESCRIPTOR` environment variable.
|
|
124
|
+
- **Errors**: connecting / starting a run can raise `CosmonConnectionError`,
|
|
125
|
+
`CosmonAuthError`, `CosmonTimeoutError`, `CosmonProtocolError`; a run that fails
|
|
126
|
+
while running raises `CosmonRunError` from `.text()`.
|
|
127
|
+
- Docs: <https://docs.cosmon.com/>
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# Examples
|
|
2
|
+
|
|
3
|
+
Runnable examples for the Cosmon Agent SDK.
|
|
4
|
+
|
|
5
|
+
## Prerequisites
|
|
6
|
+
|
|
7
|
+
1. Install the SDK — `pip install cosmon-agent-sdk` (or `pip install -e .` from the repo root).
|
|
8
|
+
2. Open Cosmon, **sign in**, and turn on **Settings → Developer access**.
|
|
9
|
+
3. Check the connection: `cosmon doctor`.
|
|
10
|
+
|
|
11
|
+
> **Dev builds:** the app's data dir is named `nexus`, not `Cosmon`, so the SDK's
|
|
12
|
+
> default descriptor path won't match. Point it at the path `cosmon doctor` reports:
|
|
13
|
+
>
|
|
14
|
+
> ```bash
|
|
15
|
+
> export COSMON_SDK_DESCRIPTOR="$HOME/Library/Application Support/nexus/mcp-gateway.json"
|
|
16
|
+
> ```
|
|
17
|
+
|
|
18
|
+
## The examples
|
|
19
|
+
|
|
20
|
+
| File | What it shows |
|
|
21
|
+
|---|---|
|
|
22
|
+
| `quickstart.py` | The one-liner: `client.run(...).text()`. |
|
|
23
|
+
| `streaming.py` | Stream the run — iterate typed `ToolCall` / `TextChunk` events. |
|
|
24
|
+
| `structural_study.py` | Interactive run — answer the agent's questions and approve actions with `on_question` / `on_confirm`. |
|
|
25
|
+
| `batch.py` | Unattended batch over many files (no handlers). |
|
|
26
|
+
| `async_quickstart.py` | The async client (`AsyncClient`). |
|
|
27
|
+
| `playground.py` | Interactive REPL — drive the agent from your terminal. |
|
|
28
|
+
|
|
29
|
+
Run any of them:
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
python examples/quickstart.py
|
|
33
|
+
python examples/playground.py solidworks
|
|
34
|
+
python examples/batch.py beam1.sldprt beam2.sldprt
|
|
35
|
+
```
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"""Async — drive the agent from an asyncio program (FastAPI, pipelines, etc.).
|
|
2
|
+
Same surface as the sync client, awaited.
|
|
3
|
+
|
|
4
|
+
python examples/async_quickstart.py
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import asyncio
|
|
10
|
+
|
|
11
|
+
from cosmon_agent_sdk import AsyncClient
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
async def main() -> None:
|
|
15
|
+
async with AsyncClient() as client:
|
|
16
|
+
answer = await client.run(
|
|
17
|
+
"In one sentence, who are you and what can you help me with?",
|
|
18
|
+
agent="nexus",
|
|
19
|
+
).text()
|
|
20
|
+
print(answer)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
if __name__ == "__main__":
|
|
24
|
+
asyncio.run(main())
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"""Hand files to a run — the agent reads them alongside the prompt.
|
|
2
|
+
|
|
3
|
+
python examples/attachments.py <path> [<path> ...]
|
|
4
|
+
|
|
5
|
+
Relative paths are resolved against YOUR working directory (the SDK
|
|
6
|
+
absolutizes them before sending — the app's tool server has its own cwd), and
|
|
7
|
+
a missing file raises immediately instead of leaving the agent hunting for it.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
import sys
|
|
13
|
+
|
|
14
|
+
from cosmon_agent_sdk import Client
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def main() -> None:
|
|
18
|
+
paths = sys.argv[1:]
|
|
19
|
+
if not paths:
|
|
20
|
+
raise SystemExit("usage: python examples/attachments.py <path> [<path> ...]")
|
|
21
|
+
with Client() as client:
|
|
22
|
+
client.run(
|
|
23
|
+
"describe the attached file(s) in a couple of sentences",
|
|
24
|
+
agent="nexus",
|
|
25
|
+
attachments=paths,
|
|
26
|
+
).text(live=True)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
if __name__ == "__main__":
|
|
30
|
+
main()
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"""Run an analysis across many files, unattended.
|
|
2
|
+
|
|
3
|
+
A client with no ``on_question`` / ``on_confirm`` handlers runs the agent
|
|
4
|
+
unattended: it's never offered ``ask_user`` (it proceeds on its own) and
|
|
5
|
+
confirmations are auto-resolved — approve safe actions, decline dangerous ones.
|
|
6
|
+
So ``.text()`` just runs to completion. Put any decisions the agent might
|
|
7
|
+
otherwise ask about directly in the prompt. ``timeout`` caps each run so one
|
|
8
|
+
stuck file can't hang the whole batch.
|
|
9
|
+
|
|
10
|
+
python examples/batch.py beam1.sldprt beam2.sldprt
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from __future__ import annotations
|
|
14
|
+
|
|
15
|
+
import sys
|
|
16
|
+
|
|
17
|
+
from cosmon_agent_sdk import Client, CosmonError
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def main() -> None:
|
|
21
|
+
files = sys.argv[1:] or ["beam1.sldprt", "beam2.sldprt"]
|
|
22
|
+
with Client(timeout=600) as client: # unattended; 10-minute ceiling per run
|
|
23
|
+
for path in files:
|
|
24
|
+
print(f"\n=== {path} ===")
|
|
25
|
+
try:
|
|
26
|
+
answer = client.run(
|
|
27
|
+
f"Run a static structural study on {path} with a 10mm mesh seed "
|
|
28
|
+
f"and report the peak von Mises stress.",
|
|
29
|
+
agent="solidworks",
|
|
30
|
+
).text()
|
|
31
|
+
print(answer)
|
|
32
|
+
except CosmonError as err:
|
|
33
|
+
print(f" failed: {err}")
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
if __name__ == "__main__":
|
|
37
|
+
main()
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
"""Answer the agent's questions and approval gates from the terminal.
|
|
2
|
+
|
|
3
|
+
python examples/elicitation.py
|
|
4
|
+
|
|
5
|
+
Passing handlers declares the elicitation capability: `on_question` answers
|
|
6
|
+
`ask_user`, `on_confirm` answers confirmation gates — both out of band while
|
|
7
|
+
`.text()` blocks, so expect the prompts to appear mid-call. (Passing only
|
|
8
|
+
`on_confirm` runs question-free: the agent is never offered ask_user, but
|
|
9
|
+
every approval gate still reaches the handler.) `live=True` prints the run
|
|
10
|
+
live while you wait; the returned text stays the authoritative transcript.
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from __future__ import annotations
|
|
14
|
+
|
|
15
|
+
from cosmon_agent_sdk import Client, Confirmation, Question
|
|
16
|
+
|
|
17
|
+
DIM = "\x1b[2m"
|
|
18
|
+
RESET = "\x1b[0m"
|
|
19
|
+
|
|
20
|
+
def answer_question(question: Question) -> str:
|
|
21
|
+
# The app shows a countdown bar for this; a terminal can at least say it.
|
|
22
|
+
window = int(question.expires_in)
|
|
23
|
+
print(f"\n{DIM}[agent asks — ~{window}s before it proceeds without you]{RESET} {question.prompt}")
|
|
24
|
+
for index, option in enumerate(question.options, start=1):
|
|
25
|
+
print(f" {index}) {option}")
|
|
26
|
+
# An empty answer is NOT a submission (a stray Enter would read as "no
|
|
27
|
+
# answer" and the agent would proceed on its own judgment) — re-prompt,
|
|
28
|
+
# exactly like the app disables its submit button on empty text.
|
|
29
|
+
while not (raw := input("answer> ").strip()):
|
|
30
|
+
pass
|
|
31
|
+
if question.options and raw.isdigit() and 1 <= int(raw) <= len(question.options):
|
|
32
|
+
return question.options[int(raw) - 1]
|
|
33
|
+
return raw
|
|
34
|
+
|
|
35
|
+
def approve(confirmation: Confirmation) -> bool:
|
|
36
|
+
return input(f"\n[confirm] {confirmation.summary} [y/N]> ").strip().lower() in ("y", "yes")
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def main() -> None:
|
|
40
|
+
with Client(on_confirm=approve, on_question=answer_question) as client:
|
|
41
|
+
client.run(
|
|
42
|
+
"please ask me a question with multiple choices and then summarize the answer in one sentence",
|
|
43
|
+
agent="nexus",
|
|
44
|
+
).text(live=True)
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
if __name__ == "__main__":
|
|
48
|
+
main()
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"""A run that spans a handoff chain, answered from the terminal.
|
|
2
|
+
|
|
3
|
+
python examples/handoff.py
|
|
4
|
+
|
|
5
|
+
The entry agent hands off mid-run; `live=True` labels the stream with the
|
|
6
|
+
agent in control (`[solidworks]`, `[comsol]`, …) so multi-agent runs stay
|
|
7
|
+
attributable. Questions from ANY agent in the chain reach `on_question`;
|
|
8
|
+
without `on_confirm`, approval gates auto-resolve (approve safe, decline
|
|
9
|
+
dangerous), so the handoff itself proceeds unprompted.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from __future__ import annotations
|
|
13
|
+
|
|
14
|
+
from cosmon_agent_sdk import Client, Confirmation, Question
|
|
15
|
+
|
|
16
|
+
DIM = "\x1b[2m"
|
|
17
|
+
RESET = "\x1b[0m"
|
|
18
|
+
|
|
19
|
+
def answer_question(question: Question) -> str:
|
|
20
|
+
print(f"\n{DIM}[agent asks]{RESET} {question.prompt}")
|
|
21
|
+
for index, option in enumerate(question.options, start=1):
|
|
22
|
+
print(f" {index}) {option}")
|
|
23
|
+
raw = input("answer> ").strip()
|
|
24
|
+
if question.options and raw.isdigit() and 1 <= int(raw) <= len(question.options):
|
|
25
|
+
return question.options[int(raw) - 1]
|
|
26
|
+
return raw
|
|
27
|
+
|
|
28
|
+
def approve(confirmation: Confirmation) -> bool:
|
|
29
|
+
return input(f"\n[confirm] {confirmation.summary} [y/N]> ").strip().lower() in ("y", "yes")
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def main() -> None:
|
|
33
|
+
with Client(
|
|
34
|
+
# on_confirm=approve,
|
|
35
|
+
on_question=answer_question
|
|
36
|
+
) as client:
|
|
37
|
+
client.run(
|
|
38
|
+
"please handoff to the comsol agent and ask me a multiple-choice "
|
|
39
|
+
"question through the ask question tool call, then summarize the "
|
|
40
|
+
"answer in one sentence",
|
|
41
|
+
agent="solidworks",
|
|
42
|
+
).text(live=True)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
if __name__ == "__main__":
|
|
46
|
+
main()
|