npcpy 1.2.28__py3-none-any.whl → 1.2.29__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.
- npcpy/ft/sft.py +25 -7
- npcpy/gen/image_gen.py +37 -15
- npcpy/npc_compiler.py +37 -40
- {npcpy-1.2.28.dist-info → npcpy-1.2.29.dist-info}/METADATA +1 -1
- {npcpy-1.2.28.dist-info → npcpy-1.2.29.dist-info}/RECORD +8 -8
- {npcpy-1.2.28.dist-info → npcpy-1.2.29.dist-info}/WHEEL +0 -0
- {npcpy-1.2.28.dist-info → npcpy-1.2.29.dist-info}/licenses/LICENSE +0 -0
- {npcpy-1.2.28.dist-info → npcpy-1.2.29.dist-info}/top_level.txt +0 -0
npcpy/ft/sft.py
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
232
|
+
full_response = tokenizer.decode(
|
|
226
233
|
outputs[0],
|
|
227
|
-
skip_special_tokens=
|
|
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
|
npcpy/gen/image_gen.py
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
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",
|
npcpy/npc_compiler.py
CHANGED
|
@@ -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
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
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
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
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
|
-
|
|
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,7 +1,7 @@
|
|
|
1
1
|
npcpy/__init__.py,sha256=9imxFtK74_6Rw9rz0kyMnZYl_voPb569tkTlYLt0Urg,131
|
|
2
2
|
npcpy/llm_funcs.py,sha256=UkesCnRmclEoqBZPMZa2hKoSTjFzjxDCzPGKgeDegPQ,85101
|
|
3
3
|
npcpy/main.py,sha256=RWoRIj6VQLxKdOKvdVyaq2kwG35oRpeXPvp1CAAoG-w,81
|
|
4
|
-
npcpy/npc_compiler.py,sha256=
|
|
4
|
+
npcpy/npc_compiler.py,sha256=hXnUV9idoqZeNMiv2U1TNQxBNucvMXfF3HZqNbZB_8E,87276
|
|
5
5
|
npcpy/npc_sysenv.py,sha256=QSLkkAsCeSiyE525yWMZCBwm5_UB8--A8O-8vgx5unY,35218
|
|
6
6
|
npcpy/npcs.py,sha256=eExuVsbTfrRobTRRptRpDm46jCLWUgbvy4_U7IUQo-c,744
|
|
7
7
|
npcpy/serve.py,sha256=htmVw2o5WiCOgUgkrIs90dnszBqwKPKnPLQlAgG_Uds,115397
|
|
@@ -20,12 +20,12 @@ npcpy/ft/ge.py,sha256=0VzIiXq2wCzGcK1x0Wd-myJ3xRf-FNaPg0GkHEZegUM,3552
|
|
|
20
20
|
npcpy/ft/memory_trainer.py,sha256=QZPznxEEwXbOGroHdMUMa5xpqlNwgV6nqOazI2xgrnQ,6635
|
|
21
21
|
npcpy/ft/model_ensembler.py,sha256=BRX4hJ_rvF1vKTzjMhlahZqPttUgc3PqmzUJDqIfIps,10038
|
|
22
22
|
npcpy/ft/rl.py,sha256=EcPD8t5MFg0zYWSS-A7KJ9bWd0qCTsL5SSvDxV556Z4,9245
|
|
23
|
-
npcpy/ft/sft.py,sha256=
|
|
23
|
+
npcpy/ft/sft.py,sha256=74gRaJTTrZcO4np4DqRMr79ADkGhPcDKutR74rag03E,6659
|
|
24
24
|
npcpy/ft/usft.py,sha256=O025GGYGZQf2ZVLowyAmBwh5bJyuy2dUAM6v03YcboY,3435
|
|
25
25
|
npcpy/gen/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
26
26
|
npcpy/gen/audio_gen.py,sha256=w4toESu7nmli1T5FOwRRCGC_QK9W-SMWknYYkbRv9jE,635
|
|
27
27
|
npcpy/gen/embeddings.py,sha256=QStTJ2ELiC379OEZsLEgGGIIFD267Y8zQchs7HRn2Zg,2089
|
|
28
|
-
npcpy/gen/image_gen.py,sha256=
|
|
28
|
+
npcpy/gen/image_gen.py,sha256=mAlLG9jo9RnuuMU0jJVV0CpIgHqdizU9sfC6A0w5kKE,15599
|
|
29
29
|
npcpy/gen/response.py,sha256=btsfdoEYV1T6tEW31L7vh13eTB44Ykam7uJ5CmjKKLE,28944
|
|
30
30
|
npcpy/gen/video_gen.py,sha256=JMp2s2qMp5uy0rOgv6BRZ7nkQI4vdT1hbJ2nSu4s-KA,3243
|
|
31
31
|
npcpy/memory/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -47,8 +47,8 @@ npcpy/work/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
|
47
47
|
npcpy/work/desktop.py,sha256=F3I8mUtJp6LAkXodsh8hGZIncoads6c_2Utty-0EdDA,2986
|
|
48
48
|
npcpy/work/plan.py,sha256=QyUwg8vElWiHuoS-xK4jXTxxHvkMD3VkaCEsCmrEPQk,8300
|
|
49
49
|
npcpy/work/trigger.py,sha256=P1Y8u1wQRsS2WACims_2IdkBEar-iBQix-2TDWoW0OM,9948
|
|
50
|
-
npcpy-1.2.
|
|
51
|
-
npcpy-1.2.
|
|
52
|
-
npcpy-1.2.
|
|
53
|
-
npcpy-1.2.
|
|
54
|
-
npcpy-1.2.
|
|
50
|
+
npcpy-1.2.29.dist-info/licenses/LICENSE,sha256=j0YPvce7Ng9e32zYOu0EmXjXeJ0Nwawd0RA3uSGGH4E,1070
|
|
51
|
+
npcpy-1.2.29.dist-info/METADATA,sha256=d05EPbUMmAl-KBXuMDKETwbdDPVjkaoogOscOodeOe0,29895
|
|
52
|
+
npcpy-1.2.29.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
53
|
+
npcpy-1.2.29.dist-info/top_level.txt,sha256=g1pbSvrOOncB74Bg5-J0Olg4V0A5VzDw-Xz5YObq8BU,6
|
|
54
|
+
npcpy-1.2.29.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|