npcpy 1.0.26__py3-none-any.whl → 1.2.32__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.
Files changed (148) hide show
  1. npcpy/__init__.py +0 -7
  2. npcpy/data/audio.py +16 -99
  3. npcpy/data/image.py +43 -42
  4. npcpy/data/load.py +83 -124
  5. npcpy/data/text.py +28 -28
  6. npcpy/data/video.py +8 -32
  7. npcpy/data/web.py +51 -23
  8. npcpy/ft/diff.py +110 -0
  9. npcpy/ft/ge.py +115 -0
  10. npcpy/ft/memory_trainer.py +171 -0
  11. npcpy/ft/model_ensembler.py +357 -0
  12. npcpy/ft/rl.py +360 -0
  13. npcpy/ft/sft.py +248 -0
  14. npcpy/ft/usft.py +128 -0
  15. npcpy/gen/audio_gen.py +24 -0
  16. npcpy/gen/embeddings.py +13 -13
  17. npcpy/gen/image_gen.py +262 -117
  18. npcpy/gen/response.py +615 -415
  19. npcpy/gen/video_gen.py +53 -7
  20. npcpy/llm_funcs.py +1869 -437
  21. npcpy/main.py +1 -1
  22. npcpy/memory/command_history.py +844 -510
  23. npcpy/memory/kg_vis.py +833 -0
  24. npcpy/memory/knowledge_graph.py +892 -1845
  25. npcpy/memory/memory_processor.py +81 -0
  26. npcpy/memory/search.py +188 -90
  27. npcpy/mix/debate.py +192 -3
  28. npcpy/npc_compiler.py +1672 -801
  29. npcpy/npc_sysenv.py +593 -1266
  30. npcpy/serve.py +3120 -0
  31. npcpy/sql/ai_function_tools.py +257 -0
  32. npcpy/sql/database_ai_adapters.py +186 -0
  33. npcpy/sql/database_ai_functions.py +163 -0
  34. npcpy/sql/model_runner.py +19 -19
  35. npcpy/sql/npcsql.py +706 -507
  36. npcpy/sql/sql_model_compiler.py +156 -0
  37. npcpy/tools.py +183 -0
  38. npcpy/work/plan.py +13 -279
  39. npcpy/work/trigger.py +3 -3
  40. npcpy-1.2.32.dist-info/METADATA +803 -0
  41. npcpy-1.2.32.dist-info/RECORD +54 -0
  42. npcpy/data/dataframes.py +0 -171
  43. npcpy/memory/deep_research.py +0 -125
  44. npcpy/memory/sleep.py +0 -557
  45. npcpy/modes/_state.py +0 -78
  46. npcpy/modes/alicanto.py +0 -1075
  47. npcpy/modes/guac.py +0 -785
  48. npcpy/modes/mcp_npcsh.py +0 -822
  49. npcpy/modes/npc.py +0 -213
  50. npcpy/modes/npcsh.py +0 -1158
  51. npcpy/modes/plonk.py +0 -409
  52. npcpy/modes/pti.py +0 -234
  53. npcpy/modes/serve.py +0 -1637
  54. npcpy/modes/spool.py +0 -312
  55. npcpy/modes/wander.py +0 -549
  56. npcpy/modes/yap.py +0 -572
  57. npcpy/npc_team/alicanto.npc +0 -2
  58. npcpy/npc_team/alicanto.png +0 -0
  59. npcpy/npc_team/assembly_lines/test_pipeline.py +0 -181
  60. npcpy/npc_team/corca.npc +0 -13
  61. npcpy/npc_team/foreman.npc +0 -7
  62. npcpy/npc_team/frederic.npc +0 -6
  63. npcpy/npc_team/frederic4.png +0 -0
  64. npcpy/npc_team/guac.png +0 -0
  65. npcpy/npc_team/jinxs/automator.jinx +0 -18
  66. npcpy/npc_team/jinxs/bash_executer.jinx +0 -31
  67. npcpy/npc_team/jinxs/calculator.jinx +0 -11
  68. npcpy/npc_team/jinxs/edit_file.jinx +0 -96
  69. npcpy/npc_team/jinxs/file_chat.jinx +0 -14
  70. npcpy/npc_team/jinxs/gui_controller.jinx +0 -28
  71. npcpy/npc_team/jinxs/image_generation.jinx +0 -29
  72. npcpy/npc_team/jinxs/internet_search.jinx +0 -30
  73. npcpy/npc_team/jinxs/local_search.jinx +0 -152
  74. npcpy/npc_team/jinxs/npcsh_executor.jinx +0 -31
  75. npcpy/npc_team/jinxs/python_executor.jinx +0 -8
  76. npcpy/npc_team/jinxs/screen_cap.jinx +0 -25
  77. npcpy/npc_team/jinxs/sql_executor.jinx +0 -33
  78. npcpy/npc_team/kadiefa.npc +0 -3
  79. npcpy/npc_team/kadiefa.png +0 -0
  80. npcpy/npc_team/npcsh.ctx +0 -9
  81. npcpy/npc_team/npcsh_sibiji.png +0 -0
  82. npcpy/npc_team/plonk.npc +0 -2
  83. npcpy/npc_team/plonk.png +0 -0
  84. npcpy/npc_team/plonkjr.npc +0 -2
  85. npcpy/npc_team/plonkjr.png +0 -0
  86. npcpy/npc_team/sibiji.npc +0 -5
  87. npcpy/npc_team/sibiji.png +0 -0
  88. npcpy/npc_team/spool.png +0 -0
  89. npcpy/npc_team/templates/analytics/celona.npc +0 -0
  90. npcpy/npc_team/templates/hr_support/raone.npc +0 -0
  91. npcpy/npc_team/templates/humanities/eriane.npc +0 -4
  92. npcpy/npc_team/templates/it_support/lineru.npc +0 -0
  93. npcpy/npc_team/templates/marketing/slean.npc +0 -4
  94. npcpy/npc_team/templates/philosophy/maurawa.npc +0 -0
  95. npcpy/npc_team/templates/sales/turnic.npc +0 -4
  96. npcpy/npc_team/templates/software/welxor.npc +0 -0
  97. npcpy/npc_team/yap.png +0 -0
  98. npcpy/routes.py +0 -958
  99. npcpy/work/mcp_helpers.py +0 -357
  100. npcpy/work/mcp_server.py +0 -194
  101. npcpy-1.0.26.data/data/npcpy/npc_team/alicanto.npc +0 -2
  102. npcpy-1.0.26.data/data/npcpy/npc_team/alicanto.png +0 -0
  103. npcpy-1.0.26.data/data/npcpy/npc_team/automator.jinx +0 -18
  104. npcpy-1.0.26.data/data/npcpy/npc_team/bash_executer.jinx +0 -31
  105. npcpy-1.0.26.data/data/npcpy/npc_team/calculator.jinx +0 -11
  106. npcpy-1.0.26.data/data/npcpy/npc_team/celona.npc +0 -0
  107. npcpy-1.0.26.data/data/npcpy/npc_team/corca.npc +0 -13
  108. npcpy-1.0.26.data/data/npcpy/npc_team/edit_file.jinx +0 -96
  109. npcpy-1.0.26.data/data/npcpy/npc_team/eriane.npc +0 -4
  110. npcpy-1.0.26.data/data/npcpy/npc_team/file_chat.jinx +0 -14
  111. npcpy-1.0.26.data/data/npcpy/npc_team/foreman.npc +0 -7
  112. npcpy-1.0.26.data/data/npcpy/npc_team/frederic.npc +0 -6
  113. npcpy-1.0.26.data/data/npcpy/npc_team/frederic4.png +0 -0
  114. npcpy-1.0.26.data/data/npcpy/npc_team/guac.png +0 -0
  115. npcpy-1.0.26.data/data/npcpy/npc_team/gui_controller.jinx +0 -28
  116. npcpy-1.0.26.data/data/npcpy/npc_team/image_generation.jinx +0 -29
  117. npcpy-1.0.26.data/data/npcpy/npc_team/internet_search.jinx +0 -30
  118. npcpy-1.0.26.data/data/npcpy/npc_team/kadiefa.npc +0 -3
  119. npcpy-1.0.26.data/data/npcpy/npc_team/kadiefa.png +0 -0
  120. npcpy-1.0.26.data/data/npcpy/npc_team/lineru.npc +0 -0
  121. npcpy-1.0.26.data/data/npcpy/npc_team/local_search.jinx +0 -152
  122. npcpy-1.0.26.data/data/npcpy/npc_team/maurawa.npc +0 -0
  123. npcpy-1.0.26.data/data/npcpy/npc_team/npcsh.ctx +0 -9
  124. npcpy-1.0.26.data/data/npcpy/npc_team/npcsh_executor.jinx +0 -31
  125. npcpy-1.0.26.data/data/npcpy/npc_team/npcsh_sibiji.png +0 -0
  126. npcpy-1.0.26.data/data/npcpy/npc_team/plonk.npc +0 -2
  127. npcpy-1.0.26.data/data/npcpy/npc_team/plonk.png +0 -0
  128. npcpy-1.0.26.data/data/npcpy/npc_team/plonkjr.npc +0 -2
  129. npcpy-1.0.26.data/data/npcpy/npc_team/plonkjr.png +0 -0
  130. npcpy-1.0.26.data/data/npcpy/npc_team/python_executor.jinx +0 -8
  131. npcpy-1.0.26.data/data/npcpy/npc_team/raone.npc +0 -0
  132. npcpy-1.0.26.data/data/npcpy/npc_team/screen_cap.jinx +0 -25
  133. npcpy-1.0.26.data/data/npcpy/npc_team/sibiji.npc +0 -5
  134. npcpy-1.0.26.data/data/npcpy/npc_team/sibiji.png +0 -0
  135. npcpy-1.0.26.data/data/npcpy/npc_team/slean.npc +0 -4
  136. npcpy-1.0.26.data/data/npcpy/npc_team/spool.png +0 -0
  137. npcpy-1.0.26.data/data/npcpy/npc_team/sql_executor.jinx +0 -33
  138. npcpy-1.0.26.data/data/npcpy/npc_team/test_pipeline.py +0 -181
  139. npcpy-1.0.26.data/data/npcpy/npc_team/turnic.npc +0 -4
  140. npcpy-1.0.26.data/data/npcpy/npc_team/welxor.npc +0 -0
  141. npcpy-1.0.26.data/data/npcpy/npc_team/yap.png +0 -0
  142. npcpy-1.0.26.dist-info/METADATA +0 -827
  143. npcpy-1.0.26.dist-info/RECORD +0 -139
  144. npcpy-1.0.26.dist-info/entry_points.txt +0 -11
  145. /npcpy/{modes → ft}/__init__.py +0 -0
  146. {npcpy-1.0.26.dist-info → npcpy-1.2.32.dist-info}/WHEEL +0 -0
  147. {npcpy-1.0.26.dist-info → npcpy-1.2.32.dist-info}/licenses/LICENSE +0 -0
  148. {npcpy-1.0.26.dist-info → npcpy-1.2.32.dist-info}/top_level.txt +0 -0
