npcpy 1.2.32__py3-none-any.whl → 1.2.34__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/llm_funcs.py CHANGED
@@ -379,6 +379,8 @@ def execute_llm_command(
379
379
  "messages": messages,
380
380
  "output": "Max attempts reached. Unable to execute the command successfully.",
381
381
  }
382
+
383
+ # --- START OF CORRECTED handle_jinx_call ---
382
384
  def handle_jinx_call(
383
385
  command: str,
384
386
  jinx_name: str,
@@ -391,7 +393,7 @@ def handle_jinx_call(
391
393
  n_attempts=3,
392
394
  attempt=0,
393
395
  context=None,
394
- extra_globals=None, # ADD THIS
396
+ extra_globals=None,
395
397
  **kwargs
396
398
  ) -> Union[str, Dict[str, Any]]:
397
399
  """This function handles a jinx call.
@@ -411,10 +413,13 @@ def handle_jinx_call(
411
413
  if npc is None and team is None:
412
414
  return f"No jinxs are available. "
413
415
  else:
416
+ jinx = None
417
+ if npc and hasattr(npc, 'jinxs_dict') and jinx_name in npc.jinxs_dict:
418
+ jinx = npc.jinxs_dict[jinx_name]
419
+ elif team and hasattr(team, 'jinxs_dict') and jinx_name in team.jinxs_dict:
420
+ jinx = team.jinxs_dict[jinx_name]
414
421
 
415
-
416
-
417
- if jinx_name not in npc.jinxs_dict and jinx_name not in team.jinxs_dict:
422
+ if not jinx:
418
423
  print(f"Jinx {jinx_name} not available")
419
424
  if attempt < n_attempts:
420
425
  print(f"attempt {attempt+1} to generate jinx name failed, trying again")
@@ -442,16 +447,11 @@ def handle_jinx_call(
442
447
  "messages": messages,
443
448
  }
444
449
 
445
-
446
-
447
-
448
- elif jinx_name in npc.jinxs_dict:
449
- jinx = npc.jinxs_dict[jinx_name]
450
- elif jinx_name in team.jinxs_dict:
451
- jinx = team.jinxs_dict[jinx_name]
452
-
453
450
  render_markdown(f"jinx found: {jinx.jinx_name}")
454
- jinja_env = Environment(loader=FileSystemLoader("."), undefined=Undefined)
451
+
452
+ # This jinja_env is for parsing the Jinx's *inputs* from the LLM response, not for Jinx.execute's second pass.
453
+ local_jinja_env_for_input_parsing = Environment(loader=FileSystemLoader("."), undefined=Undefined)
454
+
455
455
  example_format = {}
456
456
  for inp in jinx.inputs:
457
457
  if isinstance(inp, str):
@@ -563,20 +563,32 @@ def handle_jinx_call(
563
563
 
564
564
  render_markdown( "\n".join(['\n - ' + str(key) + ': ' +str(val) for key, val in input_values.items()]))
565
565
 
566
+ # Initialize jinx_output before the try block to prevent UnboundLocalError
567
+ jinx_output = {"output": "Jinx execution did not complete."}
568
+
566
569
  try:
570
+ # --- CRITICAL FIX HERE ---
571
+ # Pass arguments as keyword arguments to avoid positional confusion
572
+ # Use npc.jinja_env for the second-pass rendering
567
573
  jinx_output = jinx.execute(
568
- input_values,
569
- jinja_env,
570
- npc=npc,
574
+ input_values=input_values,
575
+ npc=npc, # This is the orchestrating NPC
571
576
  messages=messages,
572
- extra_globals=extra_globals # ADD THIS
573
-
577
+ extra_globals=extra_globals,
578
+ jinja_env=npc.jinja_env if npc else (team.forenpc.jinja_env if team and team.forenpc else None) # Use NPC's or Team's forenpc's jinja_env
574
579
  )
580
+ # Ensure jinx_output is a dict with an 'output' key
581
+ if jinx_output is None:
582
+ jinx_output = {"output": "Jinx executed, but returned no explicit output."}
583
+ elif not isinstance(jinx_output, dict):
584
+ jinx_output = {"output": str(jinx_output)}
585
+
575
586
  except Exception as e:
576
587
  print(f"An error occurred while executing the jinx: {e}")
577
588
  print(f"trying again, attempt {attempt+1}")
578
589
  print('command', command)
579
590
  if attempt < n_attempts:
591
+ # Recursively call handle_jinx_call for retry
580
592
  jinx_output = handle_jinx_call(
581
593
  command,
582
594
  jinx_name,
@@ -589,9 +601,15 @@ def handle_jinx_call(
589
601
  attempt=attempt + 1,
590
602
  n_attempts=n_attempts,
591
603
  context=f""" \n \n \n "jinx failed: {e} \n \n \n here was the previous attempt: {input_values}""",
604
+ extra_globals=extra_globals
592
605
  )
606
+ else:
607
+ # If max attempts reached, set a clear error output
608
+ jinx_output = {"output": f"Jinx '{jinx_name}' failed after {n_attempts} attempts: {e}", "error": True}
609
+
610
+
593
611
  if not stream and len(messages) > 0 :
