llm-sdk-core 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.
- llm_sdk_core-0.1.0/.gitignore +113 -0
- llm_sdk_core-0.1.0/LICENSE +0 -0
- llm_sdk_core-0.1.0/PKG-INFO +365 -0
- llm_sdk_core-0.1.0/README.md +343 -0
- llm_sdk_core-0.1.0/app.py +29 -0
- llm_sdk_core-0.1.0/pyproject.toml +34 -0
- llm_sdk_core-0.1.0/src/llm_sdk/__init__.py +7 -0
- llm_sdk_core-0.1.0/src/llm_sdk/async_sdk.py +354 -0
- llm_sdk_core-0.1.0/src/llm_sdk/context.py +22 -0
- llm_sdk_core-0.1.0/src/llm_sdk/domain/__init__.py +1 -0
- llm_sdk_core-0.1.0/src/llm_sdk/domain/chat.py +205 -0
- llm_sdk_core-0.1.0/src/llm_sdk/domain/embeddings.py +38 -0
- llm_sdk_core-0.1.0/src/llm_sdk/domain/models.py +17 -0
- llm_sdk_core-0.1.0/src/llm_sdk/exceptions.py +64 -0
- llm_sdk_core-0.1.0/src/llm_sdk/lifecycle.py +81 -0
- llm_sdk_core-0.1.0/src/llm_sdk/plugin_loader.py +72 -0
- llm_sdk_core-0.1.0/src/llm_sdk/providers/__init__.py +0 -0
- llm_sdk_core-0.1.0/src/llm_sdk/providers/__main__.py +23 -0
- llm_sdk_core-0.1.0/src/llm_sdk/providers/async_base.py +88 -0
- llm_sdk_core-0.1.0/src/llm_sdk/providers/async_noop_client.py +49 -0
- llm_sdk_core-0.1.0/src/llm_sdk/providers/async_registry.py +85 -0
- llm_sdk_core-0.1.0/src/llm_sdk/providers/noop_client.py +52 -0
- llm_sdk_core-0.1.0/src/llm_sdk/providers/sync_base.py +77 -0
- llm_sdk_core-0.1.0/src/llm_sdk/providers/sync_registry.py +105 -0
- llm_sdk_core-0.1.0/src/llm_sdk/retries.py +125 -0
- llm_sdk_core-0.1.0/src/llm_sdk/settings.py +74 -0
- llm_sdk_core-0.1.0/src/llm_sdk/sync_sdk.py +362 -0
- llm_sdk_core-0.1.0/src/llm_sdk/timeouts.py +23 -0
- llm_sdk_core-0.1.0/src/llm_sdk/typing.py +26 -0
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
# Byte-compiled / optimized / DLL files
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*$py.class
|
|
5
|
+
|
|
6
|
+
# C extensions
|
|
7
|
+
*.so
|
|
8
|
+
|
|
9
|
+
# Distribution / packaging
|
|
10
|
+
.Python
|
|
11
|
+
build/
|
|
12
|
+
develop-eggs/
|
|
13
|
+
dist/
|
|
14
|
+
downloads/
|
|
15
|
+
eggs/
|
|
16
|
+
.eggs/
|
|
17
|
+
lib/
|
|
18
|
+
lib64/
|
|
19
|
+
parts/
|
|
20
|
+
sdist/
|
|
21
|
+
var/
|
|
22
|
+
wheels/
|
|
23
|
+
share/python-wheels/
|
|
24
|
+
*.egg-info/
|
|
25
|
+
.installed.cfg
|
|
26
|
+
*.egg
|
|
27
|
+
MANIFEST
|
|
28
|
+
|
|
29
|
+
# Virtual environments
|
|
30
|
+
venv/
|
|
31
|
+
.venv/
|
|
32
|
+
.virtualenv/
|
|
33
|
+
.virtual-enviroment/
|
|
34
|
+
env_plantillasV2/
|
|
35
|
+
*.venv
|
|
36
|
+
pip-log.txt
|
|
37
|
+
pip-delete-this-directory.txt
|
|
38
|
+
|
|
39
|
+
# Enviroment variables
|
|
40
|
+
*.env
|
|
41
|
+
.env*
|
|
42
|
+
|
|
43
|
+
# Unit test / coverage reports
|
|
44
|
+
htmlcov/
|
|
45
|
+
.tox/
|
|
46
|
+
.nox/
|
|
47
|
+
.coverage
|
|
48
|
+
.coverage.*
|
|
49
|
+
.cache
|
|
50
|
+
nosetests.xml
|
|
51
|
+
coverage.xml
|
|
52
|
+
*.cover
|
|
53
|
+
*.py,cover
|
|
54
|
+
.hypothesis/
|
|
55
|
+
.pytest_cache/
|
|
56
|
+
|
|
57
|
+
# PyCharm / VSCode / other IDEs
|
|
58
|
+
.idea/
|
|
59
|
+
.vscode/
|
|
60
|
+
*.swp
|
|
61
|
+
*.swo
|
|
62
|
+
*.swn
|
|
63
|
+
*.swp
|
|
64
|
+
*.bak
|
|
65
|
+
|
|
66
|
+
# Jupyter Notebook
|
|
67
|
+
.ipynb_checkpoints
|
|
68
|
+
*.ipynb
|
|
69
|
+
|
|
70
|
+
# Logs
|
|
71
|
+
*.log
|
|
72
|
+
logs/
|
|
73
|
+
*.out
|
|
74
|
+
|
|
75
|
+
# mypy / pytype / pyre
|
|
76
|
+
.mypy_cache/
|
|
77
|
+
.dmypy.json
|
|
78
|
+
dmypy.json
|
|
79
|
+
.pytype/
|
|
80
|
+
.pyre/
|
|
81
|
+
|
|
82
|
+
# Django / Flask stuff
|
|
83
|
+
*.mo
|
|
84
|
+
*.pot
|
|
85
|
+
*.db
|
|
86
|
+
*.sqlite3
|
|
87
|
+
instance/
|
|
88
|
+
.webassets-cache
|
|
89
|
+
|
|
90
|
+
# Static / media
|
|
91
|
+
staticfiles/
|
|
92
|
+
media/
|
|
93
|
+
|
|
94
|
+
# Local config
|
|
95
|
+
config.local.*
|
|
96
|
+
secrets.json
|
|
97
|
+
|
|
98
|
+
# Mac / Linux / Windows system files
|
|
99
|
+
.DS_Store
|
|
100
|
+
Thumbs.db
|
|
101
|
+
desktop.ini
|
|
102
|
+
|
|
103
|
+
# Other
|
|
104
|
+
*.pkl
|
|
105
|
+
*.sage.py
|
|
106
|
+
|
|
107
|
+
# Normal Files
|
|
108
|
+
*.json
|
|
109
|
+
*.csv
|
|
110
|
+
*.xlsx
|
|
111
|
+
|
|
112
|
+
# Node modules
|
|
113
|
+
/node_modules/
|
|
File without changes
|
|
@@ -0,0 +1,365 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: llm-sdk-core
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Provider-agnostic SDK for LLMs with async support and plugin providers.
|
|
5
|
+
Project-URL: Homepage, https://github.com/EvanFloresLv/LLM-Generate.git
|
|
6
|
+
Project-URL: Repository, https://github.com/EvanFloresLv/LLM-Generate.git
|
|
7
|
+
Author: Esteban Flores
|
|
8
|
+
License: MIT
|
|
9
|
+
License-File: LICENSE
|
|
10
|
+
Requires-Python: >=3.11
|
|
11
|
+
Requires-Dist: httpx>=0.28.1
|
|
12
|
+
Requires-Dist: pydantic-settings>=2.2
|
|
13
|
+
Requires-Dist: pydantic>=2.6
|
|
14
|
+
Provides-Extra: dev
|
|
15
|
+
Requires-Dist: mypy>=1.8; extra == 'dev'
|
|
16
|
+
Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
|
|
17
|
+
Requires-Dist: pytest-cov>=5.0; extra == 'dev'
|
|
18
|
+
Requires-Dist: pytest>=8.0; extra == 'dev'
|
|
19
|
+
Requires-Dist: respx>=0.20; extra == 'dev'
|
|
20
|
+
Requires-Dist: ruff>=0.3; extra == 'dev'
|
|
21
|
+
Description-Content-Type: text/markdown
|
|
22
|
+
|
|
23
|
+
# llm-sdk
|
|
24
|
+
|
|
25
|
+
Provider-agnostic Python SDK for Large Language Models (LLMs) with a production-ready async architecture, strong typing, unified errors, retries, logging, and a plugin system based on Python entry points.
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
## Table of Contents
|
|
30
|
+
|
|
31
|
+
- [Overview](#overview)
|
|
32
|
+
- [Architecture](#architecture)
|
|
33
|
+
- [Features](#features)
|
|
34
|
+
- [Installation](#installation)
|
|
35
|
+
- [Usage](#usage)
|
|
36
|
+
- [Configuration](#configuration)
|
|
37
|
+
- [Project Structure](#project-structure)
|
|
38
|
+
- [Development](#development)
|
|
39
|
+
- [Testing](#testing)
|
|
40
|
+
- [Deployment](#deployment)
|
|
41
|
+
- [Roadmap](#roadmap)
|
|
42
|
+
- [Contributing](#contributing)
|
|
43
|
+
- [License](#license)
|
|
44
|
+
- [Contact](#contact)
|
|
45
|
+
|
|
46
|
+
---
|
|
47
|
+
|
|
48
|
+
## Overview
|
|
49
|
+
|
|
50
|
+
`llm-sdk` is the **core package** that provides:
|
|
51
|
+
|
|
52
|
+
- A unified API for multiple LLM providers
|
|
53
|
+
- Async-first design (`AsyncSDK`)
|
|
54
|
+
- A provider registry that loads providers via **plugins**
|
|
55
|
+
- Domain models (requests/responses) independent of providers
|
|
56
|
+
- Unified error model
|
|
57
|
+
- Retries, timeouts, and structured logging
|
|
58
|
+
|
|
59
|
+
This package intentionally **does not** ship provider implementations directly.
|
|
60
|
+
Providers are installed as separate packages, for example:
|
|
61
|
+
|
|
62
|
+
- `llm-sdk-provider-gemini`
|
|
63
|
+
- `llm-sdk-provider-openai` (future)
|
|
64
|
+
- `llm-sdk-provider-ollama` (future)
|
|
65
|
+
|
|
66
|
+
---
|
|
67
|
+
|
|
68
|
+
## Architecture
|
|
69
|
+
|
|
70
|
+
### Core design principles
|
|
71
|
+
|
|
72
|
+
- **Provider-agnostic domain layer** (no vendor types leak into the SDK)
|
|
73
|
+
- **Open/Closed Principle**: providers are added via entry points, without modifying core code
|
|
74
|
+
- **SOLID**:
|
|
75
|
+
- `AsyncSDK` orchestrates only
|
|
76
|
+
- Providers implement only provider calls
|
|
77
|
+
- Registry handles discovery
|
|
78
|
+
- Domain models validate input/output
|
|
79
|
+
- **DRY/KISS**: minimal abstractions, strong conventions
|
|
80
|
+
|
|
81
|
+
### Key components
|
|
82
|
+
|
|
83
|
+
- **AsyncSDK**: main entrypoint (`sdk.chat`, `sdk.embed`, `sdk.stream_chat`)
|
|
84
|
+
- **ProviderRegistry**: discovers provider plugins via entry points
|
|
85
|
+
- **ProviderFactory**: plugin contract (`spec()` + `create(settings)`)
|
|
86
|
+
- **Domain models**: `ChatRequest`, `ChatResponse`, `EmbeddingRequest`, etc.
|
|
87
|
+
- **Unified errors**: `ProviderError`, `ValidationError`, `TimeoutError`
|
|
88
|
+
- **Retries**: async retry policy with exponential backoff + jitter
|
|
89
|
+
- **Settings**: `SDKSettings` (Pydantic Settings)
|
|
90
|
+
|
|
91
|
+
---
|
|
92
|
+
|
|
93
|
+
## Features
|
|
94
|
+
|
|
95
|
+
- Async-first API
|
|
96
|
+
- Provider plugins via entry points
|
|
97
|
+
- Chat completions
|
|
98
|
+
- Embeddings
|
|
99
|
+
- Streaming chat (provider-dependent)
|
|
100
|
+
- Strong typing (mypy-friendly)
|
|
101
|
+
- Unified error handling
|
|
102
|
+
- Retry policies with jitter
|
|
103
|
+
- Structured logging support
|
|
104
|
+
- Configuration via environment variables
|
|
105
|
+
- Provider + model resolution at runtime
|
|
106
|
+
|
|
107
|
+
---
|
|
108
|
+
|
|
109
|
+
## Installation
|
|
110
|
+
|
|
111
|
+
### Install core SDK
|
|
112
|
+
|
|
113
|
+
```bash
|
|
114
|
+
pip install llm-sdk
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
Install at least one provider plugin
|
|
118
|
+
|
|
119
|
+
### Example (Gemini):
|
|
120
|
+
|
|
121
|
+
```bash
|
|
122
|
+
pip install llm-sdk-provider-gemini
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
---
|
|
126
|
+
|
|
127
|
+
## Usage
|
|
128
|
+
|
|
129
|
+
### Async Quickstart
|
|
130
|
+
|
|
131
|
+
```python
|
|
132
|
+
import asyncio
|
|
133
|
+
from llm_sdk.async_sdk import AsyncSDK
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
async def main() -> None:
|
|
137
|
+
sdk = AsyncSDK.default()
|
|
138
|
+
|
|
139
|
+
# IMPORTANT: load provider plugins (entry points)
|
|
140
|
+
sdk.registry.load_plugins()
|
|
141
|
+
|
|
142
|
+
resp = await sdk.chat(
|
|
143
|
+
provider="gemini",
|
|
144
|
+
model="gemini-2.5-flash",
|
|
145
|
+
messages=[("user", "Hola, ¿cómo estás?")],
|
|
146
|
+
)
|
|
147
|
+
|
|
148
|
+
print(resp.content)
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
if __name__ == "__main__":
|
|
152
|
+
asyncio.run(main())
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
### Streaming chat
|
|
157
|
+
|
|
158
|
+
```python
|
|
159
|
+
import asyncio
|
|
160
|
+
from llm_sdk.async_sdk import AsyncSDK
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
async def main() -> None:
|
|
164
|
+
sdk = AsyncSDK.default()
|
|
165
|
+
sdk.registry.load_plugins()
|
|
166
|
+
|
|
167
|
+
async for ev in sdk.stream_chat(
|
|
168
|
+
provider="gemini",
|
|
169
|
+
model="gemini-2.5-flash",
|
|
170
|
+
messages=[("user", "Explica qué es un Transformer en 5 puntos.")],
|
|
171
|
+
):
|
|
172
|
+
print(ev.delta, end="", flush=True)
|
|
173
|
+
|
|
174
|
+
print("\n[done]")
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
if __name__ == "__main__":
|
|
178
|
+
asyncio.run(main())
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
### Embeddings
|
|
182
|
+
|
|
183
|
+
```python
|
|
184
|
+
import asyncio
|
|
185
|
+
from llm_sdk.async_sdk import AsyncSDK
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
async def main() -> None:
|
|
189
|
+
sdk = AsyncSDK.default()
|
|
190
|
+
sdk.registry.load_plugins()
|
|
191
|
+
|
|
192
|
+
resp = await sdk.embed(
|
|
193
|
+
provider="gemini",
|
|
194
|
+
model="text-multilingual-embedding-002",
|
|
195
|
+
input=["Hola mundo", "Hello world"],
|
|
196
|
+
)
|
|
197
|
+
|
|
198
|
+
print(len(resp.vectors), "vectors")
|
|
199
|
+
print("dim:", len(resp.vectors[0]))
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
if __name__ == "__main__":
|
|
203
|
+
asyncio.run(main())
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
### Listing installed providers (debug)
|
|
207
|
+
|
|
208
|
+
```python
|
|
209
|
+
from llm_sdk.providers.async_registry import ProviderRegistry
|
|
210
|
+
|
|
211
|
+
reg = ProviderRegistry()
|
|
212
|
+
reg.load_plugins()
|
|
213
|
+
|
|
214
|
+
print(reg.available())
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
---
|
|
218
|
+
|
|
219
|
+
## Configuration
|
|
220
|
+
|
|
221
|
+
llm-sdk uses Pydantic Settings.
|
|
222
|
+
|
|
223
|
+
Example environment variables
|
|
224
|
+
|
|
225
|
+
```bash
|
|
226
|
+
export LLM_SDK_DEFAULT_PROVIDER="gemini"
|
|
227
|
+
export LLM_SDK_DEFAULT_MODEL="gemini-2.5-flash"
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
### Typical config options
|
|
231
|
+
|
|
232
|
+
- Default provider
|
|
233
|
+
- Default model
|
|
234
|
+
- Retry policy (max attempts, delays)
|
|
235
|
+
- Timeouts
|
|
236
|
+
- Provider-specific settings (usually defined in provider plugin packages)
|
|
237
|
+
|
|
238
|
+
---
|
|
239
|
+
|
|
240
|
+
### Project Structure
|
|
241
|
+
|
|
242
|
+
Typical structure:
|
|
243
|
+
|
|
244
|
+
```bash
|
|
245
|
+
llm-sdk/
|
|
246
|
+
├─ src/
|
|
247
|
+
│ └─ llm_sdk/
|
|
248
|
+
│ ├─ async_sdk.py
|
|
249
|
+
│ ├─ sync_sdk.py
|
|
250
|
+
│ ├─ settings.py
|
|
251
|
+
│ ├─ typing.py
|
|
252
|
+
│ ├─ timeouts.py
|
|
253
|
+
│ ├─ exceptions.py
|
|
254
|
+
│ ├─ retries.py
|
|
255
|
+
│ ├─ plugin_loader.py
|
|
256
|
+
│ ├─ lifecycle.py
|
|
257
|
+
│ ├─ context.py
|
|
258
|
+
│ ├─ domain/
|
|
259
|
+
│ │ ├─ chat.py
|
|
260
|
+
│ │ ├─ embeddings.py
|
|
261
|
+
│ │ └─ models.py
|
|
262
|
+
│ └─ providers/
|
|
263
|
+
│ ├─ async_base.py
|
|
264
|
+
│ ├─ sync_base.py
|
|
265
|
+
│ ├─ async_registry.py
|
|
266
|
+
│ ├─ sync_registry.py
|
|
267
|
+
│ └─ __main__.py
|
|
268
|
+
├─ tests/
|
|
269
|
+
│ ├─ test_registry.py
|
|
270
|
+
│ ├─ test_retries.py
|
|
271
|
+
│ └─ test_contracts.py
|
|
272
|
+
├─ pyproject.toml
|
|
273
|
+
└─ README.md
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
---
|
|
277
|
+
|
|
278
|
+
## Setup
|
|
279
|
+
|
|
280
|
+
### Development
|
|
281
|
+
|
|
282
|
+
|
|
283
|
+
```bash
|
|
284
|
+
python -m venv venv
|
|
285
|
+
source venv/bin/activate
|
|
286
|
+
|
|
287
|
+
pip install -U pip
|
|
288
|
+
pip install -e ".[dev]"
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
### Install a provider plugin in editable mode
|
|
292
|
+
|
|
293
|
+
Example:
|
|
294
|
+
|
|
295
|
+
```bash
|
|
296
|
+
pip install -e ../llm-sdk-provider-gemini
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
### Testing
|
|
300
|
+
|
|
301
|
+
```bash
|
|
302
|
+
pytest -q
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
### Notes
|
|
306
|
+
- Core tests should not require any provider network access
|
|
307
|
+
- Provider plugins are tested independently in their own packages
|
|
308
|
+
|
|
309
|
+
---
|
|
310
|
+
|
|
311
|
+
## Deployment
|
|
312
|
+
|
|
313
|
+
### Build package
|
|
314
|
+
|
|
315
|
+
```bash
|
|
316
|
+
python -m build
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
### Upload to TestPyPI
|
|
320
|
+
|
|
321
|
+
```bash
|
|
322
|
+
python -m twine upload --repository testpypi dist/*
|
|
323
|
+
```
|
|
324
|
+
|
|
325
|
+
### Upload to PyPI
|
|
326
|
+
|
|
327
|
+
```bash
|
|
328
|
+
python -m twine upload dist/*
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
---
|
|
332
|
+
|
|
333
|
+
## Roadmap
|
|
334
|
+
|
|
335
|
+
- Sync SDK wrapper (LLM) built on top of AsyncSDK
|
|
336
|
+
- More providers (OpenAI, Ollama, Anthropic, etc.)
|
|
337
|
+
- Built-in observability hooks (OpenTelemetry)
|
|
338
|
+
- Tool calling abstraction
|
|
339
|
+
- Response caching
|
|
340
|
+
- Rate limiting middleware
|
|
341
|
+
|
|
342
|
+
- First-class tracing
|
|
343
|
+
|
|
344
|
+
---
|
|
345
|
+
|
|
346
|
+
## Contributing
|
|
347
|
+
|
|
348
|
+
PRs are welcome.
|
|
349
|
+
|
|
350
|
+
### Recommended workflow:
|
|
351
|
+
1. Fork the repo
|
|
352
|
+
2. Create a feature branch
|
|
353
|
+
3. Add tests for changes
|
|
354
|
+
4. Run pytest
|
|
355
|
+
5. Submit PR
|
|
356
|
+
|
|
357
|
+
---
|
|
358
|
+
|
|
359
|
+
## License
|
|
360
|
+
MIT License
|
|
361
|
+
|
|
362
|
+
---
|
|
363
|
+
|
|
364
|
+
## Contact
|
|
365
|
+
Author: Esteban Flores
|