silvol 0.2.0__py3-none-any.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.
silvol/__init__.py ADDED
@@ -0,0 +1,40 @@
1
+ """
2
+ Silvol — Python SDK for the Silvol inference + fine-tuning API.
3
+
4
+ Drop-in replacement for the OpenAI SDK pointing at Silvol's
5
+ decentralised GPU gateway, plus a fine-tuning client at ``client.finetune``.
6
+
7
+ Quickstart::
8
+
9
+ from silvol import Silvol
10
+
11
+ client = Silvol(api_key="sk-svl-...")
12
+
13
+ # Inference
14
+ resp = client.chat.completions.create(
15
+ model="DeepSeek-R1-Distill-Qwen-7B",
16
+ messages=[{"role": "user", "content": "Hello"}],
17
+ )
18
+ print(resp.choices[0].message.content)
19
+
20
+ # Fine-tuning ($15 flat per run)
21
+ job = client.finetune.submit_job(
22
+ job_name="legal-assistant-v1",
23
+ base_model="llama-3.1-8b",
24
+ dataset_path="training.jsonl",
25
+ )
26
+ client.finetune.upload_dataset(job["dataset_upload_url"], "training.jsonl")
27
+ """
28
+
29
+ from ._version import __version__
30
+ from .client import AsyncSilvol, Silvol
31
+ from .finetune import AsyncFinetune, Finetune, FinetuneError
32
+
33
+ __all__ = [
34
+ "Silvol",
35
+ "AsyncSilvol",
36
+ "Finetune",
37
+ "AsyncFinetune",
38
+ "FinetuneError",
39
+ "__version__",
40
+ ]
silvol/_version.py ADDED
@@ -0,0 +1 @@
1
+ __version__ = "0.2.0"
silvol/client.py ADDED
@@ -0,0 +1,87 @@
1
+ """
2
+ Silvol Python SDK — sync and async clients.
3
+
4
+ Drop-in replacements for openai.OpenAI / openai.AsyncOpenAI that point at
5
+ the Silvol inference gateway by default.
6
+ """
7
+
8
+ from openai import OpenAI, AsyncOpenAI
9
+
10
+ from ._version import __version__
11
+ from .finetune import Finetune, AsyncFinetune
12
+
13
+ __all__ = ["Silvol", "AsyncSilvol"]
14
+
15
+ _DEFAULT_BASE_URL = "https://api.silvol.ai/v1"
16
+
17
+
18
+ class Silvol(OpenAI):
19
+ """
20
+ Synchronous Silvol client.
21
+
22
+ Usage::
23
+
24
+ from silvol import Silvol
25
+
26
+ client = Silvol(api_key="sk-svl-...")
27
+ resp = client.chat.completions.create(
28
+ model="DeepSeek-R1-Distill-Qwen-7B",
29
+ messages=[{"role": "user", "content": "Hello"}],
30
+ )
31
+ print(resp.choices[0].message.content)
32
+
33
+ Fine-tuning is available at ``client.finetune`` — see ``silvol.finetune``.
34
+ """
35
+
36
+ DEFAULT_BASE_URL: str = _DEFAULT_BASE_URL
37
+
38
+ def __init__(
39
+ self,
40
+ api_key: str | None = None,
41
+ base_url: str | None = None,
42
+ **kwargs,
43
+ ) -> None:
44
+ super().__init__(
45
+ api_key=api_key,
46
+ base_url=base_url or self.DEFAULT_BASE_URL,
47
+ **kwargs,
48
+ )
49
+ self.finetune: Finetune = Finetune(self)
50
+
51
+
52
+ class AsyncSilvol(AsyncOpenAI):
53
+ """
54
+ Asynchronous Silvol client.
55
+
56
+ Usage::
57
+
58
+ import asyncio
59
+ from silvol import AsyncSilvol
60
+
61
+ async def main():
62
+ client = AsyncSilvol(api_key="sk-svl-...")
63
+ resp = await client.chat.completions.create(
64
+ model="DeepSeek-R1-Distill-Qwen-7B",
65
+ messages=[{"role": "user", "content": "Hello"}],
66
+ )
67
+ print(resp.choices[0].message.content)
68
+
69
+ asyncio.run(main())
70
+
71
+ Fine-tuning is available at ``client.finetune`` — see ``silvol.finetune``.
72
+ """
73
+
74
+ DEFAULT_BASE_URL: str = _DEFAULT_BASE_URL
75
+
76
+ def __init__(
77
+ self,
78
+ api_key: str | None = None,
79
+ base_url: str | None = None,
80
+ **kwargs,
81
+ ) -> None:
82
+ super().__init__(
83
+ api_key=api_key,
84
+ base_url=base_url or self.DEFAULT_BASE_URL,
85
+ **kwargs,
86
+ )
87
+ self.finetune: AsyncFinetune = AsyncFinetune(self)
silvol/finetune.py ADDED
@@ -0,0 +1,285 @@
1
+ """
2
+ Silvol fine-tuning API client.
3
+
4
+ Wraps the gateway endpoints under ``/v1/finetune/*`` with a small typed surface.
5
+ Attached to ``Silvol`` and ``AsyncSilvol`` clients as ``client.finetune``.
6
+
7
+ Quickstart::
8
+
9
+ from silvol import Silvol
10
+
11
+ client = Silvol(api_key="sk-svl-...")
12
+
13
+ # Submit a training job
14
+ with open("dataset.jsonl", "rb") as f:
15
+ job = client.finetune.submit_job(
16
+ job_name="legal-assistant-v1",
17
+ base_model="llama-3.1-8b",
18
+ dataset_file=f,
19
+ )
20
+ print(job["id"], job["price_cents"])
21
+
22
+ # Poll until ready
23
+ while True:
24
+ job = client.finetune.get_job(job["id"])
25
+ if job["status"] in ("ready", "failed", "cancelled"):
26
+ break
27
+
28
+ # Deploy and use
29
+ if job["status"] == "ready":
30
+ client.finetune.deploy_job(job["id"])
31
+ resp = client.chat.completions.create(
32
+ model=f"silvol/{job['job_name']}",
33
+ messages=[{"role": "user", "content": "What is force majeure?"}],
34
+ )
35
+ """
36
+
37
+ from __future__ import annotations
38
+
39
+ import base64
40
+ from typing import Any, BinaryIO, Iterable, Literal
41
+
42
+
43
+ BaseModel = Literal["llama-3.1-8b", "mistral-7b"]
44
+ JobStatus = Literal[
45
+ "pending", "training", "uploading", "ready",
46
+ "deploying", "deployed", "failed", "cancelled",
47
+ ]
48
+
49
+
50
+ def _encode_dataset(dataset: bytes | BinaryIO | str | Iterable[dict]) -> str:
51
+ """Accept multiple input shapes and return base64-encoded JSONL."""
52
+ if isinstance(dataset, str):
53
+ # Already JSONL text
54
+ data = dataset.encode("utf-8")
55
+ elif isinstance(dataset, bytes):
56
+ data = dataset
57
+ elif hasattr(dataset, "read"):
58
+ chunk = dataset.read()
59
+ data = chunk.encode("utf-8") if isinstance(chunk, str) else chunk
60
+ else:
61
+ # Iterable of message dicts → JSONL
62
+ import json
63
+ data = "\n".join(json.dumps(ex) for ex in dataset).encode("utf-8")
64
+ return base64.b64encode(data).decode("ascii")
65
+
66
+
67
+ def _build_path(*parts: str) -> str:
68
+ return "/" + "/".join(p.strip("/") for p in parts)
69
+
70
+
71
+ # ── Sync ─────────────────────────────────────────────────────────────────────
72
+
73
+
74
+ class Finetune:
75
+ """Synchronous fine-tuning API. Accessed via ``client.finetune``."""
76
+
77
+ def __init__(self, client: Any) -> None:
78
+ # `client` is a Silvol (subclass of OpenAI) — reuse its underlying
79
+ # httpx client so we share auth, base URL, timeouts, retries, etc.
80
+ self._client = client
81
+
82
+ def _request(self, method: str, path: str, **kwargs: Any) -> Any:
83
+ # openai SDK exposes `_client._request` on the OpenAI instance; we use
84
+ # `_client._client.request` via the underlying httpx client for raw access.
85
+ url = path # base_url is already set on the openai client
86
+ # The openai sync client carries a base URL in self.base_url and an
87
+ # httpx client at self._client. We hit that directly for non-OpenAI routes.
88
+ http = self._client._client # httpx.Client
89
+ full_url = f"{str(self._client.base_url).rstrip('/')}{url}"
90
+ headers = {
91
+ "Authorization": f"Bearer {self._client.api_key}",
92
+ "Content-Type": "application/json",
93
+ }
94
+ if "headers" in kwargs:
95
+ headers.update(kwargs.pop("headers"))
96
+ resp = http.request(method, full_url, headers=headers, **kwargs)
97
+ if resp.status_code >= 400:
98
+ try:
99
+ detail = resp.json().get("error") or resp.text
100
+ except Exception:
101
+ detail = resp.text
102
+ raise FinetuneError(f"{method} {path} failed ({resp.status_code}): {detail}")
103
+ if resp.status_code == 204 or not resp.content:
104
+ return None
105
+ return resp.json()
106
+
107
+ # ── Jobs ────────────────────────────────────────────────────────────────
108
+
109
+ def submit_job(
110
+ self,
111
+ *,
112
+ job_name: str,
113
+ base_model: BaseModel,
114
+ dataset_file: bytes | BinaryIO | str | Iterable[dict] | None = None,
115
+ dataset_path: str | None = None,
116
+ ) -> dict[str, Any]:
117
+ """
118
+ Submit a training job. Charges $15 via Stripe immediately.
119
+
120
+ Provide either ``dataset_file`` (bytes / file object / JSONL string /
121
+ iterable of message dicts) or ``dataset_path`` (path to a .jsonl file).
122
+
123
+ Returns the job record including a ``dataset_upload_url`` — the SDK
124
+ does NOT upload your dataset for you in this version. Use the returned
125
+ signed URL with your own HTTP client (or call ``upload_dataset()``).
126
+ """
127
+ if dataset_path and not dataset_file:
128
+ with open(dataset_path, "rb") as f:
129
+ dataset_file = f.read()
130
+ if dataset_file is None:
131
+ raise ValueError("Provide dataset_file or dataset_path")
132
+
133
+ return self._request(
134
+ "POST",
135
+ "/v1/finetune/jobs",
136
+ json={
137
+ "jobName": job_name,
138
+ "baseModel": base_model,
139
+ "jsonlBase64": _encode_dataset(dataset_file),
140
+ },
141
+ )
142
+
143
+ def upload_dataset(self, signed_upload_url: str, dataset_path: str) -> None:
144
+ """
145
+ Upload a JSONL file to the signed URL returned by ``submit_job``.
146
+ Training starts automatically on the next poll cycle (~5 min).
147
+ """
148
+ with open(dataset_path, "rb") as f:
149
+ data = f.read()
150
+ # The signed URL hits Supabase Storage directly, not the gateway —
151
+ # use a fresh httpx call without our Bearer token.
152
+ import httpx
153
+ resp = httpx.put(
154
+ signed_upload_url,
155
+ content=data,
156
+ headers={"Content-Type": "application/jsonl"},
157
+ timeout=60.0,
158
+ )
159
+ if resp.status_code >= 400:
160
+ raise FinetuneError(f"Dataset upload failed ({resp.status_code}): {resp.text}")
161
+
162
+ def list_jobs(self) -> list[dict[str, Any]]:
163
+ return self._request("GET", "/v1/finetune/jobs")["jobs"]
164
+
165
+ def get_job(self, job_id: str) -> dict[str, Any]:
166
+ return self._request("GET", _build_path("v1", "finetune", "jobs", job_id))
167
+
168
+ def cancel_job(self, job_id: str) -> dict[str, Any]:
169
+ """Cancel a pending job. Refund issued automatically."""
170
+ return self._request("DELETE", _build_path("v1", "finetune", "jobs", job_id))
171
+
172
+ def deploy_job(self, job_id: str) -> dict[str, Any]:
173
+ """Deploy a ready model. Provisions a dedicated GPU node at $0.80/hr."""
174
+ return self._request(
175
+ "POST", _build_path("v1", "finetune", "jobs", job_id, "deploy")
176
+ )
177
+
178
+ # ── Models ──────────────────────────────────────────────────────────────
179
+
180
+ def list_models(self) -> list[dict[str, Any]]:
181
+ return self._request("GET", "/v1/finetune/models")["models"]
182
+
183
+ def delete_model(self, model_id: str) -> dict[str, Any]:
184
+ """Stop a deployed model or delete a stored one."""
185
+ return self._request(
186
+ "DELETE", _build_path("v1", "finetune", "models", model_id)
187
+ )
188
+
189
+
190
+ # ── Async ────────────────────────────────────────────────────────────────────
191
+
192
+
193
+ class AsyncFinetune:
194
+ """Asynchronous fine-tuning API. Accessed via ``async_client.finetune``."""
195
+
196
+ def __init__(self, client: Any) -> None:
197
+ self._client = client
198
+
199
+ async def _request(self, method: str, path: str, **kwargs: Any) -> Any:
200
+ http = self._client._client # httpx.AsyncClient
201
+ full_url = f"{str(self._client.base_url).rstrip('/')}{path}"
202
+ headers = {
203
+ "Authorization": f"Bearer {self._client.api_key}",
204
+ "Content-Type": "application/json",
205
+ }
206
+ if "headers" in kwargs:
207
+ headers.update(kwargs.pop("headers"))
208
+ resp = await http.request(method, full_url, headers=headers, **kwargs)
209
+ if resp.status_code >= 400:
210
+ try:
211
+ detail = resp.json().get("error") or resp.text
212
+ except Exception:
213
+ detail = resp.text
214
+ raise FinetuneError(f"{method} {path} failed ({resp.status_code}): {detail}")
215
+ if resp.status_code == 204 or not resp.content:
216
+ return None
217
+ return resp.json()
218
+
219
+ async def submit_job(
220
+ self,
221
+ *,
222
+ job_name: str,
223
+ base_model: BaseModel,
224
+ dataset_file: bytes | BinaryIO | str | Iterable[dict] | None = None,
225
+ dataset_path: str | None = None,
226
+ ) -> dict[str, Any]:
227
+ if dataset_path and not dataset_file:
228
+ with open(dataset_path, "rb") as f:
229
+ dataset_file = f.read()
230
+ if dataset_file is None:
231
+ raise ValueError("Provide dataset_file or dataset_path")
232
+
233
+ return await self._request(
234
+ "POST",
235
+ "/v1/finetune/jobs",
236
+ json={
237
+ "jobName": job_name,
238
+ "baseModel": base_model,
239
+ "jsonlBase64": _encode_dataset(dataset_file),
240
+ },
241
+ )
242
+
243
+ async def upload_dataset(self, signed_upload_url: str, dataset_path: str) -> None:
244
+ with open(dataset_path, "rb") as f:
245
+ data = f.read()
246
+ import httpx
247
+ async with httpx.AsyncClient(timeout=60.0) as http:
248
+ resp = await http.put(
249
+ signed_upload_url,
250
+ content=data,
251
+ headers={"Content-Type": "application/jsonl"},
252
+ )
253
+ if resp.status_code >= 400:
254
+ raise FinetuneError(f"Dataset upload failed ({resp.status_code}): {resp.text}")
255
+
256
+ async def list_jobs(self) -> list[dict[str, Any]]:
257
+ return (await self._request("GET", "/v1/finetune/jobs"))["jobs"]
258
+
259
+ async def get_job(self, job_id: str) -> dict[str, Any]:
260
+ return await self._request("GET", _build_path("v1", "finetune", "jobs", job_id))
261
+
262
+ async def cancel_job(self, job_id: str) -> dict[str, Any]:
263
+ return await self._request("DELETE", _build_path("v1", "finetune", "jobs", job_id))
264
+
265
+ async def deploy_job(self, job_id: str) -> dict[str, Any]:
266
+ return await self._request(
267
+ "POST", _build_path("v1", "finetune", "jobs", job_id, "deploy")
268
+ )
269
+
270
+ async def list_models(self) -> list[dict[str, Any]]:
271
+ return (await self._request("GET", "/v1/finetune/models"))["models"]
272
+
273
+ async def delete_model(self, model_id: str) -> dict[str, Any]:
274
+ return await self._request(
275
+ "DELETE", _build_path("v1", "finetune", "models", model_id)
276
+ )
277
+
278
+
279
+ # ── Errors ───────────────────────────────────────────────────────────────────
280
+
281
+
282
+ class FinetuneError(Exception):
283
+ """Raised on non-2xx responses from the Silvol fine-tuning API."""
284
+
285
+ pass
@@ -0,0 +1,4 @@
1
+ # Integrations are imported lazily to avoid hard-coding optional dependencies.
2
+ # Import from submodules directly:
3
+ # from silvol.integrations.langchain import SilvolChat
4
+ # from silvol.integrations.crewai import SilvolLLM
@@ -0,0 +1,47 @@
1
+ """
2
+ CrewAI integration for Silvol.
3
+
4
+ Requires: pip install silvol[crewai]
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ __all__ = ["SilvolLLM"]
10
+
11
+ _BASE_URL = "https://api.silvol.ai/v1"
12
+ _DEFAULT_MODEL = "DeepSeek-R1-Distill-Qwen-7B"
13
+
14
+
15
+ def SilvolLLM(
16
+ api_key: str | None = None,
17
+ model: str = _DEFAULT_MODEL,
18
+ **kwargs,
19
+ ):
20
+ """
21
+ Return a CrewAI ``LLM`` instance wired to the Silvol gateway.
22
+
23
+ Usage::
24
+
25
+ from silvol.integrations.crewai import SilvolLLM
26
+ from crewai import Agent
27
+
28
+ llm = SilvolLLM(api_key="sk-svl-...")
29
+ agent = Agent(role="researcher", goal="...", llm=llm)
30
+
31
+ Parameters
32
+ ----------
33
+ api_key:
34
+ Your Silvol API key (``sk-svl-...``).
35
+ model:
36
+ Model ID to use. Defaults to ``DeepSeek-R1-Distill-Qwen-7B``.
37
+ **kwargs:
38
+ Forwarded verbatim to ``LLM``.
39
+ """
40
+ from crewai import LLM # noqa: PLC0415
41
+
42
+ return LLM(
43
+ model=f"openai/{model}",
44
+ api_key=api_key,
45
+ base_url=_BASE_URL,
46
+ **kwargs,
47
+ )
@@ -0,0 +1,47 @@
1
+ """
2
+ LangChain integration for Silvol.
3
+
4
+ Requires: pip install silvol[langchain]
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ __all__ = ["SilvolChat"]
10
+
11
+ _BASE_URL = "https://api.silvol.ai/v1"
12
+ _DEFAULT_MODEL = "DeepSeek-R1-Distill-Qwen-7B"
13
+
14
+
15
+ def SilvolChat(
16
+ api_key: str | None = None,
17
+ model: str = _DEFAULT_MODEL,
18
+ **kwargs,
19
+ ):
20
+ """
21
+ Return a LangChain ``ChatOpenAI`` instance wired to the Silvol gateway.
22
+
23
+ Usage::
24
+
25
+ from silvol.integrations.langchain import SilvolChat
26
+
27
+ llm = SilvolChat(api_key="sk-svl-...")
28
+ print(llm.invoke("Hello"))
29
+
30
+ Parameters
31
+ ----------
32
+ api_key:
33
+ Your Silvol API key (``sk-svl-...``). Falls back to the
34
+ ``OPENAI_API_KEY`` environment variable if omitted.
35
+ model:
36
+ Model ID to use. Defaults to ``DeepSeek-R1-Distill-Qwen-7B``.
37
+ **kwargs:
38
+ Forwarded verbatim to ``ChatOpenAI``.
39
+ """
40
+ from langchain_openai import ChatOpenAI # noqa: PLC0415
41
+
42
+ return ChatOpenAI(
43
+ openai_api_key=api_key,
44
+ openai_api_base=_BASE_URL,
45
+ model=model,
46
+ **kwargs,
47
+ )
@@ -0,0 +1,173 @@
1
+ Metadata-Version: 2.4
2
+ Name: silvol
3
+ Version: 0.2.0
4
+ Summary: Python SDK for the Silvol inference API — OpenAI-compatible, decentralised GPU.
5
+ Project-URL: Homepage, https://silvol.ai
6
+ Project-URL: Documentation, https://silvol.ai/docs
7
+ Project-URL: Repository, https://github.com/optimuscodexprimus/silvol-python
8
+ Project-URL: Bug Tracker, https://github.com/optimuscodexprimus/silvol-python/issues
9
+ Author-email: Silvol <hello@silvol.ai>
10
+ License: MIT License
11
+
12
+ Copyright (c) 2026 Silvol
13
+
14
+ Permission is hereby granted, free of charge, to any person obtaining a copy
15
+ of this software and associated documentation files (the "Software"), to deal
16
+ in the Software without restriction, including without limitation the rights
17
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
18
+ copies of the Software, and to permit persons to whom the Software is
19
+ furnished to do so, subject to the following conditions:
20
+
21
+ The above copyright notice and this permission notice shall be included in all
22
+ copies or substantial portions of the Software.
23
+
24
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
25
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
26
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
27
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
28
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
29
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
30
+ SOFTWARE.
31
+ License-File: LICENSE
32
+ Keywords: gpu,inference,llm,nosana,openai,silvol
33
+ Classifier: Development Status :: 3 - Alpha
34
+ Classifier: Intended Audience :: Developers
35
+ Classifier: License :: OSI Approved :: MIT License
36
+ Classifier: Programming Language :: Python :: 3
37
+ Classifier: Programming Language :: Python :: 3.10
38
+ Classifier: Programming Language :: Python :: 3.11
39
+ Classifier: Programming Language :: Python :: 3.12
40
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
41
+ Requires-Python: >=3.10
42
+ Requires-Dist: httpx>=0.25
43
+ Requires-Dist: openai>=1.0
44
+ Provides-Extra: all
45
+ Requires-Dist: crewai>=0.30; extra == 'all'
46
+ Requires-Dist: langchain-openai>=0.1; extra == 'all'
47
+ Provides-Extra: crewai
48
+ Requires-Dist: crewai>=0.30; extra == 'crewai'
49
+ Provides-Extra: dev
50
+ Requires-Dist: build; extra == 'dev'
51
+ Requires-Dist: pytest>=7; extra == 'dev'
52
+ Requires-Dist: twine; extra == 'dev'
53
+ Provides-Extra: langchain
54
+ Requires-Dist: langchain-openai>=0.1; extra == 'langchain'
55
+ Description-Content-Type: text/markdown
56
+
57
+ # silvol-python
58
+
59
+ Python SDK for [Silvol](https://silvol.ai) — an OpenAI-compatible inference API running on
60
+ Nosana's decentralised GPU grid.
61
+
62
+ Drop-in replacement for the OpenAI SDK. Change the base URL and your key; keep the rest
63
+ of your code.
64
+
65
+ ---
66
+
67
+ ## Install
68
+
69
+ ```bash
70
+ pip install silvol
71
+ ```
72
+
73
+ With optional framework integrations:
74
+
75
+ ```bash
76
+ pip install silvol[langchain] # LangChain
77
+ pip install silvol[crewai] # CrewAI
78
+ pip install silvol[all] # both
79
+ ```
80
+
81
+ ---
82
+
83
+ ## Quickstart
84
+
85
+ ```python
86
+ from silvol import Silvol
87
+
88
+ client = Silvol(api_key="sk-svl-...") # or set SILVOL_API_KEY env var
89
+
90
+ resp = client.chat.completions.create(
91
+ model="DeepSeek-R1-Distill-Qwen-7B",
92
+ messages=[{"role": "user", "content": "Hello"}],
93
+ )
94
+ print(resp.choices[0].message.content)
95
+ ```
96
+
97
+ Async:
98
+
99
+ ```python
100
+ import asyncio
101
+ from silvol import AsyncSilvol
102
+
103
+ async def main():
104
+ client = AsyncSilvol(api_key="sk-svl-...")
105
+ resp = await client.chat.completions.create(
106
+ model="DeepSeek-R1-Distill-Qwen-7B",
107
+ messages=[{"role": "user", "content": "Hello"}],
108
+ stream=True,
109
+ )
110
+ async for chunk in resp:
111
+ print(chunk.choices[0].delta.content or "", end="", flush=True)
112
+
113
+ asyncio.run(main())
114
+ ```
115
+
116
+ ---
117
+
118
+ ## LangChain
119
+
120
+ ```python
121
+ from silvol.integrations.langchain import SilvolChat
122
+
123
+ llm = SilvolChat(api_key="sk-svl-...")
124
+ result = llm.invoke("Summarise the Silvol architecture in one sentence.")
125
+ print(result.content)
126
+ ```
127
+
128
+ ---
129
+
130
+ ## CrewAI
131
+
132
+ ```python
133
+ from silvol.integrations.crewai import SilvolLLM
134
+ from crewai import Agent, Task, Crew
135
+
136
+ llm = SilvolLLM(api_key="sk-svl-...")
137
+
138
+ researcher = Agent(
139
+ role="Senior Researcher",
140
+ goal="Uncover groundbreaking technologies in AI",
141
+ backstory="...",
142
+ llm=llm,
143
+ )
144
+ ```
145
+
146
+ ---
147
+
148
+ ## Models
149
+
150
+ | Model ID | Context | Notes |
151
+ |---|---|---|
152
+ | `DeepSeek-R1-Distill-Qwen-7B` | 32k | Always-on (free tier) |
153
+ | `llama-3.1-70b` | 128k | On-demand deployment |
154
+ | `qwen-2.5-coder-32b` | 32k | On-demand deployment |
155
+
156
+ Full list: `GET https://api.silvol.ai/v1/models`
157
+
158
+ ---
159
+
160
+ ## Authentication
161
+
162
+ Get your API key from the [Silvol Dashboard](https://silvol.ai/dashboard).
163
+ Keys are prefixed `sk-svl-`. Pass it as `api_key=` or set the `OPENAI_API_KEY`
164
+ environment variable (the SDK checks it automatically).
165
+
166
+ ---
167
+
168
+ ## Links
169
+
170
+ - Docs: [silvol.ai/docs](https://silvol.ai/docs)
171
+ - Dashboard: [silvol.ai/dashboard](https://silvol.ai/dashboard)
172
+ - PyPI: [pypi.org/project/silvol](https://pypi.org/project/silvol)
173
+ - GitHub: [github.com/optimuscodexprimus/silvol-python](https://github.com/optimuscodexprimus/silvol-python)
@@ -0,0 +1,11 @@
1
+ silvol/__init__.py,sha256=yQyCF6Rr2pSG3nFZp-KNTQhkNXXfvXkzYZ1EtemOqJs,1047
2
+ silvol/_version.py,sha256=Zn1KFblwuFHiDRdRAiRnDBRkbPttWh44jKa5zG2ov0E,22
3
+ silvol/client.py,sha256=MMv0KcfKBhXf24wTdRZoQX153p8dQQXMOMjVXRlIOns,2195
4
+ silvol/finetune.py,sha256=XQ8P9wUxiQ-kwfX_qdnnXP5nc2z0JR3oxxQxBP3ietg,11232
5
+ silvol/integrations/__init__.py,sha256=hTjlDVsCUJnEMTsfYqfna_txr0JIqlDmbDSOpSYw8mc,226
6
+ silvol/integrations/crewai.py,sha256=O7l4uAwD_t3YUAMULhJJal5iJ_OQjhmkNfIgcoqJ2_0,1015
7
+ silvol/integrations/langchain.py,sha256=OUV1hY_I4VUBXbVozxjcvzNkIVmQN00T8fT_Tmp6dJk,1090
8
+ silvol-0.2.0.dist-info/METADATA,sha256=AYSXHtH7QWL16pJaxliCQRGMl8qhkieLyG7MTXUZrrs,5236
9
+ silvol-0.2.0.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
10
+ silvol-0.2.0.dist-info/licenses/LICENSE,sha256=qiFscecdrSkdosFBZBJaHUw2m0Fu8bh9YtE3drb6lTk,1063
11
+ silvol-0.2.0.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.29.0
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Silvol
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.