npcpy 1.3.10__py3-none-any.whl → 1.3.11__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
@@ -726,9 +726,6 @@ Instructions:
726
726
 
727
727
  decision = response.get("response", {})
728
728
  logger.debug(f"[_react_fallback] Raw decision: {str(decision)[:200]}")
729
- print(f"[REACT-DEBUG] Full response keys: {response.keys()}")
730
- print(f"[REACT-DEBUG] Raw response['response']: {str(response.get('response', 'NONE'))[:500]}")
731
- print(f"[REACT-DEBUG] Raw decision type: {type(decision)}, value: {str(decision)[:500]}")
732
729
 
733
730
  # Handle None response - model decided no action needed
734
731
  if decision is None:
@@ -787,7 +784,6 @@ Instructions:
787
784
  # Extract all keys except 'action', 'jinx_name', 'inputs' as potential inputs
788
785
  inputs = {k: v for k, v in decision.items() if k not in ('action', 'jinx_name', 'inputs', 'response')}
789
786
  logger.debug(f"[_react_fallback] Jinx action: {jinx_name} with inputs: {inputs}")
790
- print(f"[REACT-DEBUG] Chose jinx: {jinx_name}, inputs: {str(inputs)[:200]}")
791
787
 
792
788
  if jinx_name not in jinxs:
793
789
  context = f"Error: '{jinx_name}' not found. Available: {list(jinxs.keys())}"
@@ -824,7 +820,6 @@ Instructions:
824
820
  all_params = required_names + optional_names
825
821
  context = f"Error: jinx '{jinx_name}' requires parameters {required_names} but got {provided}. Missing: {missing}. Optional params with defaults: {optional_names}. Please retry with correct parameter names."
826
822
  logger.debug(f"[_react_fallback] Missing required params: {missing}")
827
- print(f"[REACT-DEBUG] Missing params for {jinx_name}: {missing}, got: {provided}")
828
823
  continue
829
824
 
830
825
  logger.debug(f"[_react_fallback] Executing jinx: {jinx_name}")
@@ -847,15 +842,12 @@ Instructions:
847
842
  # Check various possible locations
848
843
  if os.path.exists(local_path):
849
844
  generated_images.append(local_path)
850
- print(f"[REACT-DEBUG] Added generated image: {local_path}")
851
845
  elif os.path.exists(os.path.join(os.getcwd(), local_path)):
852
846
  full_path = os.path.join(os.getcwd(), local_path)
853
847
  generated_images.append(full_path)
854
- print(f"[REACT-DEBUG] Added generated image (cwd): {full_path}")
855
848
  else:
856
849
  # Just add the URL path anyway - let get_llm_response handle it
857
850
  generated_images.append(local_path)
858
- print(f"[REACT-DEBUG] Added generated image (not found, using anyway): {local_path}")
859
851
 
860
852
  # Truncate output for context to avoid sending huge base64 data back to LLM
861
853
  output_for_context = str(output)[:8000] + "..." if len(str(output)) > 8000 else str(output)
@@ -871,12 +863,11 @@ Instructions:
871
863
  if not decision or decision == {}:
872
864
  if jinxs and iteration < max_iterations - 1:
873
865
  # Retry with explicit instruction to use a jinx
874
- print(f"[REACT-DEBUG] Empty decision on iteration {iteration}, retrying with clearer prompt")
875
866
  context = f"You MUST use one of these tools to complete the task: {list(jinxs.keys())}. Return JSON with action and inputs."
876
867
  continue
877
868
  else:
878
869
  # Last resort: get a text response
879
- print(f"[REACT-DEBUG] Empty decision, getting text response instead")
870
+ pass
880
871
  current_messages.append({"role": "user", "content": command})
881
872
  fallback_response = get_llm_response(
882
873
  command,
@@ -610,7 +610,8 @@ class CommandHistory:
610
610
  Column('team', String(100)),
611
611
  Column('reasoning_content', Text), # For thinking tokens / chain of thought
612
612
  Column('tool_calls', Text), # JSON array of tool calls made by assistant
613
- Column('tool_results', Text) # JSON array of tool call results
613
+ Column('tool_results', Text), # JSON array of tool call results
614
+ Column('parent_message_id', String(50)) # Links assistant response to parent user message for broadcast grouping
614
615
  )
615
616
 
