livellm 1.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,5 @@
1
+ .venv
2
+ __pycache__
3
+ .pytest_cache
4
+
5
+ coverage
livellm-1.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 Kamil Saliamov
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.
livellm-1.1.0/PKG-INFO ADDED
@@ -0,0 +1,573 @@
1
+ Metadata-Version: 2.4
2
+ Name: livellm
3
+ Version: 1.1.0
4
+ Summary: Python client for the LiveLLM Server
5
+ Project-URL: Homepage, https://github.com/qalby-tech/livellm-client-py
6
+ Project-URL: Repository, https://github.com/qalby-tech/livellm-client-py
7
+ Author: Kamil Saliamov
8
+ License-File: LICENSE
9
+ Classifier: Development Status :: 3 - Alpha
10
+ Classifier: Intended Audience :: Developers
11
+ Classifier: License :: OSI Approved :: MIT License
12
+ Classifier: Programming Language :: Python :: 3
13
+ Classifier: Programming Language :: Python :: 3.10
14
+ Classifier: Programming Language :: Python :: 3.11
15
+ Classifier: Programming Language :: Python :: 3.12
16
+ Classifier: Typing :: Typed
17
+ Requires-Python: >=3.10
18
+ Requires-Dist: httpx>=0.27.0
19
+ Requires-Dist: pydantic>=2.0.0
20
+ Provides-Extra: testing
21
+ Requires-Dist: pytest-asyncio>=0.21.0; extra == 'testing'
22
+ Requires-Dist: pytest-cov>=4.1.0; extra == 'testing'
23
+ Requires-Dist: pytest>=8.4.2; extra == 'testing'
24
+ Description-Content-Type: text/markdown
25
+
26
+ # LiveLLM Python Client
27
+
28
+ [![Python 3.10+](https://img.shields.io/badge/python-3.10+-blue.svg)](https://www.python.org/downloads/)
29
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
30
+
31
+ Python client library for the LiveLLM Server - a unified proxy for AI agent, audio, and transcription services.
32
+
33
+ ## Features
34
+
35
+ - 🚀 **Async-first design** - Built on httpx for high-performance async operations
36
+ - 🔒 **Type-safe** - Full type hints and Pydantic validation
37
+ - 🎯 **Multi-provider support** - OpenAI, Google, Anthropic, Groq, ElevenLabs
38
+ - 🔄 **Streaming support** - Real-time streaming for agent and audio responses
39
+ - 🛠️ **Agent tools** - Web search and MCP server integration
40
+ - 🎙️ **Audio services** - Text-to-speech and transcription
41
+ - ⚡ **Fallback strategies** - Sequential and parallel fallback handling
42
+ - 📦 **Context manager support** - Automatic cleanup with async context managers
43
+
44
+ ## Installation
45
+
46
+ ```bash
47
+ pip install livellm-client
48
+ ```
49
+
50
+ Or with development dependencies:
51
+
52
+ ```bash
53
+ pip install livellm-client[testing]
54
+ ```
55
+
56
+ ## Quick Start
57
+
58
+ ```python
59
+ import asyncio
60
+ from livellm import LivellmClient
61
+ from livellm.models import Settings, ProviderKind, AgentRequest, TextMessage, MessageRole
62
+ from pydantic import SecretStr
63
+
64
+ async def main():
65
+ # Initialize the client with context manager for automatic cleanup
66
+ async with LivellmClient(base_url="http://localhost:8000") as client:
67
+ # Configure a provider
68
+ config = Settings(
69
+ uid="my-openai-config",
70
+ provider=ProviderKind.OPENAI,
71
+ api_key=SecretStr("your-api-key")
72
+ )
73
+ await client.update_config(config)
74
+
75
+ # Run an agent query
76
+ request = AgentRequest(
77
+ provider_uid="my-openai-config",
78
+ model="gpt-4",
79
+ messages=[
80
+ TextMessage(role=MessageRole.USER, content="Hello, how are you?")
81
+ ],
82
+ tools=[]
83
+ )
84
+
85
+ response = await client.agent_run(request)
86
+ print(response.output)
87
+
88
+ asyncio.run(main())
89
+ ```
90
+
91
+ ## Configuration
92
+
93
+ ### Client Initialization
94
+
95
+ ```python
96
+ from livellm import LivellmClient
97
+
98
+ # Basic initialization
99
+ client = LivellmClient(base_url="http://localhost:8000")
100
+
101
+ # With timeout
102
+ client = LivellmClient(
103
+ base_url="http://localhost:8000",
104
+ timeout=30.0
105
+ )
106
+
107
+ # With pre-configured providers (sync operation)
108
+ from livellm.models import Settings, ProviderKind
109
+ from pydantic import SecretStr
110
+
111
+ configs = [
112
+ Settings(
113
+ uid="openai-config",
114
+ provider=ProviderKind.OPENAI,
115
+ api_key=SecretStr("sk-..."),
116
+ base_url="https://api.openai.com/v1" # Optional custom base URL
117
+ ),
118
+ Settings(
119
+ uid="anthropic-config",
120
+ provider=ProviderKind.ANTHROPIC,
121
+ api_key=SecretStr("sk-ant-..."),
122
+ blacklist_models=["claude-instant-1"] # Optional model blacklist
123
+ )
124
+ ]
125
+
126
+ client = LivellmClient(
127
+ base_url="http://localhost:8000",
128
+ configs=configs
129
+ )
130
+ ```
131
+
132
+ ### Provider Configuration
133
+
134
+ Supported providers:
135
+ - `OPENAI` - OpenAI GPT models
136
+ - `GOOGLE` - Google Gemini models
137
+ - `ANTHROPIC` - Anthropic Claude models
138
+ - `GROQ` - Groq models
139
+ - `ELEVENLABS` - ElevenLabs text-to-speech
140
+
141
+ ```python
142
+ # Add a provider configuration
143
+ config = Settings(
144
+ uid="unique-provider-id",
145
+ provider=ProviderKind.OPENAI,
146
+ api_key=SecretStr("your-api-key"),
147
+ base_url="https://custom-endpoint.com", # Optional
148
+ blacklist_models=["deprecated-model"] # Optional
149
+ )
150
+ await client.update_config(config)
151
+
152
+ # Get all configurations
153
+ configs = await client.get_configs()
154
+
155
+ # Delete a configuration
156
+ await client.delete_config("unique-provider-id")
157
+ ```
158
+
159
+ ## Usage Examples
160
+
161
+ ### Agent Services
162
+
163
+ #### Basic Agent Run
164
+
165
+ ```python
166
+ from livellm.models import AgentRequest, TextMessage, MessageRole
167
+
168
+ request = AgentRequest(
169
+ provider_uid="my-openai-config",
170
+ model="gpt-4",
171
+ messages=[
172
+ TextMessage(role=MessageRole.SYSTEM, content="You are a helpful assistant."),
173
+ TextMessage(role=MessageRole.USER, content="Explain quantum computing")
174
+ ],
175
+ tools=[],
176
+ gen_config={"temperature": 0.7, "max_tokens": 500}
177
+ )
178
+
179
+ response = await client.agent_run(request)
180
+ print(f"Output: {response.output}")
181
+ print(f"Tokens used - Input: {response.usage.input_tokens}, Output: {response.usage.output_tokens}")
182
+ ```
183
+
184
+ #### Streaming Agent Response
185
+
186
+ ```python
187
+ request = AgentRequest(
188
+ provider_uid="my-openai-config",
189
+ model="gpt-4",
190
+ messages=[
191
+ TextMessage(role=MessageRole.USER, content="Tell me a story")
192
+ ],
193
+ tools=[]
194
+ )
195
+
196
+ stream = await client.agent_run_stream(request)
197
+ async for chunk in stream:
198
+ print(chunk.output, end="", flush=True)
199
+ ```
200
+
201
+ #### Agent with Binary Messages
202
+
203
+ ```python
204
+ import base64
205
+
206
+ # Read and encode image
207
+ with open("image.jpg", "rb") as f:
208
+ image_data = base64.b64encode(f.read()).decode("utf-8")
209
+
210
+ from livellm.models import BinaryMessage
211
+
212
+ request = AgentRequest(
213
+ provider_uid="my-openai-config",
214
+ model="gpt-4-vision",
215
+ messages=[
216
+ BinaryMessage(
217
+ role=MessageRole.USER,
218
+ content=image_data,
219
+ mime_type="image/jpeg",
220
+ caption="What's in this image?"
221
+ )
222
+ ],
223
+ tools=[]
224
+ )
225
+
226
+ response = await client.agent_run(request)
227
+ ```
228
+
229
+ #### Agent with Web Search Tool
230
+
231
+ ```python
232
+ from livellm.models import WebSearchInput, ToolKind
233
+
234
+ request = AgentRequest(
235
+ provider_uid="my-openai-config",
236
+ model="gpt-4",
237
+ messages=[
238
+ TextMessage(role=MessageRole.USER, content="What's the latest news about AI?")
239
+ ],
240
+ tools=[
241
+ WebSearchInput(
242
+ kind=ToolKind.WEB_SEARCH,
243
+ search_context_size="high" # Options: "low", "medium", "high"
244
+ )
245
+ ]
246
+ )
247
+
248
+ response = await client.agent_run(request)
249
+ ```
250
+
251
+ #### Agent with MCP Server Tool
252
+
253
+ ```python
254
+ from livellm.models import MCPStreamableServerInput, ToolKind
255
+
256
+ request = AgentRequest(
257
+ provider_uid="my-openai-config",
258
+ model="gpt-4",
259
+ messages=[
260
+ TextMessage(role=MessageRole.USER, content="Execute tool")
261
+ ],
262
+ tools=[
263
+ MCPStreamableServerInput(
264
+ kind=ToolKind.MCP_STREAMABLE_SERVER,
265
+ url="http://mcp-server:8080",
266
+ prefix="mcp_",
267
+ timeout=15,
268
+ kwargs={"custom_param": "value"}
269
+ )
270
+ ]
271
+ )
272
+
273
+ response = await client.agent_run(request)
274
+ ```
275
+
276
+ ### Audio Services
277
+
278
+ #### Text-to-Speech
279
+
280
+ ```python
281
+ from livellm.models import SpeakRequest, SpeakMimeType
282
+
283
+ request = SpeakRequest(
284
+ provider_uid="elevenlabs-config",
285
+ model="eleven_turbo_v2",
286
+ text="Hello, this is a test of text to speech.",
287
+ voice="rachel",
288
+ mime_type=SpeakMimeType.MP3,
289
+ sample_rate=44100,
290
+ gen_config={"stability": 0.5, "similarity_boost": 0.75}
291
+ )
292
+
293
+ # Get audio as bytes
294
+ audio_bytes = await client.speak(request)
295
+ with open("output.mp3", "wb") as f:
296
+ f.write(audio_bytes)
297
+ ```
298
+
299
+ #### Streaming Text-to-Speech
300
+
301
+ ```python
302
+ request = SpeakRequest(
303
+ provider_uid="elevenlabs-config",
304
+ model="eleven_turbo_v2",
305
+ text="This is a longer text that will be streamed.",
306
+ voice="rachel",
307
+ mime_type=SpeakMimeType.MP3,
308
+ sample_rate=44100,
309
+ chunk_size=20 # Chunk size in milliseconds
310
+ )
311
+
312
+ # Stream audio chunks
313
+ stream = await client.speak_stream(request)
314
+ with open("output.mp3", "wb") as f:
315
+ async for chunk in stream:
316
+ f.write(chunk)
317
+ ```
318
+
319
+ #### Audio Transcription (Multipart)
320
+
321
+ ```python
322
+ # Using multipart upload
323
+ with open("audio.mp3", "rb") as f:
324
+ file_tuple = ("audio.mp3", f.read(), "audio/mpeg")
325
+
326
+ response = await client.transcribe(
327
+ provider_uid="openai-config",
328
+ file=file_tuple,
329
+ model="whisper-1",
330
+ language="en",
331
+ gen_config={"temperature": 0.2}
332
+ )
333
+
334
+ print(f"Transcription: {response.text}")
335
+ print(f"Detected language: {response.language}")
336
+ ```
337
+
338
+ #### Audio Transcription (JSON)
339
+
340
+ ```python
341
+ import base64
342
+ from livellm.models import TranscribeRequest
343
+
344
+ with open("audio.mp3", "rb") as f:
345
+ audio_data = base64.b64encode(f.read()).decode("utf-8")
346
+
347
+ request = TranscribeRequest(
348
+ provider_uid="openai-config",
349
+ model="whisper-1",
350
+ file=("audio.mp3", audio_data, "audio/mpeg"),
351
+ language="en"
352
+ )
353
+
354
+ response = await client.transcribe_json(request)
355
+ ```
356
+
357
+ ### Fallback Strategies
358
+
359
+ #### Sequential Fallback (Try each provider in order)
360
+
361
+ ```python
362
+ from livellm.models import AgentFallbackRequest, FallbackStrategy
363
+
364
+ fallback_request = AgentFallbackRequest(
365
+ requests=[
366
+ AgentRequest(
367
+ provider_uid="primary-provider",
368
+ model="gpt-4",
369
+ messages=[TextMessage(role=MessageRole.USER, content="Hello")],
370
+ tools=[]
371
+ ),
372
+ AgentRequest(
373
+ provider_uid="backup-provider",
374
+ model="claude-3",
375
+ messages=[TextMessage(role=MessageRole.USER, content="Hello")],
376
+ tools=[]
377
+ )
378
+ ],
379
+ strategy=FallbackStrategy.SEQUENTIAL,
380
+ timeout_per_request=30
381
+ )
382
+
383
+ response = await client.agent_run(fallback_request)
384
+ ```
385
+
386
+ #### Parallel Fallback (Try all providers simultaneously)
387
+
388
+ ```python
389
+ fallback_request = AgentFallbackRequest(
390
+ requests=[
391
+ AgentRequest(provider_uid="provider-1", model="gpt-4", messages=messages, tools=[]),
392
+ AgentRequest(provider_uid="provider-2", model="claude-3", messages=messages, tools=[]),
393
+ AgentRequest(provider_uid="provider-3", model="gemini-pro", messages=messages, tools=[])
394
+ ],
395
+ strategy=FallbackStrategy.PARALLEL,
396
+ timeout_per_request=10
397
+ )
398
+
399
+ response = await client.agent_run(fallback_request)
400
+ ```
401
+
402
+ #### Audio Fallback
403
+
404
+ ```python
405
+ from livellm.models import AudioFallbackRequest
406
+
407
+ fallback_request = AudioFallbackRequest(
408
+ requests=[
409
+ SpeakRequest(provider_uid="elevenlabs", model="model-1", text=text, voice="voice1",
410
+ mime_type=SpeakMimeType.MP3, sample_rate=44100),
411
+ SpeakRequest(provider_uid="openai", model="tts-1", text=text, voice="alloy",
412
+ mime_type=SpeakMimeType.MP3, sample_rate=44100)
413
+ ],
414
+ strategy=FallbackStrategy.SEQUENTIAL
415
+ )
416
+
417
+ audio = await client.speak(fallback_request)
418
+ ```
419
+
420
+ ## Context Manager Support
421
+
422
+ The client supports async context managers for automatic cleanup:
423
+
424
+ ```python
425
+ async with LivellmClient(base_url="http://localhost:8000") as client:
426
+ config = Settings(uid="temp-config", provider=ProviderKind.OPENAI,
427
+ api_key=SecretStr("key"))
428
+ await client.update_config(config)
429
+
430
+ # Use client...
431
+ response = await client.ping()
432
+
433
+ # Automatically cleans up configs and closes HTTP client
434
+ ```
435
+
436
+ Or manually:
437
+
438
+ ```python
439
+ client = LivellmClient(base_url="http://localhost:8000")
440
+ try:
441
+ # Use client...
442
+ pass
443
+ finally:
444
+ await client.cleanup()
445
+ ```
446
+
447
+ ## API Reference
448
+
449
+ ### Client Methods
450
+
451
+ #### Health Check
452
+ - `ping() -> SuccessResponse` - Check server health
453
+
454
+ #### Configuration Management
455
+ - `update_config(config: Settings) -> SuccessResponse` - Add/update a provider config
456
+ - `update_configs(configs: List[Settings]) -> SuccessResponse` - Add/update multiple configs
457
+ - `get_configs() -> List[Settings]` - Get all provider configurations
458
+ - `delete_config(config_uid: str) -> SuccessResponse` - Delete a provider config
459
+
460
+ #### Agent Services
461
+ - `agent_run(request: AgentRequest | AgentFallbackRequest) -> AgentResponse` - Run agent query
462
+ - `agent_run_stream(request: AgentRequest | AgentFallbackRequest) -> AsyncIterator[AgentResponse]` - Stream agent response
463
+
464
+ #### Audio Services
465
+ - `speak(request: SpeakRequest | AudioFallbackRequest) -> bytes` - Text-to-speech
466
+ - `speak_stream(request: SpeakRequest | AudioFallbackRequest) -> AsyncIterator[bytes]` - Streaming TTS
467
+ - `transcribe(provider_uid, file, model, language?, gen_config?) -> TranscribeResponse` - Multipart transcription
468
+ - `transcribe_json(request: TranscribeRequest | TranscribeFallbackRequest) -> TranscribeResponse` - JSON transcription
469
+
470
+ #### Cleanup
471
+ - `cleanup() -> None` - Clean up resources and close client
472
+ - `__aenter__() / __aexit__()` - Async context manager support
473
+
474
+ ### Models
475
+
476
+ #### Common Models
477
+ - `Settings` - Provider configuration
478
+ - `ProviderKind` - Enum of supported providers
479
+ - `SuccessResponse` - Generic success response
480
+ - `BaseRequest` - Base class for all requests
481
+
482
+ #### Agent Models
483
+ - `AgentRequest` - Agent query request
484
+ - `AgentResponse` - Agent query response
485
+ - `AgentResponseUsage` - Token usage information
486
+ - `TextMessage` - Text-based message
487
+ - `BinaryMessage` - Binary message (images, audio, etc.)
488
+ - `MessageRole` - Enum: USER, MODEL, SYSTEM
489
+
490
+ #### Tool Models
491
+ - `ToolKind` - Enum: WEB_SEARCH, MCP_STREAMABLE_SERVER
492
+ - `WebSearchInput` - Web search tool configuration
493
+ - `MCPStreamableServerInput` - MCP server tool configuration
494
+
495
+ #### Audio Models
496
+ - `SpeakRequest` - Text-to-speech request
497
+ - `SpeakMimeType` - Enum: PCM, WAV, MP3, ULAW, ALAW
498
+ - `TranscribeRequest` - Transcription request
499
+ - `TranscribeResponse` - Transcription response
500
+
501
+ #### Fallback Models
502
+ - `FallbackStrategy` - Enum: SEQUENTIAL, PARALLEL
503
+ - `AgentFallbackRequest` - Agent fallback configuration
504
+ - `AudioFallbackRequest` - Audio fallback configuration
505
+ - `TranscribeFallbackRequest` - Transcription fallback configuration
506
+
507
+ ## Error Handling
508
+
509
+ The client raises exceptions for HTTP errors:
510
+
511
+ ```python
512
+ try:
513
+ response = await client.agent_run(request)
514
+ except Exception as e:
515
+ print(f"Error: {e}")
516
+ ```
517
+
518
+ For more granular error handling:
519
+
520
+ ```python
521
+ import httpx
522
+
523
+ try:
524
+ response = await client.ping()
525
+ except httpx.HTTPStatusError as e:
526
+ print(f"HTTP error: {e.response.status_code}")
527
+ except httpx.RequestError as e:
528
+ print(f"Request error: {e}")
529
+ ```
530
+
531
+ ## Development
532
+
533
+ ### Running Tests
534
+
535
+ ```bash
536
+ # Install development dependencies
537
+ pip install -e ".[testing]"
538
+
539
+ # Run tests
540
+ pytest tests/
541
+ ```
542
+
543
+ ### Type Checking
544
+
545
+ The library is fully typed. Run type checking with:
546
+
547
+ ```bash
548
+ pip install mypy
549
+ mypy livellm
550
+ ```
551
+
552
+ ## Requirements
553
+
554
+ - Python 3.10+
555
+ - httpx >= 0.27.0
556
+ - pydantic >= 2.0.0
557
+
558
+ ## License
559
+
560
+ MIT License - see [LICENSE](LICENSE) file for details.
561
+
562
+ ## Contributing
563
+
564
+ Contributions are welcome! Please feel free to submit a Pull Request.
565
+
566
+ ## Links
567
+
568
+ - [GitHub Repository](https://github.com/qalby-tech/livellm-client-py)
569
+ - [Issue Tracker](https://github.com/qalby-tech/livellm-client-py/issues)
570
+
571
+ ## Changelog
572
+
573
+ See [CHANGELOG.md](CHANGELOG.md) for version history and changes.