npcsh 1.0.34__tar.gz → 1.0.35__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.
Files changed (52) hide show
  1. {npcsh-1.0.34 → npcsh-1.0.35}/PKG-INFO +1 -1
  2. {npcsh-1.0.34 → npcsh-1.0.35}/npcsh/_state.py +214 -42
  3. {npcsh-1.0.34 → npcsh-1.0.35}/npcsh.egg-info/PKG-INFO +1 -1
  4. {npcsh-1.0.34 → npcsh-1.0.35}/setup.py +1 -1
  5. {npcsh-1.0.34 → npcsh-1.0.35}/LICENSE +0 -0
  6. {npcsh-1.0.34 → npcsh-1.0.35}/README.md +0 -0
  7. {npcsh-1.0.34 → npcsh-1.0.35}/npcsh/__init__.py +0 -0
  8. {npcsh-1.0.34 → npcsh-1.0.35}/npcsh/alicanto.py +0 -0
  9. {npcsh-1.0.34 → npcsh-1.0.35}/npcsh/corca.py +0 -0
  10. {npcsh-1.0.34 → npcsh-1.0.35}/npcsh/guac.py +0 -0
  11. {npcsh-1.0.34 → npcsh-1.0.35}/npcsh/mcp_helpers.py +0 -0
  12. {npcsh-1.0.34 → npcsh-1.0.35}/npcsh/mcp_server.py +0 -0
  13. {npcsh-1.0.34 → npcsh-1.0.35}/npcsh/npc.py +0 -0
  14. {npcsh-1.0.34 → npcsh-1.0.35}/npcsh/npc_team/alicanto.npc +0 -0
  15. {npcsh-1.0.34 → npcsh-1.0.35}/npcsh/npc_team/alicanto.png +0 -0
  16. {npcsh-1.0.34 → npcsh-1.0.35}/npcsh/npc_team/corca.npc +0 -0
  17. {npcsh-1.0.34 → npcsh-1.0.35}/npcsh/npc_team/corca.png +0 -0
  18. {npcsh-1.0.34 → npcsh-1.0.35}/npcsh/npc_team/foreman.npc +0 -0
  19. {npcsh-1.0.34 → npcsh-1.0.35}/npcsh/npc_team/frederic.npc +0 -0
  20. {npcsh-1.0.34 → npcsh-1.0.35}/npcsh/npc_team/frederic4.png +0 -0
  21. {npcsh-1.0.34 → npcsh-1.0.35}/npcsh/npc_team/guac.png +0 -0
  22. {npcsh-1.0.34 → npcsh-1.0.35}/npcsh/npc_team/jinxs/bash_executer.jinx +0 -0
  23. {npcsh-1.0.34 → npcsh-1.0.35}/npcsh/npc_team/jinxs/edit_file.jinx +0 -0
  24. {npcsh-1.0.34 → npcsh-1.0.35}/npcsh/npc_team/jinxs/image_generation.jinx +0 -0
  25. {npcsh-1.0.34 → npcsh-1.0.35}/npcsh/npc_team/jinxs/internet_search.jinx +0 -0
  26. {npcsh-1.0.34 → npcsh-1.0.35}/npcsh/npc_team/jinxs/python_executor.jinx +0 -0
  27. {npcsh-1.0.34 → npcsh-1.0.35}/npcsh/npc_team/jinxs/screen_cap.jinx +0 -0
  28. {npcsh-1.0.34 → npcsh-1.0.35}/npcsh/npc_team/kadiefa.npc +0 -0
  29. {npcsh-1.0.34 → npcsh-1.0.35}/npcsh/npc_team/kadiefa.png +0 -0
  30. {npcsh-1.0.34 → npcsh-1.0.35}/npcsh/npc_team/npcsh.ctx +0 -0
  31. {npcsh-1.0.34 → npcsh-1.0.35}/npcsh/npc_team/npcsh_sibiji.png +0 -0
  32. {npcsh-1.0.34 → npcsh-1.0.35}/npcsh/npc_team/plonk.npc +0 -0
  33. {npcsh-1.0.34 → npcsh-1.0.35}/npcsh/npc_team/plonk.png +0 -0
  34. {npcsh-1.0.34 → npcsh-1.0.35}/npcsh/npc_team/plonkjr.npc +0 -0
  35. {npcsh-1.0.34 → npcsh-1.0.35}/npcsh/npc_team/plonkjr.png +0 -0
  36. {npcsh-1.0.34 → npcsh-1.0.35}/npcsh/npc_team/sibiji.npc +0 -0
  37. {npcsh-1.0.34 → npcsh-1.0.35}/npcsh/npc_team/sibiji.png +0 -0
  38. {npcsh-1.0.34 → npcsh-1.0.35}/npcsh/npc_team/spool.png +0 -0
  39. {npcsh-1.0.34 → npcsh-1.0.35}/npcsh/npc_team/yap.png +0 -0
  40. {npcsh-1.0.34 → npcsh-1.0.35}/npcsh/npcsh.py +0 -0
  41. {npcsh-1.0.34 → npcsh-1.0.35}/npcsh/plonk.py +0 -0
  42. {npcsh-1.0.34 → npcsh-1.0.35}/npcsh/pti.py +0 -0
  43. {npcsh-1.0.34 → npcsh-1.0.35}/npcsh/routes.py +0 -0
  44. {npcsh-1.0.34 → npcsh-1.0.35}/npcsh/spool.py +0 -0
  45. {npcsh-1.0.34 → npcsh-1.0.35}/npcsh/wander.py +0 -0
  46. {npcsh-1.0.34 → npcsh-1.0.35}/npcsh/yap.py +0 -0
  47. {npcsh-1.0.34 → npcsh-1.0.35}/npcsh.egg-info/SOURCES.txt +0 -0
  48. {npcsh-1.0.34 → npcsh-1.0.35}/npcsh.egg-info/dependency_links.txt +0 -0
  49. {npcsh-1.0.34 → npcsh-1.0.35}/npcsh.egg-info/entry_points.txt +0 -0
  50. {npcsh-1.0.34 → npcsh-1.0.35}/npcsh.egg-info/requires.txt +0 -0
  51. {npcsh-1.0.34 → npcsh-1.0.35}/npcsh.egg-info/top_level.txt +0 -0
  52. {npcsh-1.0.34 → npcsh-1.0.35}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: npcsh