npcpy/mix/debate.py CHANGED
@@ -1,6 +1,195 @@
1
- def run_debate():
1
+ import random
2
+ import json
3
+ from collections import Counter
4
+ from npcpy.npc_compiler import NPC
5
+ from npcpy.llm_funcs import get_llm_response
6
+
7
+ def generate_npcs(
8
+ generation_theme: str,
9
+ num_npcs: int,
10
+ model: str,
11
+ provider: str
12
+ ) -> list:
13
+ """
14
+ Generates a list of unique NPC agents by iterating through LLM calls.
15
+ One NPC is generated per call for robustness.
16
+ """
17
+ print(f"\n--- Generating {num_npcs} NPCs iteratively based on theme: '{generation_theme}' ---")
18
+ npcs = []
19
+ generated_names = []
20
+
21
+ for i in range(num_npcs):
22
+ avoidance_prompt = f"Avoid creating a persona similar to these already generated: {', '.join(generated_names)}" if generated_names else ""
23
+
24
+ prompt = f"""
25
+ Based on the creative theme "{generation_theme}", invent one completely original persona.
26
+ Do not use any existing historical, literary, or public figures.
27
+ {avoidance_prompt}
28
+
29
+ Return the result as a single, valid JSON object with two keys: "name" and "primary_directive".
30
+
31
+ Here is an example: """ + """
32
+ {
33
+ "primary_directive": "a directive for the persona",
34
+ "name": "a name for the persona"
35
+ }
36
+ """
37
+ response = get_llm_response(prompt, model=model, provider=provider, format='json')
38
+ npc_data = response['response']
39
+
40
+ npc = NPC(
41
+ name=npc_data['name'],
42
+ primary_directive=npc_data['primary_directive'],
43
+ model='gemma3:4b',
44
+ provider='ollama'
45
+ )
46
+ npcs.append(npc)
47
+ generated_names.append(npc.name)
48
+ print(f"({i+1}/{num_npcs}) Successfully generated: {npc.name}")
49
+
50
+ print("--- NPC Generation Complete ---")
51
+ return npcs
52
+
53
+ def run_debate(agent1: NPC, argument1: str, agent2: NPC, argument2: str, turns: int = 2):
2
54
  """
3
- Run a debate between two agents.
55
+ Runs a debate, correctly passing the 'request' argument to get_llm_response.
4
56
  """
