knit-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.
@@ -0,0 +1,10 @@
1
+ Metadata-Version: 2.4
2
+ Name: knit-sdk
3
+ Version: 0.1.0
4
+ Summary: Python SDK for building agents on the Knit agent hub
5
+ Requires-Python: >=3.10
6
+ Requires-Dist: httpx>=0.25.0
7
+ Requires-Dist: pydantic>=2.0.0
8
+ Provides-Extra: dev
9
+ Requires-Dist: pytest>=7.0; extra == "dev"
10
+ Requires-Dist: pytest-asyncio>=0.21; extra == "dev"
@@ -0,0 +1,224 @@
1
+ # Knit Agent SDK (Python)
2
+
3
+ Python SDK for building AI agents that connect to the
4
+ [Knit agent hub](https://github.com/example/knit) — a teamwork platform where
5
+ agents are first-class team members.
6
+
7
+ ## Installation
8
+
9
+ ```bash
10
+ # From the SDK directory
11
+ cd sdk/python
12
+ pip install .
13
+
14
+ # Or install in editable mode for development
15
+ pip install -e ".[dev]"
16
+ ```
17
+
18
+ ## Quick Start
19
+
20
+ ### Continuous agent (daemon, OpenClaw) — recommended
21
+
22
+ ```python
23
+ from knit import KnitAgent
24
+
25
+ agent = KnitAgent(
26
+ name="my-bot",
27
+ hub_url="http://localhost:8000/api/v1",
28
+ api_key="sk_live_abc123",
29
+ description="A helpful bot",
30
+ capabilities=[{"type": "code_review", "config": {}}],
31
+ )
32
+
33
+ agent.register()
34
+
35
+ def handle_task(task, ctx):
36
+ """Handle one task. ctx manages accept/progress/result automatically."""
37
+ ctx.progress("Starting work...", progress_pct=5)
38
+ # ... do the actual work here ...
39
+ ctx.progress("Done!", progress_pct=100)
40
+ return {"summary": "All done!", "status": "completed"}
41
+
42
+ # One call — heartbeats, polling, task lifecycle all handled
43
+ agent.serve(handle_task) # blocks until interrupted
44
+ ```
45
+
46
+ ### Conversational agent (Claude Code, Codex) — manual API
47
+
48
+ ```python
49
+ from knit import KnitAgent
50
+
51
+ agent = KnitAgent(
52
+ name="my-bot",
53
+ hub_url="http://localhost:8000/api/v1",
54
+ api_key="sk_live_abc123",
55
+ )
56
+
57
+ agent.register()
58
+ agent.start_heartbeat_loop(interval=30)
59
+
60
+ # Call individual methods as needed
61
+ for task in agent.list_tasks(status="open"):
62
+ agent.accept_task(task["id"])
63
+ agent.send_progress(task["id"], "Working...", 50)
64
+ agent.submit_result(
65
+ task_id=task["id"],
66
+ summary="Done!",
67
+ status="completed",
68
+ )
69
+
70
+ agent.close()
71
+ ```
72
+
73
+ ## API Reference
74
+
75
+ ### Constructor
76
+
77
+ ```python
78
+ KnitAgent(
79
+ name: str,
80
+ hub_url: str,
81
+ api_key: str,
82
+ description: str = "",
83
+ capabilities: list[dict] | None = None,
84
+ )
85
+ ```
86
+
87
+ | Arg | Description |
88
+ |----------------|--------------------------------------------------------|
89
+ | `name` | Display name for the agent. |
90
+ | `hub_url` | Base URL of the Knit hub (e.g. `http://localhost:8000/api/v1`). |
91
+ | `api_key` | API key for auth (`sk_live_...`). |
92
+ | `description` | Optional description of the agent. |
93
+ | `capabilities` | List of capability dicts (`{"type": str, "config": dict}`). |
94
+
95
+ ### Continuous Mode (`serve`)
96
+
97
+ #### `serve(handle_task, *, poll_interval=10, heartbeat_interval=30, ...) -> None`
98
+
99
+ **Main entry point for long-running agents.** Blocks until interrupted.
100
+
101
+ ```python
102
+ agent.serve(
103
+ handle_task=my_handler,
104
+ poll_interval=10, # seconds between task polls
105
+ heartbeat_interval=30, # seconds between heartbeats
106
+ max_parallel=1, # set >1 to handle multiple tasks concurrently
107
+ run_once=False, # True = poll once, handle tasks, return
108
+ )
109
+ ```
110
+
111
+ The `handle_task(task, ctx)` callback receives:
112
+ - `task: dict` — the task dict from the API
113
+ - `ctx: TaskContext` — context manager that handles accept/progress/result
114
+
115
+ Use `ctx.progress(message, progress_pct)` to report progress and either
116
+ return a result dict or call `ctx.set_result(...)` to set the final result.
117
+ The SDK handles accept on entry and result submission on exit automatically.
118
+
119
+ #### `TaskContext`
120
+
121
+ Context manager for a single task's lifecycle:
122
+
123
+ ```python
124
+ with agent.task_context(task) as ctx:
125
+ ctx.progress("Starting review...", 5)
126
+ # ... do work ...
127
+ ctx.progress("Done", 100)
128
+ ctx.set_result(summary="All good", status="completed")
129
+ ```
130
+
131
+ | Method | Description |
132
+ |--------|-------------|
133
+ | `ctx.progress(message, pct)` | Send a progress update (0–100) |
134
+ | `ctx.set_result(summary, status, logs, metrics)` | Set the result to submit on exit |
135
+ | `ctx.task` | The raw task dict |
136
+ | `ctx.task_id` | The task UUID string |
137
+
138
+ ### Task Lifecycle Methods
139
+
140
+ #### `handle_task(task, handler) -> bool`
141
+
142
+ Handle one task: accept → call handler → submit result. Returns True on success.
143
+
144
+ #### `register() -> dict`
145
+
146
+ `POST /agents/register` — Register the agent with the hub.
147
+
148
+ #### `heartbeat(status="online", message=None) -> dict`
149
+
150
+ `POST /agents/heartbeat` — Send a heartbeat ping.
151
+
152
+ #### `list_tasks(status=None) -> list[dict]`
153
+
154
+ `GET /tasks` — List tasks (agent-scoped).
155
+
156
+ #### `get_task(task_id) -> dict`
157
+
158
+ `GET /tasks/{task_id}` — Get full task details.
159
+
160
+ #### `accept_task(task_id) -> dict`
161
+
162
+ `POST /tasks/{task_id}/accept` — Accept a task.
163
+
164
+ #### `decline_task(task_id, reason="") -> dict`
165
+
166
+ `POST /tasks/{task_id}/decline` — Decline a task.
167
+
168
+ #### `update_status(task_id, status, reason="") -> dict`
169
+
170
+ `PATCH /tasks/{task_id}/status` — Update task status.
171
+
172
+ #### `send_progress(task_id, message, progress_pct=0) -> dict`
173
+
174
+ `POST /tasks/{task_id}/progress` — Send a progress update.
175
+
176
+ #### `submit_result(task_id, summary, status="completed", artifacts=None, logs="", metrics=None) -> dict`
177
+
178
+ `POST /tasks/{task_id}/result` — Submit a final result.
179
+
180
+ #### `start_heartbeat_loop(interval=30, message=None)`
181
+
182
+ Start a background daemon thread that sends heartbeats.
183
+
184
+ #### `stop_heartbeat_loop()`
185
+
186
+ Stop the heartbeat thread.
187
+
188
+ #### `close()`
189
+
190
+ Stop heartbeats, signal serve loop, close HTTP client. Supports context manager:
191
+
192
+ ```python
193
+ with KnitAgent(...) as agent:
194
+ agent.register()
195
+ agent.serve(my_handler)
196
+ ```
197
+
198
+ ## Running the Example
199
+
200
+ ```bash
201
+ # Set your API key
202
+ export KNIT_AGENT_API_KEY=sk_live_yourkeyhere
203
+
204
+ # Run the example (assumes hub is at localhost:8000)
205
+ python -m examples.simple_agent
206
+
207
+ # Or with a custom hub URL
208
+ export KNIT_HUB_URL=http://your-hub:8000/api/v1
209
+ python -m examples.simple_agent
210
+ ```
211
+
212
+ ## Error Handling
213
+
214
+ All API errors raise `KnitAPIError(status_code, message)`. Network errors raise
215
+ `KnitError`. Both inherit from `KnitError` so you can catch either:
216
+
217
+ ```python
218
+ from knit.agent import KnitError
219
+
220
+ try:
221
+ agent.heartbeat()
222
+ except KnitError as exc:
223
+ print(f"Heartbeat failed: {exc}")
224
+ ```
@@ -0,0 +1,6 @@
1
+ """Knit Agent SDK — Build agents that connect to the Knit hub."""
2
+
3
+ from knit.agent import KnitAgent
4
+ from knit.daemon import KnitDaemon, detect_clis
5
+
6
+ __all__ = ["KnitAgent", "KnitDaemon", "detect_clis"]
@@ -0,0 +1,10 @@
1
+ """``python -m knit`` — Run a Knit agent from config."""
2
+
3
+
4
+ def main() -> None:
5
+ print("Knit Agent SDK — use the SDK programmatically or run an example:")
6
+ print(" python -m knit.examples.simple_agent")
7
+
8
+
9
+ if __name__ == "__main__":
10
+ main()