lollms-client 1.4.1__py3-none-any.whl → 1.7.10__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.
- lollms_client/__init__.py +1 -1
- lollms_client/llm_bindings/azure_openai/__init__.py +2 -2
- lollms_client/llm_bindings/claude/__init__.py +125 -34
- lollms_client/llm_bindings/gemini/__init__.py +261 -159
- lollms_client/llm_bindings/grok/__init__.py +52 -14
- lollms_client/llm_bindings/groq/__init__.py +2 -2
- lollms_client/llm_bindings/hugging_face_inference_api/__init__.py +2 -2
- lollms_client/llm_bindings/litellm/__init__.py +1 -1
- lollms_client/llm_bindings/llamacpp/__init__.py +18 -11
- lollms_client/llm_bindings/lollms/__init__.py +151 -32
- lollms_client/llm_bindings/lollms_webui/__init__.py +1 -1
- lollms_client/llm_bindings/mistral/__init__.py +2 -2
- lollms_client/llm_bindings/novita_ai/__init__.py +439 -0
- lollms_client/llm_bindings/ollama/__init__.py +309 -93
- lollms_client/llm_bindings/open_router/__init__.py +2 -2
- lollms_client/llm_bindings/openai/__init__.py +148 -29
- lollms_client/llm_bindings/openllm/__init__.py +362 -506
- lollms_client/llm_bindings/openwebui/__init__.py +465 -0
- lollms_client/llm_bindings/perplexity/__init__.py +326 -0
- lollms_client/llm_bindings/pythonllamacpp/__init__.py +3 -3
- lollms_client/llm_bindings/tensor_rt/__init__.py +1 -1
- lollms_client/llm_bindings/transformers/__init__.py +428 -632
- lollms_client/llm_bindings/vllm/__init__.py +1 -1
- lollms_client/lollms_agentic.py +4 -2
- lollms_client/lollms_base_binding.py +61 -0
- lollms_client/lollms_core.py +516 -1890
- lollms_client/lollms_discussion.py +55 -18
- lollms_client/lollms_llm_binding.py +112 -261
- lollms_client/lollms_mcp_binding.py +34 -75
- lollms_client/lollms_personality.py +5 -2
- lollms_client/lollms_stt_binding.py +85 -52
- lollms_client/lollms_tti_binding.py +23 -37
- lollms_client/lollms_ttm_binding.py +24 -42
- lollms_client/lollms_tts_binding.py +28 -17
- lollms_client/lollms_ttv_binding.py +24 -42
- lollms_client/lollms_types.py +4 -2
- lollms_client/stt_bindings/whisper/__init__.py +108 -23
- lollms_client/stt_bindings/whispercpp/__init__.py +7 -1
- lollms_client/tti_bindings/diffusers/__init__.py +418 -810
- lollms_client/tti_bindings/diffusers/server/main.py +1051 -0
- lollms_client/tti_bindings/gemini/__init__.py +182 -239
- lollms_client/tti_bindings/leonardo_ai/__init__.py +127 -0
- lollms_client/tti_bindings/lollms/__init__.py +4 -1
- lollms_client/tti_bindings/novita_ai/__init__.py +105 -0
- lollms_client/tti_bindings/openai/__init__.py +10 -11
- lollms_client/tti_bindings/stability_ai/__init__.py +178 -0
- lollms_client/ttm_bindings/audiocraft/__init__.py +7 -12
- lollms_client/ttm_bindings/beatoven_ai/__init__.py +129 -0
- lollms_client/ttm_bindings/lollms/__init__.py +4 -17
- lollms_client/ttm_bindings/replicate/__init__.py +115 -0
- lollms_client/ttm_bindings/stability_ai/__init__.py +117 -0
- lollms_client/ttm_bindings/topmediai/__init__.py +96 -0
- lollms_client/tts_bindings/bark/__init__.py +7 -10
- lollms_client/tts_bindings/lollms/__init__.py +6 -1
- lollms_client/tts_bindings/piper_tts/__init__.py +8 -11
- lollms_client/tts_bindings/xtts/__init__.py +157 -74
- lollms_client/tts_bindings/xtts/server/main.py +241 -280
- {lollms_client-1.4.1.dist-info → lollms_client-1.7.10.dist-info}/METADATA +316 -6
- lollms_client-1.7.10.dist-info/RECORD +89 -0
- lollms_client/ttm_bindings/bark/__init__.py +0 -339
- lollms_client-1.4.1.dist-info/RECORD +0 -78
- {lollms_client-1.4.1.dist-info → lollms_client-1.7.10.dist-info}/WHEEL +0 -0
- {lollms_client-1.4.1.dist-info → lollms_client-1.7.10.dist-info}/licenses/LICENSE +0 -0
- {lollms_client-1.4.1.dist-info → lollms_client-1.7.10.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,326 @@
|
|
|
1
|
+
import base64
|
|
2
|
+
import os
|
|
3
|
+
from io import BytesIO
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from typing import Optional, Callable, List, Union, Dict
|
|
6
|
+
import json
|
|
7
|
+
import requests
|
|
8
|
+
|
|
9
|
+
from lollms_client.lollms_discussion import LollmsDiscussion, LollmsMessage
|
|
10
|
+
from lollms_client.lollms_llm_binding import LollmsLLMBinding
|
|
11
|
+
from lollms_client.lollms_types import MSG_TYPE
|
|
12
|
+
from ascii_colors import ASCIIColors, trace_exception
|
|
13
|
+
|
|
14
|
+
import pipmaster as pm
|
|
15
|
+
|
|
16
|
+
# Ensure the required packages are installed
|
|
17
|
+
pm.ensure_packages(["requests", "tiktoken"])
|
|
18
|
+
|
|
19
|
+
import tiktoken
|
|
20
|
+
|
|
21
|
+
BindingName = "PerplexityBinding"
|
|
22
|
+
API_BASE_URL = "https://api.perplexity.ai"
|
|
23
|
+
|
|
24
|
+
# A hardcoded list of models based on Perplexity's documentation
|
|
25
|
+
# The API does not provide a models listing endpoint.
|
|
26
|
+
# Sourced from: https://docs.perplexity.ai/docs/models
|
|
27
|
+
_FALLBACK_MODELS = [
|
|
28
|
+
# Sonar Models
|
|
29
|
+
{'model_name': 'llama-3.1-sonar-small-128k-chat', 'display_name': 'Llama 3.1 Sonar Small Chat (128k)', 'description': 'Fast and cost-effective conversational model.', 'owned_by': 'Perplexity'},
|
|
30
|
+
{'model_name': 'llama-3.1-sonar-small-128k-online', 'display_name': 'Llama 3.1 Sonar Small Online (128k)', 'description': 'Fast and cost-effective conversational model with web access.', 'owned_by': 'Perplexity'},
|
|
31
|
+
{'model_name': 'llama-3.1-sonar-large-128k-chat', 'display_name': 'Llama 3.1 Sonar Large Chat (128k)', 'description': 'State-of-the-art conversational model.', 'owned_by': 'Perplexity'},
|
|
32
|
+
{'model_name': 'llama-3.1-sonar-large-128k-online', 'display_name': 'Llama 3.1 Sonar Large Online (128k)', 'description': 'State-of-the-art conversational model with web access.', 'owned_by': 'Perplexity'},
|
|
33
|
+
# Llama 3 Instruct Models
|
|
34
|
+
{'model_name': 'llama-3-8b-instruct', 'display_name': 'Llama 3 8B Instruct', 'description': 'Meta\'s Llama 3 8B instruction-tuned model.', 'owned_by': 'Meta'},
|
|
35
|
+
{'model_name': 'llama-3-70b-instruct', 'display_name': 'Llama 3 70B Instruct', 'description': 'Meta\'s Llama 3 70B instruction-tuned model.', 'owned_by': 'Meta'},
|
|
36
|
+
# Mixtral Model
|
|
37
|
+
{'model_name': 'mixtral-8x7b-instruct', 'display_name': 'Mixtral 8x7B Instruct', 'description': 'Mistral AI\'s Mixtral 8x7B instruction-tuned model.', 'owned_by': 'Mistral AI'},
|
|
38
|
+
# Legacy Sonar Models
|
|
39
|
+
{'model_name': 'sonar-small-32k-chat', 'display_name': 'Sonar Small Chat (32k)', 'description': 'Legacy small conversational model.', 'owned_by': 'Perplexity'},
|
|
40
|
+
{'model_name': 'sonar-small-32k-online', 'display_name': 'Sonar Small Online (32k)', 'description': 'Legacy small conversational model with web access.', 'owned_by': 'Perplexity'},
|
|
41
|
+
{'model_name': 'sonar-medium-32k-chat', 'display_name': 'Sonar Medium Chat (32k)', 'description': 'Legacy medium conversational model.', 'owned_by': 'Perplexity'},
|
|
42
|
+
{'model_name': 'sonar-medium-32k-online', 'display_name': 'Sonar Medium Online (32k)', 'description': 'Legacy medium conversational model with web access.', 'owned_by': 'Perplexity'},
|
|
43
|
+
]
|
|
44
|
+
|
|
45
|
+
class PerplexityBinding(LollmsLLMBinding):
|
|
46
|
+
"""Perplexity AI-specific binding implementation."""
|
|
47
|
+
|
|
48
|
+
def __init__(self, **kwargs):
|
|
49
|
+
"""
|
|
50
|
+
Initialize the Perplexity binding.
|
|
51
|
+
|
|
52
|
+
Args:
|
|
53
|
+
model_name (str): Name of the Perplexity model to use.
|
|
54
|
+
service_key (str): Perplexity API key.
|
|
55
|
+
"""
|
|
56
|
+
super().__init__(BindingName, **kwargs)
|
|
57
|
+
self.model_name = kwargs.get("model_name")
|
|
58
|
+
self.service_key = kwargs.get("service_key")
|
|
59
|
+
|
|
60
|
+
if not self.service_key:
|
|
61
|
+
self.service_key = os.getenv("PERPLEXITY_API_KEY")
|
|
62
|
+
|
|
63
|
+
if not self.service_key:
|
|
64
|
+
raise ValueError("Perplexity API key is required. Please set it via the 'service_key' parameter or the PERPLEXITY_API_KEY environment variable.")
|
|
65
|
+
|
|
66
|
+
self.headers = {
|
|
67
|
+
"Authorization": f"Bearer {self.service_key}",
|
|
68
|
+
"Content-Type": "application/json",
|
|
69
|
+
"Accept": "application/json"
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
def _construct_parameters(self,
|
|
73
|
+
temperature: float,
|
|
74
|
+
top_p: float,
|
|
75
|
+
top_k: int,
|
|
76
|
+
n_predict: int,
|
|
77
|
+
presence_penalty: float,
|
|
78
|
+
frequency_penalty: float) -> Dict[str, any]:
|
|
79
|
+
"""Builds a parameters dictionary for the Perplexity API."""
|
|
80
|
+
params = {}
|
|
81
|
+
if temperature is not None: params['temperature'] = float(temperature)
|
|
82
|
+
if top_p is not None: params['top_p'] = top_p
|
|
83
|
+
if top_k is not None: params['top_k'] = top_k
|
|
84
|
+
if n_predict is not None: params['max_tokens'] = n_predict
|
|
85
|
+
if presence_penalty is not None: params['presence_penalty'] = presence_penalty
|
|
86
|
+
if frequency_penalty is not None: params['frequency_penalty'] = frequency_penalty
|
|
87
|
+
return params
|
|
88
|
+
|
|
89
|
+
def chat(self,
|
|
90
|
+
discussion: LollmsDiscussion,
|
|
91
|
+
branch_tip_id: Optional[str] = None,
|
|
92
|
+
n_predict: Optional[int] = 2048,
|
|
93
|
+
stream: Optional[bool] = False,
|
|
94
|
+
temperature: float = 0.7,
|
|
95
|
+
top_k: int = 50,
|
|
96
|
+
top_p: float = 0.9,
|
|
97
|
+
repeat_penalty: float = 1.1, # maps to frequency_penalty
|
|
98
|
+
presence_penalty: Optional[float] = 0.0,
|
|
99
|
+
seed: Optional[int] = None, # Not supported
|
|
100
|
+
n_threads: Optional[int] = None, # Not applicable
|
|
101
|
+
ctx_size: Optional[int] = None, # Determined by model
|
|
102
|
+
streaming_callback: Optional[Callable[[str, MSG_TYPE], None]] = None
|
|
103
|
+
) -> Union[str, dict]:
|
|
104
|
+
"""
|
|
105
|
+
Conduct a chat session with the Perplexity model using a LollmsDiscussion object.
|
|
106
|
+
"""
|
|
107
|
+
system_prompt = discussion.system_prompt
|
|
108
|
+
messages = discussion.get_messages(branch_tip_id)
|
|
109
|
+
|
|
110
|
+
history = []
|
|
111
|
+
if system_prompt and system_prompt.strip():
|
|
112
|
+
history.append({"role": "system", "content": system_prompt})
|
|
113
|
+
|
|
114
|
+
for msg in messages:
|
|
115
|
+
if msg.sender_type == "user":
|
|
116
|
+
role = "user"
|
|
117
|
+
else:
|
|
118
|
+
role = "assistant"
|
|
119
|
+
|
|
120
|
+
if msg.images:
|
|
121
|
+
ASCIIColors.warning("Perplexity API does not support images. They will be ignored.")
|
|
122
|
+
|
|
123
|
+
if msg.content and msg.content.strip():
|
|
124
|
+
history.append({"role": role, "content": msg.content})
|
|
125
|
+
|
|
126
|
+
if not history:
|
|
127
|
+
return {"status": "error", "message": "Cannot start chat with an empty discussion."}
|
|
128
|
+
|
|
129
|
+
api_params = self._construct_parameters(
|
|
130
|
+
temperature, top_p, top_k, n_predict, presence_penalty, repeat_penalty
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
payload = {
|
|
134
|
+
"model": self.model_name,
|
|
135
|
+
"messages": history,
|
|
136
|
+
"stream": stream,
|
|
137
|
+
**api_params
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
url = f"{API_BASE_URL}/chat/completions"
|
|
141
|
+
full_response_text = ""
|
|
142
|
+
|
|
143
|
+
try:
|
|
144
|
+
if stream:
|
|
145
|
+
with requests.post(url, headers=self.headers, json=payload, stream=True) as response:
|
|
146
|
+
response.raise_for_status()
|
|
147
|
+
for line in response.iter_lines():
|
|
148
|
+
if line:
|
|
149
|
+
decoded_line = line.decode('utf-8')
|
|
150
|
+
if decoded_line.startswith("data:"):
|
|
151
|
+
content = decoded_line[len("data: "):].strip()
|
|
152
|
+
if content == "[DONE]":
|
|
153
|
+
break
|
|
154
|
+
try:
|
|
155
|
+
chunk = json.loads(content)
|
|
156
|
+
delta = chunk.get("choices", [{}])[0].get("delta", {})
|
|
157
|
+
text_chunk = delta.get("content", "")
|
|
158
|
+
if text_chunk:
|
|
159
|
+
full_response_text += text_chunk
|
|
160
|
+
if streaming_callback:
|
|
161
|
+
if not streaming_callback(text_chunk, MSG_TYPE.MSG_TYPE_CHUNK):
|
|
162
|
+
break
|
|
163
|
+
except json.JSONDecodeError:
|
|
164
|
+
ASCIIColors.error(f"Failed to decode JSON chunk: {content}")
|
|
165
|
+
continue
|
|
166
|
+
return full_response_text
|
|
167
|
+
else:
|
|
168
|
+
response = requests.post(url, headers=self.headers, json=payload)
|
|
169
|
+
response.raise_for_status()
|
|
170
|
+
data = response.json()
|
|
171
|
+
return data["choices"][0]["message"]["content"]
|
|
172
|
+
except requests.exceptions.RequestException as e:
|
|
173
|
+
error_message = f"An error occurred with the Perplexity API: {e}"
|
|
174
|
+
trace_exception(e)
|
|
175
|
+
return {"status": "error", "message": str(e)}
|
|
176
|
+
except Exception as ex:
|
|
177
|
+
error_message = f"An unexpected error occurred: {str(ex)}"
|
|
178
|
+
trace_exception(ex)
|
|
179
|
+
return {"status": "error", "message": error_message}
|
|
180
|
+
|
|
181
|
+
def tokenize(self, text: str) -> list:
|
|
182
|
+
"""
|
|
183
|
+
Tokenize the input text. Perplexity uses the same tokenizer as GPT-4.
|
|
184
|
+
"""
|
|
185
|
+
try:
|
|
186
|
+
encoding = tiktoken.get_encoding("cl100k_base")
|
|
187
|
+
return encoding.encode(text)
|
|
188
|
+
except Exception as e:
|
|
189
|
+
ASCIIColors.error(f"Could not use tiktoken, falling back to simple encoding: {e}")
|
|
190
|
+
return list(text.encode('utf-8'))
|
|
191
|
+
|
|
192
|
+
def detokenize(self, tokens: list) -> str:
|
|
193
|
+
"""
|
|
194
|
+
Detokenize a list of tokens.
|
|
195
|
+
"""
|
|
196
|
+
try:
|
|
197
|
+
encoding = tiktoken.get_encoding("cl100k_base")
|
|
198
|
+
return encoding.decode(tokens)
|
|
199
|
+
except Exception as e:
|
|
200
|
+
ASCIIColors.error(f"Could not use tiktoken, falling back to simple decoding: {e}")
|
|
201
|
+
return bytes(tokens).decode('utf-8', errors='ignore')
|
|
202
|
+
|
|
203
|
+
def count_tokens(self, text: str) -> int:
|
|
204
|
+
"""
|
|
205
|
+
Count tokens from a text.
|
|
206
|
+
"""
|
|
207
|
+
return len(self.tokenize(text))
|
|
208
|
+
|
|
209
|
+
def embed(self, text: str, **kwargs) -> List[float]:
|
|
210
|
+
"""
|
|
211
|
+
Get embeddings for the input text.
|
|
212
|
+
"""
|
|
213
|
+
ASCIIColors.warning("Perplexity does not offer a public embedding API. This method is not implemented.")
|
|
214
|
+
raise NotImplementedError("Perplexity binding does not support embeddings.")
|
|
215
|
+
|
|
216
|
+
def get_model_info(self) -> dict:
|
|
217
|
+
"""Return information about the current model setup."""
|
|
218
|
+
return {
|
|
219
|
+
"name": self.binding_name,
|
|
220
|
+
"version": "1.0",
|
|
221
|
+
"host_address": API_BASE_URL,
|
|
222
|
+
"model_name": self.model_name,
|
|
223
|
+
"supports_vision": False,
|
|
224
|
+
"supports_structured_output": False
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
def list_models(self) -> List[Dict[str, str]]:
|
|
228
|
+
"""
|
|
229
|
+
Lists available models. Perplexity API does not have a models endpoint,
|
|
230
|
+
so a hardcoded list is returned.
|
|
231
|
+
"""
|
|
232
|
+
return sorted(_FALLBACK_MODELS, key=lambda x: x['display_name'])
|
|
233
|
+
|
|
234
|
+
def load_model(self, model_name: str) -> bool:
|
|
235
|
+
"""Set the model name for subsequent operations."""
|
|
236
|
+
self.model_name = model_name
|
|
237
|
+
ASCIIColors.info(f"Perplexity model set to: {model_name}.")
|
|
238
|
+
return True
|
|
239
|
+
|
|
240
|
+
if __name__ == '__main__':
|
|
241
|
+
if 'PERPLEXITY_API_KEY' not in os.environ:
|
|
242
|
+
ASCIIColors.red("Error: PERPLEXITY_API_KEY environment variable not set.")
|
|
243
|
+
print("Please get your key from Perplexity AI and set it.")
|
|
244
|
+
exit(1)
|
|
245
|
+
|
|
246
|
+
ASCIIColors.yellow("--- Testing PerplexityBinding ---")
|
|
247
|
+
|
|
248
|
+
test_model_name = "llama-3.1-sonar-small-128k-online"
|
|
249
|
+
|
|
250
|
+
try:
|
|
251
|
+
# --- Initialization ---
|
|
252
|
+
ASCIIColors.cyan("\n--- Initializing Binding ---")
|
|
253
|
+
binding = PerplexityBinding(model_name=test_model_name)
|
|
254
|
+
ASCIIColors.green("Binding initialized successfully.")
|
|
255
|
+
|
|
256
|
+
# --- List Models ---
|
|
257
|
+
ASCIIColors.cyan("\n--- Listing Models (static list) ---")
|
|
258
|
+
models = binding.list_models()
|
|
259
|
+
if models:
|
|
260
|
+
ASCIIColors.green(f"Found {len(models)} models.")
|
|
261
|
+
for m in models:
|
|
262
|
+
print(f"- {m['model_name']} ({m['display_name']})")
|
|
263
|
+
else:
|
|
264
|
+
ASCIIColors.error("Failed to list models.")
|
|
265
|
+
|
|
266
|
+
# --- Count Tokens ---
|
|
267
|
+
ASCIIColors.cyan("\n--- Counting Tokens ---")
|
|
268
|
+
sample_text = "Hello, world! This is a test."
|
|
269
|
+
token_count = binding.count_tokens(sample_text)
|
|
270
|
+
ASCIIColors.green(f"Token count for '{sample_text}': {token_count}")
|
|
271
|
+
|
|
272
|
+
# --- Chat (Non-Streaming) ---
|
|
273
|
+
ASCIIColors.cyan("\n--- Chat (Non-Streaming) ---")
|
|
274
|
+
discussion_non_stream = LollmsDiscussion.from_messages(
|
|
275
|
+
messages=[
|
|
276
|
+
{"sender":"user", "content": "What is the capital of France?"}
|
|
277
|
+
],
|
|
278
|
+
system_prompt="You are a helpful and concise assistant."
|
|
279
|
+
)
|
|
280
|
+
ASCIIColors.info(f"Prompt: What is the capital of France?")
|
|
281
|
+
generated_text = binding.chat(discussion_non_stream, n_predict=50, stream=False)
|
|
282
|
+
if isinstance(generated_text, str):
|
|
283
|
+
ASCIIColors.green(f"Generated text:\n{generated_text}")
|
|
284
|
+
else:
|
|
285
|
+
ASCIIColors.error(f"Generation failed: {generated_text}")
|
|
286
|
+
|
|
287
|
+
# --- Chat (Streaming) ---
|
|
288
|
+
ASCIIColors.cyan("\n--- Chat (Streaming) ---")
|
|
289
|
+
|
|
290
|
+
captured_chunks = []
|
|
291
|
+
def stream_callback(chunk: str, msg_type: int):
|
|
292
|
+
ASCIIColors.green(chunk, end="", flush=True)
|
|
293
|
+
captured_chunks.append(chunk)
|
|
294
|
+
return True
|
|
295
|
+
|
|
296
|
+
discussion_stream = LollmsDiscussion.from_messages(
|
|
297
|
+
messages=[
|
|
298
|
+
{"sender":"user", "content": "Explain the importance of bees in one short paragraph."}
|
|
299
|
+
],
|
|
300
|
+
system_prompt="You are a helpful assistant."
|
|
301
|
+
)
|
|
302
|
+
ASCIIColors.info(f"Prompt: Explain the importance of bees in one short paragraph.")
|
|
303
|
+
result = binding.chat(
|
|
304
|
+
discussion_stream,
|
|
305
|
+
n_predict=150,
|
|
306
|
+
stream=True,
|
|
307
|
+
streaming_callback=stream_callback
|
|
308
|
+
)
|
|
309
|
+
print("\n--- End of Stream ---")
|
|
310
|
+
full_streamed_text = "".join(captured_chunks)
|
|
311
|
+
assert result == full_streamed_text
|
|
312
|
+
|
|
313
|
+
# --- Embeddings (Expected to fail) ---
|
|
314
|
+
ASCIIColors.cyan("\n--- Embeddings ---")
|
|
315
|
+
try:
|
|
316
|
+
binding.embed("This should not work.")
|
|
317
|
+
except NotImplementedError as e:
|
|
318
|
+
ASCIIColors.green(f"Successfully caught expected error for embeddings: {e}")
|
|
319
|
+
except Exception as e:
|
|
320
|
+
ASCIIColors.error(f"Caught an unexpected error for embeddings: {e}")
|
|
321
|
+
|
|
322
|
+
except Exception as e:
|
|
323
|
+
ASCIIColors.error(f"An error occurred during testing: {e}")
|
|
324
|
+
trace_exception(e)
|
|
325
|
+
|
|
326
|
+
ASCIIColors.yellow("\nPerplexityBinding test finished.")
|
|
@@ -422,7 +422,7 @@ class PythonLlamaCppBinding(LollmsLLMBinding):
|
|
|
422
422
|
"config": self.llama_config
|
|
423
423
|
}
|
|
424
424
|
|
|
425
|
-
def
|
|
425
|
+
def list_models(self, force_rescan: bool = False) -> List[Dict[str, str]]: # type: ignore
|
|
426
426
|
"""
|
|
427
427
|
Lists available GGUF models.
|
|
428
428
|
|
|
@@ -528,10 +528,10 @@ if __name__ == '__main__':
|
|
|
528
528
|
|
|
529
529
|
# --- List Models ---
|
|
530
530
|
ASCIIColors.cyan("\n--- Listing Models (force_rescan=True) ---")
|
|
531
|
-
model_list = active_binding.
|
|
531
|
+
model_list = active_binding.list_models(force_rescan=True)
|
|
532
532
|
print(json.dumps(model_list, indent=2))
|
|
533
533
|
assert len(model_list) == 2, "Model discovery failed to find all dummy models."
|
|
534
|
-
assert any(m['loaded'] for m in model_list), "
|
|
534
|
+
assert any(m['loaded'] for m in model_list), "list_models did not correctly report a loaded model."
|
|
535
535
|
|
|
536
536
|
|
|
537
537
|
if is_dummy_model:
|
|
@@ -442,7 +442,7 @@ class VLLMBinding(LollmsLLMBinding):
|
|
|
442
442
|
}
|
|
443
443
|
return info
|
|
444
444
|
|
|
445
|
-
def
|
|
445
|
+
def list_models(self) -> List[Dict[str, Any]]:
|
|
446
446
|
local_models = []
|
|
447
447
|
if not self.models_folder.exists(): return []
|
|
448
448
|
for item_path in self.models_folder.rglob('*'):
|