npcpy 1.3.10__py3-none-any.whl → 1.3.12__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/data/audio.py +360 -0
- npcpy/gen/audio_gen.py +693 -13
- npcpy/llm_funcs.py +1 -10
- npcpy/memory/command_history.py +26 -6
- npcpy/serve.py +752 -72
- {npcpy-1.3.10.dist-info → npcpy-1.3.12.dist-info}/METADATA +1 -1
- {npcpy-1.3.10.dist-info → npcpy-1.3.12.dist-info}/RECORD +10 -10
- {npcpy-1.3.10.dist-info → npcpy-1.3.12.dist-info}/WHEEL +0 -0
- {npcpy-1.3.10.dist-info → npcpy-1.3.12.dist-info}/licenses/LICENSE +0 -0
- {npcpy-1.3.10.dist-info → npcpy-1.3.12.dist-info}/top_level.txt +0 -0
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
|
-
|
|
870
|
+
pass
|
|
880
871
|
current_messages.append({"role": "user", "content": command})
|
|
881
872
|
fallback_response = get_llm_response(
|
|
882
873
|
command,
|
npcpy/memory/command_history.py
CHANGED
|
@@ -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
|
|
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:
|