3
- Version: 1.0.34
3
+ Version: 1.0.35
4
4
  Summary: npcsh is a command-line toolkit for using AI agents in novel ways.
5
5
  Home-page: https://github.com/NPC-Worldwide/npcsh
6
6
  Author: Christopher Agostino
@@ -2013,6 +2013,9 @@ def execute_slash_command(command: str,
2013
2013
 
2014
2014
  return state, colored(f"Unknown slash command, jinx, or NPC: {command_name}", "red")
2015
2015
 
2016
+
2017
+
2018
+
2016
2019
  def process_pipeline_command(
2017
2020
  cmd_segment: str,
2018
2021
  stdin_input: Optional[str],
@@ -2454,24 +2457,115 @@ def setup_shell() -> Tuple[CommandHistory, Team, Optional[NPC]]:
2454
2457
 
2455
2458
 
2456
2459
 
2460
+
2461
+ from npcpy.memory.memory_processor import MemoryApprovalQueue, MemoryItem, memory_approval_ui
2462
+ from npcpy.ft.memory_trainer import MemoryTrainer
2463
+ from npcpy.llm_funcs import get_facts
2464
+
2465
+ _memory_queue = None
2466
+
2467
+ def get_memory_queue(command_history):
2468
+ global _memory_queue
2469
+ if _memory_queue is None:
2470
+ _memory_queue = MemoryApprovalQueue(command_history)
2471
+ _memory_queue.start_background_processing()
2472
+ return _memory_queue
2473
+
2474
+ def format_memory_context(memory_examples):
2475
+ if not memory_examples:
2476
+ return ""
2477
+
2478
+ context_parts = []
2479
+
2480
+ approved_examples = memory_examples.get("approved", [])
2481
+ rejected_examples = memory_examples.get("rejected", [])
2482
+
2483
+ if approved_examples:
2484
+ context_parts.append("EXAMPLES OF GOOD MEMORIES:")
2485
+ for ex in approved_examples[:5]:
2486
+ final = ex.get("final_memory") or ex.get("initial_memory")
2487
+ context_parts.append(f"- {final}")
2488
+
2489
+ if rejected_examples:
2490
+ context_parts.append("\nEXAMPLES OF POOR MEMORIES TO AVOID:")
2491
+ for ex in rejected_examples[:3]:
2492
+ context_parts.append(f"- {ex.get('initial_memory')}")
2493
+
2494
+ if context_parts:
2495
+ context_parts.append("\nLearn from these examples to generate similar high-quality memories.")
2496
+ return "\n".join(context_parts)
2497
+
2498
+ return ""
2499
+
2500
+ def process_memory_approvals(command_history, memory_queue):
2501
+ pending_memories = memory_queue.get_approval_batch(max_items=5)
2502
+
2503
+ if not pending_memories:
2504
+ return
2505
+
2506
+ print(f"\n🧠 Processing {len(pending_memories)} memories...")
2507
+
2508
+ try:
2509
+ trainer = MemoryTrainer()
2510
+ auto_processed = []
2511
+ need_human_review = []
2512
+
2513
+ for memory in pending_memories:
2514
+ result = trainer.auto_approve_memory(
2515
+ memory['content'],
2516
+ memory['context'],
2517
+ confidence_threshold=0.85
2518
+ )
2519
+
2520
+ if result['auto_processed']:
2521
+ auto_processed.append((memory, result))
2522
+ else:
2523
+ need_human_review.append(memory)
2524
+
2525
+ for memory, result in auto_processed:
2526
+ command_history.update_memory_status(
2527
+ memory['memory_id'],
2528
+ result['action']
2529
+ )
2530
+ print(f" Auto-{result['action']}: {memory['content'][:50]}... (confidence: {result['confidence']:.2f})")
2531
+
2532
+ if need_human_review:
2533
+ approvals = memory_approval_ui(need_human_review)
2534
+
2535
+ for approval in approvals:
2536
+ command_history.update_memory_status(
2537
+ approval['memory_id'],
2538
+ approval['decision'],
2539
+ approval.get('final_memory')
2540
+ )
2541
+
2542
+ except Exception as e:
2543
+ print(f"Auto-approval failed: {e}")
2544
+ approvals = memory_approval_ui(pending_memories)
2545
+
2546
+ for approval in approvals:
2547
+ command_history.update_memory_status(
2548
+ approval['memory_id'],
2549
+ approval['decision'],
2550
+ approval.get('final_memory')
2551
+ )
2552
+
2457
2553
  def process_result(
2458
2554
  user_input: str,
2459
2555
  result_state: ShellState,
2460
2556
  output: Any,
2461
- command_history: CommandHistory,
2462
-
2463
-
2557
+ command_history: CommandHistory,
2464
2558
  ):
2465
-
2466
2559
  team_name = result_state.team.name if result_state.team else "__none__"
2467
2560
  npc_name = result_state.npc.name if isinstance(result_state.npc, NPC) else "__none__"
2468
2561
 
2469
-
2470
2562
  active_npc = result_state.npc if isinstance(result_state.npc, NPC) else NPC(
2471
2563
  name="default",
2472
2564
  model=result_state.chat_model,
2473
2565
  provider=result_state.chat_provider,
2474
- db_conn=command_history.engine)
2566
+ db_conn=command_history.engine
2567
+ )
2568
+
2475
2569
  save_conversation_message(
2476
2570
  command_history,
2477
2571
  result_state.conversation_id,
@@ -2492,23 +2586,27 @@ def process_result(
2492
2586
  provider_for_stream = output.get('provider', active_npc.provider) if isinstance(output, dict) else active_npc.provider
2493
2587
 
2494
2588
  print('\n')
2495
- if user_input =='/help':
2589
+ if user_input == '/help':
2496
2590
  render_markdown(output.get('output'))
2497
2591
  elif result_state.stream_output:
2498
-
2499
- final_output_str = print_and_process_stream_with_markdown(output_content,
2500
- model_for_stream,
2501
- provider_for_stream,
2502
- show=True)
2592
+ final_output_str = print_and_process_stream_with_markdown(
2593
+ output_content,
2594
+ model_for_stream,
2595
+ provider_for_stream,
2596
+ show=True
2597
+ )
2503
2598
  elif output_content is not None:
2504
2599
  final_output_str = str(output_content)
2505
2600
  render_markdown(final_output_str)
2506
2601
 
2507
2602
  if final_output_str:
2508
2603
  if result_state.messages:
2509
- if result_state.messages[-1].get("role") != "assistant":
2510
- result_state.messages.append({"role": "assistant",
2511
- "content": final_output_str})
2604
+ if not result_state.messages or result_state.messages[-1].get("role") != "assistant":
2605
+ result_state.messages.append({
2606
+ "role": "assistant",
2607
+ "content": final_output_str
2608
+ })
2609
+
2512
2610
  save_conversation_message(
2513
2611
  command_history,
2514
2612
  result_state.conversation_id,
@@ -2524,80 +2622,154 @@ def process_result(
2524
2622
  conversation_turn_text = f"User: {user_input}\nAssistant: {final_output_str}"
2525
2623
  engine = command_history.engine
2526
2624
 
2625
+ memory_examples = command_history.get_memory_examples_for_context(
2626
+ npc=npc_name,
2627
+ team=team_name,
2628
+ directory_path=result_state.current_path
2629
+ )
2630
+
2631
+ memory_context = format_memory_context(memory_examples)
2632
+
2633
+ approved_facts = []
2634
+ try:
2635
+ facts = get_facts(
2636
+ conversation_turn_text,
2637
+ model=active_npc.model,
2638
+ provider=active_npc.provider,
2639
+ npc=active_npc,
2640
+ context=memory_context
2641
+ )
2642
+
2643
+ if facts:
2644
+ memories_for_approval = []
2645
+ for i, fact in enumerate(facts):
2646
+ memories_for_approval.append({
2647
+ "memory_id": f"temp_{i}",
2648
+ "content": fact['statement'],
2649
+ "context": f"Type: {fact.get('type', 'unknown')}, Source: {fact.get('source_text', '')}",
2650
+ "npc": npc_name,
2651
+ "fact_data": fact
2652
+ })
2653
+
2654
+ approvals = memory_approval_ui(memories_for_approval)
2655
+
2656
+ for approval in approvals:
2657
+ fact_data = next(m['fact_data'] for m in memories_for_approval
2658
+ if m['memory_id'] == approval['memory_id'])
2659
+
2660
+ command_history.add_memory_to_database(
2661
+ message_id=f"{result_state.conversation_id}_{len(result_state.messages)}",
2662
+ conversation_id=result_state.conversation_id,
2663
+ npc=npc_name,
2664
+ team=team_name,
2665
+ directory_path=result_state.current_path,
2666
+ initial_memory=fact_data['statement'],
2667
+ status=approval['decision'],
2668
+ model=active_npc.model,
2669
+ provider=active_npc.provider,
2670
+ final_memory=approval.get('final_memory')
2671
+ )
2672
+
2673
+ if approval['decision'] in ['human-approved', 'human-edited']:
2674
+ approved_fact = {
2675
+ 'statement': approval.get('final_memory') or fact_data['statement'],
2676
+ 'source_text': fact_data.get('source_text', ''),
2677
+ 'type': fact_data.get('type', 'explicit'),
2678
+ 'generation': 0
2679
+ }
2680
+ approved_facts.append(approved_fact)
2681
+
2682
+ except Exception as e:
2683
+ print(colored(f"Memory generation error: {e}", "yellow"))
2527
2684
 
2528
- if result_state.build_kg:
2529
- import pdb
2530
- pdb.set_trace()
2685
+ if result_state.build_kg and approved_facts:
2531
2686
  try:
2532
2687
  if not should_skip_kg_processing(user_input, final_output_str):
2533
2688
  npc_kg = load_kg_from_db(engine, team_name, npc_name, result_state.current_path)
2534
2689
  evolved_npc_kg, _ = kg_evolve_incremental(
2535
2690
  existing_kg=npc_kg,
2536
- new_content_text=conversation_turn_text,
2691
+ new_facts=approved_facts,
2537
2692
  model=active_npc.model,
2538
2693
  provider=active_npc.provider,
2694
+ npc=active_npc,
2539
2695
  get_concepts=True,
2540
- link_concepts_facts = False,
2541
- link_concepts_concepts = False,
2542
- link_facts_facts = False,
2696
+ link_concepts_facts=False,
2697
+ link_concepts_concepts=False,
2698
+ link_facts_facts=False,
2699
+ )
2700
+ save_kg_to_db(
2701
+ engine,
2702
+ evolved_npc_kg,
2703
+ team_name,
2704
+ npc_name,
2705
+ result_state.current_path
2543
2706
  )
2544
- save_kg_to_db(engine,
2545
- evolved_npc_kg,
2546
- team_name,
2547
- npc_name,
2548
- result_state.current_path)
2549
2707
  except Exception as e:
2550
2708
  print(colored(f"Error during real-time KG evolution: {e}", "red"))
2551
2709
 
2552
-
2553
2710
  result_state.turn_count += 1
2554
2711
 
2555
- if result_state.turn_count > 0 and result_state.turn_count % 10 == 0:
2712
+ if result_state.turn_count % 10 == 0:
2556
2713
  print(colored("\nChecking for potential team improvements...", "cyan"))
2557
2714
  try:
2558
- summary = breathe(messages=result_state.messages[-20:],
2559
- npc=active_npc)
2715
+ summary = breathe(messages=result_state.messages[-20:], npc=active_npc)
2560
2716
  characterization = summary.get('output')
2561
2717
 
2562
2718
  if characterization and result_state.team:
2563
-
2564
- team_ctx_path = os.path.join(result_state.team.team_path, ".ctx")
2719
+ team_ctx_path = get_team_ctx_path(result_state.team.team_path)
2720
+ if not team_ctx_path:
2721
+ team_ctx_path = os.path.join(result_state.team.team_path, "team.ctx")
2722
+
2565
2723
  ctx_data = {}
2566
2724
  if os.path.exists(team_ctx_path):
2567
2725
  with open(team_ctx_path, 'r') as f:
2568
2726
  ctx_data = yaml.safe_load(f) or {}
2727
+
2569
2728
  current_context = ctx_data.get('context', '')
2570
2729
 
2571
2730
  prompt = f"""Based on this characterization: {characterization},
2572
-
2573
2731
  suggest changes (additions, deletions, edits) to the team's context.
2574
2732
  Additions need not be fully formed sentences and can simply be equations, relationships, or other plain clear items.
2575
2733
 
2576
2734
  Current Context: "{current_context}".
2577
2735
 
2578
- Respond with JSON: {{"suggestion": "Your sentence."
2579
- }}"""
2580
- response = get_llm_response(prompt,
2581
- npc=active_npc,
2582
- format="json")
2736
+ Respond with JSON: {{"suggestion": "Your sentence."}}"""
2737
+
2738
+ response = get_llm_response(
2739
+ prompt,
2740
+ npc=active_npc,
2741
+ format="json"
2742
+ )
2583
2743
  suggestion = response.get("response", {}).get("suggestion")
2584
2744
 
2585
2745
  if suggestion:
2586
2746
  new_context = (current_context + " " + suggestion).strip()
2587
- print(colored(f"{result_state.npc.name} suggests updating team context:", "yellow"))
2747
+ print(colored(f"{npc_name} suggests updating team context:", "yellow"))
2588
2748
  print(f" - OLD: {current_context}\n + NEW: {new_context}")
2589
- if input("Apply? [y/N]: ").strip().lower() == 'y':
2749
+
2750
+ choice = input("Apply? [y/N/e(dit)]: ").strip().lower()
2751
+
2752
+ if choice == 'y':
2590
2753
  ctx_data['context'] = new_context
2591
2754
  with open(team_ctx_path, 'w') as f:
2592
2755
  yaml.dump(ctx_data, f)
2593
2756
  print(colored("Team context updated.", "green"))
2757
+ elif choice == 'e':
2758
+ edited_context = input(f"Edit context [{new_context}]: ").strip()
2759
+ if edited_context:
2760
+ ctx_data['context'] = edited_context
2761
+ else:
2762
+ ctx_data['context'] = new_context
2763
+ with open(team_ctx_path, 'w') as f:
2764
+ yaml.dump(ctx_data, f)
2765
+ print(colored("Team context updated with edits.", "green"))
2594
2766
  else:
2595
2767
  print("Suggestion declined.")
2596
2768
  except Exception as e:
2597
2769
  import traceback
2598
2770
  print(colored(f"Could not generate team suggestions: {e}", "yellow"))
2599
2771
  traceback.print_exc()
2600
-
2772
+
2601
2773
  initial_state = ShellState(
2602
2774
  conversation_id=start_new_conversation(),
2603
2775
  stream_output=NPCSH_STREAM_OUTPUT,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: npcsh
3
- Version: 1.0.34
3
+ Version: 1.0.35
4
4
  Summary: npcsh is a command-line toolkit for using AI agents in novel ways.
5
5
  Home-page: https://github.com/NPC-Worldwide/npcsh
6
6
  Author: Christopher Agostino
@@ -78,7 +78,7 @@ extra_files = package_files("npcsh/npc_team/")
78
78
 
79
79
  setup(
80
80
  name="npcsh",
81
- version="1.0.34",
81
+ version="1.0.35",
82
82
  packages=find_packages(exclude=["tests*"]),
83
83
  install_requires=base_requirements, # Only install base requirements by default
84
84
  extras_require={
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