616
617
  Table('message_attachments', metadata,
@@ -676,6 +677,14 @@ class CommandHistory:
676
677
  metadata.create_all(self.engine, checkfirst=True)
677
678
  init_kg_schema(self.engine)
678
679
 
680
+ # Add parent_message_id column if it doesn't exist (for broadcast grouping)
681
+ if 'sqlite' in str(self.engine.url):
682
+ with self.engine.begin() as conn:
683
+ try:
684
+ conn.execute(text("ALTER TABLE conversation_history ADD COLUMN parent_message_id VARCHAR(50)"))
685
+ except Exception:
686
+ pass # Column already exists
687
+
679
688
  def _setup_execution_triggers(self):
680
689
  if 'sqlite' in str(self.engine.url):
681
690
  with self.engine.begin() as conn:
@@ -857,6 +866,7 @@ class CommandHistory:
857
866
  reasoning_content=None,
858
867
  tool_calls=None,
859
868
  tool_results=None,
869
+ parent_message_id=None,
860
870
  ):
861
871
  if isinstance(content, (dict, list)):
862
872
  content = json.dumps(content, cls=CustomJSONEncoder)
@@ -872,14 +882,14 @@ class CommandHistory:
872
882
 
873
883
  stmt = """
874
884
  INSERT INTO conversation_history
875
- (message_id, timestamp, role, content, conversation_id, directory_path, model, provider, npc, team, reasoning_content, tool_calls, tool_results)
876
- VALUES (:message_id, :timestamp, :role, :content, :conversation_id, :directory_path, :model, :provider, :npc, :team, :reasoning_content, :tool_calls, :tool_results)
885
+ (message_id, timestamp, role, content, conversation_id, directory_path, model, provider, npc, team, reasoning_content, tool_calls, tool_results, parent_message_id)
886
+ VALUES (:message_id, :timestamp, :role, :content, :conversation_id, :directory_path, :model, :provider, :npc, :team, :reasoning_content, :tool_calls, :tool_results, :parent_message_id)
877
887
  """
878
888
  params = {
879
889
  "message_id": message_id, "timestamp": timestamp, "role": role, "content": content,
880
890
  "conversation_id": conversation_id, "directory_path": normalized_directory_path, "model": model,
881
891
  "provider": provider, "npc": npc, "team": team, "reasoning_content": reasoning_content,
882
- "tool_calls": tool_calls, "tool_results": tool_results
892
+ "tool_calls": tool_calls, "tool_results": tool_results, "parent_message_id": parent_message_id
883
893
  }
884
894
  with self.engine.begin() as conn:
885
895
  conn.execute(text(stmt), params)
@@ -1449,10 +1459,13 @@ def save_conversation_message(
1449
1459
  reasoning_content: str = None,
1450
1460
  tool_calls: List[Dict] = None,
1451
1461
  tool_results: List[Dict] = None,
1462
+ parent_message_id: str = None,
1463
+ skip_if_exists: bool = True,
1452
1464
  ):
1453
1465
  """
1454
1466
  Saves a conversation message linked to a conversation ID with optional attachments.
1455
- Now also supports reasoning_content, tool_calls, and tool_results.
1467
+ Now also supports reasoning_content, tool_calls, tool_results, and parent_message_id for broadcast grouping.
1468
+ If skip_if_exists is True and message_id already exists, skip saving to prevent duplicates.
1456
1469
  """
1457
1470
  if wd is None:
1458
1471
  wd = os.getcwd()
@@ -1460,6 +1473,12 @@ def save_conversation_message(
1460
1473
  if message_id is None:
1461
1474
  message_id = generate_message_id()
1462
1475
 
1476
+ # Check if message already exists to prevent duplicates
1477
+ if skip_if_exists and message_id:
1478
+ existing = command_history.get_message_by_id(message_id)
1479
+ if existing:
1480
+ print(f"[SAVE_MSG] Skipping save - message_id {message_id} already exists")
1481
+ return None
1463
1482
 
1464
1483
  return command_history.add_conversation(
1465
1484
  message_id,
@@ -1475,7 +1494,8 @@ def save_conversation_message(
1475
1494
  attachments=attachments,
1476
1495
  reasoning_content=reasoning_content,
1477
1496
  tool_calls=tool_calls,
1478
- tool_results=tool_results)
1497
+ tool_results=tool_results,
1498
+ parent_message_id=parent_message_id)
1479
1499
  def retrieve_last_conversation(
1480
1500
  command_history: CommandHistory, conversation_id: str
1481
1501
  ) -> str: