npcpy 1.2.29__tar.gz → 1.2.30__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.
- {npcpy-1.2.29/npcpy.egg-info → npcpy-1.2.30}/PKG-INFO +1 -1
- {npcpy-1.2.29 → npcpy-1.2.30}/npcpy/llm_funcs.py +17 -9
- {npcpy-1.2.29 → npcpy-1.2.30}/npcpy/npc_compiler.py +87 -45
- {npcpy-1.2.29 → npcpy-1.2.30}/npcpy/serve.py +46 -13
- {npcpy-1.2.29 → npcpy-1.2.30/npcpy.egg-info}/PKG-INFO +1 -1
- {npcpy-1.2.29 → npcpy-1.2.30}/setup.py +1 -1
- {npcpy-1.2.29 → npcpy-1.2.30}/LICENSE +0 -0
- {npcpy-1.2.29 → npcpy-1.2.30}/MANIFEST.in +0 -0
- {npcpy-1.2.29 → npcpy-1.2.30}/README.md +0 -0
- {npcpy-1.2.29 → npcpy-1.2.30}/npcpy/__init__.py +0 -0
- {npcpy-1.2.29 → npcpy-1.2.30}/npcpy/data/__init__.py +0 -0
- {npcpy-1.2.29 → npcpy-1.2.30}/npcpy/data/audio.py +0 -0
- {npcpy-1.2.29 → npcpy-1.2.30}/npcpy/data/data_models.py +0 -0
- {npcpy-1.2.29 → npcpy-1.2.30}/npcpy/data/image.py +0 -0
- {npcpy-1.2.29 → npcpy-1.2.30}/npcpy/data/load.py +0 -0
- {npcpy-1.2.29 → npcpy-1.2.30}/npcpy/data/text.py +0 -0
- {npcpy-1.2.29 → npcpy-1.2.30}/npcpy/data/video.py +0 -0
- {npcpy-1.2.29 → npcpy-1.2.30}/npcpy/data/web.py +0 -0
- {npcpy-1.2.29 → npcpy-1.2.30}/npcpy/ft/__init__.py +0 -0
- {npcpy-1.2.29 → npcpy-1.2.30}/npcpy/ft/diff.py +0 -0
- {npcpy-1.2.29 → npcpy-1.2.30}/npcpy/ft/ge.py +0 -0
- {npcpy-1.2.29 → npcpy-1.2.30}/npcpy/ft/memory_trainer.py +0 -0
- {npcpy-1.2.29 → npcpy-1.2.30}/npcpy/ft/model_ensembler.py +0 -0
- {npcpy-1.2.29 → npcpy-1.2.30}/npcpy/ft/rl.py +0 -0
- {npcpy-1.2.29 → npcpy-1.2.30}/npcpy/ft/sft.py +0 -0
- {npcpy-1.2.29 → npcpy-1.2.30}/npcpy/ft/usft.py +0 -0
- {npcpy-1.2.29 → npcpy-1.2.30}/npcpy/gen/__init__.py +0 -0
- {npcpy-1.2.29 → npcpy-1.2.30}/npcpy/gen/audio_gen.py +0 -0
- {npcpy-1.2.29 → npcpy-1.2.30}/npcpy/gen/embeddings.py +0 -0
- {npcpy-1.2.29 → npcpy-1.2.30}/npcpy/gen/image_gen.py +0 -0
- {npcpy-1.2.29 → npcpy-1.2.30}/npcpy/gen/response.py +0 -0
- {npcpy-1.2.29 → npcpy-1.2.30}/npcpy/gen/video_gen.py +0 -0
- {npcpy-1.2.29 → npcpy-1.2.30}/npcpy/main.py +0 -0
- {npcpy-1.2.29 → npcpy-1.2.30}/npcpy/memory/__init__.py +0 -0
- {npcpy-1.2.29 → npcpy-1.2.30}/npcpy/memory/command_history.py +0 -0
- {npcpy-1.2.29 → npcpy-1.2.30}/npcpy/memory/kg_vis.py +0 -0
- {npcpy-1.2.29 → npcpy-1.2.30}/npcpy/memory/knowledge_graph.py +0 -0
- {npcpy-1.2.29 → npcpy-1.2.30}/npcpy/memory/memory_processor.py +0 -0
- {npcpy-1.2.29 → npcpy-1.2.30}/npcpy/memory/search.py +0 -0
- {npcpy-1.2.29 → npcpy-1.2.30}/npcpy/mix/__init__.py +0 -0
- {npcpy-1.2.29 → npcpy-1.2.30}/npcpy/mix/debate.py +0 -0
- {npcpy-1.2.29 → npcpy-1.2.30}/npcpy/npc_sysenv.py +0 -0
- {npcpy-1.2.29 → npcpy-1.2.30}/npcpy/npcs.py +0 -0
- {npcpy-1.2.29 → npcpy-1.2.30}/npcpy/sql/__init__.py +0 -0
- {npcpy-1.2.29 → npcpy-1.2.30}/npcpy/sql/ai_function_tools.py +0 -0
- {npcpy-1.2.29 → npcpy-1.2.30}/npcpy/sql/database_ai_adapters.py +0 -0
- {npcpy-1.2.29 → npcpy-1.2.30}/npcpy/sql/database_ai_functions.py +0 -0
- {npcpy-1.2.29 → npcpy-1.2.30}/npcpy/sql/model_runner.py +0 -0
- {npcpy-1.2.29 → npcpy-1.2.30}/npcpy/sql/npcsql.py +0 -0
- {npcpy-1.2.29 → npcpy-1.2.30}/npcpy/sql/sql_model_compiler.py +0 -0
- {npcpy-1.2.29 → npcpy-1.2.30}/npcpy/tools.py +0 -0
- {npcpy-1.2.29 → npcpy-1.2.30}/npcpy/work/__init__.py +0 -0
- {npcpy-1.2.29 → npcpy-1.2.30}/npcpy/work/desktop.py +0 -0
- {npcpy-1.2.29 → npcpy-1.2.30}/npcpy/work/plan.py +0 -0
- {npcpy-1.2.29 → npcpy-1.2.30}/npcpy/work/trigger.py +0 -0
- {npcpy-1.2.29 → npcpy-1.2.30}/npcpy.egg-info/SOURCES.txt +0 -0
- {npcpy-1.2.29 → npcpy-1.2.30}/npcpy.egg-info/dependency_links.txt +0 -0
- {npcpy-1.2.29 → npcpy-1.2.30}/npcpy.egg-info/requires.txt +0 -0
- {npcpy-1.2.29 → npcpy-1.2.30}/npcpy.egg-info/top_level.txt +0 -0
- {npcpy-1.2.29 → npcpy-1.2.30}/setup.cfg +0 -0
- {npcpy-1.2.29 → npcpy-1.2.30}/tests/test_audio.py +0 -0
- {npcpy-1.2.29 → npcpy-1.2.30}/tests/test_command_history.py +0 -0
- {npcpy-1.2.29 → npcpy-1.2.30}/tests/test_image.py +0 -0
- {npcpy-1.2.29 → npcpy-1.2.30}/tests/test_llm_funcs.py +0 -0
- {npcpy-1.2.29 → npcpy-1.2.30}/tests/test_load.py +0 -0
- {npcpy-1.2.29 → npcpy-1.2.30}/tests/test_npc_compiler.py +0 -0
- {npcpy-1.2.29 → npcpy-1.2.30}/tests/test_npcsql.py +0 -0
- {npcpy-1.2.29 → npcpy-1.2.30}/tests/test_response.py +0 -0
- {npcpy-1.2.29 → npcpy-1.2.30}/tests/test_serve.py +0 -0
- {npcpy-1.2.29 → npcpy-1.2.30}/tests/test_text.py +0 -0
- {npcpy-1.2.29 → npcpy-1.2.30}/tests/test_tools.py +0 -0
- {npcpy-1.2.29 → npcpy-1.2.30}/tests/test_web.py +0 -0
|
@@ -378,7 +378,6 @@ def execute_llm_command(
|
|
|
378
378
|
"messages": messages,
|
|
379
379
|
"output": "Max attempts reached. Unable to execute the command successfully.",
|
|
380
380
|
}
|
|
381
|
-
|
|
382
381
|
def handle_jinx_call(
|
|
383
382
|
command: str,
|
|
384
383
|
jinx_name: str,
|
|
@@ -391,6 +390,7 @@ def handle_jinx_call(
|
|
|
391
390
|
n_attempts=3,
|
|
392
391
|
attempt=0,
|
|
393
392
|
context=None,
|
|
393
|
+
extra_globals=None, # ADD THIS
|
|
394
394
|
**kwargs
|
|
395
395
|
) -> Union[str, Dict[str, Any]]:
|
|
396
396
|
"""This function handles a jinx call.
|
|
@@ -568,6 +568,8 @@ def handle_jinx_call(
|
|
|
568
568
|
jinja_env,
|
|
569
569
|
npc=npc,
|
|
570
570
|
messages=messages,
|
|
571
|
+
extra_globals=extra_globals # ADD THIS
|
|
572
|
+
|
|
571
573
|
)
|
|
572
574
|
except Exception as e:
|
|
573
575
|
print(f"An error occurred while executing the jinx: {e}")
|
|
@@ -664,10 +666,10 @@ def jinx_handler(command, extracted_data, **kwargs):
|
|
|
664
666
|
api_key=kwargs.get('api_key'),
|
|
665
667
|
messages=kwargs.get('messages'),
|
|
666
668
|
npc=kwargs.get('npc'),
|
|
667
|
-
team
|
|
669
|
+
team=kwargs.get('team'),
|
|
668
670
|
stream=kwargs.get('stream'),
|
|
669
|
-
|
|
670
|
-
|
|
671
|
+
context=kwargs.get('context'),
|
|
672
|
+
extra_globals=kwargs.get('extra_globals') # ADD THIS
|
|
671
673
|
)
|
|
672
674
|
|
|
673
675
|
def answer_handler(command, extracted_data, **kwargs):
|
|
@@ -714,6 +716,7 @@ def check_llm_command(
|
|
|
714
716
|
stream=False,
|
|
715
717
|
context=None,
|
|
716
718
|
actions: Dict[str, Dict] = None,
|
|
719
|
+
extra_globals=None,
|
|
717
720
|
):
|
|
718
721
|
"""This function checks an LLM command and returns sequences of steps with parallel actions."""
|
|
719
722
|
if messages is None:
|
|
@@ -734,6 +737,7 @@ def check_llm_command(
|
|
|
734
737
|
stream=stream,
|
|
735
738
|
context=context,
|
|
736
739
|
actions=actions,
|
|
740
|
+
extra_globals=extra_globals,
|
|
737
741
|
|
|
738
742
|
)
|
|
739
743
|
return exec
|
|
@@ -873,6 +877,7 @@ def plan_multi_step_actions(
|
|
|
873
877
|
api_key: str = None,
|
|
874
878
|
context: str = None,
|
|
875
879
|
messages: List[Dict[str, str]] = None,
|
|
880
|
+
|
|
876
881
|
|
|
877
882
|
):
|
|
878
883
|
"""
|
|
@@ -992,6 +997,7 @@ def execute_multi_step_plan(
|
|
|
992
997
|
messages=messages,
|
|
993
998
|
team=team,
|
|
994
999
|
|
|
1000
|
+
|
|
995
1001
|
)
|
|
996
1002
|
|
|
997
1003
|
if not planned_actions:
|
|
@@ -1007,7 +1013,8 @@ def execute_multi_step_plan(
|
|
|
1007
1013
|
stream=stream,
|
|
1008
1014
|
team = team,
|
|
1009
1015
|
images=images,
|
|
1010
|
-
context=context
|
|
1016
|
+
context=context
|
|
1017
|
+
)
|
|
1011
1018
|
return {"messages": result.get('messages',
|
|
1012
1019
|
messages),
|
|
1013
1020
|
"output": result.get('response')}
|
|
@@ -1037,7 +1044,7 @@ def execute_multi_step_plan(
|
|
|
1037
1044
|
render_markdown(
|
|
1038
1045
|
f"- Executing Action: {action_name} \n- Explanation: {action_data.get('explanation')}\n "
|
|
1039
1046
|
)
|
|
1040
|
-
|
|
1047
|
+
|
|
1041
1048
|
result = handler(
|
|
1042
1049
|
command=command,
|
|
1043
1050
|
extracted_data=action_data,
|
|
@@ -1049,10 +1056,10 @@ def execute_multi_step_plan(
|
|
|
1049
1056
|
npc=npc,
|
|
1050
1057
|
team=team,
|
|
1051
1058
|
stream=stream,
|
|
1052
|
-
|
|
1053
1059
|
context=context+step_context,
|
|
1054
|
-
images=images
|
|
1055
|
-
)
|
|
1060
|
+
images=images,
|
|
1061
|
+
extra_globals=kwargs.get('extra_globals') # ADD THIS
|
|
1062
|
+
)
|
|
1056
1063
|
except KeyError as e:
|
|
1057
1064
|
|
|
1058
1065
|
return execute_multi_step_plan(
|
|
@@ -1068,6 +1075,7 @@ def execute_multi_step_plan(
|
|
|
1068
1075
|
stream=stream,
|
|
1069
1076
|
context=context,
|
|
1070
1077
|
actions=actions,
|
|
1078
|
+
|
|
1071
1079
|
**kwargs,
|
|
1072
1080
|
)
|
|
1073
1081
|
|
|
@@ -280,50 +280,59 @@ class Jinx:
|
|
|
280
280
|
else:
|
|
281
281
|
raise ValueError(f"Invalid step format: {step}")
|
|
282
282
|
return parsed_steps
|
|
283
|
+
|
|
283
284
|
def execute(self,
|
|
284
|
-
input_values,
|
|
285
|
-
jinxs_dict,
|
|
286
|
-
jinja_env = None,
|
|
287
|
-
npc = None,
|
|
288
|
-
messages=None
|
|
289
|
-
|
|
285
|
+
input_values: Dict[str, Any],
|
|
286
|
+
jinxs_dict: Dict[str, 'Jinx'],
|
|
287
|
+
jinja_env: Optional[Environment] = None,
|
|
288
|
+
npc: Optional[Any] = None,
|
|
289
|
+
messages: Optional[List[Dict[str, str]]] = None,
|
|
290
|
+
**kwargs: Any):
|
|
291
|
+
"""
|
|
292
|
+
Execute the jinx with given inputs.
|
|
293
|
+
**kwargs can be used to pass 'extra_globals' for the python engine.
|
|
294
|
+
"""
|
|
290
295
|
if jinja_env is None:
|
|
291
|
-
|
|
292
|
-
|
|
293
296
|
from jinja2 import DictLoader
|
|
294
297
|
jinja_env = Environment(
|
|
295
|
-
loader=DictLoader({}),
|
|
298
|
+
loader=DictLoader({}),
|
|
296
299
|
undefined=SilentUndefined,
|
|
297
300
|
)
|
|
298
301
|
|
|
299
|
-
context = (npc.shared_context.copy() if npc else {})
|
|
302
|
+
context = (npc.shared_context.copy() if npc and hasattr(npc, 'shared_context') else {})
|
|
300
303
|
context.update(input_values)
|
|
301
304
|
context.update({
|
|
302
305
|
"jinxs": jinxs_dict,
|
|
303
306
|
"llm_response": None,
|
|
304
|
-
"output": None,
|
|
307
|
+
"output": None,
|
|
305
308
|
"messages": messages,
|
|
306
309
|
})
|
|
307
310
|
|
|
308
|
-
|
|
311
|
+
# This is the key change: Extract 'extra_globals' from kwargs
|
|
312
|
+
extra_globals = kwargs.get('extra_globals')
|
|
313
|
+
|
|
309
314
|
for i, step in enumerate(self.steps):
|
|
310
315
|
context = self._execute_step(
|
|
311
|
-
step,
|
|
316
|
+
step,
|
|
312
317
|
context,
|
|
313
|
-
jinja_env,
|
|
314
|
-
npc=npc,
|
|
315
|
-
messages=messages,
|
|
316
|
-
|
|
317
|
-
)
|
|
318
|
+
jinja_env,
|
|
319
|
+
npc=npc,
|
|
320
|
+
messages=messages,
|
|
321
|
+
extra_globals=extra_globals # Pass it down to the step executor
|
|
322
|
+
)
|
|
318
323
|
|
|
319
324
|
return context
|
|
325
|
+
|
|
320
326
|
def _execute_step(self,
|
|
321
|
-
step,
|
|
322
|
-
context,
|
|
323
|
-
jinja_env,
|
|
324
|
-
npc=None,
|
|
325
|
-
messages=None,
|
|
326
|
-
|
|
327
|
+
step: Dict[str, Any],
|
|
328
|
+
context: Dict[str, Any],
|
|
329
|
+
jinja_env: Environment,
|
|
330
|
+
npc: Optional[Any] = None,
|
|
331
|
+
messages: Optional[List[Dict[str, str]]] = None,
|
|
332
|
+
extra_globals: Optional[Dict[str, Any]] = None):
|
|
333
|
+
"""
|
|
334
|
+
Execute a single step of the jinx.
|
|
335
|
+
"""
|
|
327
336
|
engine = step.get("engine", "natural")
|
|
328
337
|
code = step.get("code", "")
|
|
329
338
|
step_name = step.get("name", "unnamed_step")
|
|
@@ -364,7 +373,9 @@ class Jinx:
|
|
|
364
373
|
context["results"] = response_text
|
|
365
374
|
context[step_name] = response_text
|
|
366
375
|
context['messages'] = response.get('messages')
|
|
376
|
+
|
|
367
377
|
elif rendered_engine == "python":
|
|
378
|
+
# Base globals available to all python jinxes, defined within the library (npcpy)
|
|
368
379
|
exec_globals = {
|
|
369
380
|
"__builtins__": __builtins__,
|
|
370
381
|
"npc": npc,
|
|
@@ -379,21 +390,33 @@ class Jinx:
|
|
|
379
390
|
"fnmatch": fnmatch,
|
|
380
391
|
"pathlib": pathlib,
|
|
381
392
|
"subprocess": subprocess,
|
|
382
|
-
"get_llm_response": npy.llm_funcs.get_llm_response,
|
|
383
|
-
|
|
393
|
+
"get_llm_response": npy.llm_funcs.get_llm_response,
|
|
394
|
+
"CommandHistory": CommandHistory, # This is fine, it's part of npcpy
|
|
395
|
+
}
|
|
384
396
|
|
|
385
|
-
|
|
386
|
-
|
|
397
|
+
# This is the fix: Update the globals with the dictionary passed in from the application (npcsh)
|
|
398
|
+
if extra_globals:
|
|
399
|
+
exec_globals.update(extra_globals)
|
|
387
400
|
|
|
401
|
+
exec_locals = {}
|
|
402
|
+
try:
|
|
403
|
+
exec(rendered_code, exec_globals, exec_locals)
|
|
404
|
+
except Exception as e:
|
|
405
|
+
# Provide a clear error message in the output if execution fails
|
|
406
|
+
error_msg = f"Error executing jinx python code: {type(e).__name__}: {e}"
|
|
407
|
+
context['output'] = error_msg
|
|
408
|
+
return context
|
|
409
|
+
|
|
388
410
|
context.update(exec_locals)
|
|
389
411
|
|
|
390
412
|
if "output" in exec_locals:
|
|
391
413
|
outp = exec_locals["output"]
|
|
392
414
|
context["output"] = outp
|
|
393
415
|
context[step_name] = outp
|
|
394
|
-
messages
|
|
395
|
-
|
|
396
|
-
|
|
416
|
+
if messages is not None:
|
|
417
|
+
messages.append({'role':'assistant',
|
|
418
|
+
'content': f'Jinx executed with following output: {outp}'})
|
|
419
|
+
context['messages'] = messages
|
|
397
420
|
|
|
398
421
|
else:
|
|
399
422
|
context[step_name] = {"error": f"Unsupported engine: {rendered_engine}"}
|
|
@@ -561,6 +584,9 @@ def get_npc_action_space(npc=None, team=None):
|
|
|
561
584
|
|
|
562
585
|
return actions
|
|
563
586
|
def extract_jinx_inputs(args: List[str], jinx: Jinx) -> Dict[str, Any]:
|
|
587
|
+
print(f"DEBUG extract_jinx_inputs called with args: {args}")
|
|
588
|
+
print(f"DEBUG jinx.inputs: {jinx.inputs}")
|
|
589
|
+
|
|
564
590
|
inputs = {}
|
|
565
591
|
|
|
566
592
|
flag_mapping = {}
|
|
@@ -585,7 +611,6 @@ def extract_jinx_inputs(args: List[str], jinx: Jinx) -> Dict[str, Any]:
|
|
|
585
611
|
else:
|
|
586
612
|
used_args = set()
|
|
587
613
|
|
|
588
|
-
|
|
589
614
|
for i, arg in enumerate(args):
|
|
590
615
|
if i in used_args:
|
|
591
616
|
continue
|
|
@@ -603,21 +628,38 @@ def extract_jinx_inputs(args: List[str], jinx: Jinx) -> Dict[str, Any]:
|
|
|
603
628
|
|
|
604
629
|
unused_args = [arg for i, arg in enumerate(args) if i not in used_args]
|
|
605
630
|
|
|
606
|
-
|
|
631
|
+
print(f"DEBUG unused_args: {unused_args}")
|
|
632
|
+
|
|
633
|
+
# Find first required input (no default value)
|
|
634
|
+
first_required = None
|
|
607
635
|
for input_ in jinx.inputs:
|
|
608
636
|
if isinstance(input_, str):
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
637
|
+
first_required = input_
|
|
638
|
+
break
|
|
639
|
+
|
|
640
|
+
print(f"DEBUG first_required: {first_required}")
|
|
641
|
+
|
|
642
|
+
# Give all unused args to first required input
|
|
643
|
+
if first_required and unused_args:
|
|
644
|
+
inputs[first_required] = ' '.join(unused_args).strip()
|
|
645
|
+
print(f"DEBUG assigned to first_required: {inputs[first_required]}")
|
|
614
646
|
else:
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
647
|
+
# Fallback to original behavior
|
|
648
|
+
jinx_input_names = []
|
|
649
|
+
for input_ in jinx.inputs:
|
|
650
|
+
if isinstance(input_, str):
|
|
651
|
+
jinx_input_names.append(input_)
|
|
652
|
+
elif isinstance(input_, dict):
|
|
653
|
+
jinx_input_names.append(list(input_.keys())[0])
|
|
654
|
+
|
|
655
|
+
if len(jinx_input_names) == 1:
|
|
656
|
+
inputs[jinx_input_names[0]] = ' '.join(unused_args).strip()
|
|
657
|
+
else:
|
|
658
|
+
for i, arg in enumerate(unused_args):
|
|
659
|
+
if i < len(jinx_input_names):
|
|
660
|
+
input_name = jinx_input_names[i]
|
|
661
|
+
if input_name not in inputs:
|
|
662
|
+
inputs[input_name] = arg
|
|
621
663
|
|
|
622
664
|
for input_ in jinx.inputs:
|
|
623
665
|
if isinstance(input_, str):
|
|
@@ -629,8 +671,8 @@ def extract_jinx_inputs(args: List[str], jinx: Jinx) -> Dict[str, Any]:
|
|
|
629
671
|
if key not in inputs:
|
|
630
672
|
inputs[key] = default_value
|
|
631
673
|
|
|
674
|
+
print(f"DEBUG final inputs: {inputs}")
|
|
632
675
|
return inputs
|
|
633
|
-
|
|
634
676
|
from npcpy.memory.command_history import load_kg_from_db, save_kg_to_db
|
|
635
677
|
from npcpy.memory.knowledge_graph import kg_initial, kg_evolve_incremental, kg_sleep_process, kg_dream_process
|
|
636
678
|
from npcpy.llm_funcs import get_llm_response, breathe
|
|
@@ -149,7 +149,7 @@ def load_kg_data(generation=None):
|
|
|
149
149
|
app = Flask(__name__)
|
|
150
150
|
app.config["REDIS_URL"] = "redis://localhost:6379"
|
|
151
151
|
app.config['DB_PATH'] = ''
|
|
152
|
-
|
|
152
|
+
app.jinx_conversation_contexts ={}
|
|
153
153
|
|
|
154
154
|
redis_client = redis.Redis(host="localhost", port=6379, decode_responses=True)
|
|
155
155
|
|
|
@@ -585,19 +585,27 @@ def execute_jinx():
|
|
|
585
585
|
with cancellation_lock:
|
|
586
586
|
cancellation_flags[stream_id] = False
|
|
587
587
|
|
|
588
|
-
print(
|
|
588
|
+
print(f"--- Jinx Execution Request for streamId: {stream_id} ---")
|
|
589
|
+
print(f"Request Data: {json.dumps(data, indent=2)}")
|
|
589
590
|
|
|
590
591
|
jinx_name = data.get("jinxName")
|
|
591
592
|
jinx_args = data.get("jinxArgs", [])
|
|
592
|
-
print(jinx_args)
|
|
593
|
+
print(f"Jinx Name: {jinx_name}, Jinx Args: {jinx_args}")
|
|
593
594
|
conversation_id = data.get("conversationId")
|
|
594
595
|
model = data.get("model")
|
|
595
596
|
provider = data.get("provider")
|
|
597
|
+
|
|
598
|
+
# --- IMPORTANT: Ensure conversation_id is present for context persistence ---
|
|
599
|
+
if not conversation_id:
|
|
600
|
+
print("ERROR: conversationId is required for Jinx execution with persistent variables")
|
|
601
|
+
return jsonify({"error": "conversationId is required for Jinx execution with persistent variables"}), 400
|
|
602
|
+
|
|
596
603
|
npc_name = data.get("npc")
|
|
597
604
|
npc_source = data.get("npcSource", "global")
|
|
598
605
|
current_path = data.get("currentPath")
|
|
599
606
|
|
|
600
607
|
if not jinx_name:
|
|
608
|
+
print("ERROR: jinxName is required")
|
|
601
609
|
return jsonify({"error": "jinxName is required"}), 400
|
|
602
610
|
|
|
603
611
|
# Load project environment if applicable
|
|
@@ -632,12 +640,12 @@ def execute_jinx():
|
|
|
632
640
|
jinx = Jinx(jinx_path=global_jinx_path)
|
|
633
641
|
|
|
634
642
|
if not jinx:
|
|
643
|
+
print(f"ERROR: Jinx '{jinx_name}' not found")
|
|
635
644
|
return jsonify({"error": f"Jinx '{jinx_name}' not found"}), 404
|
|
636
645
|
|
|
637
646
|
# Extract inputs from args
|
|
638
647
|
from npcpy.npc_compiler import extract_jinx_inputs
|
|
639
648
|
|
|
640
|
-
# --- Start of Fix ---
|
|
641
649
|
# Re-assemble arguments that were incorrectly split by spaces.
|
|
642
650
|
fixed_args = []
|
|
643
651
|
i = 0
|
|
@@ -666,15 +674,11 @@ def execute_jinx():
|
|
|
666
674
|
# This handles positional arguments, just in case.
|
|
667
675
|
fixed_args.append(arg)
|
|
668
676
|
i += 1
|
|
669
|
-
# --- End of Fix ---
|
|
670
677
|
|
|
671
678
|
# Now, use the corrected arguments to extract inputs.
|
|
672
679
|
input_values = extract_jinx_inputs(fixed_args, jinx)
|
|
673
680
|
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
print('executing jinx with input_values ,', input_values)
|
|
681
|
+
print(f'Executing jinx with input_values: {input_values}')
|
|
678
682
|
# Get conversation history
|
|
679
683
|
command_history = CommandHistory(app.config.get('DB_PATH'))
|
|
680
684
|
messages = fetch_messages_for_conversation(conversation_id)
|
|
@@ -684,20 +688,48 @@ def execute_jinx():
|
|
|
684
688
|
if npc_object and hasattr(npc_object, 'jinxs_dict'):
|
|
685
689
|
all_jinxs.update(npc_object.jinxs_dict)
|
|
686
690
|
|
|
691
|
+
# --- IMPORTANT: Retrieve or initialize the persistent Jinx context for this conversation ---
|
|
692
|
+
if conversation_id not in app.jinx_conversation_contexts:
|
|
693
|
+
app.jinx_conversation_contexts[conversation_id] = {}
|
|
694
|
+
jinx_local_context = app.jinx_conversation_contexts[conversation_id]
|
|
695
|
+
|
|
696
|
+
print(f"--- CONTEXT STATE (conversationId: {conversation_id}) ---")
|
|
697
|
+
print(f"jinx_local_context BEFORE Jinx execution: {jinx_local_context}")
|
|
698
|
+
|
|
687
699
|
def event_stream(current_stream_id):
|
|
688
700
|
try:
|
|
689
|
-
#
|
|
701
|
+
# --- IMPORTANT: Pass the persistent context as 'extra_globals' ---
|
|
690
702
|
result = jinx.execute(
|
|
691
703
|
input_values=input_values,
|
|
692
704
|
jinxs_dict=all_jinxs,
|
|
693
705
|
jinja_env=npc_object.jinja_env if npc_object else None,
|
|
694
706
|
npc=npc_object,
|
|
695
|
-
messages=messages
|
|
707
|
+
messages=messages,
|
|
708
|
+
extra_globals=jinx_local_context # <--- THIS IS WHERE THE PERSISTENT CONTEXT IS PASSED
|
|
696
709
|
)
|
|
697
710
|
|
|
698
|
-
#
|
|
711
|
+
# --- CRITICAL FIX: Capture and update local_vars from the Jinx's result ---
|
|
712
|
+
# The Jinx.execute method returns its internal 'context' dictionary.
|
|
713
|
+
# We need to update our persistent 'jinx_local_context' with the new variables
|
|
714
|
+
# from the Jinx's returned context.
|
|
715
|
+
if isinstance(result, dict):
|
|
716
|
+
# We need to be careful not to overwrite core Jinx/NPC context keys
|
|
717
|
+
# that are not meant for variable persistence.
|
|
718
|
+
keys_to_exclude = ['output', 'llm_response', 'messages', 'results', 'npc', 'context', 'jinxs', 'team']
|
|
719
|
+
|
|
720
|
+
# Update jinx_local_context with all non-excluded keys from the result
|
|
721
|
+
for key, value in result.items():
|
|
722
|
+
if key not in keys_to_exclude and not key.startswith('_'): # Exclude internal/temporary keys
|
|
723
|
+
jinx_local_context[key] = value
|
|
724
|
+
|
|
725
|
+
print(f"jinx_local_context UPDATED from Jinx result: {jinx_local_context}") # NEW LOG
|
|
726
|
+
|
|
727
|
+
# Get output (this still comes from the 'output' key in the result)
|
|
699
728
|
output = result.get('output', str(result))
|
|
700
729
|
messages_updated = result.get('messages', messages)
|
|
730
|
+
|
|
731
|
+
print(f"jinx_local_context AFTER Jinx execution (final state): {jinx_local_context}")
|
|
732
|
+
print(f"Jinx execution result output: {output}")
|
|
701
733
|
|
|
702
734
|
# Check for interruption
|
|
703
735
|
with cancellation_lock:
|
|
@@ -774,7 +806,7 @@ def execute_jinx():
|
|
|
774
806
|
)
|
|
775
807
|
|
|
776
808
|
except Exception as e:
|
|
777
|
-
print(f"
|
|
809
|
+
print(f"ERROR: Exception during jinx execution {jinx_name}: {str(e)}")
|
|
778
810
|
traceback.print_exc()
|
|
779
811
|
error_data = {
|
|
780
812
|
"type": "error",
|
|
@@ -786,6 +818,7 @@ def execute_jinx():
|
|
|
786
818
|
with cancellation_lock:
|
|
787
819
|
if current_stream_id in cancellation_flags:
|
|
788
820
|
del cancellation_flags[current_stream_id]
|
|
821
|
+
print(f"--- Jinx Execution Finished for streamId: {stream_id} ---")
|
|
789
822
|
|
|
790
823
|
return Response(event_stream(stream_id), mimetype="text/event-stream")
|
|
791
824
|
|
|
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
|
|
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
|