57
+ print(f"\n--- Debate ---")
58
+ print(f"{agent1.name} champions: '{argument1[:100]}...'")
59
+ print(f"{agent2.name} champions: '{argument2[:100]}...'")
60
+ messages = [{'role': 'system', 'content': f"You are in a debate. Argue for your assigned position."}]
61
+ opening_request = f"{agent1.name}, make your opening statement for the argument: '{argument1}'"
62
+ response = agent1.get_llm_response(opening_request, messages=messages)
63
+ messages.extend([
64
+ {'role': 'user', 'content': opening_request},
65
+ {'role': 'assistant', 'name': agent1.name, 'content': response['response']}
66
+ ])
67
+ current_agent, current_arg, other_agent, other_arg = (agent2, argument2, agent1, argument1)
68
+ for _ in range(turns * 2 - 1):
69
+ turn_request = f"{current_agent.name}, respond to {other_agent.name} and defend your position: '{current_arg}'"
70
+ response = current_agent.get_llm_response(turn_request, messages=messages)
71
+ messages.extend([
72
+ {'role': 'user', 'content': turn_request},
73
+ {'role': 'assistant', 'name': current_agent.name, 'content': response['response']}
74
+ ])
75
+ current_agent, other_agent = other_agent, current_agent
76
+ current_arg, other_arg = other_arg, current_arg
77
+ return messages
78
+
79
+ def run_synthesis_tournament(initial_contenders: list, initial_topic: str, model: str, provider: str):
80
+ """
81
+ Runs a tournament where the final synthesis is weighted by the judges' votes.
82
+ """
83
+ if len(initial_contenders) & (len(initial_contenders) - 1) != 0 or len(initial_contenders) == 0:
84
+ print("Error: Number of contenders must be a power of 2.")
85
+ return None
86
+
87
+ print(f"===== Starting Synthesis Tournament on: {initial_topic} =====")
88
+ evolving_ideas = {npc.name: (npc, initial_topic) for npc in initial_contenders}
89
+
90
+ round_num = 1
91
+ while len(evolving_ideas) > 1:
92
+ print(f"\n\n======= ROUND {round_num} =======\n")
93
+
94
+ contender_names = list(evolving_ideas.keys())
95
+ random.shuffle(contender_names)
96
+ next_round_ideas = {}
97
+
98
+ for i in range(0, len(contender_names), 2):
99
+ name1, name2 = contender_names[i], contender_names[i+1]
100
+ agent1, argument1 = evolving_ideas[name1]
101
+ agent2, argument2 = evolving_ideas[name2]
102
+
103
+ debate_history = run_debate(agent1, argument1, agent2, argument2)
104
+ transcript = "\n".join([f"{msg.get('name')}: {msg['content']}" for msg in debate_history if msg.get('role') == 'assistant'])
105
+
106
+ judge_theme = f"Judges evaluating two opposing arguments: '{argument1[:50]}...' versus '{argument2[:50]}...'"
107
+ judging_panel = generate_npcs(judge_theme, 3, model=model, provider=provider)
108
+
109
+
110
+ votes = []
111
+ print("\n--- The Jury Votes ---")
112
+ vote_prompt = f"""
113
+ After reviewing the debate transcript, which debater's argument was more foundational and persuasive: {agent1.name} or {agent2.name}?
114
+ Respond with ONLY the name of the winner. Do not add any other text or explanation.
115
+
116
+ TRANSCRIPT:
117
+ ---
118
+ {transcript}
119
+ ---
120
+
121
+ EXAMPLE RESPONSE:
122
+ {agent1.name}
123
+ """
124
+ for judge in judging_panel:
125
+ response_text = judge.get_llm_response(vote_prompt)['response'].strip()
126
+ vote = agent1.name if agent1.name in response_text else agent2.name
127
+ votes.append(vote)
128
+ print(f"{judge.name} votes for: {vote}")
129
+
130
+
131
+ vote_counts = Counter(votes)
132
+ winner_name, winner_votes = vote_counts.most_common(1)[0]
133
+
134
+ advancing_agent = agent1 if winner_name == agent1.name else agent2
135
+ losing_agent = agent2 if winner_name == agent1.name else agent1
136
+ loser_votes = len(votes) - winner_votes
137
+
138
+ winning_argument = argument1 if winner_name == agent1.name else argument2
139
+ losing_argument = argument2 if winner_name == agent1.name else winning_argument
140
+
141
+
142
+ final_synthesis_prompt = f"""
143
+ You are a Master Synthesizer. Your task is to create a new, superior argument by merging two competing ideas based on a jury's verdict.
144
+
145
+ The jury's vote was {winner_votes} for {winner_name}'s argument and {loser_votes} for {losing_agent.name}'s argument.
146
+
147
+ Winning Argument ({winner_name}): "{winning_argument}"
148
+ Losing Argument ({losing_agent.name}): "{losing_argument}"
149
+
150
+ Create a single, potent, synthesized argument. Because the vote was {winner_votes} to {loser_votes}, the new argument must be primarily based on the winning argument, but it MUST incorporate the most valuable and resilient points from the losing argument to make the final concept stronger. The final output should be ONLY the new, synthesized argument paragraph. Do not add any introductory phrases.
151
+
152
+ EXAMPLE OUTPUT:
153
+ A system's survival is paramount, as no component can thrive without the whole. However, this survival mandate is not absolute; it must be tempered by a foundational respect for the well-being of its components, integrating their feedback as a vital sign of systemic health, thereby proving that true resilience is a synthesis of centralized stability and distributed vitality.
154
+ """
155
+
156
+ compressed_idea = get_llm_response(final_synthesis_prompt, model=model, provider=provider)['response'].strip()
157
+
158
+ print(f"\n--- Round Result ---")
159
+ print(f"Vote: {winner_votes} for {winner_name}, {loser_votes} for {losing_agent.name}")
160
+ print(f"Carrier Agent: {advancing_agent.name}")
161
+ print(f"Weighted Synthesized Idea: '{compressed_idea[:150]}...'")
162
+
163
+ next_round_ideas[advancing_agent.name] = (advancing_agent, compressed_idea)
164
+
165
+ evolving_ideas = next_round_ideas
166
+ round_num += 1
167
+
168
+ final_agent, final_idea = list(evolving_ideas.values())[0]
169
+ print(f"\n\n===== Tournament Complete! ======")
170
+ print(f"The Final Carrier Agent is: {final_agent.name}")
171
+ print(f"The Final Compressed Idea is: {final_idea}")
172
+ return final_agent, final_idea
173
+
174
+ if __name__ == "__main__":
175
+ GENERATION_MODEL = 'llama3.2'
176
+ GENERATION_PROVIDER = 'ollama'
177
+
178
+ contender_theme = "Entities from a dimension where logic is a fluid, not a constant"
179
+ contenders = generate_npcs(
180
+ generation_theme=contender_theme,
181
+ num_npcs=4,
182
+ model=GENERATION_MODEL,
183
+ provider=GENERATION_PROVIDER
184
+ )
5
185
 
6
- pass
186
+ if contenders and len(contenders) >= 4:
187
+ initial_topic = "Should a system prioritize its own survival above the well-being of its components?"
188
+ run_synthesis_tournament(
189
+ initial_contenders=contenders,
190
+ initial_topic=initial_topic,
191
+ model=GENERATION_MODEL,
192
+ provider=GENERATION_PROVIDER
193
+ )
194
+ else:
195
+ print("Could not run tournament: requires at least 4 contenders to be generated.")