npcpy 1.2.28__tar.gz → 1.2.29__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.
Files changed (72) hide show
  1. {npcpy-1.2.28/npcpy.egg-info → npcpy-1.2.29}/PKG-INFO +1 -1
  2. {npcpy-1.2.28 → npcpy-1.2.29}/npcpy/ft/sft.py +25 -7
  3. {npcpy-1.2.28 → npcpy-1.2.29}/npcpy/gen/image_gen.py +37 -15
  4. {npcpy-1.2.28 → npcpy-1.2.29}/npcpy/npc_compiler.py +37 -40
  5. {npcpy-1.2.28 → npcpy-1.2.29/npcpy.egg-info}/PKG-INFO +1 -1
  6. {npcpy-1.2.28 → npcpy-1.2.29}/setup.py +1 -1
  7. {npcpy-1.2.28 → npcpy-1.2.29}/LICENSE +0 -0
  8. {npcpy-1.2.28 → npcpy-1.2.29}/MANIFEST.in +0 -0
  9. {npcpy-1.2.28 → npcpy-1.2.29}/README.md +0 -0
  10. {npcpy-1.2.28 → npcpy-1.2.29}/npcpy/__init__.py +0 -0
  11. {npcpy-1.2.28 → npcpy-1.2.29}/npcpy/data/__init__.py +0 -0
  12. {npcpy-1.2.28 → npcpy-1.2.29}/npcpy/data/audio.py +0 -0
  13. {npcpy-1.2.28 → npcpy-1.2.29}/npcpy/data/data_models.py +0 -0
  14. {npcpy-1.2.28 → npcpy-1.2.29}/npcpy/data/image.py +0 -0
  15. {npcpy-1.2.28 → npcpy-1.2.29}/npcpy/data/load.py +0 -0
  16. {npcpy-1.2.28 → npcpy-1.2.29}/npcpy/data/text.py +0 -0
  17. {npcpy-1.2.28 → npcpy-1.2.29}/npcpy/data/video.py +0 -0
  18. {npcpy-1.2.28 → npcpy-1.2.29}/npcpy/data/web.py +0 -0
  19. {npcpy-1.2.28 → npcpy-1.2.29}/npcpy/ft/__init__.py +0 -0
  20. {npcpy-1.2.28 → npcpy-1.2.29}/npcpy/ft/diff.py +0 -0
  21. {npcpy-1.2.28 → npcpy-1.2.29}/npcpy/ft/ge.py +0 -0
  22. {npcpy-1.2.28 → npcpy-1.2.29}/npcpy/ft/memory_trainer.py +0 -0
  23. {npcpy-1.2.28 → npcpy-1.2.29}/npcpy/ft/model_ensembler.py +0 -0
  24. {npcpy-1.2.28 → npcpy-1.2.29}/npcpy/ft/rl.py +0 -0
  25. {npcpy-1.2.28 → npcpy-1.2.29}/npcpy/ft/usft.py +0 -0
  26. {npcpy-1.2.28 → npcpy-1.2.29}/npcpy/gen/__init__.py +0 -0
  27. {npcpy-1.2.28 → npcpy-1.2.29}/npcpy/gen/audio_gen.py +0 -0
  28. {npcpy-1.2.28 → npcpy-1.2.29}/npcpy/gen/embeddings.py +0 -0
  29. {npcpy-1.2.28 → npcpy-1.2.29}/npcpy/gen/response.py +0 -0
  30. {npcpy-1.2.28 → npcpy-1.2.29}/npcpy/gen/video_gen.py +0 -0
  31. {npcpy-1.2.28 → npcpy-1.2.29}/npcpy/llm_funcs.py +0 -0
  32. {npcpy-1.2.28 → npcpy-1.2.29}/npcpy/main.py +0 -0
  33. {npcpy-1.2.28 → npcpy-1.2.29}/npcpy/memory/__init__.py +0 -0
  34. {npcpy-1.2.28 → npcpy-1.2.29}/npcpy/memory/command_history.py +0 -0
  35. {npcpy-1.2.28 → npcpy-1.2.29}/npcpy/memory/kg_vis.py +0 -0
  36. {npcpy-1.2.28 → npcpy-1.2.29}/npcpy/memory/knowledge_graph.py +0 -0
  37. {npcpy-1.2.28 → npcpy-1.2.29}/npcpy/memory/memory_processor.py +0 -0
  38. {npcpy-1.2.28 → npcpy-1.2.29}/npcpy/memory/search.py +0 -0
  39. {npcpy-1.2.28 → npcpy-1.2.29}/npcpy/mix/__init__.py +0 -0
  40. {npcpy-1.2.28 → npcpy-1.2.29}/npcpy/mix/debate.py +0 -0
  41. {npcpy-1.2.28 → npcpy-1.2.29}/npcpy/npc_sysenv.py +0 -0
  42. {npcpy-1.2.28 → npcpy-1.2.29}/npcpy/npcs.py +0 -0
  43. {npcpy-1.2.28 → npcpy-1.2.29}/npcpy/serve.py +0 -0
  44. {npcpy-1.2.28 → npcpy-1.2.29}/npcpy/sql/__init__.py +0 -0
  45. {npcpy-1.2.28 → npcpy-1.2.29}/npcpy/sql/ai_function_tools.py +0 -0
  46. {npcpy-1.2.28 → npcpy-1.2.29}/npcpy/sql/database_ai_adapters.py +0 -0
  47. {npcpy-1.2.28 → npcpy-1.2.29}/npcpy/sql/database_ai_functions.py +0 -0
  48. {npcpy-1.2.28 → npcpy-1.2.29}/npcpy/sql/model_runner.py +0 -0
  49. {npcpy-1.2.28 → npcpy-1.2.29}/npcpy/sql/npcsql.py +0 -0
  50. {npcpy-1.2.28 → npcpy-1.2.29}/npcpy/sql/sql_model_compiler.py +0 -0
  51. {npcpy-1.2.28 → npcpy-1.2.29}/npcpy/tools.py +0 -0
  52. {npcpy-1.2.28 → npcpy-1.2.29}/npcpy/work/__init__.py +0 -0
  53. {npcpy-1.2.28 → npcpy-1.2.29}/npcpy/work/desktop.py +0 -0
  54. {npcpy-1.2.28 → npcpy-1.2.29}/npcpy/work/plan.py +0 -0
  55. {npcpy-1.2.28 → npcpy-1.2.29}/npcpy/work/trigger.py +0 -0
  56. {npcpy-1.2.28 → npcpy-1.2.29}/npcpy.egg-info/SOURCES.txt +0 -0
  57. {npcpy-1.2.28 → npcpy-1.2.29}/npcpy.egg-info/dependency_links.txt +0 -0
  58. {npcpy-1.2.28 → npcpy-1.2.29}/npcpy.egg-info/requires.txt +0 -0
  59. {npcpy-1.2.28 → npcpy-1.2.29}/npcpy.egg-info/top_level.txt +0 -0
  60. {npcpy-1.2.28 → npcpy-1.2.29}/setup.cfg +0 -0
  61. {npcpy-1.2.28 → npcpy-1.2.29}/tests/test_audio.py +0 -0
  62. {npcpy-1.2.28 → npcpy-1.2.29}/tests/test_command_history.py +0 -0
  63. {npcpy-1.2.28 → npcpy-1.2.29}/tests/test_image.py +0 -0
  64. {npcpy-1.2.28 → npcpy-1.2.29}/tests/test_llm_funcs.py +0 -0
  65. {npcpy-1.2.28 → npcpy-1.2.29}/tests/test_load.py +0 -0
  66. {npcpy-1.2.28 → npcpy-1.2.29}/tests/test_npc_compiler.py +0 -0
  67. {npcpy-1.2.28 → npcpy-1.2.29}/tests/test_npcsql.py +0 -0
  68. {npcpy-1.2.28 → npcpy-1.2.29}/tests/test_response.py +0 -0
  69. {npcpy-1.2.28 → npcpy-1.2.29}/tests/test_serve.py +0 -0
  70. {npcpy-1.2.28 → npcpy-1.2.29}/tests/test_text.py +0 -0
  71. {npcpy-1.2.28 → npcpy-1.2.29}/tests/test_tools.py +0 -0
  72. {npcpy-1.2.28 → npcpy-1.2.29}/tests/test_web.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: npcpy
