codegraff 0.1.1__cp313-cp313-win_amd64.whl
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.
- codegraff/__init__.py +270 -0
- codegraff/_native.cp313-win_amd64.pyd +0 -0
- codegraff/_validation.py +81 -0
- codegraff/py.typed +0 -0
- codegraff-0.1.1.dist-info/METADATA +88 -0
- codegraff-0.1.1.dist-info/RECORD +8 -0
- codegraff-0.1.1.dist-info/WHEEL +4 -0
- codegraff-0.1.1.dist-info/sboms/forge_sdk_python.cyclonedx.json +22353 -0
codegraff/__init__.py
ADDED
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
"""Python SDK for the codegraff agent."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import json
|
|
6
|
+
import os
|
|
7
|
+
from dataclasses import dataclass, field
|
|
8
|
+
from typing import Any, Iterator, Optional
|
|
9
|
+
|
|
10
|
+
from codegraff._native import GraffApi, ChatStreamHandle, version as _native_version
|
|
11
|
+
from codegraff._validation import validate
|
|
12
|
+
|
|
13
|
+
__all__ = ["Graff", "GraffSession", "Sandbox", "version"]
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def version() -> str:
|
|
17
|
+
return _native_version()
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
@dataclass
|
|
21
|
+
class AgentEvent:
|
|
22
|
+
type: str
|
|
23
|
+
data: dict[str, Any] = field(default_factory=dict)
|
|
24
|
+
|
|
25
|
+
def __repr__(self) -> str:
|
|
26
|
+
return f"AgentEvent({self.type!r}, {self.data})"
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def _parse_event(raw: str) -> AgentEvent:
|
|
30
|
+
d = json.loads(raw)
|
|
31
|
+
t = d.pop("type", "unknown")
|
|
32
|
+
return AgentEvent(type=t, data=d)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class Graff:
|
|
36
|
+
"""Long-lived codegraff instance. Mirrors the TS SDK's Graff class."""
|
|
37
|
+
|
|
38
|
+
def __init__(
|
|
39
|
+
self,
|
|
40
|
+
*,
|
|
41
|
+
cwd: Optional[str] = None,
|
|
42
|
+
provider: Optional[str] = None,
|
|
43
|
+
api_key: Optional[str] = None,
|
|
44
|
+
model: Optional[str] = None,
|
|
45
|
+
max_tokens: Optional[int] = None,
|
|
46
|
+
):
|
|
47
|
+
validate("init", {"cwd": cwd, "provider": provider, "api_key": api_key, "model": model, "max_tokens": max_tokens})
|
|
48
|
+
if api_key and not provider:
|
|
49
|
+
raise ValueError("provider is required when api_key is supplied")
|
|
50
|
+
|
|
51
|
+
if provider:
|
|
52
|
+
os.environ["FORGE_SESSION__PROVIDER_ID"] = provider
|
|
53
|
+
if model:
|
|
54
|
+
os.environ["FORGE_SESSION__MODEL_ID"] = model
|
|
55
|
+
if max_tokens is not None:
|
|
56
|
+
os.environ["FORGE_MAX_TOKENS"] = str(max_tokens)
|
|
57
|
+
|
|
58
|
+
self._api = GraffApi.init(cwd or os.getcwd())
|
|
59
|
+
|
|
60
|
+
if api_key and provider:
|
|
61
|
+
# extra_params is a required positional in the native binding
|
|
62
|
+
# (Option without a #[pyo3(signature)] default), so pass None.
|
|
63
|
+
self._api.upsert_credential(provider, api_key, None)
|
|
64
|
+
|
|
65
|
+
def chat(
|
|
66
|
+
self,
|
|
67
|
+
prompt: str,
|
|
68
|
+
*,
|
|
69
|
+
conversation_id: Optional[str] = None,
|
|
70
|
+
model: Optional[str] = None,
|
|
71
|
+
) -> Iterator[AgentEvent]:
|
|
72
|
+
"""Run a chat turn. Yields AgentEvent objects."""
|
|
73
|
+
validate("chat", {"prompt": prompt, "conversation_id": conversation_id, "model": model})
|
|
74
|
+
handle = self._api.chat(prompt, conversation_id, model)
|
|
75
|
+
yield AgentEvent(type="ConversationStarted", data={"conversationId": handle.conversation_id})
|
|
76
|
+
while True:
|
|
77
|
+
raw = handle.next_event()
|
|
78
|
+
if raw is None:
|
|
79
|
+
break
|
|
80
|
+
yield _parse_event(raw)
|
|
81
|
+
|
|
82
|
+
def session(
|
|
83
|
+
self,
|
|
84
|
+
*,
|
|
85
|
+
conversation_id: Optional[str] = None,
|
|
86
|
+
model: Optional[str] = None,
|
|
87
|
+
) -> "GraffSession":
|
|
88
|
+
return GraffSession(self, conversation_id=conversation_id, model=model)
|
|
89
|
+
|
|
90
|
+
# -- Conversation management --
|
|
91
|
+
|
|
92
|
+
def list_conversations(self, limit: Optional[int] = None) -> list[dict]:
|
|
93
|
+
return json.loads(self._api.list_conversations(limit))
|
|
94
|
+
|
|
95
|
+
def get_conversation(self, id: str) -> Optional[dict]:
|
|
96
|
+
raw = self._api.get_conversation(id)
|
|
97
|
+
return json.loads(raw) if raw else None
|
|
98
|
+
|
|
99
|
+
def last_conversation(self) -> Optional[dict]:
|
|
100
|
+
raw = self._api.last_conversation()
|
|
101
|
+
return json.loads(raw) if raw else None
|
|
102
|
+
|
|
103
|
+
def delete_conversation(self, id: str) -> None:
|
|
104
|
+
self._api.delete_conversation(id)
|
|
105
|
+
|
|
106
|
+
def compact_conversation(self, id: str) -> dict:
|
|
107
|
+
return json.loads(self._api.compact_conversation(id))
|
|
108
|
+
|
|
109
|
+
# -- Agents --
|
|
110
|
+
|
|
111
|
+
def get_agent_infos(self) -> list[dict]:
|
|
112
|
+
return json.loads(self._api.get_agent_infos())
|
|
113
|
+
|
|
114
|
+
# -- Auth --
|
|
115
|
+
|
|
116
|
+
def upsert_credential(
|
|
117
|
+
self,
|
|
118
|
+
provider_id: str,
|
|
119
|
+
api_key: str,
|
|
120
|
+
extra_params: Optional[list[list[str]]] = None,
|
|
121
|
+
) -> None:
|
|
122
|
+
self._api.upsert_credential(provider_id, api_key, extra_params)
|
|
123
|
+
|
|
124
|
+
def remove_credential(self, provider_id: str) -> None:
|
|
125
|
+
self._api.remove_credential(provider_id)
|
|
126
|
+
|
|
127
|
+
# -- Sandboxes (gateway HTTP) --
|
|
128
|
+
|
|
129
|
+
def create_sandbox(self, **kwargs) -> "Sandbox":
|
|
130
|
+
return Sandbox.create(self, **kwargs)
|
|
131
|
+
|
|
132
|
+
def get_sandbox(self, id: str) -> "Sandbox":
|
|
133
|
+
return Sandbox.get(self, id)
|
|
134
|
+
|
|
135
|
+
def list_sandboxes(self) -> list[dict]:
|
|
136
|
+
return Sandbox.list(self)
|
|
137
|
+
|
|
138
|
+
def version(self) -> str:
|
|
139
|
+
return self._api.version()
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
class GraffSession:
|
|
143
|
+
"""Multi-turn session that preserves conversation_id across sends."""
|
|
144
|
+
|
|
145
|
+
def __init__(
|
|
146
|
+
self,
|
|
147
|
+
graff: Graff,
|
|
148
|
+
*,
|
|
149
|
+
conversation_id: Optional[str] = None,
|
|
150
|
+
model: Optional[str] = None,
|
|
151
|
+
):
|
|
152
|
+
self._graff = graff
|
|
153
|
+
self._conversation_id = conversation_id
|
|
154
|
+
self._model = model
|
|
155
|
+
|
|
156
|
+
@property
|
|
157
|
+
def conversation_id(self) -> Optional[str]:
|
|
158
|
+
return self._conversation_id
|
|
159
|
+
|
|
160
|
+
def send(self, prompt: str) -> Iterator[AgentEvent]:
|
|
161
|
+
for event in self._graff.chat(prompt, conversation_id=self._conversation_id, model=self._model):
|
|
162
|
+
if event.type == "ConversationStarted" and not self._conversation_id:
|
|
163
|
+
self._conversation_id = event.data.get("conversationId")
|
|
164
|
+
yield event
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
# -- Sandbox (thin HTTP client hitting the gateway) --
|
|
168
|
+
|
|
169
|
+
import urllib.request
|
|
170
|
+
import urllib.error
|
|
171
|
+
|
|
172
|
+
_GATEWAY_URL = os.environ.get("CODEGRAFF_GATEWAY_URL", "https://gateway.codegraff.com")
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
def _gateway_fetch(api_key: str, method: str, path: str, body: Optional[dict] = None) -> Any:
|
|
176
|
+
url = f"{_GATEWAY_URL}{path}"
|
|
177
|
+
data = json.dumps(body).encode() if body is not None else None
|
|
178
|
+
req = urllib.request.Request(
|
|
179
|
+
url,
|
|
180
|
+
data=data,
|
|
181
|
+
method=method,
|
|
182
|
+
headers={
|
|
183
|
+
"Authorization": f"Bearer {api_key}",
|
|
184
|
+
"Content-Type": "application/json",
|
|
185
|
+
"User-Agent": "codegraff-python-sdk/0.1.0",
|
|
186
|
+
},
|
|
187
|
+
)
|
|
188
|
+
try:
|
|
189
|
+
with urllib.request.urlopen(req) as resp:
|
|
190
|
+
ct = resp.headers.get("Content-Type", "")
|
|
191
|
+
raw = resp.read()
|
|
192
|
+
if "application/json" in ct:
|
|
193
|
+
return json.loads(raw)
|
|
194
|
+
return raw.decode()
|
|
195
|
+
except urllib.error.HTTPError as e:
|
|
196
|
+
body_text = e.read().decode()[:300] if e.fp else ""
|
|
197
|
+
raise RuntimeError(f"Gateway {method} {path} failed ({e.code}): {body_text}") from e
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
def _resolve_api_key() -> str:
|
|
201
|
+
for var in ("CODEGRAFF_API_KEY", "CG_API_KEY"):
|
|
202
|
+
val = os.environ.get(var, "")
|
|
203
|
+
if val:
|
|
204
|
+
return val
|
|
205
|
+
return ""
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
class Sandbox:
|
|
209
|
+
"""Cloud sandbox managed through the codegraff gateway."""
|
|
210
|
+
|
|
211
|
+
def __init__(self, graff: Graff, id: str):
|
|
212
|
+
self._graff = graff
|
|
213
|
+
self._api_key = _resolve_api_key()
|
|
214
|
+
self.id = id
|
|
215
|
+
|
|
216
|
+
@staticmethod
|
|
217
|
+
def create(
|
|
218
|
+
graff: Graff,
|
|
219
|
+
*,
|
|
220
|
+
language: str = "javascript",
|
|
221
|
+
auto_stop_minutes: int = 30,
|
|
222
|
+
labels: Optional[dict[str, str]] = None,
|
|
223
|
+
) -> "Sandbox":
|
|
224
|
+
api_key = _resolve_api_key()
|
|
225
|
+
data = _gateway_fetch(api_key, "POST", "/v1/sandboxes", {
|
|
226
|
+
"language": language,
|
|
227
|
+
"autoStopMinutes": auto_stop_minutes,
|
|
228
|
+
"labels": labels or {},
|
|
229
|
+
})
|
|
230
|
+
return Sandbox(graff, data["id"])
|
|
231
|
+
|
|
232
|
+
@staticmethod
|
|
233
|
+
def get(graff: Graff, id: str) -> "Sandbox":
|
|
234
|
+
api_key = _resolve_api_key()
|
|
235
|
+
_gateway_fetch(api_key, "GET", f"/v1/sandboxes/{id}")
|
|
236
|
+
return Sandbox(graff, id)
|
|
237
|
+
|
|
238
|
+
@staticmethod
|
|
239
|
+
def list(graff: Graff) -> list[dict]:
|
|
240
|
+
api_key = _resolve_api_key()
|
|
241
|
+
return _gateway_fetch(api_key, "GET", "/v1/sandboxes")
|
|
242
|
+
|
|
243
|
+
def info(self) -> dict:
|
|
244
|
+
return _gateway_fetch(self._api_key, "GET", f"/v1/sandboxes/{self.id}")
|
|
245
|
+
|
|
246
|
+
def exec(self, command: str, *, cwd: Optional[str] = None, env: Optional[dict] = None, timeout_seconds: int = 300) -> dict:
|
|
247
|
+
return _gateway_fetch(self._api_key, "POST", f"/v1/sandboxes/{self.id}/exec", {
|
|
248
|
+
"command": command, "cwd": cwd, "env": env, "timeoutSeconds": timeout_seconds,
|
|
249
|
+
})
|
|
250
|
+
|
|
251
|
+
def upload(self, content: bytes | str, dest_path: str) -> None:
|
|
252
|
+
import base64
|
|
253
|
+
b = content if isinstance(content, bytes) else content.encode()
|
|
254
|
+
_gateway_fetch(self._api_key, "POST", f"/v1/sandboxes/{self.id}/upload", {
|
|
255
|
+
"path": dest_path, "contentBase64": base64.b64encode(b).decode(),
|
|
256
|
+
})
|
|
257
|
+
|
|
258
|
+
def download(self, path: str) -> bytes:
|
|
259
|
+
import base64
|
|
260
|
+
data = _gateway_fetch(self._api_key, "POST", f"/v1/sandboxes/{self.id}/download", {"path": path})
|
|
261
|
+
return base64.b64decode(data["contentBase64"])
|
|
262
|
+
|
|
263
|
+
def stop(self) -> dict:
|
|
264
|
+
return _gateway_fetch(self._api_key, "POST", f"/v1/sandboxes/{self.id}/stop")
|
|
265
|
+
|
|
266
|
+
def start(self) -> dict:
|
|
267
|
+
return _gateway_fetch(self._api_key, "POST", f"/v1/sandboxes/{self.id}/start")
|
|
268
|
+
|
|
269
|
+
def destroy(self) -> None:
|
|
270
|
+
_gateway_fetch(self._api_key, "DELETE", f"/v1/sandboxes/{self.id}")
|
|
Binary file
|
codegraff/_validation.py
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
"""dhi-backed input validation for the codegraff Python SDK.
|
|
2
|
+
|
|
3
|
+
Mirrors the TS SDK's ``validation.js``. dhi (https://github.com/justrach/dhi) is
|
|
4
|
+
a Pydantic-compatible validator. Validation is **advisory**: if dhi can't be
|
|
5
|
+
imported, we fall back to a minimal check so validation never becomes a *new*
|
|
6
|
+
hard failure. We only read the pass/fail result — the original option values
|
|
7
|
+
reach the native layer unchanged (no coercion, no mutation).
|
|
8
|
+
|
|
9
|
+
dhi ships native wheels for cp39-cp314 (incl. free-threaded cp313t/cp314t). The
|
|
10
|
+
native build (>=1.3.3) matches Pydantic on the type checks we use and, on a
|
|
11
|
+
free-threaded interpreter, no longer re-enables the GIL. A pure-Python fallback
|
|
12
|
+
is only used where no native wheel exists (e.g. Windows) and is looser on
|
|
13
|
+
``Optional[...]`` fields.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
# Keep these annotations as real objects (no `from __future__ import annotations`).
|
|
17
|
+
# dhi's old pure-Python fallback silently skipped validation when annotations were
|
|
18
|
+
# stringized; native dhi (>=1.3.2) resolves them, but there's no reason to add the
|
|
19
|
+
# future import here.
|
|
20
|
+
import warnings
|
|
21
|
+
from typing import Optional
|
|
22
|
+
|
|
23
|
+
_models = None
|
|
24
|
+
_warned = False
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def _build():
|
|
28
|
+
from dhi import BaseModel # lazy: SDK still imports if dhi is absent
|
|
29
|
+
|
|
30
|
+
class InitOptions(BaseModel):
|
|
31
|
+
cwd: Optional[str] = None
|
|
32
|
+
provider: Optional[str] = None
|
|
33
|
+
api_key: Optional[str] = None
|
|
34
|
+
model: Optional[str] = None
|
|
35
|
+
max_tokens: Optional[int] = None
|
|
36
|
+
|
|
37
|
+
class ChatOptions(BaseModel):
|
|
38
|
+
prompt: str
|
|
39
|
+
conversation_id: Optional[str] = None
|
|
40
|
+
model: Optional[str] = None
|
|
41
|
+
|
|
42
|
+
return {"init": InitOptions, "chat": ChatOptions}
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def _get():
|
|
46
|
+
global _models
|
|
47
|
+
if _models is None:
|
|
48
|
+
_models = _build()
|
|
49
|
+
return _models
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def _fallback(kind: str, opts: dict) -> None:
|
|
53
|
+
if kind == "chat" and not isinstance(opts.get("prompt"), str):
|
|
54
|
+
raise TypeError("codegraff chat: `prompt` must be a string")
|
|
55
|
+
if kind == "init":
|
|
56
|
+
ak = opts.get("api_key")
|
|
57
|
+
if ak is not None and not isinstance(ak, str):
|
|
58
|
+
raise TypeError("codegraff init: `api_key` must be a string")
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def validate(kind: str, opts: dict) -> None:
|
|
62
|
+
"""Validate public options for `kind` in ('init', 'chat').
|
|
63
|
+
|
|
64
|
+
Raises ValueError on invalid input; returns None on success. Never mutates
|
|
65
|
+
`opts`.
|
|
66
|
+
"""
|
|
67
|
+
global _warned
|
|
68
|
+
try:
|
|
69
|
+
models = _get()
|
|
70
|
+
except Exception as e: # dhi missing / failed to import
|
|
71
|
+
if not _warned:
|
|
72
|
+
_warned = True
|
|
73
|
+
warnings.warn(f"codegraff: dhi validation unavailable ({e}); using basic checks")
|
|
74
|
+
return _fallback(kind, opts)
|
|
75
|
+
model = models.get(kind)
|
|
76
|
+
if model is None:
|
|
77
|
+
return
|
|
78
|
+
try:
|
|
79
|
+
model(**opts)
|
|
80
|
+
except Exception as e:
|
|
81
|
+
raise ValueError(f"codegraff {kind}: invalid options — {e}") from None
|
codegraff/py.typed
ADDED
|
File without changes
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: codegraff
|
|
3
|
+
Version: 0.1.1
|
|
4
|
+
Classifier: Programming Language :: Rust
|
|
5
|
+
Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
6
|
+
Classifier: Programming Language :: Python :: 3
|
|
7
|
+
Classifier: Topic :: Software Development :: Libraries
|
|
8
|
+
Requires-Dist: dhi>=1.3.3
|
|
9
|
+
Summary: Python SDK for the codegraff agent (PyO3 bindings).
|
|
10
|
+
Keywords: ai,agent,sandbox,codegraff
|
|
11
|
+
License: MIT
|
|
12
|
+
Requires-Python: >=3.9
|
|
13
|
+
Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
|
|
14
|
+
Project-URL: Homepage, https://codegraff.com
|
|
15
|
+
Project-URL: Repository, https://github.com/justrach/codegraff
|
|
16
|
+
|
|
17
|
+
# codegraff
|
|
18
|
+
|
|
19
|
+
The Python SDK for the [CodeGraff](https://github.com/justrach/codegraff) coding
|
|
20
|
+
agent. Run the agent **in-process** (PyO3 native bindings) — streaming events,
|
|
21
|
+
multi-turn sessions, BYOK auth, and cloud sandboxes — with synchronous
|
|
22
|
+
generators that fit straight into any Python app or web framework.
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
pip install codegraff
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
```python
|
|
29
|
+
from codegraff import Graff
|
|
30
|
+
|
|
31
|
+
graff = Graff() # reads ~/.forge/forge.toml, like the graff CLI
|
|
32
|
+
|
|
33
|
+
for ev in graff.chat("explain monads in 3 sentences"):
|
|
34
|
+
if ev.type == "TaskMessage" and ev.data.get("content", {}).get("kind") == "Markdown":
|
|
35
|
+
print(ev.data["content"]["text"], end="", flush=True)
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## BYOK
|
|
39
|
+
|
|
40
|
+
```python
|
|
41
|
+
import os
|
|
42
|
+
from codegraff import Graff
|
|
43
|
+
|
|
44
|
+
graff = Graff(
|
|
45
|
+
provider="codegraff", # or "openai" / "anthropic" / "open_router" / "xai" / ...
|
|
46
|
+
api_key=os.environ["CODEGRAFF_API_KEY"],
|
|
47
|
+
model="deepseek-v4-pro",
|
|
48
|
+
)
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
`Graff(...)` is a synchronous constructor; `chat()` is a blocking generator that
|
|
52
|
+
releases the GIL while it waits on the agent, so it drives async servers without
|
|
53
|
+
starving the loop. Inputs are validated at the boundary with
|
|
54
|
+
[dhi](https://github.com/justrach/dhi).
|
|
55
|
+
|
|
56
|
+
## What you get
|
|
57
|
+
|
|
58
|
+
- **Streaming chat** — `for ev in graff.chat(prompt)` yields `AgentEvent`s
|
|
59
|
+
(`TaskMessage`, `ToolCallStart`/`End`, `TaskReasoning`, `TaskComplete`, …).
|
|
60
|
+
- **Multi-turn** — pass `conversation_id`, or use `graff.session()`.
|
|
61
|
+
- **Conversation management** — `list_conversations`, `get_conversation`,
|
|
62
|
+
`last_conversation`, `compact_conversation`, `delete_conversation`.
|
|
63
|
+
- **Agents & auth** — `get_agent_infos`, `upsert_credential`, `remove_credential`.
|
|
64
|
+
- **Cloud sandboxes** — `graff.create_sandbox()` → `exec` / `upload` / `download` /
|
|
65
|
+
`stop` / `start` / `destroy` (via the CodeGraff gateway).
|
|
66
|
+
|
|
67
|
+
## Install notes
|
|
68
|
+
|
|
69
|
+
**Platform support (0.1.0):** prebuilt wheels are published **only for macOS
|
|
70
|
+
arm64** on Python **3.12 / 3.13 / 3.14t**, and there is **no sdist** — so
|
|
71
|
+
`pip install codegraff` fails on Linux, Windows, Intel macOS, or Python
|
|
72
|
+
3.9–3.11 until the CI wheel matrix lands. To run elsewhere today, build from the
|
|
73
|
+
full repo (not `pip install` — the crate has workspace path deps):
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
git clone https://github.com/justrach/codegraff
|
|
77
|
+
cd codegraff/sdk/python
|
|
78
|
+
pip install maturin && maturin develop --release
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
## Docs & examples
|
|
82
|
+
|
|
83
|
+
- **In-depth guide:** [docs/sdk/python.md](../../docs/sdk/python.md)
|
|
84
|
+
- **turboAPI HTTP example:** [`example/`](./example)
|
|
85
|
+
- **TypeScript SDK:** [`@codegraff/sdk`](../typescript) · [guide](../../docs/sdk/typescript.md)
|
|
86
|
+
|
|
87
|
+
Requires Python >= 3.9. MIT licensed.
|
|
88
|
+
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
codegraff/__init__.py,sha256=Nm0dtzrb5Qt-T91dA0Nbx4hNGuQpExotRvKBk2EUAYQ,9055
|
|
2
|
+
codegraff/_native.cp313-win_amd64.pyd,sha256=WymNF3K2YNDBeNUqQIZ2TKV6loux4o-SUxIJeJaESTc,40040960
|
|
3
|
+
codegraff/_validation.py,sha256=uWyK5LBeiNJ4eyOWZr8y2iNRJzWWp2IgsA7Twfb-wTM,2896
|
|
4
|
+
codegraff/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
5
|
+
codegraff-0.1.1.dist-info/METADATA,sha256=3AYzPFm_cvgfo74s7EiNZecgXdItGaQdwU1QJ3301fk,3298
|
|
6
|
+
codegraff-0.1.1.dist-info/WHEEL,sha256=SOvIh6ndLyztvkrMKsamHag5pxo6YUrIipqSIMTC7-I,97
|
|
7
|
+
codegraff-0.1.1.dist-info/sboms/forge_sdk_python.cyclonedx.json,sha256=7_7xuVtJ9lEEO5yP8ckec-Sp7bOCT0V-OUi_OoOPVVs,758081
|
|
8
|
+
codegraff-0.1.1.dist-info/RECORD,,
|