local-llm-kit 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.
- local_llm_kit-0.1.0/LICENSE +21 -0
- local_llm_kit-0.1.0/MANIFEST.in +9 -0
- local_llm_kit-0.1.0/PKG-INFO +249 -0
- local_llm_kit-0.1.0/README.md +207 -0
- local_llm_kit-0.1.0/examples/__init__.py +3 -0
- local_llm_kit-0.1.0/examples/simple_chat.py +66 -0
- local_llm_kit-0.1.0/local_llm_kit/__init__.py +11 -0
- local_llm_kit-0.1.0/local_llm_kit/backends/__init__.py +9 -0
- local_llm_kit-0.1.0/local_llm_kit/backends/base.py +113 -0
- local_llm_kit-0.1.0/local_llm_kit/backends/llamacpp.py +293 -0
- local_llm_kit-0.1.0/local_llm_kit/backends/transformers.py +526 -0
- local_llm_kit-0.1.0/local_llm_kit/chat.py +114 -0
- local_llm_kit-0.1.0/local_llm_kit/cli.py +239 -0
- local_llm_kit-0.1.0/local_llm_kit/function_calling.py +245 -0
- local_llm_kit-0.1.0/local_llm_kit/llm.py +536 -0
- local_llm_kit-0.1.0/local_llm_kit/memory.py +178 -0
- local_llm_kit-0.1.0/local_llm_kit/prompt_formatting.py +478 -0
- local_llm_kit-0.1.0/local_llm_kit.egg-info/PKG-INFO +249 -0
- local_llm_kit-0.1.0/local_llm_kit.egg-info/SOURCES.txt +27 -0
- local_llm_kit-0.1.0/local_llm_kit.egg-info/dependency_links.txt +1 -0
- local_llm_kit-0.1.0/local_llm_kit.egg-info/entry_points.txt +2 -0
- local_llm_kit-0.1.0/local_llm_kit.egg-info/requires.txt +15 -0
- local_llm_kit-0.1.0/local_llm_kit.egg-info/top_level.txt +3 -0
- local_llm_kit-0.1.0/requirements.txt +6 -0
- local_llm_kit-0.1.0/setup.cfg +4 -0
- local_llm_kit-0.1.0/setup.py +61 -0
- local_llm_kit-0.1.0/tests/__init__.py +3 -0
- local_llm_kit-0.1.0/tests/test_function_calling.py +137 -0
- local_llm_kit-0.1.0/tests/test_prompt_formatting.py +105 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Utkarsh Rajput (GitHub: 1Utkarsh1)
|
|
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.
|
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: local-llm-kit
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: OpenAI-like interface for local LLMs
|
|
5
|
+
Home-page: https://github.com/1Utkarsh1/local-llm-kit
|
|
6
|
+
Author: Utkarsh Rajput
|
|
7
|
+
Author-email: info@example.com
|
|
8
|
+
Classifier: Development Status :: 3 - Alpha
|
|
9
|
+
Classifier: Intended Audience :: Developers
|
|
10
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
16
|
+
Requires-Python: >=3.8
|
|
17
|
+
Description-Content-Type: text/markdown
|
|
18
|
+
License-File: LICENSE
|
|
19
|
+
Requires-Dist: typing-extensions>=4.0.0
|
|
20
|
+
Provides-Extra: transformers
|
|
21
|
+
Requires-Dist: torch>=1.12.0; extra == "transformers"
|
|
22
|
+
Requires-Dist: transformers>=4.25.0; extra == "transformers"
|
|
23
|
+
Requires-Dist: accelerate>=0.16.0; extra == "transformers"
|
|
24
|
+
Provides-Extra: llamacpp
|
|
25
|
+
Requires-Dist: llama-cpp-python>=0.1.77; extra == "llamacpp"
|
|
26
|
+
Provides-Extra: all
|
|
27
|
+
Requires-Dist: torch>=1.12.0; extra == "all"
|
|
28
|
+
Requires-Dist: transformers>=4.25.0; extra == "all"
|
|
29
|
+
Requires-Dist: accelerate>=0.16.0; extra == "all"
|
|
30
|
+
Requires-Dist: llama-cpp-python>=0.1.77; extra == "all"
|
|
31
|
+
Dynamic: author
|
|
32
|
+
Dynamic: author-email
|
|
33
|
+
Dynamic: classifier
|
|
34
|
+
Dynamic: description
|
|
35
|
+
Dynamic: description-content-type
|
|
36
|
+
Dynamic: home-page
|
|
37
|
+
Dynamic: license-file
|
|
38
|
+
Dynamic: provides-extra
|
|
39
|
+
Dynamic: requires-dist
|
|
40
|
+
Dynamic: requires-python
|
|
41
|
+
Dynamic: summary
|
|
42
|
+
|
|
43
|
+
# Local LLM Kit
|
|
44
|
+
|
|
45
|
+
An OpenAI-like interface for local Large Language Models. This package allows you to interact with local LLMs using an API similar to OpenAI's, including support for chat completions, function calling, streaming responses, and more.
|
|
46
|
+
|
|
47
|
+
**Created by Utkarsh Rajput (GitHub: [1Utkarsh1](https://github.com/1Utkarsh1))** - A powerful tool for working with local language models with all the convenience of the OpenAI API.
|
|
48
|
+
|
|
49
|
+
## Features
|
|
50
|
+
|
|
51
|
+
- 🗣️ **Chat and Completion API**: Similar to OpenAI's API
|
|
52
|
+
- 🧩 **Multiple Backend Support**: Works with Hugging Face Transformers and llama.cpp
|
|
53
|
+
- 🛠️ **Function Calling**: Register Python functions that LLMs can call
|
|
54
|
+
- 📱 **Streaming Support**: Stream responses token by token
|
|
55
|
+
- 🧠 **Memory Management**: Auto-truncation and context management
|
|
56
|
+
- 📊 **Logprobs**: Get token probabilities for generations
|
|
57
|
+
- 📝 **Prompt Formatting**: Supports various model template formats (Llama, Mistral, etc.)
|
|
58
|
+
- 🌐 **JSON Mode**: Enforce structured JSON output
|
|
59
|
+
|
|
60
|
+
## Installation
|
|
61
|
+
|
|
62
|
+
### Basic Installation
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
pip install local-llm-kit
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### With Backend Support
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
# For Hugging Face Transformers support
|
|
72
|
+
pip install "local-llm-kit[transformers]"
|
|
73
|
+
|
|
74
|
+
# For llama.cpp support
|
|
75
|
+
pip install "local-llm-kit[llamacpp]"
|
|
76
|
+
|
|
77
|
+
# For all backends
|
|
78
|
+
pip install "local-llm-kit[all]"
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
## Quick Start
|
|
82
|
+
|
|
83
|
+
### Chat with a Model
|
|
84
|
+
|
|
85
|
+
```python
|
|
86
|
+
from local_llm_kit import chat
|
|
87
|
+
|
|
88
|
+
response = chat(
|
|
89
|
+
messages=[
|
|
90
|
+
{"role": "system", "content": "You are a helpful assistant."},
|
|
91
|
+
{"role": "user", "content": "Tell me about the solar system."}
|
|
92
|
+
],
|
|
93
|
+
model_path="path/to/your/model", # Local model path or HF model name
|
|
94
|
+
temperature=0.7,
|
|
95
|
+
max_tokens=512
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
print(response["choices"][0]["message"]["content"])
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
### Text Completion
|
|
102
|
+
|
|
103
|
+
```python
|
|
104
|
+
from local_llm_kit import complete
|
|
105
|
+
|
|
106
|
+
response = complete(
|
|
107
|
+
prompt="The solar system consists of",
|
|
108
|
+
model_path="path/to/your/model",
|
|
109
|
+
temperature=0.7,
|
|
110
|
+
max_tokens=512
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
print(response["choices"][0]["text"])
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
### Function Calling
|
|
117
|
+
|
|
118
|
+
```python
|
|
119
|
+
from local_llm_kit import LLM
|
|
120
|
+
|
|
121
|
+
# Define a function with its schema
|
|
122
|
+
def get_weather(location: str, unit: str = "celsius"):
|
|
123
|
+
"""Get the weather for a location."""
|
|
124
|
+
# In a real app, this would call a weather API
|
|
125
|
+
return {"temperature": 22, "unit": unit, "description": f"Sunny in {location}"}
|
|
126
|
+
|
|
127
|
+
weather_function = {
|
|
128
|
+
"name": "get_weather",
|
|
129
|
+
"description": "Get the current weather for a location",
|
|
130
|
+
"parameters": {
|
|
131
|
+
"type": "object",
|
|
132
|
+
"properties": {
|
|
133
|
+
"location": {
|
|
134
|
+
"type": "string",
|
|
135
|
+
"description": "The city and state, e.g. San Francisco, CA"
|
|
136
|
+
},
|
|
137
|
+
"unit": {
|
|
138
|
+
"type": "string",
|
|
139
|
+
"enum": ["celsius", "fahrenheit"],
|
|
140
|
+
"description": "The unit of temperature"
|
|
141
|
+
}
|
|
142
|
+
},
|
|
143
|
+
"required": ["location"]
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
# Create LLM instance
|
|
148
|
+
llm = LLM(model_path="path/to/your/model")
|
|
149
|
+
|
|
150
|
+
# Register the function
|
|
151
|
+
llm.add_function(
|
|
152
|
+
name="get_weather",
|
|
153
|
+
schema=weather_function,
|
|
154
|
+
implementation=get_weather
|
|
155
|
+
)
|
|
156
|
+
|
|
157
|
+
# Chat with function calling
|
|
158
|
+
response = llm.chat(
|
|
159
|
+
messages=[
|
|
160
|
+
{"role": "user", "content": "What's the weather like in Paris?"}
|
|
161
|
+
],
|
|
162
|
+
# The function will be automatically called if the model chooses to use it
|
|
163
|
+
)
|
|
164
|
+
|
|
165
|
+
print(response["choices"][0]["message"]["content"])
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
### Streaming Responses
|
|
169
|
+
|
|
170
|
+
```python
|
|
171
|
+
from local_llm_kit import chat
|
|
172
|
+
|
|
173
|
+
for chunk in chat(
|
|
174
|
+
messages=[
|
|
175
|
+
{"role": "user", "content": "Write a short poem about nature."}
|
|
176
|
+
],
|
|
177
|
+
model_path="path/to/your/model",
|
|
178
|
+
stream=True
|
|
179
|
+
):
|
|
180
|
+
# Process each token as it's generated
|
|
181
|
+
if "choices" in chunk and chunk["choices"] and "delta" in chunk["choices"][0]:
|
|
182
|
+
delta = chunk["choices"][0]["delta"]
|
|
183
|
+
if "content" in delta:
|
|
184
|
+
print(delta["content"], end="", flush=True)
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
## Command Line Interface
|
|
188
|
+
|
|
189
|
+
Local LLM Kit includes a CLI for easy interaction with models.
|
|
190
|
+
|
|
191
|
+
### Interactive Chat
|
|
192
|
+
|
|
193
|
+
```bash
|
|
194
|
+
local-llm-kit chat --model path/to/your/model --system "You are a helpful assistant."
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
### Text Completion
|
|
198
|
+
|
|
199
|
+
```bash
|
|
200
|
+
local-llm-kit complete --model path/to/your/model --prompt "Once upon a time,"
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
## Advanced Usage
|
|
204
|
+
|
|
205
|
+
### Using Different Backends
|
|
206
|
+
|
|
207
|
+
```python
|
|
208
|
+
from local_llm_kit import LLM
|
|
209
|
+
|
|
210
|
+
# Using Transformers backend
|
|
211
|
+
llm_transformers = LLM(
|
|
212
|
+
model_path="mistralai/Mistral-7B-Instruct-v0.1",
|
|
213
|
+
backend="transformers",
|
|
214
|
+
backend_kwargs={"device": "cuda", "torch_dtype": "float16"}
|
|
215
|
+
)
|
|
216
|
+
|
|
217
|
+
# Using llama.cpp backend
|
|
218
|
+
llm_llamacpp = LLM(
|
|
219
|
+
model_path="path/to/model.gguf",
|
|
220
|
+
backend="llamacpp",
|
|
221
|
+
backend_kwargs={"n_gpu_layers": -1, "n_ctx": 4096}
|
|
222
|
+
)
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
### JSON Mode
|
|
226
|
+
|
|
227
|
+
```python
|
|
228
|
+
from local_llm_kit import chat
|
|
229
|
+
|
|
230
|
+
response = chat(
|
|
231
|
+
messages=[
|
|
232
|
+
{"role": "user", "content": "Generate a JSON list of 3 planets with their diameter."}
|
|
233
|
+
],
|
|
234
|
+
model_path="path/to/your/model",
|
|
235
|
+
format="json"
|
|
236
|
+
)
|
|
237
|
+
|
|
238
|
+
import json
|
|
239
|
+
planets = json.loads(response["choices"][0]["message"]["content"])
|
|
240
|
+
print(planets)
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
## License
|
|
244
|
+
|
|
245
|
+
MIT License
|
|
246
|
+
|
|
247
|
+
## Contributing
|
|
248
|
+
|
|
249
|
+
Contributions are welcome! Please feel free to submit a Pull Request.
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
# Local LLM Kit
|
|
2
|
+
|
|
3
|
+
An OpenAI-like interface for local Large Language Models. This package allows you to interact with local LLMs using an API similar to OpenAI's, including support for chat completions, function calling, streaming responses, and more.
|
|
4
|
+
|
|
5
|
+
**Created by Utkarsh Rajput (GitHub: [1Utkarsh1](https://github.com/1Utkarsh1))** - A powerful tool for working with local language models with all the convenience of the OpenAI API.
|
|
6
|
+
|
|
7
|
+
## Features
|
|
8
|
+
|
|
9
|
+
- 🗣️ **Chat and Completion API**: Similar to OpenAI's API
|
|
10
|
+
- 🧩 **Multiple Backend Support**: Works with Hugging Face Transformers and llama.cpp
|
|
11
|
+
- 🛠️ **Function Calling**: Register Python functions that LLMs can call
|
|
12
|
+
- 📱 **Streaming Support**: Stream responses token by token
|
|
13
|
+
- 🧠 **Memory Management**: Auto-truncation and context management
|
|
14
|
+
- 📊 **Logprobs**: Get token probabilities for generations
|
|
15
|
+
- 📝 **Prompt Formatting**: Supports various model template formats (Llama, Mistral, etc.)
|
|
16
|
+
- 🌐 **JSON Mode**: Enforce structured JSON output
|
|
17
|
+
|
|
18
|
+
## Installation
|
|
19
|
+
|
|
20
|
+
### Basic Installation
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
pip install local-llm-kit
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
### With Backend Support
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
# For Hugging Face Transformers support
|
|
30
|
+
pip install "local-llm-kit[transformers]"
|
|
31
|
+
|
|
32
|
+
# For llama.cpp support
|
|
33
|
+
pip install "local-llm-kit[llamacpp]"
|
|
34
|
+
|
|
35
|
+
# For all backends
|
|
36
|
+
pip install "local-llm-kit[all]"
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Quick Start
|
|
40
|
+
|
|
41
|
+
### Chat with a Model
|
|
42
|
+
|
|
43
|
+
```python
|
|
44
|
+
from local_llm_kit import chat
|
|
45
|
+
|
|
46
|
+
response = chat(
|
|
47
|
+
messages=[
|
|
48
|
+
{"role": "system", "content": "You are a helpful assistant."},
|
|
49
|
+
{"role": "user", "content": "Tell me about the solar system."}
|
|
50
|
+
],
|
|
51
|
+
model_path="path/to/your/model", # Local model path or HF model name
|
|
52
|
+
temperature=0.7,
|
|
53
|
+
max_tokens=512
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
print(response["choices"][0]["message"]["content"])
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### Text Completion
|
|
60
|
+
|
|
61
|
+
```python
|
|
62
|
+
from local_llm_kit import complete
|
|
63
|
+
|
|
64
|
+
response = complete(
|
|
65
|
+
prompt="The solar system consists of",
|
|
66
|
+
model_path="path/to/your/model",
|
|
67
|
+
temperature=0.7,
|
|
68
|
+
max_tokens=512
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
print(response["choices"][0]["text"])
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### Function Calling
|
|
75
|
+
|
|
76
|
+
```python
|
|
77
|
+
from local_llm_kit import LLM
|
|
78
|
+
|
|
79
|
+
# Define a function with its schema
|
|
80
|
+
def get_weather(location: str, unit: str = "celsius"):
|
|
81
|
+
"""Get the weather for a location."""
|
|
82
|
+
# In a real app, this would call a weather API
|
|
83
|
+
return {"temperature": 22, "unit": unit, "description": f"Sunny in {location}"}
|
|
84
|
+
|
|
85
|
+
weather_function = {
|
|
86
|
+
"name": "get_weather",
|
|
87
|
+
"description": "Get the current weather for a location",
|
|
88
|
+
"parameters": {
|
|
89
|
+
"type": "object",
|
|
90
|
+
"properties": {
|
|
91
|
+
"location": {
|
|
92
|
+
"type": "string",
|
|
93
|
+
"description": "The city and state, e.g. San Francisco, CA"
|
|
94
|
+
},
|
|
95
|
+
"unit": {
|
|
96
|
+
"type": "string",
|
|
97
|
+
"enum": ["celsius", "fahrenheit"],
|
|
98
|
+
"description": "The unit of temperature"
|
|
99
|
+
}
|
|
100
|
+
},
|
|
101
|
+
"required": ["location"]
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
# Create LLM instance
|
|
106
|
+
llm = LLM(model_path="path/to/your/model")
|
|
107
|
+
|
|
108
|
+
# Register the function
|
|
109
|
+
llm.add_function(
|
|
110
|
+
name="get_weather",
|
|
111
|
+
schema=weather_function,
|
|
112
|
+
implementation=get_weather
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
# Chat with function calling
|
|
116
|
+
response = llm.chat(
|
|
117
|
+
messages=[
|
|
118
|
+
{"role": "user", "content": "What's the weather like in Paris?"}
|
|
119
|
+
],
|
|
120
|
+
# The function will be automatically called if the model chooses to use it
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
print(response["choices"][0]["message"]["content"])
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
### Streaming Responses
|
|
127
|
+
|
|
128
|
+
```python
|
|
129
|
+
from local_llm_kit import chat
|
|
130
|
+
|
|
131
|
+
for chunk in chat(
|
|
132
|
+
messages=[
|
|
133
|
+
{"role": "user", "content": "Write a short poem about nature."}
|
|
134
|
+
],
|
|
135
|
+
model_path="path/to/your/model",
|
|
136
|
+
stream=True
|
|
137
|
+
):
|
|
138
|
+
# Process each token as it's generated
|
|
139
|
+
if "choices" in chunk and chunk["choices"] and "delta" in chunk["choices"][0]:
|
|
140
|
+
delta = chunk["choices"][0]["delta"]
|
|
141
|
+
if "content" in delta:
|
|
142
|
+
print(delta["content"], end="", flush=True)
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
## Command Line Interface
|
|
146
|
+
|
|
147
|
+
Local LLM Kit includes a CLI for easy interaction with models.
|
|
148
|
+
|
|
149
|
+
### Interactive Chat
|
|
150
|
+
|
|
151
|
+
```bash
|
|
152
|
+
local-llm-kit chat --model path/to/your/model --system "You are a helpful assistant."
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
### Text Completion
|
|
156
|
+
|
|
157
|
+
```bash
|
|
158
|
+
local-llm-kit complete --model path/to/your/model --prompt "Once upon a time,"
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
## Advanced Usage
|
|
162
|
+
|
|
163
|
+
### Using Different Backends
|
|
164
|
+
|
|
165
|
+
```python
|
|
166
|
+
from local_llm_kit import LLM
|
|
167
|
+
|
|
168
|
+
# Using Transformers backend
|
|
169
|
+
llm_transformers = LLM(
|
|
170
|
+
model_path="mistralai/Mistral-7B-Instruct-v0.1",
|
|
171
|
+
backend="transformers",
|
|
172
|
+
backend_kwargs={"device": "cuda", "torch_dtype": "float16"}
|
|
173
|
+
)
|
|
174
|
+
|
|
175
|
+
# Using llama.cpp backend
|
|
176
|
+
llm_llamacpp = LLM(
|
|
177
|
+
model_path="path/to/model.gguf",
|
|
178
|
+
backend="llamacpp",
|
|
179
|
+
backend_kwargs={"n_gpu_layers": -1, "n_ctx": 4096}
|
|
180
|
+
)
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
### JSON Mode
|
|
184
|
+
|
|
185
|
+
```python
|
|
186
|
+
from local_llm_kit import chat
|
|
187
|
+
|
|
188
|
+
response = chat(
|
|
189
|
+
messages=[
|
|
190
|
+
{"role": "user", "content": "Generate a JSON list of 3 planets with their diameter."}
|
|
191
|
+
],
|
|
192
|
+
model_path="path/to/your/model",
|
|
193
|
+
format="json"
|
|
194
|
+
)
|
|
195
|
+
|
|
196
|
+
import json
|
|
197
|
+
planets = json.loads(response["choices"][0]["message"]["content"])
|
|
198
|
+
print(planets)
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
## License
|
|
202
|
+
|
|
203
|
+
MIT License
|
|
204
|
+
|
|
205
|
+
## Contributing
|
|
206
|
+
|
|
207
|
+
Contributions are welcome! Please feel free to submit a Pull Request.
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Simple example script for local_llm_kit chat.
|
|
3
|
+
"""
|
|
4
|
+
import argparse
|
|
5
|
+
import sys
|
|
6
|
+
|
|
7
|
+
from local_llm_kit import chat
|
|
8
|
+
|
|
9
|
+
if __name__ == "__main__":
|
|
10
|
+
parser = argparse.ArgumentParser(description="Simple chat with a local LLM")
|
|
11
|
+
parser.add_argument("--model", "-m", required=True, help="Path to the model or model name")
|
|
12
|
+
parser.add_argument("--backend", "-b", choices=["transformers", "llamacpp"],
|
|
13
|
+
help="Backend to use (default: auto-detect)")
|
|
14
|
+
args = parser.parse_args()
|
|
15
|
+
|
|
16
|
+
# Initial message
|
|
17
|
+
print(f"Simple chat with {args.model}")
|
|
18
|
+
print("Type 'exit' or 'quit' to end the conversation.")
|
|
19
|
+
print()
|
|
20
|
+
|
|
21
|
+
# Chat loop
|
|
22
|
+
messages = [
|
|
23
|
+
{"role": "system", "content": "You are a helpful assistant."}
|
|
24
|
+
]
|
|
25
|
+
|
|
26
|
+
try:
|
|
27
|
+
while True:
|
|
28
|
+
# Get user input
|
|
29
|
+
user_input = input("You: ")
|
|
30
|
+
if user_input.lower() in ["exit", "quit"]:
|
|
31
|
+
print("Goodbye!")
|
|
32
|
+
break
|
|
33
|
+
|
|
34
|
+
# Add user message
|
|
35
|
+
messages.append({"role": "user", "content": user_input})
|
|
36
|
+
|
|
37
|
+
# Generate and display response
|
|
38
|
+
try:
|
|
39
|
+
print("Assistant: ", end="", flush=True)
|
|
40
|
+
response_text = ""
|
|
41
|
+
|
|
42
|
+
# Stream the response token by token
|
|
43
|
+
for chunk in chat(
|
|
44
|
+
messages=messages,
|
|
45
|
+
model_path=args.model,
|
|
46
|
+
backend=args.backend,
|
|
47
|
+
stream=True
|
|
48
|
+
):
|
|
49
|
+
# Extract and print chunk text
|
|
50
|
+
if "choices" in chunk and chunk["choices"] and "delta" in chunk["choices"][0]:
|
|
51
|
+
delta = chunk["choices"][0]["delta"]
|
|
52
|
+
if "content" in delta and delta["content"]:
|
|
53
|
+
print(delta["content"], end="", flush=True)
|
|
54
|
+
response_text += delta["content"]
|
|
55
|
+
|
|
56
|
+
print() # Add newline after response
|
|
57
|
+
|
|
58
|
+
# Add assistant response to history
|
|
59
|
+
messages.append({"role": "assistant", "content": response_text})
|
|
60
|
+
|
|
61
|
+
except Exception as e:
|
|
62
|
+
print(f"\nError: {e}")
|
|
63
|
+
|
|
64
|
+
except KeyboardInterrupt:
|
|
65
|
+
print("\nGoodbye!")
|
|
66
|
+
sys.exit(0)
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Base class for model backends.
|
|
3
|
+
"""
|
|
4
|
+
import time
|
|
5
|
+
from abc import ABC, abstractmethod
|
|
6
|
+
from typing import Dict, Any, Iterator, Optional
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class BaseBackend(ABC):
|
|
10
|
+
"""
|
|
11
|
+
Abstract base class for model backends.
|
|
12
|
+
|
|
13
|
+
All backend implementations must inherit from this class and
|
|
14
|
+
implement the required methods.
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
@abstractmethod
|
|
18
|
+
def generate(
|
|
19
|
+
self,
|
|
20
|
+
prompt: str,
|
|
21
|
+
temperature: float = 0.7,
|
|
22
|
+
max_new_tokens: int = 512,
|
|
23
|
+
top_p: float = 0.95,
|
|
24
|
+
top_k: int = 40,
|
|
25
|
+
repetition_penalty: float = 1.1,
|
|
26
|
+
stream: bool = False,
|
|
27
|
+
logprobs: bool = False,
|
|
28
|
+
top_logprobs: Optional[int] = None,
|
|
29
|
+
**kwargs
|
|
30
|
+
) -> Dict[str, Any]:
|
|
31
|
+
"""
|
|
32
|
+
Generate text completion for a prompt.
|
|
33
|
+
|
|
34
|
+
Args:
|
|
35
|
+
prompt: Input text to complete
|
|
36
|
+
temperature: Sampling temperature (higher = more random)
|
|
37
|
+
max_new_tokens: Maximum number of tokens to generate
|
|
38
|
+
top_p: Top-p sampling parameter
|
|
39
|
+
top_k: Top-k sampling parameter
|
|
40
|
+
repetition_penalty: Penalty for repetition
|
|
41
|
+
stream: Whether to stream the response (should be False for this method)
|
|
42
|
+
logprobs: Whether to return log probabilities
|
|
43
|
+
top_logprobs: Number of top tokens to return logprobs for
|
|
44
|
+
**kwargs: Additional backend-specific parameters
|
|
45
|
+
|
|
46
|
+
Returns:
|
|
47
|
+
Dictionary with at least a "text" key containing the generated text
|
|
48
|
+
"""
|
|
49
|
+
pass
|
|
50
|
+
|
|
51
|
+
@abstractmethod
|
|
52
|
+
def generate_stream(
|
|
53
|
+
self,
|
|
54
|
+
prompt: str,
|
|
55
|
+
temperature: float = 0.7,
|
|
56
|
+
max_new_tokens: int = 512,
|
|
57
|
+
top_p: float = 0.95,
|
|
58
|
+
top_k: int = 40,
|
|
59
|
+
repetition_penalty: float = 1.1,
|
|
60
|
+
logprobs: bool = False,
|
|
61
|
+
top_logprobs: Optional[int] = None,
|
|
62
|
+
**kwargs
|
|
63
|
+
) -> Iterator[Dict[str, Any]]:
|
|
64
|
+
"""
|
|
65
|
+
Stream text completion for a prompt.
|
|
66
|
+
|
|
67
|
+
Args:
|
|
68
|
+
prompt: Input text to complete
|
|
69
|
+
temperature: Sampling temperature (higher = more random)
|
|
70
|
+
max_new_tokens: Maximum number of tokens to generate
|
|
71
|
+
top_p: Top-p sampling parameter
|
|
72
|
+
top_k: Top-k sampling parameter
|
|
73
|
+
repetition_penalty: Penalty for repetition
|
|
74
|
+
logprobs: Whether to return log probabilities
|
|
75
|
+
top_logprobs: Number of top tokens to return logprobs for
|
|
76
|
+
**kwargs: Additional backend-specific parameters
|
|
77
|
+
|
|
78
|
+
Yields:
|
|
79
|
+
Dictionaries with at least a "text" key containing a chunk of generated text
|
|
80
|
+
"""
|
|
81
|
+
pass
|
|
82
|
+
|
|
83
|
+
@abstractmethod
|
|
84
|
+
def get_context_window(self) -> int:
|
|
85
|
+
"""
|
|
86
|
+
Get the context window size of the model.
|
|
87
|
+
|
|
88
|
+
Returns:
|
|
89
|
+
Maximum context window size in tokens
|
|
90
|
+
"""
|
|
91
|
+
pass
|
|
92
|
+
|
|
93
|
+
@abstractmethod
|
|
94
|
+
def count_tokens(self, text: str) -> int:
|
|
95
|
+
"""
|
|
96
|
+
Count the number of tokens in a text.
|
|
97
|
+
|
|
98
|
+
Args:
|
|
99
|
+
text: Input text
|
|
100
|
+
|
|
101
|
+
Returns:
|
|
102
|
+
Token count
|
|
103
|
+
"""
|
|
104
|
+
pass
|
|
105
|
+
|
|
106
|
+
def get_timestamp(self) -> int:
|
|
107
|
+
"""
|
|
108
|
+
Get current timestamp.
|
|
109
|
+
|
|
110
|
+
Returns:
|
|
111
|
+
Current timestamp in seconds
|
|
112
|
+
"""
|
|
113
|
+
return int(time.time())
|