cteno-sdk 2.10.13__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.
- cteno_sdk-2.10.13/LICENSE +21 -0
- cteno_sdk-2.10.13/PKG-INFO +119 -0
- cteno_sdk-2.10.13/README.md +95 -0
- cteno_sdk-2.10.13/cteno_sdk/__init__.py +65 -0
- cteno_sdk-2.10.13/cteno_sdk/client.py +772 -0
- cteno_sdk-2.10.13/cteno_sdk/daemon.py +332 -0
- cteno_sdk-2.10.13/cteno_sdk.egg-info/PKG-INFO +119 -0
- cteno_sdk-2.10.13/cteno_sdk.egg-info/SOURCES.txt +12 -0
- cteno_sdk-2.10.13/cteno_sdk.egg-info/dependency_links.txt +1 -0
- cteno_sdk-2.10.13/cteno_sdk.egg-info/top_level.txt +1 -0
- cteno_sdk-2.10.13/pyproject.toml +33 -0
- cteno_sdk-2.10.13/setup.cfg +4 -0
- cteno_sdk-2.10.13/tests/test_client.py +233 -0
- cteno_sdk-2.10.13/tests/test_daemon.py +81 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) Cteno contributors
|
|
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,119 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: cteno-sdk
|
|
3
|
+
Version: 2.10.13
|
|
4
|
+
Summary: Run Cteno agents from Python. Platform wheels bundle a local Cteno daemon; no desktop app or account needed.
|
|
5
|
+
Author: Cteno contributors
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/zalan159/cteno-sdk
|
|
8
|
+
Project-URL: Source, https://github.com/zalan159/cteno-sdk/tree/main/python
|
|
9
|
+
Project-URL: Issues, https://github.com/zalan159/cteno-sdk/issues
|
|
10
|
+
Project-URL: Changelog, https://github.com/zalan159/cteno-sdk/blob/main/CHANGELOG.md
|
|
11
|
+
Keywords: cteno,agent,sdk,llm,ai-agent
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
16
|
+
Classifier: Framework :: AsyncIO
|
|
17
|
+
Classifier: Operating System :: MacOS
|
|
18
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
19
|
+
Classifier: Operating System :: Microsoft :: Windows
|
|
20
|
+
Requires-Python: >=3.9
|
|
21
|
+
Description-Content-Type: text/markdown
|
|
22
|
+
License-File: LICENSE
|
|
23
|
+
Dynamic: license-file
|
|
24
|
+
|
|
25
|
+
# cteno-sdk (Python)
|
|
26
|
+
|
|
27
|
+
Run Cteno agents from Python — same protocol and shape as the Node package
|
|
28
|
+
`@cteno/sdk`. Standard library only, Python ≥ 3.9, asyncio.
|
|
29
|
+
|
|
30
|
+
> **The PyPI package is coming soon** (with Cteno 2.10.12). Until then, install from
|
|
31
|
+
> source with `pip install ./python` and point the SDK at a daemon: see the repository
|
|
32
|
+
> README, "Use from source".
|
|
33
|
+
|
|
34
|
+
The platform wheels (planned: macOS arm64/x64, Linux x64 with glibc ≥ 2.34, Windows x64;
|
|
35
|
+
Linux arm64 is not supported yet)
|
|
36
|
+
include the Cteno daemon. `CtenoSession.open()` starts it in local mode on first use:
|
|
37
|
+
no desktop app, no account, your own model key.
|
|
38
|
+
|
|
39
|
+
## Install
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
# coming soon — not published yet
|
|
43
|
+
pip install cteno-sdk
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
On a platform without a wheel pip falls back to the sdist, which has no daemon: set
|
|
47
|
+
`CTENO_SDK_DAEMON_PATH` to a `cteno-host-cli` binary, or pass `socket_path` to a daemon
|
|
48
|
+
you run yourself.
|
|
49
|
+
|
|
50
|
+
## Quickstart
|
|
51
|
+
|
|
52
|
+
```python
|
|
53
|
+
import asyncio, os
|
|
54
|
+
from cteno_sdk import CtenoSession
|
|
55
|
+
|
|
56
|
+
async def main():
|
|
57
|
+
async with await CtenoSession.open() as cteno: # starts the bundled daemon if needed
|
|
58
|
+
await cteno.configure_model(base_url="https://api.openai.com/v1", model="gpt-4.1-mini",
|
|
59
|
+
api_key=os.environ["OPENAI_API_KEY"]) # once; stays the default
|
|
60
|
+
sid = await cteno.create(cwd=".")
|
|
61
|
+
result = await cteno.run(sid, "Reply with exactly: hello from Cteno")
|
|
62
|
+
print(result.text if result.ok else result.error) # hello from Cteno
|
|
63
|
+
await cteno.close_session(sid)
|
|
64
|
+
|
|
65
|
+
asyncio.run(main())
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Tools that are not read-only are denied at once unless you approve them:
|
|
69
|
+
|
|
70
|
+
```python
|
|
71
|
+
result = await cteno.run(sid, "Run the tests", can_use_tool=lambda req: req["tool_name"] == "Bash")
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
`can_use_tool` may be async and may return `{"allow": False, "reason": "..."}`. The
|
|
75
|
+
daemon keeps running after your process exits; `await stop_daemon()` stops it.
|
|
76
|
+
|
|
77
|
+
## Your own agent
|
|
78
|
+
|
|
79
|
+
```python
|
|
80
|
+
import asyncio
|
|
81
|
+
from cteno_sdk import CtenoSession, AgentSpec, ClientTool
|
|
82
|
+
|
|
83
|
+
async def search_places(args, ctx): # runs in YOUR process
|
|
84
|
+
return {"name": "Quiet Corner Cafe", "keyword": args["keyword"]}
|
|
85
|
+
|
|
86
|
+
async def main():
|
|
87
|
+
async with await CtenoSession.open() as cteno: # bundled daemon, or open("/path/to/host.sock")
|
|
88
|
+
sid = await cteno.create(model="my-profile", spec=AgentSpec(
|
|
89
|
+
system_prompt="You are a city guide.",
|
|
90
|
+
tools={"allow": []},
|
|
91
|
+
client_tools=[ClientTool("search_places", "Search places",
|
|
92
|
+
{"type": "object", "properties": {"keyword": {"type": "string"}},
|
|
93
|
+
"required": ["keyword"]},
|
|
94
|
+
handler=search_places)],
|
|
95
|
+
limits={"maxIterations": 10, "turnTimeoutMs": 60_000},
|
|
96
|
+
))
|
|
97
|
+
result = await cteno.run(sid, "Somewhere quiet for coffee?", timeout=90)
|
|
98
|
+
print(result.ok, result.text, result.tool_calls)
|
|
99
|
+
await cteno.close_session(sid)
|
|
100
|
+
|
|
101
|
+
asyncio.run(main())
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
`AgentSpec` (custom prompt, client tools, terminal tools, output schema, limits,
|
|
105
|
+
history, tenant) is supported by the bundled daemon; with an explicit `socket_path` to
|
|
106
|
+
another daemon it needs ≥ 2.10.10 (`"sdk-session-spec-v1" in await cteno.capabilities()`).
|
|
107
|
+
See the [repository README](../README.md) for authentication and compatibility, and
|
|
108
|
+
the Node [README](../js/README.md) for the full spec field reference (same fields, snake_case here).
|
|
109
|
+
|
|
110
|
+
## Tests
|
|
111
|
+
|
|
112
|
+
```bash
|
|
113
|
+
python -m unittest discover -s tests
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
## License
|
|
117
|
+
|
|
118
|
+
MIT. The daemon binaries under `cteno_sdk/_bin` are not MIT; see
|
|
119
|
+
`cteno_sdk/_bin/LICENSE.daemon` in the wheel.
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
# cteno-sdk (Python)
|
|
2
|
+
|
|
3
|
+
Run Cteno agents from Python — same protocol and shape as the Node package
|
|
4
|
+
`@cteno/sdk`. Standard library only, Python ≥ 3.9, asyncio.
|
|
5
|
+
|
|
6
|
+
> **The PyPI package is coming soon** (with Cteno 2.10.12). Until then, install from
|
|
7
|
+
> source with `pip install ./python` and point the SDK at a daemon: see the repository
|
|
8
|
+
> README, "Use from source".
|
|
9
|
+
|
|
10
|
+
The platform wheels (planned: macOS arm64/x64, Linux x64 with glibc ≥ 2.34, Windows x64;
|
|
11
|
+
Linux arm64 is not supported yet)
|
|
12
|
+
include the Cteno daemon. `CtenoSession.open()` starts it in local mode on first use:
|
|
13
|
+
no desktop app, no account, your own model key.
|
|
14
|
+
|
|
15
|
+
## Install
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
# coming soon — not published yet
|
|
19
|
+
pip install cteno-sdk
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
On a platform without a wheel pip falls back to the sdist, which has no daemon: set
|
|
23
|
+
`CTENO_SDK_DAEMON_PATH` to a `cteno-host-cli` binary, or pass `socket_path` to a daemon
|
|
24
|
+
you run yourself.
|
|
25
|
+
|
|
26
|
+
## Quickstart
|
|
27
|
+
|
|
28
|
+
```python
|
|
29
|
+
import asyncio, os
|
|
30
|
+
from cteno_sdk import CtenoSession
|
|
31
|
+
|
|
32
|
+
async def main():
|
|
33
|
+
async with await CtenoSession.open() as cteno: # starts the bundled daemon if needed
|
|
34
|
+
await cteno.configure_model(base_url="https://api.openai.com/v1", model="gpt-4.1-mini",
|
|
35
|
+
api_key=os.environ["OPENAI_API_KEY"]) # once; stays the default
|
|
36
|
+
sid = await cteno.create(cwd=".")
|
|
37
|
+
result = await cteno.run(sid, "Reply with exactly: hello from Cteno")
|
|
38
|
+
print(result.text if result.ok else result.error) # hello from Cteno
|
|
39
|
+
await cteno.close_session(sid)
|
|
40
|
+
|
|
41
|
+
asyncio.run(main())
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Tools that are not read-only are denied at once unless you approve them:
|
|
45
|
+
|
|
46
|
+
```python
|
|
47
|
+
result = await cteno.run(sid, "Run the tests", can_use_tool=lambda req: req["tool_name"] == "Bash")
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
`can_use_tool` may be async and may return `{"allow": False, "reason": "..."}`. The
|
|
51
|
+
daemon keeps running after your process exits; `await stop_daemon()` stops it.
|
|
52
|
+
|
|
53
|
+
## Your own agent
|
|
54
|
+
|
|
55
|
+
```python
|
|
56
|
+
import asyncio
|
|
57
|
+
from cteno_sdk import CtenoSession, AgentSpec, ClientTool
|
|
58
|
+
|
|
59
|
+
async def search_places(args, ctx): # runs in YOUR process
|
|
60
|
+
return {"name": "Quiet Corner Cafe", "keyword": args["keyword"]}
|
|
61
|
+
|
|
62
|
+
async def main():
|
|
63
|
+
async with await CtenoSession.open() as cteno: # bundled daemon, or open("/path/to/host.sock")
|
|
64
|
+
sid = await cteno.create(model="my-profile", spec=AgentSpec(
|
|
65
|
+
system_prompt="You are a city guide.",
|
|
66
|
+
tools={"allow": []},
|
|
67
|
+
client_tools=[ClientTool("search_places", "Search places",
|
|
68
|
+
{"type": "object", "properties": {"keyword": {"type": "string"}},
|
|
69
|
+
"required": ["keyword"]},
|
|
70
|
+
handler=search_places)],
|
|
71
|
+
limits={"maxIterations": 10, "turnTimeoutMs": 60_000},
|
|
72
|
+
))
|
|
73
|
+
result = await cteno.run(sid, "Somewhere quiet for coffee?", timeout=90)
|
|
74
|
+
print(result.ok, result.text, result.tool_calls)
|
|
75
|
+
await cteno.close_session(sid)
|
|
76
|
+
|
|
77
|
+
asyncio.run(main())
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
`AgentSpec` (custom prompt, client tools, terminal tools, output schema, limits,
|
|
81
|
+
history, tenant) is supported by the bundled daemon; with an explicit `socket_path` to
|
|
82
|
+
another daemon it needs ≥ 2.10.10 (`"sdk-session-spec-v1" in await cteno.capabilities()`).
|
|
83
|
+
See the [repository README](../README.md) for authentication and compatibility, and
|
|
84
|
+
the Node [README](../js/README.md) for the full spec field reference (same fields, snake_case here).
|
|
85
|
+
|
|
86
|
+
## Tests
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
python -m unittest discover -s tests
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
## License
|
|
93
|
+
|
|
94
|
+
MIT. The daemon binaries under `cteno_sdk/_bin` are not MIT; see
|
|
95
|
+
`cteno_sdk/_bin/LICENSE.daemon` in the wheel.
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
"""Cteno Agent SDK for Python — same owner-socket protocol as ``@cteno/sdk/core``.
|
|
2
|
+
|
|
3
|
+
import asyncio
|
|
4
|
+
from cteno_sdk import CtenoSession, AgentSpec, ClientTool
|
|
5
|
+
|
|
6
|
+
async def main():
|
|
7
|
+
# Starts (or reuses) the SDK's own local daemon; no Cteno app or login needed.
|
|
8
|
+
async with await CtenoSession.open() as cteno:
|
|
9
|
+
await cteno.configure_model(base_url="https://api.deepseek.com/anthropic",
|
|
10
|
+
model="deepseek-chat", api_key=os.environ["DEEPSEEK_API_KEY"],
|
|
11
|
+
api_format="anthropic")
|
|
12
|
+
sid = await cteno.create(spec=AgentSpec(
|
|
13
|
+
system_prompt="You are a food guide.",
|
|
14
|
+
client_tools=[ClientTool("lookup_place", "Find a place", {"type": "object"},
|
|
15
|
+
handler=lambda args, ctx: {"name": args.get("name")})],
|
|
16
|
+
))
|
|
17
|
+
result = await cteno.run(sid, "Where should I eat?")
|
|
18
|
+
print(result.text)
|
|
19
|
+
|
|
20
|
+
asyncio.run(main())
|
|
21
|
+
|
|
22
|
+
See the repository README for the full guide.
|
|
23
|
+
"""
|
|
24
|
+
from .client import (
|
|
25
|
+
AGENT_SPEC_CAPABILITY,
|
|
26
|
+
AgentSpec,
|
|
27
|
+
ClientTool,
|
|
28
|
+
ClientToolContext,
|
|
29
|
+
CtenoSession,
|
|
30
|
+
RunResult,
|
|
31
|
+
SdkError,
|
|
32
|
+
TenantRef,
|
|
33
|
+
discover_owner_sockets,
|
|
34
|
+
spec_to_wire,
|
|
35
|
+
)
|
|
36
|
+
from .daemon import (
|
|
37
|
+
DaemonError,
|
|
38
|
+
default_instance_root,
|
|
39
|
+
ensure_daemon,
|
|
40
|
+
instance_layout,
|
|
41
|
+
isolated_pipe_name,
|
|
42
|
+
resolve_daemon_binary,
|
|
43
|
+
stop_daemon,
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
__all__ = [
|
|
47
|
+
"AGENT_SPEC_CAPABILITY",
|
|
48
|
+
"AgentSpec",
|
|
49
|
+
"ClientTool",
|
|
50
|
+
"ClientToolContext",
|
|
51
|
+
"CtenoSession",
|
|
52
|
+
"RunResult",
|
|
53
|
+
"SdkError",
|
|
54
|
+
"TenantRef",
|
|
55
|
+
"discover_owner_sockets",
|
|
56
|
+
"spec_to_wire",
|
|
57
|
+
"DaemonError",
|
|
58
|
+
"default_instance_root",
|
|
59
|
+
"ensure_daemon",
|
|
60
|
+
"instance_layout",
|
|
61
|
+
"isolated_pipe_name",
|
|
62
|
+
"resolve_daemon_binary",
|
|
63
|
+
"stop_daemon",
|
|
64
|
+
]
|
|
65
|
+
__version__ = "0.2.0"
|