lollms-client 0.15.2__tar.gz → 0.17.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.
Potentially problematic release.
This version of lollms-client might be problematic. Click here for more details.
- lollms_client-0.17.0/PKG-INFO +183 -0
- lollms_client-0.17.0/README.md +153 -0
- lollms_client-0.17.0/examples/generate_and_speak/generate_and_speak.py +251 -0
- lollms_client-0.17.0/examples/generate_game_sfx/generate_game_fx.py +240 -0
- {lollms_client-0.15.2 → lollms_client-0.17.0}/examples/simple_text_gen_with_image_test.py +8 -8
- {lollms_client-0.15.2 → lollms_client-0.17.0}/examples/text_2_image.py +0 -1
- {lollms_client-0.15.2 → lollms_client-0.17.0}/examples/text_gen.py +1 -1
- {lollms_client-0.15.2 → lollms_client-0.17.0}/lollms_client/__init__.py +1 -1
- {lollms_client-0.15.2 → lollms_client-0.17.0}/lollms_client/llm_bindings/llamacpp/__init__.py +61 -11
- {lollms_client-0.15.2 → lollms_client-0.17.0}/lollms_client/llm_bindings/lollms/__init__.py +31 -24
- {lollms_client-0.15.2 → lollms_client-0.17.0}/lollms_client/llm_bindings/ollama/__init__.py +47 -27
- {lollms_client-0.15.2 → lollms_client-0.17.0}/lollms_client/llm_bindings/openai/__init__.py +62 -35
- {lollms_client-0.15.2 → lollms_client-0.17.0}/lollms_client/llm_bindings/openllm/__init__.py +4 -1
- {lollms_client-0.15.2 → lollms_client-0.17.0}/lollms_client/llm_bindings/pythonllamacpp/__init__.py +3 -0
- {lollms_client-0.15.2 → lollms_client-0.17.0}/lollms_client/llm_bindings/tensor_rt/__init__.py +4 -1
- {lollms_client-0.15.2 → lollms_client-0.17.0}/lollms_client/llm_bindings/transformers/__init__.py +3 -0
- {lollms_client-0.15.2 → lollms_client-0.17.0}/lollms_client/llm_bindings/vllm/__init__.py +4 -1
- {lollms_client-0.15.2 → lollms_client-0.17.0}/lollms_client/lollms_core.py +65 -33
- {lollms_client-0.15.2 → lollms_client-0.17.0}/lollms_client/lollms_llm_binding.py +76 -22
- {lollms_client-0.15.2 → lollms_client-0.17.0}/lollms_client/lollms_stt_binding.py +3 -15
- {lollms_client-0.15.2 → lollms_client-0.17.0}/lollms_client/lollms_tti_binding.py +5 -29
- {lollms_client-0.15.2 → lollms_client-0.17.0}/lollms_client/lollms_ttm_binding.py +5 -28
- {lollms_client-0.15.2 → lollms_client-0.17.0}/lollms_client/lollms_tts_binding.py +4 -28
- {lollms_client-0.15.2 → lollms_client-0.17.0}/lollms_client/lollms_ttv_binding.py +4 -28
- {lollms_client-0.15.2 → lollms_client-0.17.0}/lollms_client/lollms_utilities.py +5 -3
- {lollms_client-0.15.2 → lollms_client-0.17.0}/lollms_client/stt_bindings/lollms/__init__.py +5 -4
- lollms_client-0.17.0/lollms_client/stt_bindings/whisper/__init__.py +304 -0
- lollms_client-0.17.0/lollms_client/stt_bindings/whispercpp/__init__.py +380 -0
- {lollms_client-0.15.2 → lollms_client-0.17.0}/lollms_client/tti_bindings/lollms/__init__.py +4 -6
- lollms_client-0.17.0/lollms_client/ttm_bindings/audiocraft/__init__.py +281 -0
- lollms_client-0.17.0/lollms_client/ttm_bindings/bark/__init__.py +339 -0
- lollms_client-0.17.0/lollms_client/tts_bindings/bark/__init__.py +336 -0
- lollms_client-0.17.0/lollms_client/tts_bindings/piper_tts/__init__.py +343 -0
- lollms_client-0.17.0/lollms_client/tts_bindings/xtts/__init__.py +317 -0
- lollms_client-0.17.0/lollms_client.egg-info/PKG-INFO +183 -0
- {lollms_client-0.15.2 → lollms_client-0.17.0}/lollms_client.egg-info/SOURCES.txt +9 -0
- {lollms_client-0.15.2 → lollms_client-0.17.0}/lollms_client.egg-info/top_level.txt +1 -0
- lollms_client-0.15.2/PKG-INFO +0 -192
- lollms_client-0.15.2/README.md +0 -162
- lollms_client-0.15.2/lollms_client.egg-info/PKG-INFO +0 -192
- {lollms_client-0.15.2 → lollms_client-0.17.0}/LICENSE +0 -0
- {lollms_client-0.15.2 → lollms_client-0.17.0}/examples/article_summary/article_summary.py +0 -0
- {lollms_client-0.15.2 → lollms_client-0.17.0}/examples/deep_analyze/deep_analyse.py +0 -0
- {lollms_client-0.15.2 → lollms_client-0.17.0}/examples/deep_analyze/deep_analyze_multiple_files.py +0 -0
- {lollms_client-0.15.2 → lollms_client-0.17.0}/examples/function_call/functions_call_with images.py +0 -0
- {lollms_client-0.15.2 → lollms_client-0.17.0}/examples/personality_test/chat_test.py +0 -0
- {lollms_client-0.15.2 → lollms_client-0.17.0}/examples/personality_test/chat_with_aristotle.py +0 -0
- {lollms_client-0.15.2 → lollms_client-0.17.0}/examples/personality_test/tesks_test.py +0 -0
- {lollms_client-0.15.2 → lollms_client-0.17.0}/examples/simple_text_gen_test.py +0 -0
- {lollms_client-0.15.2 → lollms_client-0.17.0}/examples/test_local_models/local_chat.py +0 -0
- {lollms_client-0.15.2 → lollms_client-0.17.0}/examples/text_2_audio.py +0 -0
- {lollms_client-0.15.2 → lollms_client-0.17.0}/examples/text_and_image_2_audio.py +0 -0
- {lollms_client-0.15.2 → lollms_client-0.17.0}/examples/text_gen_system_prompt.py +0 -0
- {lollms_client-0.15.2 → lollms_client-0.17.0}/lollms_client/llm_bindings/__init__.py +0 -0
- {lollms_client-0.15.2 → lollms_client-0.17.0}/lollms_client/lollms_config.py +0 -0
- {lollms_client-0.15.2 → lollms_client-0.17.0}/lollms_client/lollms_discussion.py +0 -0
- {lollms_client-0.15.2 → lollms_client-0.17.0}/lollms_client/lollms_functions.py +0 -0
- {lollms_client-0.15.2 → lollms_client-0.17.0}/lollms_client/lollms_js_analyzer.py +0 -0
- {lollms_client-0.15.2 → lollms_client-0.17.0}/lollms_client/lollms_python_analyzer.py +0 -0
- {lollms_client-0.15.2 → lollms_client-0.17.0}/lollms_client/lollms_tasks.py +0 -0
- {lollms_client-0.15.2 → lollms_client-0.17.0}/lollms_client/lollms_types.py +0 -0
- {lollms_client-0.15.2 → lollms_client-0.17.0}/lollms_client/stt_bindings/__init__.py +0 -0
- {lollms_client-0.15.2 → lollms_client-0.17.0}/lollms_client/tti_bindings/__init__.py +0 -0
- {lollms_client-0.15.2 → lollms_client-0.17.0}/lollms_client/ttm_bindings/__init__.py +0 -0
- {lollms_client-0.15.2 → lollms_client-0.17.0}/lollms_client/ttm_bindings/lollms/__init__.py +0 -0
- {lollms_client-0.15.2 → lollms_client-0.17.0}/lollms_client/tts_bindings/__init__.py +0 -0
- {lollms_client-0.15.2 → lollms_client-0.17.0}/lollms_client/tts_bindings/lollms/__init__.py +0 -0
- {lollms_client-0.15.2 → lollms_client-0.17.0}/lollms_client/ttv_bindings/__init__.py +0 -0
- {lollms_client-0.15.2 → lollms_client-0.17.0}/lollms_client/ttv_bindings/lollms/__init__.py +0 -0
- {lollms_client-0.15.2 → lollms_client-0.17.0}/lollms_client.egg-info/dependency_links.txt +0 -0
- {lollms_client-0.15.2 → lollms_client-0.17.0}/lollms_client.egg-info/requires.txt +0 -0
- {lollms_client-0.15.2 → lollms_client-0.17.0}/pyproject.toml +0 -0
- {lollms_client-0.15.2 → lollms_client-0.17.0}/setup.cfg +0 -0
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: lollms_client
|
|
3
|
+
Version: 0.17.0
|
|
4
|
+
Summary: A client library for LoLLMs generate endpoint
|
|
5
|
+
Author-email: ParisNeo <parisneoai@gmail.com>
|
|
6
|
+
License: Apache Software License
|
|
7
|
+
Project-URL: Homepage, https://github.com/ParisNeo/lollms_client
|
|
8
|
+
Classifier: Programming Language :: Python :: 3
|
|
9
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
10
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
14
|
+
Classifier: License :: OSI Approved :: Apache Software License
|
|
15
|
+
Classifier: Operating System :: OS Independent
|
|
16
|
+
Classifier: Intended Audience :: Developers
|
|
17
|
+
Classifier: Intended Audience :: Science/Research
|
|
18
|
+
Requires-Python: >=3.7
|
|
19
|
+
Description-Content-Type: text/markdown
|
|
20
|
+
License-File: LICENSE
|
|
21
|
+
Requires-Dist: requests
|
|
22
|
+
Requires-Dist: ascii-colors
|
|
23
|
+
Requires-Dist: pipmaster
|
|
24
|
+
Requires-Dist: pyyaml
|
|
25
|
+
Requires-Dist: tiktoken
|
|
26
|
+
Requires-Dist: pydantic
|
|
27
|
+
Requires-Dist: numpy
|
|
28
|
+
Requires-Dist: pillow
|
|
29
|
+
Dynamic: license-file
|
|
30
|
+
|
|
31
|
+
# LoLLMs Client Library
|
|
32
|
+
|
|
33
|
+
[](https://opensource.org/licenses/Apache-2.0)
|
|
34
|
+
[](https://badge.fury.io/py/lollms_client)
|
|
35
|
+
[](https://pypi.org/project/lollms_client/)
|
|
36
|
+
[](https://pepy.tech/project/lollms-client)
|
|
37
|
+
[](DOC_USE.md)
|
|
38
|
+
[](DOC_DEV.md)
|
|
39
|
+
[](https://github.com/ParisNeo/lollms_client/stargazers/)
|
|
40
|
+
[](https://github.com/ParisNeo/lollms_client/issues)
|
|
41
|
+
|
|
42
|
+
**`lollms_client`** is a powerful and flexible Python library designed to simplify interactions with the **LoLLMs (Lord of Large Language Models)** ecosystem and various other Large Language Model (LLM) backends. It provides a unified API for text generation, multimodal operations (text-to-image, text-to-speech, etc.), function calling, and advanced AI-driven tasks.
|
|
43
|
+
|
|
44
|
+
Whether you're connecting to a remote LoLLMs server, an Ollama instance, the OpenAI API, or running models locally using GGUF (via `llama-cpp-python` or a managed `llama.cpp` server), Hugging Face Transformers, or vLLM, `lollms-client` offers a consistent and developer-friendly experience.
|
|
45
|
+
|
|
46
|
+
## Key Features
|
|
47
|
+
|
|
48
|
+
* 🔌 **Versatile Binding System:** Seamlessly switch between different LLM backends (LoLLMs, Ollama, OpenAI, Llama.cpp, Transformers, vLLM, OpenLLM) without major code changes.
|
|
49
|
+
* 🗣️ **Multimodal Support:** Interact with models capable of processing images and generate various outputs like speech (TTS) and images (TTI).
|
|
50
|
+
* 🚀 **Streaming & Callbacks:** Efficiently handle real-time text generation with customizable callback functions.
|
|
51
|
+
* 🛠️ **Task-Oriented Library:** High-level `TasksLibrary` for common operations like summarization, Q&A, code generation, and structured data extraction.
|
|
52
|
+
* 📞 **Function Calling:** Enable LLMs to invoke your custom Python functions, bridging the gap between language models and external tools or data sources.
|
|
53
|
+
* 💬 **Discussion Management:** Utilities to easily manage and format conversation histories for chat applications.
|
|
54
|
+
* ⚙️ **Configuration Management:** Flexible ways to configure bindings and generation parameters.
|
|
55
|
+
* 🧩 **Extensible:** Designed to easily incorporate new LLM backends and modality services.
|
|
56
|
+
|
|
57
|
+
## Installation
|
|
58
|
+
|
|
59
|
+
You can install `lollms_client` directly from PyPI:
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
pip install lollms-client
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
This will install the core library. Some bindings may require additional dependencies (e.g., `llama-cpp-python`, `torch`, `transformers`, `ollama`, `vllm`). The library attempts to manage these using `pipmaster`, but for complex dependencies (especially those requiring compilation like `llama-cpp-python` with GPU support), manual installation might be preferred.
|
|
66
|
+
|
|
67
|
+
## Quick Start
|
|
68
|
+
|
|
69
|
+
Here's a very basic example of how to use `LollmsClient` to generate text with a LoLLMs server (ensure one is running at `http://localhost:9600`):
|
|
70
|
+
|
|
71
|
+
```python
|
|
72
|
+
from lollms_client import LollmsClient, MSG_TYPE
|
|
73
|
+
from ascii_colors import ASCIIColors
|
|
74
|
+
|
|
75
|
+
# Callback for streaming output
|
|
76
|
+
def simple_streaming_callback(chunk: str, msg_type: MSG_TYPE, params=None, metadata=None) -> bool:
|
|
77
|
+
if msg_type == MSG_TYPE.MSG_TYPE_CHUNK:
|
|
78
|
+
print(chunk, end="", flush=True)
|
|
79
|
+
elif msg_type == MSG_TYPE.MSG_TYPE_EXCEPTION:
|
|
80
|
+
ASCIIColors.error(f"\nStreaming Error: {chunk}")
|
|
81
|
+
return True # True to continue streaming
|
|
82
|
+
|
|
83
|
+
try:
|
|
84
|
+
# Initialize client to connect to a LoLLMs server
|
|
85
|
+
# For other backends, change 'binding_name' and provide necessary parameters.
|
|
86
|
+
# See DOC_USE.md for detailed initialization examples.
|
|
87
|
+
lc = LollmsClient(
|
|
88
|
+
binding_name="lollms",
|
|
89
|
+
host_address="http://localhost:9600"
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
prompt = "Tell me a fun fact about space."
|
|
93
|
+
ASCIIColors.yellow(f"Prompt: {prompt}")
|
|
94
|
+
|
|
95
|
+
# Generate text with streaming
|
|
96
|
+
ASCIIColors.green("Streaming Response:")
|
|
97
|
+
response_text = lc.generate_text(
|
|
98
|
+
prompt,
|
|
99
|
+
n_predict=100,
|
|
100
|
+
stream=True,
|
|
101
|
+
streaming_callback=simple_streaming_callback
|
|
102
|
+
)
|
|
103
|
+
print("\n--- End of Stream ---")
|
|
104
|
+
|
|
105
|
+
# The 'response_text' variable will contain the full concatenated text
|
|
106
|
+
# if streaming_callback returns True throughout.
|
|
107
|
+
if isinstance(response_text, str):
|
|
108
|
+
ASCIIColors.cyan(f"\nFull streamed text collected: {response_text[:100]}...")
|
|
109
|
+
elif isinstance(response_text, dict) and "error" in response_text:
|
|
110
|
+
ASCIIColors.error(f"Error during generation: {response_text['error']}")
|
|
111
|
+
|
|
112
|
+
except ValueError as ve:
|
|
113
|
+
ASCIIColors.error(f"Initialization Error: {ve}")
|
|
114
|
+
ASCIIColors.info("Ensure a LoLLMs server is running or configure another binding.")
|
|
115
|
+
except ConnectionRefusedError:
|
|
116
|
+
ASCIIColors.error("Connection refused. Is the LoLLMs server running at http://localhost:9600?")
|
|
117
|
+
except Exception as e:
|
|
118
|
+
ASCIIColors.error(f"An unexpected error occurred: {e}")
|
|
119
|
+
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
## Documentation
|
|
123
|
+
|
|
124
|
+
For more in-depth information, please refer to:
|
|
125
|
+
|
|
126
|
+
* **[Usage Guide (DOC_USE.md)](DOC_USE.md):** Learn how to use `LollmsClient`, different bindings, modality features, `TasksLibrary`, and `FunctionCalling_Library` with comprehensive examples.
|
|
127
|
+
* **[Developer Guide (DOC_DEV.md)](DOC_DEV.md):** Understand the architecture, how to create new bindings, and contribute to the library.
|
|
128
|
+
|
|
129
|
+
## Core Concepts
|
|
130
|
+
|
|
131
|
+
```mermaid
|
|
132
|
+
graph LR
|
|
133
|
+
A[Your Application] --> LC[LollmsClient];
|
|
134
|
+
|
|
135
|
+
subgraph LollmsClient_Core
|
|
136
|
+
LC -- Manages --> LLB[LLM Binding];
|
|
137
|
+
LC -- Provides Access To --> TL[TasksLibrary];
|
|
138
|
+
LC -- Provides Access To --> FCL[FunctionCalling_Library];
|
|
139
|
+
LC -- Provides Access To --> DM[DiscussionManager];
|
|
140
|
+
LC -- Provides Access To --> ModalityBindings[TTS, TTI, STT etc.];
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
subgraph LLM_Backends
|
|
144
|
+
LLB --> LollmsServer[LoLLMs Server];
|
|
145
|
+
LLB --> OllamaServer[Ollama];
|
|
146
|
+
LLB --> OpenAPIServer[OpenAI API];
|
|
147
|
+
LLB --> LocalGGUF[Local GGUF<br>(pythonllamacpp / llamacpp server)];
|
|
148
|
+
LLB --> LocalHF[Local HuggingFace<br>(transformers / vLLM)];
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
ModalityBindings --> ModalityServices[Modality Services<br>(e.g., LoLLMs Server TTS/TTI)];
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
* **`LollmsClient`**: The central class for all interactions. It holds the currently active LLM binding and provides access to modality bindings and helper libraries.
|
|
155
|
+
* **LLM Bindings**: These are plugins that allow `LollmsClient` to communicate with different LLM backends. You choose a binding (e.g., `"ollama"`, `"lollms"`, `"pythonllamacpp"`) when you initialize `LollmsClient`.
|
|
156
|
+
* **Modality Bindings**: Similar to LLM bindings, but for services like Text-to-Speech (`tts`), Text-to-Image (`tti`), etc.
|
|
157
|
+
* **`TasksLibrary`**: Offers high-level functions for common AI tasks (summarization, Q&A) built on top of `LollmsClient`.
|
|
158
|
+
* **`FunctionCalling_Library`**: Enables you to define Python functions that the LLM can request to execute, allowing for tool usage.
|
|
159
|
+
* **`LollmsDiscussion`**: Helps manage and format conversation histories for chat applications.
|
|
160
|
+
|
|
161
|
+
## Examples
|
|
162
|
+
|
|
163
|
+
The `examples/` directory in this repository contains a rich set of scripts demonstrating various features:
|
|
164
|
+
* Basic text generation with different bindings.
|
|
165
|
+
* Streaming and non-streaming examples.
|
|
166
|
+
* Multimodal generation (text with images).
|
|
167
|
+
* Using `TasksLibrary` for summarization and Q&A.
|
|
168
|
+
* Implementing and using function calls.
|
|
169
|
+
* Text-to-Speech and Text-to-Image generation.
|
|
170
|
+
|
|
171
|
+
Explore these examples to see `lollms-client` in action!
|
|
172
|
+
|
|
173
|
+
## Contributing
|
|
174
|
+
|
|
175
|
+
Contributions are welcome! Whether it's bug reports, feature suggestions, documentation improvements, or new bindings, please feel free to open an issue or submit a pull request on our [GitHub repository](https://github.com/ParisNeo/lollms_client).
|
|
176
|
+
|
|
177
|
+
## License
|
|
178
|
+
|
|
179
|
+
This project is licensed under the **Apache 2.0 License**. See the [LICENSE](LICENSE) file for details (assuming you have a LICENSE file, if not, state "Apache 2.0 License").
|
|
180
|
+
|
|
181
|
+
## Changelog
|
|
182
|
+
|
|
183
|
+
For a list of changes and updates, please refer to the [CHANGELOG.md](CHANGELOG.md) file.
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
# LoLLMs Client Library
|
|
2
|
+
|
|
3
|
+
[](https://opensource.org/licenses/Apache-2.0)
|
|
4
|
+
[](https://badge.fury.io/py/lollms_client)
|
|
5
|
+
[](https://pypi.org/project/lollms_client/)
|
|
6
|
+
[](https://pepy.tech/project/lollms-client)
|
|
7
|
+
[](DOC_USE.md)
|
|
8
|
+
[](DOC_DEV.md)
|
|
9
|
+
[](https://github.com/ParisNeo/lollms_client/stargazers/)
|
|
10
|
+
[](https://github.com/ParisNeo/lollms_client/issues)
|
|
11
|
+
|
|
12
|
+
**`lollms_client`** is a powerful and flexible Python library designed to simplify interactions with the **LoLLMs (Lord of Large Language Models)** ecosystem and various other Large Language Model (LLM) backends. It provides a unified API for text generation, multimodal operations (text-to-image, text-to-speech, etc.), function calling, and advanced AI-driven tasks.
|
|
13
|
+
|
|
14
|
+
Whether you're connecting to a remote LoLLMs server, an Ollama instance, the OpenAI API, or running models locally using GGUF (via `llama-cpp-python` or a managed `llama.cpp` server), Hugging Face Transformers, or vLLM, `lollms-client` offers a consistent and developer-friendly experience.
|
|
15
|
+
|
|
16
|
+
## Key Features
|
|
17
|
+
|
|
18
|
+
* 🔌 **Versatile Binding System:** Seamlessly switch between different LLM backends (LoLLMs, Ollama, OpenAI, Llama.cpp, Transformers, vLLM, OpenLLM) without major code changes.
|
|
19
|
+
* 🗣️ **Multimodal Support:** Interact with models capable of processing images and generate various outputs like speech (TTS) and images (TTI).
|
|
20
|
+
* 🚀 **Streaming & Callbacks:** Efficiently handle real-time text generation with customizable callback functions.
|
|
21
|
+
* 🛠️ **Task-Oriented Library:** High-level `TasksLibrary` for common operations like summarization, Q&A, code generation, and structured data extraction.
|
|
22
|
+
* 📞 **Function Calling:** Enable LLMs to invoke your custom Python functions, bridging the gap between language models and external tools or data sources.
|
|
23
|
+
* 💬 **Discussion Management:** Utilities to easily manage and format conversation histories for chat applications.
|
|
24
|
+
* ⚙️ **Configuration Management:** Flexible ways to configure bindings and generation parameters.
|
|
25
|
+
* 🧩 **Extensible:** Designed to easily incorporate new LLM backends and modality services.
|
|
26
|
+
|
|
27
|
+
## Installation
|
|
28
|
+
|
|
29
|
+
You can install `lollms_client` directly from PyPI:
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
pip install lollms-client
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
This will install the core library. Some bindings may require additional dependencies (e.g., `llama-cpp-python`, `torch`, `transformers`, `ollama`, `vllm`). The library attempts to manage these using `pipmaster`, but for complex dependencies (especially those requiring compilation like `llama-cpp-python` with GPU support), manual installation might be preferred.
|
|
36
|
+
|
|
37
|
+
## Quick Start
|
|
38
|
+
|
|
39
|
+
Here's a very basic example of how to use `LollmsClient` to generate text with a LoLLMs server (ensure one is running at `http://localhost:9600`):
|
|
40
|
+
|
|
41
|
+
```python
|
|
42
|
+
from lollms_client import LollmsClient, MSG_TYPE
|
|
43
|
+
from ascii_colors import ASCIIColors
|
|
44
|
+
|
|
45
|
+
# Callback for streaming output
|
|
46
|
+
def simple_streaming_callback(chunk: str, msg_type: MSG_TYPE, params=None, metadata=None) -> bool:
|
|
47
|
+
if msg_type == MSG_TYPE.MSG_TYPE_CHUNK:
|
|
48
|
+
print(chunk, end="", flush=True)
|
|
49
|
+
elif msg_type == MSG_TYPE.MSG_TYPE_EXCEPTION:
|
|
50
|
+
ASCIIColors.error(f"\nStreaming Error: {chunk}")
|
|
51
|
+
return True # True to continue streaming
|
|
52
|
+
|
|
53
|
+
try:
|
|
54
|
+
# Initialize client to connect to a LoLLMs server
|
|
55
|
+
# For other backends, change 'binding_name' and provide necessary parameters.
|
|
56
|
+
# See DOC_USE.md for detailed initialization examples.
|
|
57
|
+
lc = LollmsClient(
|
|
58
|
+
binding_name="lollms",
|
|
59
|
+
host_address="http://localhost:9600"
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
prompt = "Tell me a fun fact about space."
|
|
63
|
+
ASCIIColors.yellow(f"Prompt: {prompt}")
|
|
64
|
+
|
|
65
|
+
# Generate text with streaming
|
|
66
|
+
ASCIIColors.green("Streaming Response:")
|
|
67
|
+
response_text = lc.generate_text(
|
|
68
|
+
prompt,
|
|
69
|
+
n_predict=100,
|
|
70
|
+
stream=True,
|
|
71
|
+
streaming_callback=simple_streaming_callback
|
|
72
|
+
)
|
|
73
|
+
print("\n--- End of Stream ---")
|
|
74
|
+
|
|
75
|
+
# The 'response_text' variable will contain the full concatenated text
|
|
76
|
+
# if streaming_callback returns True throughout.
|
|
77
|
+
if isinstance(response_text, str):
|
|
78
|
+
ASCIIColors.cyan(f"\nFull streamed text collected: {response_text[:100]}...")
|
|
79
|
+
elif isinstance(response_text, dict) and "error" in response_text:
|
|
80
|
+
ASCIIColors.error(f"Error during generation: {response_text['error']}")
|
|
81
|
+
|
|
82
|
+
except ValueError as ve:
|
|
83
|
+
ASCIIColors.error(f"Initialization Error: {ve}")
|
|
84
|
+
ASCIIColors.info("Ensure a LoLLMs server is running or configure another binding.")
|
|
85
|
+
except ConnectionRefusedError:
|
|
86
|
+
ASCIIColors.error("Connection refused. Is the LoLLMs server running at http://localhost:9600?")
|
|
87
|
+
except Exception as e:
|
|
88
|
+
ASCIIColors.error(f"An unexpected error occurred: {e}")
|
|
89
|
+
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
## Documentation
|
|
93
|
+
|
|
94
|
+
For more in-depth information, please refer to:
|
|
95
|
+
|
|
96
|
+
* **[Usage Guide (DOC_USE.md)](DOC_USE.md):** Learn how to use `LollmsClient`, different bindings, modality features, `TasksLibrary`, and `FunctionCalling_Library` with comprehensive examples.
|
|
97
|
+
* **[Developer Guide (DOC_DEV.md)](DOC_DEV.md):** Understand the architecture, how to create new bindings, and contribute to the library.
|
|
98
|
+
|
|
99
|
+
## Core Concepts
|
|
100
|
+
|
|
101
|
+
```mermaid
|
|
102
|
+
graph LR
|
|
103
|
+
A[Your Application] --> LC[LollmsClient];
|
|
104
|
+
|
|
105
|
+
subgraph LollmsClient_Core
|
|
106
|
+
LC -- Manages --> LLB[LLM Binding];
|
|
107
|
+
LC -- Provides Access To --> TL[TasksLibrary];
|
|
108
|
+
LC -- Provides Access To --> FCL[FunctionCalling_Library];
|
|
109
|
+
LC -- Provides Access To --> DM[DiscussionManager];
|
|
110
|
+
LC -- Provides Access To --> ModalityBindings[TTS, TTI, STT etc.];
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
subgraph LLM_Backends
|
|
114
|
+
LLB --> LollmsServer[LoLLMs Server];
|
|
115
|
+
LLB --> OllamaServer[Ollama];
|
|
116
|
+
LLB --> OpenAPIServer[OpenAI API];
|
|
117
|
+
LLB --> LocalGGUF[Local GGUF<br>(pythonllamacpp / llamacpp server)];
|
|
118
|
+
LLB --> LocalHF[Local HuggingFace<br>(transformers / vLLM)];
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
ModalityBindings --> ModalityServices[Modality Services<br>(e.g., LoLLMs Server TTS/TTI)];
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
* **`LollmsClient`**: The central class for all interactions. It holds the currently active LLM binding and provides access to modality bindings and helper libraries.
|
|
125
|
+
* **LLM Bindings**: These are plugins that allow `LollmsClient` to communicate with different LLM backends. You choose a binding (e.g., `"ollama"`, `"lollms"`, `"pythonllamacpp"`) when you initialize `LollmsClient`.
|
|
126
|
+
* **Modality Bindings**: Similar to LLM bindings, but for services like Text-to-Speech (`tts`), Text-to-Image (`tti`), etc.
|
|
127
|
+
* **`TasksLibrary`**: Offers high-level functions for common AI tasks (summarization, Q&A) built on top of `LollmsClient`.
|
|
128
|
+
* **`FunctionCalling_Library`**: Enables you to define Python functions that the LLM can request to execute, allowing for tool usage.
|
|
129
|
+
* **`LollmsDiscussion`**: Helps manage and format conversation histories for chat applications.
|
|
130
|
+
|
|
131
|
+
## Examples
|
|
132
|
+
|
|
133
|
+
The `examples/` directory in this repository contains a rich set of scripts demonstrating various features:
|
|
134
|
+
* Basic text generation with different bindings.
|
|
135
|
+
* Streaming and non-streaming examples.
|
|
136
|
+
* Multimodal generation (text with images).
|
|
137
|
+
* Using `TasksLibrary` for summarization and Q&A.
|
|
138
|
+
* Implementing and using function calls.
|
|
139
|
+
* Text-to-Speech and Text-to-Image generation.
|
|
140
|
+
|
|
141
|
+
Explore these examples to see `lollms-client` in action!
|
|
142
|
+
|
|
143
|
+
## Contributing
|
|
144
|
+
|
|
145
|
+
Contributions are welcome! Whether it's bug reports, feature suggestions, documentation improvements, or new bindings, please feel free to open an issue or submit a pull request on our [GitHub repository](https://github.com/ParisNeo/lollms_client).
|
|
146
|
+
|
|
147
|
+
## License
|
|
148
|
+
|
|
149
|
+
This project is licensed under the **Apache 2.0 License**. See the [LICENSE](LICENSE) file for details (assuming you have a LICENSE file, if not, state "Apache 2.0 License").
|
|
150
|
+
|
|
151
|
+
## Changelog
|
|
152
|
+
|
|
153
|
+
For a list of changes and updates, please refer to the [CHANGELOG.md](CHANGELOG.md) file.
|
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
# lollms_client/examples/text_and_speech_demo/generate_and_speak.py
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
import time
|
|
4
|
+
import argparse
|
|
5
|
+
|
|
6
|
+
# Ensure pygame is installed for this example
|
|
7
|
+
try:
|
|
8
|
+
import pipmaster as pm
|
|
9
|
+
pm.ensure_packages(["pygame"])
|
|
10
|
+
import pygame
|
|
11
|
+
PYGAME_AVAILABLE = True
|
|
12
|
+
except ImportError:
|
|
13
|
+
print("Pygame not found or pipmaster failed. Please install it manually: pip install pygame")
|
|
14
|
+
PYGAME_AVAILABLE = False
|
|
15
|
+
except Exception as e:
|
|
16
|
+
print(f"Could not ensure pygame: {e}")
|
|
17
|
+
PYGAME_AVAILABLE = False
|
|
18
|
+
|
|
19
|
+
from lollms_client import LollmsClient, MSG_TYPE
|
|
20
|
+
from ascii_colors import ASCIIColors, trace_exception
|
|
21
|
+
|
|
22
|
+
# --- Configuration ---
|
|
23
|
+
SPEECH_OUTPUT_DIR = Path(__file__).parent / "speech_output"
|
|
24
|
+
SPEECH_OUTPUT_DIR.mkdir(exist_ok=True)
|
|
25
|
+
|
|
26
|
+
# Default path for Piper voices relative to this example script for convenience
|
|
27
|
+
DEFAULT_PIPER_VOICES_SUBDIR = Path(__file__).parent / "piper_voices_for_demo"
|
|
28
|
+
DEFAULT_PIPER_VOICE_FILENAME = "en_US-lessac-medium.onnx" # A common, good quality English voice
|
|
29
|
+
|
|
30
|
+
def text_stream_callback(chunk: str, message_type: MSG_TYPE, params: dict = None, metadata: list = None) -> bool:
|
|
31
|
+
if message_type == MSG_TYPE.MSG_TYPE_CHUNK:
|
|
32
|
+
print(chunk, end="", flush=True)
|
|
33
|
+
elif message_type == MSG_TYPE.MSG_TYPE_STEP_START:
|
|
34
|
+
ASCIIColors.yellow(f"\n>> Starting step: {chunk}")
|
|
35
|
+
elif message_type == MSG_TYPE.MSG_TYPE_STEP_END:
|
|
36
|
+
ASCIIColors.green(f"\n<< Finished step: {chunk}")
|
|
37
|
+
return True
|
|
38
|
+
|
|
39
|
+
def ensure_default_piper_voice_for_demo(voices_dir: Path, voice_filename: str):
|
|
40
|
+
"""Helper to download a default Piper voice if not present for the demo."""
|
|
41
|
+
voices_dir.mkdir(exist_ok=True)
|
|
42
|
+
onnx_path = voices_dir / voice_filename
|
|
43
|
+
json_path = voices_dir / f"{voice_filename}.json"
|
|
44
|
+
|
|
45
|
+
if not onnx_path.exists() or not json_path.exists():
|
|
46
|
+
ASCIIColors.info(f"Default Piper test voice '{voice_filename}' not found in {voices_dir}. Attempting to download...")
|
|
47
|
+
try:
|
|
48
|
+
import requests
|
|
49
|
+
# Construct URLs (assuming en_US/lessac/medium structure)
|
|
50
|
+
voice_parts = voice_filename.split('-') # e.g., ['en_US', 'lessac', 'medium.onnx']
|
|
51
|
+
lang_code = voice_parts[0].split('_')[0] # en
|
|
52
|
+
voice_name_path = "/".join(voice_parts[0:2]) # en_US/lessac
|
|
53
|
+
quality_path = voice_parts[2].split('.')[0] # medium
|
|
54
|
+
|
|
55
|
+
# Base URL for Piper voices on Hugging Face
|
|
56
|
+
PIPER_VOICES_HF_BASE_URL = "https://huggingface.co/rhasspy/piper-voices/resolve/main/"
|
|
57
|
+
|
|
58
|
+
onnx_url = f"{PIPER_VOICES_HF_BASE_URL}{lang_code}/{voice_name_path}/{quality_path}/{voice_filename}"
|
|
59
|
+
json_url = f"{PIPER_VOICES_HF_BASE_URL}{lang_code}/{voice_name_path}/{quality_path}/{voice_filename}.json"
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
if not onnx_path.exists():
|
|
63
|
+
ASCIIColors.info(f"Downloading {onnx_url} to {onnx_path}")
|
|
64
|
+
r_onnx = requests.get(onnx_url, stream=True)
|
|
65
|
+
r_onnx.raise_for_status()
|
|
66
|
+
with open(onnx_path, 'wb') as f:
|
|
67
|
+
for chunk in r_onnx.iter_content(chunk_size=8192): f.write(chunk)
|
|
68
|
+
|
|
69
|
+
if not json_path.exists():
|
|
70
|
+
ASCIIColors.info(f"Downloading {json_url} to {json_path}")
|
|
71
|
+
r_json = requests.get(json_url)
|
|
72
|
+
r_json.raise_for_status()
|
|
73
|
+
with open(json_path, 'w', encoding='utf-8') as f: f.write(r_json.text)
|
|
74
|
+
ASCIIColors.green(f"Default Piper test voice '{voice_filename}' downloaded successfully to {voices_dir}.")
|
|
75
|
+
return True
|
|
76
|
+
except Exception as e_download:
|
|
77
|
+
ASCIIColors.error(f"Failed to download default Piper test voice '{voice_filename}': {e_download}")
|
|
78
|
+
ASCIIColors.warning(f"Please manually download '{voice_filename}' and '{voice_filename}.json' "
|
|
79
|
+
f"from rhasspy.github.io/piper-voices/ or Hugging Face "
|
|
80
|
+
f"and place them in {voices_dir.resolve()}")
|
|
81
|
+
return False
|
|
82
|
+
return True
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def main():
|
|
86
|
+
parser = argparse.ArgumentParser(description="Generate text with an LLM and synthesize it to speech using LOLLMS.")
|
|
87
|
+
# LLM Arguments
|
|
88
|
+
parser.add_argument(
|
|
89
|
+
"--llm_binding", type=str, default="ollama", choices=["ollama", "openai", "lollms", "llamacpp", "pythonllamacpp", "transformers", "vllm"],
|
|
90
|
+
help="The LLM binding to use for text generation."
|
|
91
|
+
)
|
|
92
|
+
parser.add_argument(
|
|
93
|
+
"--llm_model", type=str, default="mistral",
|
|
94
|
+
help="Model name or path for the LLM binding."
|
|
95
|
+
)
|
|
96
|
+
parser.add_argument("--llm_host", type=str, default=None, help="Host address for server-based LLM bindings.")
|
|
97
|
+
parser.add_argument("--models_path", type=str, default=None, help="Path to models directory for local LLM bindings.")
|
|
98
|
+
parser.add_argument("--openai_key", type=str, default=None, help="OpenAI API key.")
|
|
99
|
+
|
|
100
|
+
# TTS Arguments
|
|
101
|
+
parser.add_argument(
|
|
102
|
+
"--tts_binding", type=str, default="bark", choices=["bark", "lollms", "xtts", "piper"],
|
|
103
|
+
help="The TTS binding to use for speech synthesis."
|
|
104
|
+
)
|
|
105
|
+
# Bark specific
|
|
106
|
+
parser.add_argument("--bark_model", type=str, default="suno/bark-small", help="Bark model ID for TTS.")
|
|
107
|
+
parser.add_argument("--bark_voice_preset", type=str, default="v2/en_speaker_6", help="Bark voice preset.")
|
|
108
|
+
# XTTS specific
|
|
109
|
+
parser.add_argument("--xtts_model", type=str, default="tts_models/multilingual/multi-dataset/xtts_v2", help="XTTS model identifier for Coqui TTS.")
|
|
110
|
+
parser.add_argument("--xtts_speaker_wav", type=str, default=None, help="Path to speaker WAV for XTTS voice cloning.")
|
|
111
|
+
parser.add_argument("--xtts_language", type=str, default="en", help="Language for XTTS.")
|
|
112
|
+
# Piper specific
|
|
113
|
+
parser.add_argument("--piper_default_voice_model_path", type=str, default=None, help="Path to the default .onnx Piper voice model.")
|
|
114
|
+
parser.add_argument("--piper_voices_dir", type=str, default=str(DEFAULT_PIPER_VOICES_SUBDIR), help="Directory containing Piper voice models.")
|
|
115
|
+
parser.add_argument("--piper_voice_file", type=str, default=DEFAULT_PIPER_VOICE_FILENAME, help="Filename of the Piper voice to use from piper_voices_dir (e.g., en_US-ryan-medium.onnx).")
|
|
116
|
+
|
|
117
|
+
# Common TTS/LLM args
|
|
118
|
+
parser.add_argument("--tts_host", type=str, default=None, help="Host address for server-based TTS bindings (e.g., lollms TTS).")
|
|
119
|
+
parser.add_argument("--device", type=str, default=None, choices=["cpu", "cuda", "mps", None], help="Device for local TTS/LLM models.")
|
|
120
|
+
args = parser.parse_args()
|
|
121
|
+
|
|
122
|
+
ASCIIColors.red("--- LOLLMS Text Generation and Speech Synthesis Demo ---")
|
|
123
|
+
ASCIIColors.info(f"Using LLM Binding: {args.llm_binding} (Model: {args.llm_model})")
|
|
124
|
+
ASCIIColors.info(f"Using TTS Binding: {args.tts_binding}")
|
|
125
|
+
if args.tts_binding == "bark":
|
|
126
|
+
ASCIIColors.info(f" Bark Model: {args.bark_model}, Voice Preset: {args.bark_voice_preset}")
|
|
127
|
+
elif args.tts_binding == "xtts":
|
|
128
|
+
ASCIIColors.info(f" XTTS Model: {args.xtts_model}, Speaker WAV: {args.xtts_speaker_wav or 'Default in binding'}, Lang: {args.xtts_language}")
|
|
129
|
+
elif args.tts_binding == "piper":
|
|
130
|
+
ASCIIColors.info(f" Piper Voices Dir: {args.piper_voices_dir}, Voice File: {args.piper_voice_file}")
|
|
131
|
+
# Ensure default Piper voice for demo if Piper is selected and no specific default path is given
|
|
132
|
+
if not args.piper_default_voice_model_path:
|
|
133
|
+
ensure_default_piper_voice_for_demo(Path(args.piper_voices_dir), args.piper_voice_file)
|
|
134
|
+
args.piper_default_voice_model_path = str(Path(args.piper_voices_dir) / args.piper_voice_file)
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
llm_binding_config = {}
|
|
138
|
+
if args.llm_binding == "openai" and args.openai_key: llm_binding_config["service_key"] = args.openai_key
|
|
139
|
+
elif args.llm_binding in ["llamacpp", "pythonllamacpp", "transformers", "vllm"]:
|
|
140
|
+
if args.device: llm_binding_config["device"] = args.device
|
|
141
|
+
if args.llm_binding == "pythonllamacpp": llm_binding_config["n_gpu_layers"] = -1 if args.device == "cuda" else 0
|
|
142
|
+
|
|
143
|
+
tts_binding_config = {"device": args.device}
|
|
144
|
+
if args.tts_binding == "bark":
|
|
145
|
+
tts_binding_config["model_name"] = args.bark_model
|
|
146
|
+
tts_binding_config["default_voice"] = args.bark_voice_preset
|
|
147
|
+
elif args.tts_binding == "xtts":
|
|
148
|
+
tts_binding_config["model_name"] = args.xtts_model
|
|
149
|
+
tts_binding_config["default_speaker_wav"] = args.xtts_speaker_wav
|
|
150
|
+
tts_binding_config["default_language"] = args.xtts_language
|
|
151
|
+
elif args.tts_binding == "piper":
|
|
152
|
+
tts_binding_config["default_voice_model_path"] = args.piper_default_voice_model_path
|
|
153
|
+
tts_binding_config["piper_voices_dir"] = args.piper_voices_dir
|
|
154
|
+
elif args.tts_binding == "lollms":
|
|
155
|
+
tts_binding_config["model_name"] = "default_lollms_voice" # Placeholder, server handles actual voice
|
|
156
|
+
|
|
157
|
+
lollms_client = None
|
|
158
|
+
try:
|
|
159
|
+
ASCIIColors.magenta("Initializing LollmsClient...")
|
|
160
|
+
lollms_client = LollmsClient(
|
|
161
|
+
binding_name=args.llm_binding, model_name=args.llm_model,
|
|
162
|
+
host_address=args.llm_host, models_path=args.models_path,
|
|
163
|
+
llm_binding_config=llm_binding_config,
|
|
164
|
+
tts_binding_name=args.tts_binding, tts_host_address=args.tts_host,
|
|
165
|
+
tts_binding_config=tts_binding_config,
|
|
166
|
+
verify_ssl_certificate=False
|
|
167
|
+
)
|
|
168
|
+
ASCIIColors.green("LollmsClient initialized.")
|
|
169
|
+
except Exception as e:
|
|
170
|
+
ASCIIColors.error(f"Failed to initialize LollmsClient: {e}"); trace_exception(e)
|
|
171
|
+
return
|
|
172
|
+
|
|
173
|
+
generated_text = ""
|
|
174
|
+
text_prompt = "Craft a very short, cheerful message about the joy of discovery."
|
|
175
|
+
ASCIIColors.cyan(f"\n--- Generating Text (Prompt: '{text_prompt[:50]}...') ---")
|
|
176
|
+
if not lollms_client.binding:
|
|
177
|
+
ASCIIColors.error("LLM binding not available."); return
|
|
178
|
+
try:
|
|
179
|
+
print(f"{ASCIIColors.YELLOW}AI is thinking: {ASCIIColors.RESET}", end="")
|
|
180
|
+
generated_text = lollms_client.generate_text(
|
|
181
|
+
prompt=text_prompt, n_predict=100, stream=True,
|
|
182
|
+
streaming_callback=text_stream_callback, temperature=0.7
|
|
183
|
+
)
|
|
184
|
+
print("\n"); ASCIIColors.green("Text generation complete.")
|
|
185
|
+
ASCIIColors.magenta("Generated Text:\n"); ASCIIColors.yellow(generated_text)
|
|
186
|
+
except Exception as e:
|
|
187
|
+
ASCIIColors.error(f"Text generation failed: {e}"); trace_exception(e); return
|
|
188
|
+
if not generated_text:
|
|
189
|
+
ASCIIColors.warning("LLM did not generate any text."); return
|
|
190
|
+
|
|
191
|
+
speech_file_path = None
|
|
192
|
+
ASCIIColors.cyan(f"\n--- Synthesizing Speech (using {args.tts_binding}) ---")
|
|
193
|
+
if not lollms_client.tts:
|
|
194
|
+
ASCIIColors.error("TTS binding not available."); return
|
|
195
|
+
try:
|
|
196
|
+
tts_call_kwargs = {}
|
|
197
|
+
if args.tts_binding == "bark":
|
|
198
|
+
# For Bark, 'voice' in generate_audio is the voice_preset.
|
|
199
|
+
# If not using the default from init, pass it here.
|
|
200
|
+
# tts_call_kwargs['voice'] = args.bark_voice_preset
|
|
201
|
+
pass # Uses default_voice from init if args.bark_voice_preset not specified to override
|
|
202
|
+
elif args.tts_binding == "xtts":
|
|
203
|
+
tts_call_kwargs['language'] = args.xtts_language
|
|
204
|
+
# 'voice' for XTTS is the speaker_wav path. If not using default from init, pass here.
|
|
205
|
+
# tts_call_kwargs['voice'] = args.xtts_speaker_wav
|
|
206
|
+
elif args.tts_binding == "piper":
|
|
207
|
+
# 'voice' for Piper is the .onnx filename.
|
|
208
|
+
tts_call_kwargs['voice'] = args.piper_voice_file
|
|
209
|
+
# Example Piper specific param:
|
|
210
|
+
# tts_call_kwargs['length_scale'] = 1.0
|
|
211
|
+
|
|
212
|
+
audio_bytes = lollms_client.tts.generate_audio(text=generated_text, **tts_call_kwargs)
|
|
213
|
+
|
|
214
|
+
if audio_bytes:
|
|
215
|
+
filename_stem = f"speech_output_{args.llm_binding}_{args.tts_binding}"
|
|
216
|
+
speech_file_path = SPEECH_OUTPUT_DIR / f"{filename_stem.replace('/', '_')}.wav"
|
|
217
|
+
with open(speech_file_path, "wb") as f: f.write(audio_bytes)
|
|
218
|
+
ASCIIColors.green(f"Speech synthesized and saved to: {speech_file_path}")
|
|
219
|
+
elif args.tts_binding == "lollms":
|
|
220
|
+
ASCIIColors.warning("LOLLMS TTS binding returned empty bytes. Server might have saved file if 'fn' was used.")
|
|
221
|
+
speech_file_path = None
|
|
222
|
+
else:
|
|
223
|
+
ASCIIColors.warning("Speech synthesis returned empty bytes."); speech_file_path = None
|
|
224
|
+
except Exception as e:
|
|
225
|
+
ASCIIColors.error(f"Speech synthesis failed: {e}"); trace_exception(e); return
|
|
226
|
+
|
|
227
|
+
if speech_file_path and PYGAME_AVAILABLE:
|
|
228
|
+
ASCIIColors.magenta("\n--- Playing Synthesized Speech ---")
|
|
229
|
+
try:
|
|
230
|
+
pygame.mixer.init()
|
|
231
|
+
speech_sound = pygame.mixer.Sound(str(speech_file_path))
|
|
232
|
+
ASCIIColors.cyan("Playing audio... Press Ctrl+C in console to stop playback early.")
|
|
233
|
+
speech_sound.play()
|
|
234
|
+
while pygame.mixer.get_busy():
|
|
235
|
+
pygame.time.Clock().tick(10)
|
|
236
|
+
for event in pygame.event.get():
|
|
237
|
+
if event.type == pygame.QUIT: pygame.mixer.stop(); break
|
|
238
|
+
ASCIIColors.green("Playback finished.")
|
|
239
|
+
except pygame.error as e: ASCIIColors.warning(f"Could not play audio with pygame: {e}")
|
|
240
|
+
except KeyboardInterrupt: pygame.mixer.stop(); ASCIIColors.yellow("\nPlayback interrupted.")
|
|
241
|
+
finally: pygame.quit()
|
|
242
|
+
elif not PYGAME_AVAILABLE:
|
|
243
|
+
ASCIIColors.warning("Pygame is not available for playback.")
|
|
244
|
+
if speech_file_path: ASCIIColors.info(f"Generated speech: {speech_file_path.resolve()}")
|
|
245
|
+
elif not speech_file_path:
|
|
246
|
+
ASCIIColors.warning("No speech file generated/path unknown. Skipping playback.")
|
|
247
|
+
|
|
248
|
+
ASCIIColors.red("\n--- Demo Finished ---")
|
|
249
|
+
|
|
250
|
+
if __name__ == "__main__":
|
|
251
|
+
main()
|