594
- render_markdown(f""" ## jinx OUTPUT FROM CALLING {jinx_name} \n \n output:{jinx_output['output']}""" )
612
+ render_markdown(f""" ## jinx OUTPUT FROM CALLING {jinx_name} \n \n output:{jinx_output.get('output', 'No output.')}""" )
595
613
  response = get_llm_response(f"""
596
614
  The user had the following request: {command}.
597
615
  Here were the jinx outputs from calling {jinx_name}: {jinx_output.get('output', '')}
@@ -610,7 +628,9 @@ def handle_jinx_call(
610
628
  response = response.get("response", {})
611
629
  return {'messages':messages, 'output':response}
612
630
 
613
- return {'messages': messages, 'output': jinx_output['output']}
631
+ return {'messages': messages, 'output': jinx_output.get('output', 'No output.')} # Ensure 'output' key exists
632
+
633
+ # --- END OF CORRECTED handle_jinx_call ---
614
634
 
615
635
 
616
636
  def handle_request_input(
@@ -670,7 +690,7 @@ def jinx_handler(command, extracted_data, **kwargs):
670
690
  team=kwargs.get('team'),
671
691
  stream=kwargs.get('stream'),
672
692
  context=kwargs.get('context'),
673
- extra_globals=kwargs.get('extra_globals') # ADD THIS
693
+ extra_globals=kwargs.get('extra_globals')
674
694
  )
675
695
 
676
696
  def answer_handler(command, extracted_data, **kwargs):
@@ -976,8 +996,8 @@ def execute_multi_step_plan(
976
996
  images: list = None,
977
997
  stream=False,
978
998
  context=None,
979
-
980
999
  actions: Dict[str, Dict] = None,
1000
+ extra_globals=None,
981
1001
  **kwargs,
982
1002
  ):
983
1003
  """
@@ -1045,7 +1065,7 @@ def execute_multi_step_plan(
1045
1065
  render_markdown(
1046
1066
  f"- Executing Action: {action_name} \n- Explanation: {action_data.get('explanation')}\n "
1047
1067
  )
1048
-
1068
+
1049
1069
  result = handler(
1050
1070
  command=command,
1051
1071
  extracted_data=action_data,
@@ -1059,7 +1079,7 @@ def execute_multi_step_plan(
1059
1079
  stream=stream,
1060
1080
  context=context+step_context,
1061
1081
  images=images,
1062
- extra_globals=kwargs.get('extra_globals') # ADD THIS
1082
+ extra_globals=extra_globals
1063
1083
  )
1064
1084
  except KeyError as e:
1065
1085
 
@@ -1862,7 +1882,7 @@ def zoom_in(facts,
1862
1882
  npc=npc,
1863
1883
  context=context,
1864
1884
  attempt_number=attempt_number+1,
1865
- n_tries=n_tries,
1885
+ n_tries=n_attempts, # Corrected from n_tries to n_attempts
1866
1886
  **kwargs)
1867
1887
  return facts
1868
1888
  def generate_groups(facts,
@@ -1945,7 +1965,6 @@ def remove_redundant_groups(groups,
1945
1965
  response = get_llm_response(prompt,
1946
1966
  model=model,
1947
1967
  provider=provider,
1948
- format="json",
1949
1968
  npc=npc,
1950
1969
  context=context,
1951
1970
  **kwargs)
@@ -2056,17 +2075,18 @@ def get_related_facts_llm(new_fact_statement,
2056
2075
  npc=npc,
2057
2076
  context=context,
2058
2077
  **kwargs)
2059
- if attempt_number > n_attempts:
2060
- print(f" Attempt {attempt_number} to find related facts yielded no results. Giving up.")
2061
- return get_related_facts_llm(new_fact_statement,
2062
- existing_fact_statements,
2063
- model=model,
2064
- provider=provider,
2065
- npc=npc,
2066
- attempt_number=attempt_number+1,
2067
- n_attempts=n_attempts,
2068
- context=context,
2069
- **kwargs)
2078
+ if attempt_number <= n_attempts: # Corrected logic: retry if attempt_number is within limits
2079
+ if not response["response"].get("related_facts", []): # Only retry if no related facts found
2080
+ print(f" Attempt {attempt_number} to find related facts yielded no results. Retrying...")
2081
+ return get_related_facts_llm(new_fact_statement,
2082
+ existing_fact_statements,
2083
+ model=model,
2084
+ provider=provider,
2085
+ npc=npc,
2086
+ attempt_number=attempt_number+1,
2087
+ n_attempts=n_attempts,
2088
+ context=context,
2089
+ **kwargs)
2070
2090
 
2071
2091
  return response["response"].get("related_facts", [])
2072
2092
 
@@ -825,6 +825,31 @@ class CommandHistory:
825
825
  FROM message_attachments WHERE message_id = :message_id
826
826
  """
827
827
  return self._fetch_all(stmt, {"message_id": message_id})
828
+ def delete_message(self, conversation_id, message_id):
829
+ """Delete a specific message from a conversation"""
830
+ conn = sqlite3.connect(self.db_path)
831
+ cursor = conn.cursor()
832
+
833
+ try:
834
+ # Delete from the messages table
835
+ cursor.execute("""
836
+ DELETE FROM messages
837
+ WHERE conversation_id = ? AND message_id = ?
838
+ """, (conversation_id, message_id))
839
+
840
+ rows_affected = cursor.rowcount
841
+ conn.commit()
842
+
843
+ print(f"[DB] Deleted message {message_id} from conversation {conversation_id}. Rows affected: {rows_affected}")
844
+
845
+ return rows_affected
846
+
847
+ except Exception as e:
848
+ print(f"[DB] Error deleting message: {e}")
849
+ conn.rollback()
850
+ raise
851
+ finally:
852
+ conn.close()
828
853
 
829
854
  def get_attachment_data(self, attachment_id) -> Optional[Tuple[bytes, str, str]]:
830
855
  stmt = "SELECT attachment_data, attachment_name, attachment_type FROM message_attachments WHERE id = :attachment_id"
@@ -1172,4 +1197,4 @@ def get_available_tables(db_path_or_engine: Union[str, Engine]) -> List[Tuple[st
1172
1197
  return [row[0] for row in result]
1173
1198
  except Exception as e:
1174
1199
  print(f"Error getting available tables: {e}")
1175
- return []
1200
+ return []