livellm 1.1.1__py3-none-any.whl → 1.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.
@@ -1,625 +0,0 @@
1
- Metadata-Version: 2.4
2
- Name: livellm
3
- Version: 1.1.1
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
- - 📦 **Smart resource management** - Automatic cleanup via GC, context managers, or manual control
43
- - 🧹 **Memory safe** - No resource leaks with multiple cleanup strategies
44
-
45
- ## Installation
46
-
47
- ```bash
48
- pip install livellm
49
- ```
50
-
51
- Or with development dependencies:
52
-
53
- ```bash
54
- pip install livellm[testing]
55
- ```
56
-
57
- ## Quick Start
58
-
59
- ```python
60
- import asyncio
61
- from livellm import LivellmClient
62
- from livellm.models import Settings, ProviderKind, AgentRequest, TextMessage, MessageRole
63
- from pydantic import SecretStr
64
-
65
- async def main():
66
- # Initialize the client with context manager for automatic cleanup
67
- async with LivellmClient(base_url="http://localhost:8000") as client:
68
- # Configure a provider
69
- config = Settings(
70
- uid="my-openai-config",
71
- provider=ProviderKind.OPENAI,
72
- api_key=SecretStr("your-api-key")
73
- )
74
- await client.update_config(config)
75
-
76
- # Run an agent query
77
- request = AgentRequest(
78
- provider_uid="my-openai-config",
79
- model="gpt-4",
80
- messages=[
81
- TextMessage(role=MessageRole.USER, content="Hello, how are you?")
82
- ],
83
- tools=[]
84
- )
85
-
86
- response = await client.agent_run(request)
87
- print(response.output)
88
-
89
- asyncio.run(main())
90
- ```
91
-
92
- ## Configuration
93
-
94
- ### Client Initialization
95
-
96
- ```python
97
- from livellm import LivellmClient
98
-
99
- # Basic initialization
100
- client = LivellmClient(base_url="http://localhost:8000")
101
-
102
- # With timeout
103
- client = LivellmClient(
104
- base_url="http://localhost:8000",
105
- timeout=30.0
106
- )
107
-
108
- # With pre-configured providers (sync operation)
109
- from livellm.models import Settings, ProviderKind
110
- from pydantic import SecretStr
111
-
112
- configs = [
113
- Settings(
114
- uid="openai-config",
115
- provider=ProviderKind.OPENAI,
116
- api_key=SecretStr("sk-..."),
117
- base_url="https://api.openai.com/v1" # Optional custom base URL
118
- ),
119
- Settings(
120
- uid="anthropic-config",
121
- provider=ProviderKind.ANTHROPIC,
122
- api_key=SecretStr("sk-ant-..."),
123
- blacklist_models=["claude-instant-1"] # Optional model blacklist
124
- )
125
- ]
126
-
127
- client = LivellmClient(
128
- base_url="http://localhost:8000",
129
- configs=configs
130
- )
131
- ```
132
-
133
- ### Provider Configuration
134
-
135
- Supported providers:
136
- - `OPENAI` - OpenAI GPT models
137
- - `GOOGLE` - Google Gemini models
138
- - `ANTHROPIC` - Anthropic Claude models
139
- - `GROQ` - Groq models
140
- - `ELEVENLABS` - ElevenLabs text-to-speech
141
-
142
- ```python
143
- # Add a provider configuration
144
- config = Settings(
145
- uid="unique-provider-id",
146
- provider=ProviderKind.OPENAI,
147
- api_key=SecretStr("your-api-key"),
148
- base_url="https://custom-endpoint.com", # Optional
149
- blacklist_models=["deprecated-model"] # Optional
150
- )
151
- await client.update_config(config)
152
-
153
- # Get all configurations
154
- configs = await client.get_configs()
155
-
156
- # Delete a configuration
157
- await client.delete_config("unique-provider-id")
158
- ```
159
-
160
- ## Usage Examples
161
-
162
- ### Agent Services
163
-
164
- #### Basic Agent Run
165
-
166
- ```python
167
- from livellm.models import AgentRequest, TextMessage, MessageRole
168
-
169
- request = AgentRequest(
170
- provider_uid="my-openai-config",
171
- model="gpt-4",
172
- messages=[
173
- TextMessage(role=MessageRole.SYSTEM, content="You are a helpful assistant."),
174
- TextMessage(role=MessageRole.USER, content="Explain quantum computing")
175
- ],
176
- tools=[],
177
- gen_config={"temperature": 0.7, "max_tokens": 500}
178
- )
179
-
180
- response = await client.agent_run(request)
181
- print(f"Output: {response.output}")
182
- print(f"Tokens used - Input: {response.usage.input_tokens}, Output: {response.usage.output_tokens}")
183
- ```
184
-
185
- **Note:** You can use either `MessageRole` enum or string values for the `role` parameter:
186
-
187
- ```python
188
- # Using enum (recommended for type safety)
189
- TextMessage(role=MessageRole.USER, content="Hello")
190
-
191
- # Using string (more convenient)
192
- TextMessage(role="user", content="Hello")
193
-
194
- # Both work identically and serialize correctly
195
- ```
196
-
197
- #### Streaming Agent Response
198
-
199
- ```python
200
- request = AgentRequest(
201
- provider_uid="my-openai-config",
202
- model="gpt-4",
203
- messages=[
204
- TextMessage(role=MessageRole.USER, content="Tell me a story")
205
- ],
206
- tools=[]
207
- )
208
-
209
- stream = await client.agent_run_stream(request)
210
- async for chunk in stream:
211
- print(chunk.output, end="", flush=True)
212
- ```
213
-
214
- #### Agent with Binary Messages
215
-
216
- ```python
217
- import base64
218
-
219
- # Read and encode image
220
- with open("image.jpg", "rb") as f:
221
- image_data = base64.b64encode(f.read()).decode("utf-8")
222
-
223
- from livellm.models import BinaryMessage
224
-
225
- request = AgentRequest(
226
- provider_uid="my-openai-config",
227
- model="gpt-4-vision",
228
- messages=[
229
- BinaryMessage(
230
- role=MessageRole.USER,
231
- content=image_data,
232
- mime_type="image/jpeg",
233
- caption="What's in this image?"
234
- )
235
- ],
236
- tools=[]
237
- )
238
-
239
- response = await client.agent_run(request)
240
- ```
241
-
242
- #### Agent with Web Search Tool
243
-
244
- ```python
245
- from livellm.models import WebSearchInput, ToolKind
246
-
247
- request = AgentRequest(
248
- provider_uid="my-openai-config",
249
- model="gpt-4",
250
- messages=[
251
- TextMessage(role=MessageRole.USER, content="What's the latest news about AI?")
252
- ],
253
- tools=[
254
- WebSearchInput(
255
- kind=ToolKind.WEB_SEARCH,
256
- search_context_size="high" # Options: "low", "medium", "high"
257
- )
258
- ]
259
- )
260
-
261
- response = await client.agent_run(request)
262
- ```
263
-
264
- #### Agent with MCP Server Tool
265
-
266
- ```python
267
- from livellm.models import MCPStreamableServerInput, ToolKind
268
-
269
- request = AgentRequest(
270
- provider_uid="my-openai-config",
271
- model="gpt-4",
272
- messages=[
273
- TextMessage(role=MessageRole.USER, content="Execute tool")
274
- ],
275
- tools=[
276
- MCPStreamableServerInput(
277
- kind=ToolKind.MCP_STREAMABLE_SERVER,
278
- url="http://mcp-server:8080",
279
- prefix="mcp_",
280
- timeout=15,
281
- kwargs={"custom_param": "value"}
282
- )
283
- ]
284
- )
285
-
286
- response = await client.agent_run(request)
287
- ```
288
-
289
- ### Audio Services
290
-
291
- #### Text-to-Speech
292
-
293
- ```python
294
- from livellm.models import SpeakRequest, SpeakMimeType
295
-
296
- request = SpeakRequest(
297
- provider_uid="elevenlabs-config",
298
- model="eleven_turbo_v2",
299
- text="Hello, this is a test of text to speech.",
300
- voice="rachel",
301
- mime_type=SpeakMimeType.MP3,
302
- sample_rate=44100,
303
- gen_config={"stability": 0.5, "similarity_boost": 0.75}
304
- )
305
-
306
- # Get audio as bytes
307
- audio_bytes = await client.speak(request)
308
- with open("output.mp3", "wb") as f:
309
- f.write(audio_bytes)
310
- ```
311
-
312
- #### Streaming Text-to-Speech
313
-
314
- ```python
315
- request = SpeakRequest(
316
- provider_uid="elevenlabs-config",
317
- model="eleven_turbo_v2",
318
- text="This is a longer text that will be streamed.",
319
- voice="rachel",
320
- mime_type=SpeakMimeType.MP3,
321
- sample_rate=44100,
322
- chunk_size=20 # Chunk size in milliseconds
323
- )
324
-
325
- # Stream audio chunks
326
- stream = await client.speak_stream(request)
327
- with open("output.mp3", "wb") as f:
328
- async for chunk in stream:
329
- f.write(chunk)
330
- ```
331
-
332
- #### Audio Transcription (Multipart)
333
-
334
- ```python
335
- # Using multipart upload
336
- with open("audio.mp3", "rb") as f:
337
- file_tuple = ("audio.mp3", f.read(), "audio/mpeg")
338
-
339
- response = await client.transcribe(
340
- provider_uid="openai-config",
341
- file=file_tuple,
342
- model="whisper-1",
343
- language="en",
344
- gen_config={"temperature": 0.2}
345
- )
346
-
347
- print(f"Transcription: {response.text}")
348
- print(f"Detected language: {response.language}")
349
- ```
350
-
351
- #### Audio Transcription (JSON)
352
-
353
- ```python
354
- import base64
355
- from livellm.models import TranscribeRequest
356
-
357
- with open("audio.mp3", "rb") as f:
358
- audio_data = base64.b64encode(f.read()).decode("utf-8")
359
-
360
- request = TranscribeRequest(
361
- provider_uid="openai-config",
362
- model="whisper-1",
363
- file=("audio.mp3", audio_data, "audio/mpeg"),
364
- language="en"
365
- )
366
-
367
- response = await client.transcribe_json(request)
368
- ```
369
-
370
- ### Fallback Strategies
371
-
372
- #### Sequential Fallback (Try each provider in order)
373
-
374
- ```python
375
- from livellm.models import AgentFallbackRequest, FallbackStrategy
376
-
377
- fallback_request = AgentFallbackRequest(
378
- requests=[
379
- AgentRequest(
380
- provider_uid="primary-provider",
381
- model="gpt-4",
382
- messages=[TextMessage(role=MessageRole.USER, content="Hello")],
383
- tools=[]
384
- ),
385
- AgentRequest(
386
- provider_uid="backup-provider",
387
- model="claude-3",
388
- messages=[TextMessage(role=MessageRole.USER, content="Hello")],
389
- tools=[]
390
- )
391
- ],
392
- strategy=FallbackStrategy.SEQUENTIAL,
393
- timeout_per_request=30
394
- )
395
-
396
- response = await client.agent_run(fallback_request)
397
- ```
398
-
399
- #### Parallel Fallback (Try all providers simultaneously)
400
-
401
- ```python
402
- fallback_request = AgentFallbackRequest(
403
- requests=[
404
- AgentRequest(provider_uid="provider-1", model="gpt-4", messages=messages, tools=[]),
405
- AgentRequest(provider_uid="provider-2", model="claude-3", messages=messages, tools=[]),
406
- AgentRequest(provider_uid="provider-3", model="gemini-pro", messages=messages, tools=[])
407
- ],
408
- strategy=FallbackStrategy.PARALLEL,
409
- timeout_per_request=10
410
- )
411
-
412
- response = await client.agent_run(fallback_request)
413
- ```
414
-
415
- #### Audio Fallback
416
-
417
- ```python
418
- from livellm.models import AudioFallbackRequest
419
-
420
- fallback_request = AudioFallbackRequest(
421
- requests=[
422
- SpeakRequest(provider_uid="elevenlabs", model="model-1", text=text, voice="voice1",
423
- mime_type=SpeakMimeType.MP3, sample_rate=44100),
424
- SpeakRequest(provider_uid="openai", model="tts-1", text=text, voice="alloy",
425
- mime_type=SpeakMimeType.MP3, sample_rate=44100)
426
- ],
427
- strategy=FallbackStrategy.SEQUENTIAL
428
- )
429
-
430
- audio = await client.speak(fallback_request)
431
- ```
432
-
433
- ## Resource Management
434
-
435
- The client provides multiple ways to manage resources and cleanup:
436
-
437
- ### 1. Automatic Cleanup (Garbage Collection)
438
-
439
- The client automatically cleans up when garbage collected:
440
-
441
- ```python
442
- async def main():
443
- client = LivellmClient(base_url="http://localhost:8000")
444
-
445
- # Use client...
446
- response = await client.ping()
447
-
448
- # No explicit cleanup needed - handled automatically when object is destroyed
449
- # Note: Provider configs are deleted synchronously from the server
450
-
451
- asyncio.run(main())
452
- ```
453
-
454
- **Note**: While automatic cleanup works, it shows a `ResourceWarning` if configs exist to encourage explicit cleanup for immediate resource release.
455
-
456
- ### 2. Context Manager (Recommended)
457
-
458
- Use async context managers for guaranteed cleanup:
459
-
460
- ```python
461
- async with LivellmClient(base_url="http://localhost:8000") as client:
462
- config = Settings(uid="temp-config", provider=ProviderKind.OPENAI,
463
- api_key=SecretStr("key"))
464
- await client.update_config(config)
465
-
466
- # Use client...
467
- response = await client.ping()
468
-
469
- # Automatically cleans up configs and closes HTTP client
470
- ```
471
-
472
- ### 3. Manual Cleanup
473
-
474
- Explicitly call cleanup in a try/finally block:
475
-
476
- ```python
477
- client = LivellmClient(base_url="http://localhost:8000")
478
- try:
479
- # Use client...
480
- response = await client.ping()
481
- finally:
482
- await client.cleanup()
483
- ```
484
-
485
- ### Cleanup Behavior
486
-
487
- The `cleanup()` method:
488
- - Deletes all provider configs created by the client
489
- - Closes the HTTP client connection
490
- - Is idempotent (safe to call multiple times)
491
-
492
- The `__del__()` destructor (automatic cleanup):
493
- - Triggers when the object is garbage collected
494
- - Synchronously deletes provider configs from the server
495
- - Closes the HTTP client connection
496
- - Shows a `ResourceWarning` if configs exist (to encourage explicit cleanup)
497
-
498
- ## API Reference
499
-
500
- ### Client Methods
501
-
502
- #### Health Check
503
- - `ping() -> SuccessResponse` - Check server health
504
-
505
- #### Configuration Management
506
- - `update_config(config: Settings) -> SuccessResponse` - Add/update a provider config
507
- - `update_configs(configs: List[Settings]) -> SuccessResponse` - Add/update multiple configs
508
- - `get_configs() -> List[Settings]` - Get all provider configurations
509
- - `delete_config(config_uid: str) -> SuccessResponse` - Delete a provider config
510
-
511
- #### Agent Services
512
- - `agent_run(request: AgentRequest | AgentFallbackRequest) -> AgentResponse` - Run agent query
513
- - `agent_run_stream(request: AgentRequest | AgentFallbackRequest) -> AsyncIterator[AgentResponse]` - Stream agent response
514
-
515
- #### Audio Services
516
- - `speak(request: SpeakRequest | AudioFallbackRequest) -> bytes` - Text-to-speech
517
- - `speak_stream(request: SpeakRequest | AudioFallbackRequest) -> AsyncIterator[bytes]` - Streaming TTS
518
- - `transcribe(provider_uid, file, model, language?, gen_config?) -> TranscribeResponse` - Multipart transcription
519
- - `transcribe_json(request: TranscribeRequest | TranscribeFallbackRequest) -> TranscribeResponse` - JSON transcription
520
-
521
- #### Cleanup
522
- - `cleanup() -> None` - Clean up resources and close client (async)
523
- - `__aenter__() / __aexit__()` - Async context manager support
524
- - `__del__()` - Automatic cleanup when garbage collected (sync)
525
-
526
- ### Models
527
-
528
- #### Common Models
529
- - `Settings` - Provider configuration
530
- - `ProviderKind` - Enum of supported providers
531
- - `SuccessResponse` - Generic success response
532
- - `BaseRequest` - Base class for all requests
533
-
534
- #### Agent Models
535
- - `AgentRequest` - Agent query request
536
- - `AgentResponse` - Agent query response
537
- - `AgentResponseUsage` - Token usage information
538
- - `TextMessage` - Text-based message
539
- - `BinaryMessage` - Binary message (images, audio, etc.)
540
- - `MessageRole` - Enum: USER, MODEL, SYSTEM
541
-
542
- #### Tool Models
543
- - `ToolKind` - Enum: WEB_SEARCH, MCP_STREAMABLE_SERVER
544
- - `WebSearchInput` - Web search tool configuration
545
- - `MCPStreamableServerInput` - MCP server tool configuration
546
-
547
- #### Audio Models
548
- - `SpeakRequest` - Text-to-speech request
549
- - `SpeakMimeType` - Enum: PCM, WAV, MP3, ULAW, ALAW
550
- - `TranscribeRequest` - Transcription request
551
- - `TranscribeResponse` - Transcription response
552
-
553
- #### Fallback Models
554
- - `FallbackStrategy` - Enum: SEQUENTIAL, PARALLEL
555
- - `AgentFallbackRequest` - Agent fallback configuration
556
- - `AudioFallbackRequest` - Audio fallback configuration
557
- - `TranscribeFallbackRequest` - Transcription fallback configuration
558
-
559
- ## Error Handling
560
-
561
- The client raises exceptions for HTTP errors:
562
-
563
- ```python
564
- try:
565
- response = await client.agent_run(request)
566
- except Exception as e:
567
- print(f"Error: {e}")
568
- ```
569
-
570
- For more granular error handling:
571
-
572
- ```python
573
- import httpx
574
-
575
- try:
576
- response = await client.ping()
577
- except httpx.HTTPStatusError as e:
578
- print(f"HTTP error: {e.response.status_code}")
579
- except httpx.RequestError as e:
580
- print(f"Request error: {e}")
581
- ```
582
-
583
- ## Development
584
-
585
- ### Running Tests
586
-
587
- ```bash
588
- # Install development dependencies
589
- pip install -e ".[testing]"
590
-
591
- # Run tests
592
- pytest tests/
593
- ```
594
-
595
- ### Type Checking
596
-
597
- The library is fully typed. Run type checking with:
598
-
599
- ```bash
600
- pip install mypy
601
- mypy livellm
602
- ```
603
-
604
- ## Requirements
605
-
606
- - Python 3.10+
607
- - httpx >= 0.27.0
608
- - pydantic >= 2.0.0
609
-
610
- ## License
611
-
612
- MIT License - see [LICENSE](LICENSE) file for details.
613
-
614
- ## Contributing
615
-
616
- Contributions are welcome! Please feel free to submit a Pull Request.
617
-
618
- ## Links
619
-
620
- - [GitHub Repository](https://github.com/qalby-tech/livellm-client-py)
621
- - [Issue Tracker](https://github.com/qalby-tech/livellm-client-py/issues)
622
-
623
- ## Changelog
624
-
625
- See [CHANGELOG.md](CHANGELOG.md) for version history and changes.