3
- Version: 1.2.28
3
+ Version: 1.2.29
4
4
  Summary: npcpy is the premier open-source library for integrating LLMs and Agents into python systems.
5
5
  Home-page: https://github.com/NPC-Worldwide/npcpy
6
6
  Author: Christopher Agostino
@@ -154,13 +154,17 @@ def run_sft(
154
154
  save_steps=config.save_steps,
155
155
  weight_decay=config.weight_decay,
156
156
  )
157
-
157
+
158
+ def formatting_func(example):
159
+ return example["text"]
160
+
158
161
  trainer = SFTTrainer(
159
162
  model=model,
160
163
  train_dataset=dataset,
161
164
  peft_config=peft_config,
162
165
  args=training_args,
163
- max_seq_length=config.max_length
166
+ processing_class=tokenizer,
167
+ formatting_func=formatting_func
164
168
  )
165
169
 
166
170
  print(f"Training on {len(dataset)} examples")
@@ -190,8 +194,6 @@ def load_sft_model(model_path: str):
190
194
  tokenizer.pad_token = tokenizer.eos_token
191
195
 
192
196
  return model, tokenizer
193
-
194
-
195
197
  def predict_sft(
196
198
  model,
197
199
  tokenizer,
@@ -202,8 +204,13 @@ def predict_sft(
202
204
 
203
205
  device = next(model.parameters()).device
204
206
 
207
+ formatted_prompt = (
208
+ f"<start_of_turn>user\n{prompt}<end_of_turn>\n"
209
+ f"<start_of_turn>model\n"
210
+ )
211
+
205
212
  inputs = tokenizer(
206
- prompt,
213
+ formatted_prompt,
207
214
  return_tensors="pt",
208
215
  truncation=True,
209
216
  max_length=512
@@ -222,9 +229,20 @@ def predict_sft(
222
229
  pad_token_id=tokenizer.eos_token_id
223
230
  )
224
231
 
225
- response = tokenizer.decode(
232
+ full_response = tokenizer.decode(
226
233
  outputs[0],
227
- skip_special_tokens=True
234
+ skip_special_tokens=False
228
235
  )
229
236
 
237
+ if "<start_of_turn>model\n" in full_response:
238
+ response = full_response.split(
239
+ "<start_of_turn>model\n"
240
+ )[-1]
241
+ response = response.split("<end_of_turn>")[0].strip()
242
+ else:
243
+ response = tokenizer.decode(
244
+ outputs[0][len(input_ids[0]):],
245
+ skip_special_tokens=True
246
+ )
247
+
230
248
  return response
@@ -86,6 +86,16 @@ def generate_image_diffusers(
86
86
  else:
87
87
  raise e
88
88
 
89
+ import os
90
+ import base64
91
+ import io
92
+ from typing import Union, List, Optional
93
+
94
+ import PIL
95
+ from PIL import Image
96
+
97
+ import requests
98
+ from urllib.request import urlopen
89
99
 
90
100
  def openai_image_gen(
91
101
  prompt: str,
@@ -97,36 +107,47 @@ def openai_image_gen(
97
107
  ):
98
108
  """Generate or edit an image using the OpenAI API."""
99
109
  from openai import OpenAI
100
-
110
+
101
111
  client = OpenAI()
102
-
112
+
103
113
  if height is None:
104
114
  height = 1024
105
115
  if width is None:
106
- width = 1024
107
-
108
- size_str = f"{width}x{height}"
116
+ width = 1024
117
+
118
+ size_str = f"{width}x{height}"
109
119
 
110
120
  if attachments is not None:
111
121
  processed_images = []
122
+ files_to_close = []
112
123
  for attachment in attachments:
113
124
  if isinstance(attachment, str):
114
- processed_images.append(open(attachment, "rb"))
125
+ file_handle = open(attachment, "rb")
126
+ processed_images.append(file_handle)
127
+ files_to_close.append(file_handle)
115
128
  elif isinstance(attachment, bytes):
116
- processed_images.append(io.BytesIO(attachment))
129
+ img_byte_arr = io.BytesIO(attachment)
130
+ img_byte_arr.name = 'image.png' # FIX: Add filename hint
131
+ processed_images.append(img_byte_arr)
117
132
  elif isinstance(attachment, Image.Image):
118
133
  img_byte_arr = io.BytesIO()
119
134
  attachment.save(img_byte_arr, format='PNG')
120
135
  img_byte_arr.seek(0)
136
+ img_byte_arr.name = 'image.png' # FIX: Add filename hint
121
137
  processed_images.append(img_byte_arr)
122
138
 
123
- result = client.images.edit(
124
- model=model,
125
- image=processed_images[0],
126
- prompt=prompt,
127
- n=n_images,
128
- size=size_str,
129
- )
139
+ try:
140
+ result = client.images.edit(
141
+ model=model,
142
+ image=processed_images[0],
143
+ prompt=prompt,
144
+ n=n_images,
145
+ size=size_str,
146
+ )
147
+ finally:
148
+ # This ensures any files we opened are properly closed
149
+ for f in files_to_close:
150
+ f.close()
130
151
  else:
131
152
  result = client.images.generate(
132
153
  model=model,
@@ -134,7 +155,7 @@ def openai_image_gen(
134
155
  n=n_images,
135
156
  size=size_str,
136
157
  )
137
-
158
+
138
159
  collected_images = []
139
160
  for item_data in result.data:
140
161
  if model == 'gpt-image-1':
@@ -153,6 +174,7 @@ def openai_image_gen(
153
174
  return collected_images
154
175
 
155
176
 
177
+
156
178
  def gemini_image_gen(
157
179
  prompt: str,
158
180
  model: str = "gemini-2.5-flash",
@@ -264,7 +264,6 @@ class Jinx:
264
264
  self.inputs = jinx_data.get("inputs", [])
265
265
  self.description = jinx_data.get("description", "")
266
266
  self.steps = self._parse_steps(jinx_data.get("steps", []))
267
-
268
267
  def _parse_steps(self, steps):
269
268
  """Parse steps from jinx definition"""
270
269
  parsed_steps = []
@@ -275,11 +274,12 @@ class Jinx:
275
274
  "engine": step.get("engine", "natural"),
276
275
  "code": step.get("code", "")
277
276
  }
277
+ if "mode" in step:
278
+ parsed_step["mode"] = step["mode"]
278
279
  parsed_steps.append(parsed_step)
279
280
  else:
280
281
  raise ValueError(f"Invalid step format: {step}")
281
282
  return parsed_steps
282
-
283
283
  def execute(self,
284
284
  input_values,
285
285
  jinxs_dict,
@@ -317,24 +317,19 @@ class Jinx:
317
317
  )
318
318
 
319
319
  return context
320
-
321
320
  def _execute_step(self,
322
- step,
323
- context,
324
- jinja_env,
325
- npc=None,
326
- messages=None,
327
- ):
328
- """Execute a single step of the jinx"""
321
+ step,
322
+ context,
323
+ jinja_env,
324
+ npc=None,
325
+ messages=None,
326
+ ):
329
327
  engine = step.get("engine", "natural")
330
328
  code = step.get("code", "")
331
329
  step_name = step.get("name", "unnamed_step")
332
-
333
-
334
-
330
+ mode = step.get("mode", "chat")
335
331
 
336
332
  try:
337
-
338
333
  template = jinja_env.from_string(code)
339
334
  rendered_code = template.render(**context)
340
335
 
@@ -346,16 +341,23 @@ class Jinx:
346
341
  rendered_code = code
347
342
  rendered_engine = engine
348
343
 
349
-
350
344
  if rendered_engine == "natural":
351
345
  if rendered_code.strip():
352
-
353
- response = npc.get_llm_response(
354
- rendered_code,
355
- context=context,
356
- messages=messages,
357
- )
358
-
346
+ if mode == "agent":
347
+ response = npc.get_llm_response(
348
+ rendered_code,
349
+ context=context,
350
+ messages=messages,
351
+ auto_process_tool_calls=True,
352
+ use_core_tools=True
353
+ )
354
+ else:
355
+ response = npc.get_llm_response(
356
+ rendered_code,
357
+ context=context,
358
+ messages=messages,
359
+ )
360
+
359
361
  response_text = response.get("response", "")
360
362
  context['output'] = response_text
361
363
  context["llm_response"] = response_text
@@ -363,7 +365,6 @@ class Jinx:
363
365
  context[step_name] = response_text
364
366
  context['messages'] = response.get('messages')
365
367
  elif rendered_engine == "python":
366
-
367
368
  exec_globals = {
368
369
  "__builtins__": __builtins__,
369
370
  "npc": npc,
@@ -379,48 +380,44 @@ class Jinx:
379
380
  "pathlib": pathlib,
380
381
  "subprocess": subprocess,
381
382
  "get_llm_response": npy.llm_funcs.get_llm_response,
382
-
383
383
  }
384
384
 
385
-
386
-
387
385
  exec_locals = {}
388
386
  exec(rendered_code, exec_globals, exec_locals)
389
387
 
390
-
391
388
  context.update(exec_locals)
392
389
 
393
-
394
390
  if "output" in exec_locals:
395
391
  outp = exec_locals["output"]
396
392
  context["output"] = outp
397
393
  context[step_name] = outp
398
394
  messages.append({'role':'assistant',
399
- 'content': f'Jinx executed with following output: {outp}'})
395
+ 'content': f'Jinx executed with following output: {outp}'})
400
396
  context['messages'] = messages
401
397
 
402
398
  else:
403
-
404
399
  context[step_name] = {"error": f"Unsupported engine: {rendered_engine}"}
405
400
 
406
401
  return context
407
-
408
402
  def to_dict(self):
409
403
  """Convert to dictionary representation"""
404
+ steps_list = []
405
+ for i, step in enumerate(self.steps):
406
+ step_dict = {
407
+ "name": step.get("name", f"step_{i}"),
408
+ "engine": step.get("engine"),
409
+ "code": step.get("code")
410
+ }
411
+ if "mode" in step:
412
+ step_dict["mode"] = step["mode"]
413
+ steps_list.append(step_dict)
414
+
410
415
  return {
411
416
  "jinx_name": self.jinx_name,
412
417
  "description": self.description,
413
418
  "inputs": self.inputs,
414
- "steps": [
415
- {
416
- "name": step.get("name", f"step_{i}"),
417
- "engine": step.get("engine"),
418
- "code": step.get("code")
419
- }
420
- for i, step in enumerate(self.steps)
421
- ]
419
+ "steps": steps_list
422
420
  }
423
-
424
421
  def save(self, directory):
425
422
  """Save jinx to file"""
426
423
  jinx_path = os.path.join(directory, f"{self.jinx_name}.jinx")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: npcpy
3
- Version: 1.2.28
3
+ Version: 1.2.29
4
4
  Summary: npcpy is the premier open-source library for integrating LLMs and Agents into python systems.
5
5
  Home-page: https://github.com/NPC-Worldwide/npcpy
6
6
  Author: Christopher Agostino
@@ -83,7 +83,7 @@ extra_files = package_files("npcpy/npc_team/")
83
83
 
84
84
  setup(
85
85
  name="npcpy",
86
- version="1.2.28",
86
+ version="1.2.29",
87
87
  packages=find_packages(exclude=["tests*"]),
88
88
  install_requires=base_requirements,
89
89
  extras_require={
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes