npcsh 0.3.31__py3-none-any.whl → 0.3.32__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.
- npcsh/audio.py +540 -181
- npcsh/audio_gen.py +1 -0
- npcsh/cli.py +8 -10
- npcsh/conversation.py +14 -251
- npcsh/dataframes.py +13 -5
- npcsh/helpers.py +5 -0
- npcsh/image.py +2 -2
- npcsh/image_gen.py +38 -38
- npcsh/knowledge_graph.py +4 -4
- npcsh/llm_funcs.py +517 -349
- npcsh/npc_compiler.py +32 -23
- npcsh/npc_sysenv.py +5 -0
- npcsh/plonk.py +2 -2
- npcsh/response.py +131 -482
- npcsh/search.py +5 -1
- npcsh/serve.py +210 -203
- npcsh/shell.py +11 -25
- npcsh/shell_helpers.py +489 -99
- npcsh/stream.py +87 -554
- npcsh/video.py +5 -2
- npcsh/video_gen.py +69 -0
- npcsh-0.3.32.dist-info/METADATA +779 -0
- {npcsh-0.3.31.dist-info → npcsh-0.3.32.dist-info}/RECORD +49 -47
- npcsh-0.3.31.dist-info/METADATA +0 -1853
- {npcsh-0.3.31.data → npcsh-0.3.32.data}/data/npcsh/npc_team/bash_executer.tool +0 -0
- {npcsh-0.3.31.data → npcsh-0.3.32.data}/data/npcsh/npc_team/calculator.tool +0 -0
- {npcsh-0.3.31.data → npcsh-0.3.32.data}/data/npcsh/npc_team/celona.npc +0 -0
- {npcsh-0.3.31.data → npcsh-0.3.32.data}/data/npcsh/npc_team/code_executor.tool +0 -0
- {npcsh-0.3.31.data → npcsh-0.3.32.data}/data/npcsh/npc_team/corca.npc +0 -0
- {npcsh-0.3.31.data → npcsh-0.3.32.data}/data/npcsh/npc_team/eriane.npc +0 -0
- {npcsh-0.3.31.data → npcsh-0.3.32.data}/data/npcsh/npc_team/foreman.npc +0 -0
- {npcsh-0.3.31.data → npcsh-0.3.32.data}/data/npcsh/npc_team/generic_search.tool +0 -0
- {npcsh-0.3.31.data → npcsh-0.3.32.data}/data/npcsh/npc_team/image_generation.tool +0 -0
- {npcsh-0.3.31.data → npcsh-0.3.32.data}/data/npcsh/npc_team/lineru.npc +0 -0
- {npcsh-0.3.31.data → npcsh-0.3.32.data}/data/npcsh/npc_team/local_search.tool +0 -0
- {npcsh-0.3.31.data → npcsh-0.3.32.data}/data/npcsh/npc_team/maurawa.npc +0 -0
- {npcsh-0.3.31.data → npcsh-0.3.32.data}/data/npcsh/npc_team/npcsh.ctx +0 -0
- {npcsh-0.3.31.data → npcsh-0.3.32.data}/data/npcsh/npc_team/npcsh_executor.tool +0 -0
- {npcsh-0.3.31.data → npcsh-0.3.32.data}/data/npcsh/npc_team/raone.npc +0 -0
- {npcsh-0.3.31.data → npcsh-0.3.32.data}/data/npcsh/npc_team/screen_cap.tool +0 -0
- {npcsh-0.3.31.data → npcsh-0.3.32.data}/data/npcsh/npc_team/sibiji.npc +0 -0
- {npcsh-0.3.31.data → npcsh-0.3.32.data}/data/npcsh/npc_team/slean.npc +0 -0
- {npcsh-0.3.31.data → npcsh-0.3.32.data}/data/npcsh/npc_team/sql_executor.tool +0 -0
- {npcsh-0.3.31.data → npcsh-0.3.32.data}/data/npcsh/npc_team/test_pipeline.py +0 -0
- {npcsh-0.3.31.data → npcsh-0.3.32.data}/data/npcsh/npc_team/turnic.npc +0 -0
- {npcsh-0.3.31.data → npcsh-0.3.32.data}/data/npcsh/npc_team/welxor.npc +0 -0
- {npcsh-0.3.31.dist-info → npcsh-0.3.32.dist-info}/WHEEL +0 -0
- {npcsh-0.3.31.dist-info → npcsh-0.3.32.dist-info}/entry_points.txt +0 -0
- {npcsh-0.3.31.dist-info → npcsh-0.3.32.dist-info}/licenses/LICENSE +0 -0
- {npcsh-0.3.31.dist-info → npcsh-0.3.32.dist-info}/top_level.txt +0 -0
npcsh/response.py
CHANGED
|
@@ -1,120 +1,28 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
import json
|
|
2
|
+
import requests
|
|
3
|
+
import base64
|
|
3
4
|
import os
|
|
4
|
-
import
|
|
5
|
-
from
|
|
6
|
-
from google.generativeai import types
|
|
7
|
-
from google import genai
|
|
5
|
+
from PIL import Image
|
|
6
|
+
from typing import Any, Dict, Generator, List, Union
|
|
8
7
|
|
|
9
|
-
|
|
10
|
-
from .npc_sysenv import (
|
|
8
|
+
from pydantic import BaseModel
|
|
9
|
+
from npcsh.npc_sysenv import (
|
|
11
10
|
get_system_message,
|
|
12
11
|
compress_image,
|
|
13
12
|
available_chat_models,
|
|
14
13
|
available_reasoning_models,
|
|
15
14
|
)
|
|
16
15
|
|
|
17
|
-
import
|
|
18
|
-
import requests
|
|
19
|
-
import base64
|
|
20
|
-
from PIL import Image
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
def get_deepseek_response(
|
|
24
|
-
prompt: str,
|
|
25
|
-
model: str,
|
|
26
|
-
images: List[Dict[str, str]] = None,
|
|
27
|
-
npc: Any = None,
|
|
28
|
-
tools: list = None,
|
|
29
|
-
format: Union[str, BaseModel] = None,
|
|
30
|
-
messages: List[Dict[str, str]] = None,
|
|
31
|
-
api_key: str = None,
|
|
32
|
-
**kwargs,
|
|
33
|
-
) -> Dict[str, Any]:
|
|
34
|
-
"""
|
|
35
|
-
Function Description:
|
|
36
|
-
This function generates a response using the DeepSeek API.
|
|
37
|
-
Args:
|
|
38
|
-
prompt (str): The prompt for generating the response.
|
|
39
|
-
model (str): The model to use for generating the response.
|
|
40
|
-
Keyword Args:
|
|
41
|
-
images (List[Dict[str, str]]): The list of images.
|
|
42
|
-
npc (Any): The NPC object.
|
|
43
|
-
format (str): The format of the response.
|
|
44
|
-
messages (List[Dict[str, str]]): The list of messages.
|
|
45
|
-
Returns:
|
|
46
|
-
Any: The response generated by the DeepSeek API.
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
"""
|
|
50
|
-
if api_key is None:
|
|
51
|
-
api_key = os.getenv("DEEPSEEK_API_KEY", None)
|
|
52
|
-
client = OpenAI(api_key=api_key, base_url="https://api.deepseek.com")
|
|
53
|
-
|
|
54
|
-
# print(client)
|
|
55
|
-
|
|
56
|
-
system_message = get_system_message(npc) if npc else "You are a helpful assistant."
|
|
57
|
-
if messages is None or len(messages) == 0:
|
|
58
|
-
messages = [
|
|
59
|
-
{"role": "system", "content": system_message},
|
|
60
|
-
{"role": "user", "content": [{"type": "text", "text": prompt}]},
|
|
61
|
-
]
|
|
62
|
-
if images:
|
|
63
|
-
for image in images:
|
|
64
|
-
# print(f"Image file exists: {os.path.exists(image['file_path'])}")
|
|
65
|
-
|
|
66
|
-
with open(image["file_path"], "rb") as image_file:
|
|
67
|
-
image_data = base64.b64encode(compress_image(image_file.read())).decode(
|
|
68
|
-
"utf-8"
|
|
69
|
-
)
|
|
70
|
-
messages[-1]["content"].append(
|
|
71
|
-
{
|
|
72
|
-
"type": "image_url",
|
|
73
|
-
"image_url": {
|
|
74
|
-
"url": f"data:image/jpeg;base64,{image_data}",
|
|
75
|
-
},
|
|
76
|
-
}
|
|
77
|
-
)
|
|
78
|
-
# print(messages)
|
|
79
|
-
# print(model)
|
|
80
|
-
response_format = None if format == "json" else format
|
|
81
|
-
if response_format is None:
|
|
82
|
-
completion = client.chat.completions.create(model=model, messages=messages)
|
|
83
|
-
llm_response = completion.choices[0].message.content
|
|
84
|
-
items_to_return = {"response": llm_response}
|
|
85
|
-
|
|
86
|
-
items_to_return["messages"] = messages
|
|
87
|
-
# print(llm_response, model)
|
|
88
|
-
if format == "json":
|
|
89
|
-
try:
|
|
90
|
-
items_to_return["response"] = json.loads(llm_response)
|
|
16
|
+
from litellm import completion
|
|
91
17
|
|
|
92
|
-
|
|
93
|
-
except json.JSONDecodeError:
|
|
94
|
-
print(f"Warning: Expected JSON response, but received: {llm_response}")
|
|
95
|
-
return {"error": "Invalid JSON response"}
|
|
96
|
-
else:
|
|
97
|
-
items_to_return["messages"].append(
|
|
98
|
-
{"role": "assistant", "content": llm_response}
|
|
99
|
-
)
|
|
100
|
-
return items_to_return
|
|
18
|
+
# import litellm
|
|
101
19
|
|
|
102
|
-
|
|
103
|
-
if model in available_reasoning_models:
|
|
104
|
-
raise NotImplementedError("Reasoning models do not support JSON output.")
|
|
105
|
-
try:
|
|
106
|
-
completion = client.beta.chat.completions.parse(
|
|
107
|
-
model=model, messages=messages, response_format=response_format
|
|
108
|
-
)
|
|
109
|
-
items_to_return = {"response": completion.choices[0].message.parsed.dict()}
|
|
110
|
-
items_to_return["messages"] = messages
|
|
20
|
+
# litellm._turn_on_debug()
|
|
111
21
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
except Exception as e:
|
|
117
|
-
print("pydantic outputs not yet implemented with deepseek?")
|
|
22
|
+
try:
|
|
23
|
+
import ollama
|
|
24
|
+
except:
|
|
25
|
+
pass
|
|
118
26
|
|
|
119
27
|
|
|
120
28
|
def get_ollama_response(
|
|
@@ -197,55 +105,46 @@ def get_ollama_response(
|
|
|
197
105
|
|
|
198
106
|
return result
|
|
199
107
|
|
|
200
|
-
# except Exception as e:
|
|
201
|
-
# return {"error": f"Exception occurred: {e}"}
|
|
202
108
|
|
|
203
|
-
|
|
204
|
-
def get_openai_response(
|
|
109
|
+
def get_litellm_response(
|
|
205
110
|
prompt: str,
|
|
206
111
|
model: str,
|
|
112
|
+
provider: str = None,
|
|
207
113
|
images: List[Dict[str, str]] = None,
|
|
208
114
|
npc: Any = None,
|
|
209
115
|
tools: list = None,
|
|
210
116
|
format: Union[str, BaseModel] = None,
|
|
211
|
-
api_key: str = None,
|
|
212
117
|
messages: List[Dict[str, str]] = None,
|
|
118
|
+
api_key: str = None,
|
|
119
|
+
api_url: str = None,
|
|
120
|
+
tool_choice: Dict = None,
|
|
213
121
|
**kwargs,
|
|
214
|
-
):
|
|
122
|
+
) -> Dict[str, Any]:
|
|
215
123
|
"""
|
|
216
|
-
|
|
217
|
-
This function generates a response using the OpenAI API.
|
|
218
|
-
Args:
|
|
219
|
-
prompt (str): The prompt for generating the response.
|
|
220
|
-
model (str): The model to use for generating the response.
|
|
221
|
-
Keyword Args:
|
|
222
|
-
images (List[Dict[str, str]]): The list of images.
|
|
223
|
-
npc (Any): The NPC object.
|
|
224
|
-
format (str): The format of the response.
|
|
225
|
-
api_key (str): The API key for accessing the OpenAI API.
|
|
226
|
-
messages (List[Dict[str, str]]): The list of messages.
|
|
227
|
-
Returns:
|
|
228
|
-
Any: The response generated by the OpenAI API.
|
|
124
|
+
Improved version with consistent JSON parsing
|
|
229
125
|
"""
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
if len(api_key) == 0:
|
|
235
|
-
raise ValueError("API key not found.")
|
|
236
|
-
client = OpenAI(api_key=api_key)
|
|
237
|
-
# print(npc)
|
|
126
|
+
if provider == "ollama":
|
|
127
|
+
return get_ollama_response(
|
|
128
|
+
prompt, model, images, npc, tools, format, messages, **kwargs
|
|
129
|
+
)
|
|
238
130
|
|
|
239
131
|
system_message = get_system_message(npc) if npc else "You are a helpful assistant."
|
|
132
|
+
if format == "json":
|
|
133
|
+
prompt += """If you are a returning a json object, begin directly with the opening {.
|
|
134
|
+
If you are returning a json array, begin directly with the opening [.
|
|
135
|
+
Do not include any additional markdown formatting or leading
|
|
136
|
+
```json tags in your response. The item keys should be based on the ones provided
|
|
137
|
+
by the user. Do not invent new ones.
|
|
138
|
+
|
|
139
|
+
"""
|
|
240
140
|
if messages is None or len(messages) == 0:
|
|
241
141
|
messages = [
|
|
242
142
|
{"role": "system", "content": system_message},
|
|
243
143
|
{"role": "user", "content": [{"type": "text", "text": prompt}]},
|
|
244
144
|
]
|
|
145
|
+
|
|
245
146
|
if images:
|
|
246
147
|
for image in images:
|
|
247
|
-
# print(f"Image file exists: {os.path.exists(image['file_path'])}")
|
|
248
|
-
|
|
249
148
|
with open(image["file_path"], "rb") as image_file:
|
|
250
149
|
image_data = base64.b64encode(compress_image(image_file.read())).decode(
|
|
251
150
|
"utf-8"
|
|
@@ -253,371 +152,121 @@ def get_openai_response(
|
|
|
253
152
|
messages[-1]["content"].append(
|
|
254
153
|
{
|
|
255
154
|
"type": "image_url",
|
|
256
|
-
"image_url": {
|
|
257
|
-
"url": f"data:image/jpeg;base64,{image_data}",
|
|
258
|
-
},
|
|
155
|
+
"image_url": {"url": f"data:image/jpeg;base64,{image_data}"},
|
|
259
156
|
}
|
|
260
157
|
)
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
#
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
)
|
|
283
|
-
llm_response = json.loads(llm_response)
|
|
284
|
-
items_to_return["response"] = llm_response
|
|
285
|
-
return items_to_return
|
|
158
|
+
api_params = {
|
|
159
|
+
"messages": messages,
|
|
160
|
+
}
|
|
161
|
+
if provider is None:
|
|
162
|
+
split = model.split("/")
|
|
163
|
+
if len(split) == 2:
|
|
164
|
+
provider = split[0]
|
|
165
|
+
# if provider == "ollama":
|
|
166
|
+
# uncomment the two lines below once litellm works better with ollama
|
|
167
|
+
# litellm works better with ollama_chat
|
|
168
|
+
# api_params["api_base"] = "http://localhost:11434"
|
|
169
|
+
# provider = "ollama_chat"
|
|
170
|
+
api_params["format"] = format
|
|
171
|
+
|
|
172
|
+
# else:
|
|
173
|
+
if api_url is not None:
|
|
174
|
+
# the default api_url is for npcsh's NPCSH_API_URL
|
|
175
|
+
# for an openai-like provider.
|
|
176
|
+
# so the proviuder should only ever be openai-like
|
|
177
|
+
if provider == "openai-like":
|
|
178
|
+
api_params["api_base"] = api_url
|
|
286
179
|
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
{"role": "assistant", "content": llm_response}
|
|
293
|
-
)
|
|
294
|
-
return items_to_return
|
|
180
|
+
if format == "json":
|
|
181
|
+
api_params["response_format"] = {"type": "json_object"}
|
|
182
|
+
elif format is not None:
|
|
183
|
+
# pydantic model
|
|
184
|
+
api_params["response_format"] = format
|
|
295
185
|
|
|
186
|
+
if "/" not in model: # litellm expects provder/model so let ppl provide like that
|
|
187
|
+
model_str = f"{provider}/{model}"
|
|
296
188
|
else:
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
Function Description:
|
|
326
|
-
This function generates a response using the Anthropic API.
|
|
327
|
-
Args:
|
|
328
|
-
prompt (str): The prompt for generating the response.
|
|
329
|
-
model (str): The model to use for generating the response.
|
|
330
|
-
Keyword Args:
|
|
331
|
-
images (List[Dict[str, str]]): The list of images.
|
|
332
|
-
npc (Any): The NPC object.
|
|
333
|
-
format (str): The format of the response.
|
|
334
|
-
api_key (str): The API key for accessing the Anthropic API.
|
|
335
|
-
messages (List[Dict[str, str]]): The list of messages.
|
|
336
|
-
Returns:
|
|
337
|
-
Any: The response generated by the Anthropic API.
|
|
338
|
-
"""
|
|
189
|
+
model_str = model
|
|
190
|
+
api_params["model"] = model_str
|
|
191
|
+
if api_key is not None:
|
|
192
|
+
api_params["api_key"] = api_key
|
|
193
|
+
# Add tools if provided
|
|
194
|
+
if tools:
|
|
195
|
+
api_params["tools"] = tools
|
|
196
|
+
# Add tool choice if specified
|
|
197
|
+
if tool_choice:
|
|
198
|
+
api_params["tool_choice"] = tool_choice
|
|
199
|
+
if kwargs:
|
|
200
|
+
for key, value in kwargs.items():
|
|
201
|
+
# minimum parameter set for anthropic to work
|
|
202
|
+
if key in [
|
|
203
|
+
"stream",
|
|
204
|
+
"stop",
|
|
205
|
+
"temperature",
|
|
206
|
+
"top_p",
|
|
207
|
+
"max_tokens",
|
|
208
|
+
"max_completion_tokens",
|
|
209
|
+
"tools",
|
|
210
|
+
"tool_choice",
|
|
211
|
+
"extra_headers",
|
|
212
|
+
"parallel_tool_calls",
|
|
213
|
+
"response_format",
|
|
214
|
+
"user",
|
|
215
|
+
]:
|
|
216
|
+
api_params[key] = value
|
|
339
217
|
|
|
340
218
|
try:
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
system_message = get_system_message(npc)
|
|
350
|
-
|
|
351
|
-
# Preprocess messages to ensure content is a list of dicts
|
|
352
|
-
for message in messages:
|
|
353
|
-
if isinstance(message["content"], str):
|
|
354
|
-
message["content"] = [{"type": "text", "text": message["content"]}]
|
|
355
|
-
# Add images if provided
|
|
356
|
-
if images:
|
|
357
|
-
for img in images:
|
|
358
|
-
with open(img["file_path"], "rb") as image_file:
|
|
359
|
-
img["data"] = base64.b64encode(image_file.read()).decode("utf-8")
|
|
360
|
-
img["media_type"] = "image/jpeg"
|
|
361
|
-
messages[-1]["content"].append(
|
|
362
|
-
{
|
|
363
|
-
"type": "image",
|
|
364
|
-
"source": {
|
|
365
|
-
"type": "base64",
|
|
366
|
-
"media_type": img["media_type"],
|
|
367
|
-
"data": img["data"],
|
|
368
|
-
},
|
|
369
|
-
}
|
|
370
|
-
)
|
|
219
|
+
# print(api_params)
|
|
220
|
+
# litellm completion appears to have some
|
|
221
|
+
# ollama issues, so will default to our
|
|
222
|
+
# custom implementation until we can revisit
|
|
223
|
+
# when its likely more better supported
|
|
224
|
+
resp = completion(
|
|
225
|
+
**api_params,
|
|
226
|
+
)
|
|
371
227
|
|
|
372
|
-
#
|
|
228
|
+
# Get the raw response content
|
|
229
|
+
llm_response = resp.choices[0].message.content
|
|
373
230
|
|
|
374
|
-
|
|
375
|
-
|
|
231
|
+
# Prepare return dict
|
|
232
|
+
items_to_return = {
|
|
233
|
+
"response": llm_response,
|
|
376
234
|
"messages": messages,
|
|
377
|
-
"
|
|
378
|
-
"stream": False,
|
|
379
|
-
"system": system_message,
|
|
235
|
+
"raw_response": resp, # Include the full response for debugging
|
|
380
236
|
}
|
|
381
237
|
|
|
382
|
-
#
|
|
383
|
-
|
|
384
|
-
api_params["tools"] = tools
|
|
385
|
-
|
|
386
|
-
# Add tool choice if specified
|
|
387
|
-
if tool_choice:
|
|
388
|
-
api_params["tool_choice"] = tool_choice
|
|
389
|
-
|
|
390
|
-
# Make the API call
|
|
391
|
-
response = client.messages.create(**api_params)
|
|
392
|
-
|
|
393
|
-
llm_response = message.content[0].text
|
|
394
|
-
items_to_return = {"response": llm_response}
|
|
395
|
-
messages.append(
|
|
396
|
-
{"role": "assistant", "content": {"type": "text", "text": llm_response}}
|
|
397
|
-
)
|
|
398
|
-
items_to_return["messages"] = messages
|
|
399
|
-
|
|
400
|
-
# Handle JSON format if requested
|
|
238
|
+
# Handle JSON format requests
|
|
239
|
+
print(format)
|
|
401
240
|
if format == "json":
|
|
402
241
|
try:
|
|
403
242
|
if isinstance(llm_response, str):
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
243
|
+
print("converting the json")
|
|
244
|
+
loaded = json.loads(llm_response)
|
|
245
|
+
else:
|
|
246
|
+
loaded = llm_response # Assume it's already parsed
|
|
247
|
+
if "json" in loaded:
|
|
248
|
+
items_to_return["response"] = loaded["json"]
|
|
249
|
+
else:
|
|
250
|
+
items_to_return["response"] = loaded
|
|
251
|
+
|
|
252
|
+
except (json.JSONDecodeError, TypeError) as e:
|
|
253
|
+
print(f"JSON parsing error: {str(e)}")
|
|
254
|
+
print(f"Raw response: {llm_response}")
|
|
255
|
+
items_to_return["error"] = "Invalid JSON response"
|
|
412
256
|
return items_to_return
|
|
413
|
-
except json.JSONDecodeError:
|
|
414
|
-
print(f"Warning: Expected JSON response, but received: {llm_response}")
|
|
415
|
-
return {"response": llm_response, "error": "Invalid JSON response"}
|
|
416
|
-
else:
|
|
417
|
-
# only append to messages if the response is not json
|
|
418
|
-
messages.append({"role": "assistant", "content": llm_response})
|
|
419
|
-
# print("teststea")
|
|
420
|
-
return items_to_return
|
|
421
|
-
|
|
422
|
-
except Exception as e:
|
|
423
|
-
return f"Error interacting with Anthropic llm response: {e}"
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
def get_openai_like_response(
|
|
427
|
-
prompt: str,
|
|
428
|
-
model: str,
|
|
429
|
-
api_url: str,
|
|
430
|
-
api_key: str = None,
|
|
431
|
-
npc: Any = None,
|
|
432
|
-
tools: list = None,
|
|
433
|
-
images: list = None,
|
|
434
|
-
messages: list = None,
|
|
435
|
-
format=None,
|
|
436
|
-
**kwargs,
|
|
437
|
-
) -> Dict[str, Any]:
|
|
438
|
-
"""
|
|
439
|
-
Function Description:
|
|
440
|
-
This function generates a response using API.
|
|
441
|
-
penai-like
|
|
442
|
-
Args:
|
|
443
|
-
prompt (str): The prompt for generating the response.
|
|
444
|
-
model (str): The model to use for generating the response.
|
|
445
|
-
Keyword Args:
|
|
446
|
-
images (List[Dict[str, str]]): The list of images.
|
|
447
|
-
npc (Any): The NPC object.
|
|
448
|
-
format (str): The format of the response.
|
|
449
|
-
messages (List[Dict[str, str]]): The list of messages.
|
|
450
|
-
Returns:
|
|
451
|
-
Any: The response generated by the DeepSeek API.
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
"""
|
|
455
|
-
if api_key is None:
|
|
456
|
-
api_key = "dummy_api_key"
|
|
457
|
-
client = OpenAI(api_key=api_key, base_url=api_url)
|
|
458
|
-
system_message = get_system_message(npc) if npc else "You are a helpful assistant."
|
|
459
|
-
if messages is None or len(messages) == 0:
|
|
460
|
-
messages = [
|
|
461
|
-
{"role": "system", "content": system_message},
|
|
462
|
-
{"role": "user", "content": [{"type": "text", "text": prompt}]},
|
|
463
|
-
]
|
|
464
|
-
if images:
|
|
465
|
-
for image in images:
|
|
466
|
-
# print(f"Image file exists: {os.path.exists(image['file_path'])}")
|
|
467
|
-
|
|
468
|
-
with open(image["file_path"], "rb") as image_file:
|
|
469
|
-
image_data = base64.b64encode(compress_image(image_file.read())).decode(
|
|
470
|
-
"utf-8"
|
|
471
|
-
)
|
|
472
|
-
messages[-1]["content"].append(
|
|
473
|
-
{
|
|
474
|
-
"type": "image_url",
|
|
475
|
-
"image_url": {
|
|
476
|
-
"url": f"data:image/jpeg;base64,{image_data}",
|
|
477
|
-
},
|
|
478
|
-
}
|
|
479
|
-
)
|
|
480
|
-
|
|
481
|
-
response_format = None if format == "json" else format
|
|
482
|
-
if response_format is None:
|
|
483
|
-
completion = client.chat.completions.create(model=model, messages=messages)
|
|
484
|
-
llm_response = completion.choices[0].message.content
|
|
485
|
-
items_to_return = {"response": llm_response}
|
|
486
|
-
|
|
487
|
-
items_to_return["messages"] = messages
|
|
488
|
-
# print(llm_response, model)
|
|
489
|
-
if format == "json":
|
|
490
|
-
if model in available_reasoning_models:
|
|
491
|
-
raise NotImplementedError(
|
|
492
|
-
"Reasoning models do not support JSON output."
|
|
493
|
-
)
|
|
494
|
-
try:
|
|
495
|
-
if isinstance(llm_response, str):
|
|
496
|
-
if llm_response.startswith("```json"):
|
|
497
|
-
llm_response = (
|
|
498
|
-
llm_response.replace("```json", "")
|
|
499
|
-
.replace("```", "")
|
|
500
|
-
.strip()
|
|
501
|
-
)
|
|
502
|
-
# print(llm_response)
|
|
503
|
-
items_to_return["response"] = json.loads(llm_response)
|
|
504
|
-
return items_to_return
|
|
505
|
-
except json.JSONDecodeError:
|
|
506
|
-
print(f"Warning: Expected JSON response, but received: {llm_response}")
|
|
507
|
-
return {"error": "Invalid JSON response"}
|
|
508
|
-
else:
|
|
509
|
-
items_to_return["messages"].append(
|
|
510
|
-
{"role": "assistant", "content": llm_response}
|
|
511
|
-
)
|
|
512
|
-
return items_to_return
|
|
513
|
-
|
|
514
|
-
else:
|
|
515
|
-
if model in available_reasoning_models:
|
|
516
|
-
raise NotImplementedError("Reasoning models do not support JSON output.")
|
|
517
|
-
|
|
518
|
-
completion = client.beta.chat.completions.parse(
|
|
519
|
-
model=model, messages=messages, response_format=response_format
|
|
520
|
-
)
|
|
521
|
-
|
|
522
|
-
items_to_return = {"response": completion.choices[0].message.parsed.dict()}
|
|
523
|
-
items_to_return["messages"] = messages
|
|
524
257
|
|
|
258
|
+
# Add assistant response to message history
|
|
525
259
|
items_to_return["messages"].append(
|
|
526
|
-
{
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
prompt: str,
|
|
533
|
-
model: str,
|
|
534
|
-
images: List[Dict[str, str]] = None,
|
|
535
|
-
npc: Any = None,
|
|
536
|
-
tools: list = None,
|
|
537
|
-
format: Union[str, BaseModel] = None,
|
|
538
|
-
messages: List[Dict[str, str]] = None,
|
|
539
|
-
api_key: str = None,
|
|
540
|
-
**kwargs,
|
|
541
|
-
) -> Dict[str, Any]:
|
|
542
|
-
"""
|
|
543
|
-
Generates a response using the Gemini API.
|
|
544
|
-
"""
|
|
545
|
-
# Configure the Gemini API
|
|
546
|
-
if api_key is None:
|
|
547
|
-
genai.configure(api_key=gemini_api_key)
|
|
548
|
-
|
|
549
|
-
# Prepare the system message
|
|
550
|
-
system_message = get_system_message(npc) if npc else "You are a helpful assistant."
|
|
551
|
-
model = genai.GenerativeModel(model, system_instruction=system_message)
|
|
552
|
-
|
|
553
|
-
# Extract just the content to send to the model
|
|
554
|
-
if messages is None or len(messages) == 0:
|
|
555
|
-
content_to_send = prompt
|
|
556
|
-
else:
|
|
557
|
-
# Get the latest message's content
|
|
558
|
-
latest_message = messages[-1]
|
|
559
|
-
content_to_send = (
|
|
560
|
-
latest_message["parts"][0]
|
|
561
|
-
if "parts" in latest_message
|
|
562
|
-
else latest_message.get("content", prompt)
|
|
260
|
+
{
|
|
261
|
+
"role": "assistant",
|
|
262
|
+
"content": (
|
|
263
|
+
llm_response if isinstance(llm_response, str) else str(llm_response)
|
|
264
|
+
),
|
|
265
|
+
}
|
|
563
266
|
)
|
|
564
|
-
history = []
|
|
565
|
-
if messages:
|
|
566
|
-
for msg in messages:
|
|
567
|
-
if "content" in msg:
|
|
568
|
-
# Convert content to parts format
|
|
569
|
-
history.append({"role": msg["role"], "parts": [msg["content"]]})
|
|
570
|
-
else:
|
|
571
|
-
# Already in parts format
|
|
572
|
-
history.append(msg)
|
|
573
|
-
# If no history, create a new message list
|
|
574
|
-
if not history:
|
|
575
|
-
history = [{"role": "user", "parts": [prompt]}]
|
|
576
|
-
elif isinstance(prompt, str): # Add new prompt to existing history
|
|
577
|
-
history.append({"role": "user", "parts": [prompt]})
|
|
578
|
-
|
|
579
|
-
# Handle images if provided
|
|
580
|
-
# Handle images by adding them to the last message's parts
|
|
581
|
-
if images:
|
|
582
|
-
for image in images:
|
|
583
|
-
with open(image["file_path"], "rb") as image_file:
|
|
584
|
-
img = Image.open(image_file)
|
|
585
|
-
history[-1]["parts"].append(img)
|
|
586
|
-
# Generate the response
|
|
587
|
-
# try:
|
|
588
|
-
# Send the entire conversation history to maintain context
|
|
589
|
-
response = model.generate_content(history)
|
|
590
|
-
llm_response = response.text
|
|
591
|
-
|
|
592
|
-
# Filter out empty parts
|
|
593
|
-
if isinstance(llm_response, list):
|
|
594
|
-
llm_response = " ".join([part for part in llm_response if part.strip()])
|
|
595
|
-
elif not llm_response.strip():
|
|
596
|
-
llm_response = ""
|
|
597
267
|
|
|
598
|
-
|
|
599
|
-
items_to_return = {"response": llm_response, "messages": history}
|
|
600
|
-
# print(llm_response, type(llm_response))
|
|
601
|
-
|
|
602
|
-
# Handle JSON format if specified
|
|
603
|
-
if format == "json":
|
|
604
|
-
if isinstance(llm_response, str):
|
|
605
|
-
if llm_response.startswith("```json"):
|
|
606
|
-
llm_response = (
|
|
607
|
-
llm_response.replace("```json", "").replace("```", "").strip()
|
|
608
|
-
)
|
|
609
|
-
|
|
610
|
-
try:
|
|
611
|
-
items_to_return["response"] = json.loads(llm_response)
|
|
612
|
-
except json.JSONDecodeError:
|
|
613
|
-
print(f"Warning: Expected JSON response, but received: {llm_response}")
|
|
614
|
-
return {"error": "Invalid JSON response"}
|
|
615
|
-
else:
|
|
616
|
-
# Append the model's response to the messages
|
|
617
|
-
history.append({"role": "model", "parts": [llm_response]})
|
|
618
|
-
items_to_return["messages"] = history
|
|
619
|
-
|
|
620
|
-
return items_to_return
|
|
268
|
+
return items_to_return
|
|
621
269
|
|
|
622
|
-
|
|
623
|
-
|
|
270
|
+
except Exception as e:
|
|
271
|
+
print(f"Error in get_litellm_response: {str(e)}")
|
|
272
|
+
return {"error": str(e), "messages": messages, "response": None}
|