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.
- npcpy/__init__.py +0 -7
- npcpy/data/audio.py +16 -99
- npcpy/data/image.py +43 -42
- npcpy/data/load.py +83 -124
- npcpy/data/text.py +28 -28
- npcpy/data/video.py +8 -32
- npcpy/data/web.py +51 -23
- npcpy/ft/diff.py +110 -0
- npcpy/ft/ge.py +115 -0
- npcpy/ft/memory_trainer.py +171 -0
- npcpy/ft/model_ensembler.py +357 -0
- npcpy/ft/rl.py +360 -0
- npcpy/ft/sft.py +248 -0
- npcpy/ft/usft.py +128 -0
- npcpy/gen/audio_gen.py +24 -0
- npcpy/gen/embeddings.py +13 -13
- npcpy/gen/image_gen.py +262 -117
- npcpy/gen/response.py +615 -415
- npcpy/gen/video_gen.py +53 -7
- npcpy/llm_funcs.py +1869 -437
- npcpy/main.py +1 -1
- npcpy/memory/command_history.py +844 -510
- npcpy/memory/kg_vis.py +833 -0
- npcpy/memory/knowledge_graph.py +892 -1845
- npcpy/memory/memory_processor.py +81 -0
- npcpy/memory/search.py +188 -90
- npcpy/mix/debate.py +192 -3
- npcpy/npc_compiler.py +1672 -801
- npcpy/npc_sysenv.py +593 -1266
- npcpy/serve.py +3120 -0
- npcpy/sql/ai_function_tools.py +257 -0
- npcpy/sql/database_ai_adapters.py +186 -0
- npcpy/sql/database_ai_functions.py +163 -0
- npcpy/sql/model_runner.py +19 -19
- npcpy/sql/npcsql.py +706 -507
- npcpy/sql/sql_model_compiler.py +156 -0
- npcpy/tools.py +183 -0
- npcpy/work/plan.py +13 -279
- npcpy/work/trigger.py +3 -3
- npcpy-1.2.32.dist-info/METADATA +803 -0
- npcpy-1.2.32.dist-info/RECORD +54 -0
- npcpy/data/dataframes.py +0 -171
- npcpy/memory/deep_research.py +0 -125
- npcpy/memory/sleep.py +0 -557
- npcpy/modes/_state.py +0 -78
- npcpy/modes/alicanto.py +0 -1075
- npcpy/modes/guac.py +0 -785
- npcpy/modes/mcp_npcsh.py +0 -822
- npcpy/modes/npc.py +0 -213
- npcpy/modes/npcsh.py +0 -1158
- npcpy/modes/plonk.py +0 -409
- npcpy/modes/pti.py +0 -234
- npcpy/modes/serve.py +0 -1637
- npcpy/modes/spool.py +0 -312
- npcpy/modes/wander.py +0 -549
- npcpy/modes/yap.py +0 -572
- npcpy/npc_team/alicanto.npc +0 -2
- npcpy/npc_team/alicanto.png +0 -0
- npcpy/npc_team/assembly_lines/test_pipeline.py +0 -181
- npcpy/npc_team/corca.npc +0 -13
- npcpy/npc_team/foreman.npc +0 -7
- npcpy/npc_team/frederic.npc +0 -6
- npcpy/npc_team/frederic4.png +0 -0
- npcpy/npc_team/guac.png +0 -0
- npcpy/npc_team/jinxs/automator.jinx +0 -18
- npcpy/npc_team/jinxs/bash_executer.jinx +0 -31
- npcpy/npc_team/jinxs/calculator.jinx +0 -11
- npcpy/npc_team/jinxs/edit_file.jinx +0 -96
- npcpy/npc_team/jinxs/file_chat.jinx +0 -14
- npcpy/npc_team/jinxs/gui_controller.jinx +0 -28
- npcpy/npc_team/jinxs/image_generation.jinx +0 -29
- npcpy/npc_team/jinxs/internet_search.jinx +0 -30
- npcpy/npc_team/jinxs/local_search.jinx +0 -152
- npcpy/npc_team/jinxs/npcsh_executor.jinx +0 -31
- npcpy/npc_team/jinxs/python_executor.jinx +0 -8
- npcpy/npc_team/jinxs/screen_cap.jinx +0 -25
- npcpy/npc_team/jinxs/sql_executor.jinx +0 -33
- npcpy/npc_team/kadiefa.npc +0 -3
- npcpy/npc_team/kadiefa.png +0 -0
- npcpy/npc_team/npcsh.ctx +0 -9
- npcpy/npc_team/npcsh_sibiji.png +0 -0
- npcpy/npc_team/plonk.npc +0 -2
- npcpy/npc_team/plonk.png +0 -0
- npcpy/npc_team/plonkjr.npc +0 -2
- npcpy/npc_team/plonkjr.png +0 -0
- npcpy/npc_team/sibiji.npc +0 -5
- npcpy/npc_team/sibiji.png +0 -0
- npcpy/npc_team/spool.png +0 -0
- npcpy/npc_team/templates/analytics/celona.npc +0 -0
- npcpy/npc_team/templates/hr_support/raone.npc +0 -0
- npcpy/npc_team/templates/humanities/eriane.npc +0 -4
- npcpy/npc_team/templates/it_support/lineru.npc +0 -0
- npcpy/npc_team/templates/marketing/slean.npc +0 -4
- npcpy/npc_team/templates/philosophy/maurawa.npc +0 -0
- npcpy/npc_team/templates/sales/turnic.npc +0 -4
- npcpy/npc_team/templates/software/welxor.npc +0 -0
- npcpy/npc_team/yap.png +0 -0
- npcpy/routes.py +0 -958
- npcpy/work/mcp_helpers.py +0 -357
- npcpy/work/mcp_server.py +0 -194
- npcpy-1.0.26.data/data/npcpy/npc_team/alicanto.npc +0 -2
- npcpy-1.0.26.data/data/npcpy/npc_team/alicanto.png +0 -0
- npcpy-1.0.26.data/data/npcpy/npc_team/automator.jinx +0 -18
- npcpy-1.0.26.data/data/npcpy/npc_team/bash_executer.jinx +0 -31
- npcpy-1.0.26.data/data/npcpy/npc_team/calculator.jinx +0 -11
- npcpy-1.0.26.data/data/npcpy/npc_team/celona.npc +0 -0
- npcpy-1.0.26.data/data/npcpy/npc_team/corca.npc +0 -13
- npcpy-1.0.26.data/data/npcpy/npc_team/edit_file.jinx +0 -96
- npcpy-1.0.26.data/data/npcpy/npc_team/eriane.npc +0 -4
- npcpy-1.0.26.data/data/npcpy/npc_team/file_chat.jinx +0 -14
- npcpy-1.0.26.data/data/npcpy/npc_team/foreman.npc +0 -7
- npcpy-1.0.26.data/data/npcpy/npc_team/frederic.npc +0 -6
- npcpy-1.0.26.data/data/npcpy/npc_team/frederic4.png +0 -0
- npcpy-1.0.26.data/data/npcpy/npc_team/guac.png +0 -0
- npcpy-1.0.26.data/data/npcpy/npc_team/gui_controller.jinx +0 -28
- npcpy-1.0.26.data/data/npcpy/npc_team/image_generation.jinx +0 -29
- npcpy-1.0.26.data/data/npcpy/npc_team/internet_search.jinx +0 -30
- npcpy-1.0.26.data/data/npcpy/npc_team/kadiefa.npc +0 -3
- npcpy-1.0.26.data/data/npcpy/npc_team/kadiefa.png +0 -0
- npcpy-1.0.26.data/data/npcpy/npc_team/lineru.npc +0 -0
- npcpy-1.0.26.data/data/npcpy/npc_team/local_search.jinx +0 -152
- npcpy-1.0.26.data/data/npcpy/npc_team/maurawa.npc +0 -0
- npcpy-1.0.26.data/data/npcpy/npc_team/npcsh.ctx +0 -9
- npcpy-1.0.26.data/data/npcpy/npc_team/npcsh_executor.jinx +0 -31
- npcpy-1.0.26.data/data/npcpy/npc_team/npcsh_sibiji.png +0 -0
- npcpy-1.0.26.data/data/npcpy/npc_team/plonk.npc +0 -2
- npcpy-1.0.26.data/data/npcpy/npc_team/plonk.png +0 -0
- npcpy-1.0.26.data/data/npcpy/npc_team/plonkjr.npc +0 -2
- npcpy-1.0.26.data/data/npcpy/npc_team/plonkjr.png +0 -0
- npcpy-1.0.26.data/data/npcpy/npc_team/python_executor.jinx +0 -8
- npcpy-1.0.26.data/data/npcpy/npc_team/raone.npc +0 -0
- npcpy-1.0.26.data/data/npcpy/npc_team/screen_cap.jinx +0 -25
- npcpy-1.0.26.data/data/npcpy/npc_team/sibiji.npc +0 -5
- npcpy-1.0.26.data/data/npcpy/npc_team/sibiji.png +0 -0
- npcpy-1.0.26.data/data/npcpy/npc_team/slean.npc +0 -4
- npcpy-1.0.26.data/data/npcpy/npc_team/spool.png +0 -0
- npcpy-1.0.26.data/data/npcpy/npc_team/sql_executor.jinx +0 -33
- npcpy-1.0.26.data/data/npcpy/npc_team/test_pipeline.py +0 -181
- npcpy-1.0.26.data/data/npcpy/npc_team/turnic.npc +0 -4
- npcpy-1.0.26.data/data/npcpy/npc_team/welxor.npc +0 -0
- npcpy-1.0.26.data/data/npcpy/npc_team/yap.png +0 -0
- npcpy-1.0.26.dist-info/METADATA +0 -827
- npcpy-1.0.26.dist-info/RECORD +0 -139
- npcpy-1.0.26.dist-info/entry_points.txt +0 -11
- /npcpy/{modes → ft}/__init__.py +0 -0
- {npcpy-1.0.26.dist-info → npcpy-1.2.32.dist-info}/WHEEL +0 -0
- {npcpy-1.0.26.dist-info → npcpy-1.2.32.dist-info}/licenses/LICENSE +0 -0
- {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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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.")
|