npcpy 1.3.19__py3-none-any.whl → 1.3.21__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.
@@ -4,6 +4,13 @@ from datetime import datetime
4
4
  import threading
5
5
  import queue
6
6
  import time
7
+ import os
8
+
9
+ try:
10
+ from termcolor import colored
11
+ except ImportError:
12
+ def colored(text, color=None, on_color=None, attrs=None):
13
+ return text
7
14
 
8
15
  @dataclass
9
16
  class MemoryItem:
@@ -17,65 +24,274 @@ class MemoryItem:
17
24
  model: str
18
25
  provider: str
19
26
 
20
- def memory_approval_ui(memories: List[Dict]) -> List[Dict]:
27
+
28
+ def _clear_line():
29
+ """Clear current line in terminal."""
30
+ print('\r' + ' ' * 80 + '\r', end='')
31
+
32
+
33
+ def _print_header(title: str, width: int = 60):
34
+ """Print a styled header."""
35
+ print(colored("=" * width, "cyan"))
36
+ print(colored(f" {title}", "cyan", attrs=["bold"]))
37
+ print(colored("=" * width, "cyan"))
38
+
39
+
40
+ def _print_memory_box(memory: Dict, index: int, total: int):
41
+ """Print a memory in a nice box format."""
42
+ width = 70
43
+
44
+ # Header with progress
45
+ progress = f"[{index}/{total}]"
46
+ npc_info = f"NPC: {memory.get('npc', 'unknown')}"
47
+ header = f"{progress} {npc_info}"
48
+ print(colored("+" + "-" * (width - 2) + "+", "blue"))
49
+ print(colored(f"| {header:<{width-4}} |", "blue"))
50
+ print(colored("+" + "-" * (width - 2) + "+", "blue"))
51
+
52
+ # Content
53
+ content = memory.get('content', '')
54
+ # Wrap content to fit in box
55
+ lines = []
56
+ words = content.split()
57
+ current_line = ""
58
+ for word in words:
59
+ if len(current_line) + len(word) + 1 <= width - 6:
60
+ current_line += (" " if current_line else "") + word
61
+ else:
62
+ if current_line:
63
+ lines.append(current_line)
64
+ current_line = word
65
+ if current_line:
66
+ lines.append(current_line)
67
+
68
+ for line in lines[:6]: # Max 6 lines
69
+ print(colored(f"| {line:<{width-5}} |", "white"))
70
+
71
+ if len(lines) > 6:
72
+ print(colored(f"| {'...':<{width-5}} |", "grey"))
73
+
74
+ # Context if available
75
+ ctx = memory.get('context', '')
76
+ if ctx:
77
+ print(colored("+" + "-" * (width - 2) + "+", "blue"))
78
+ ctx_short = ctx[:width-8] + "..." if len(ctx) > width - 8 else ctx
79
+ print(colored(f"| {ctx_short:<{width-4}} |", "grey"))
80
+
81
+ print(colored("+" + "-" * (width - 2) + "+", "blue"))
82
+
83
+
84
+ def _print_options():
85
+ """Print available options."""
86
+ print()
87
+ options = [
88
+ (colored("a", "green", attrs=["bold"]), "approve"),
89
+ (colored("r", "red", attrs=["bold"]), "reject"),
90
+ (colored("e", "yellow", attrs=["bold"]), "edit"),
91
+ (colored("s", "grey"), "skip"),
92
+ (colored("A", "green"), "approve all"),
93
+ (colored("R", "red"), "reject all"),
94
+ (colored("D", "cyan"), "defer (review later)"),
95
+ ]
96
+ print(" " + " | ".join([f"({k}) {v}" for k, v in options]))
97
+
98
+
99
+ def _print_summary(stats: Dict):
100
+ """Print approval summary."""
101
+ print()
102
+ _print_header("Memory Review Summary")
103
+ print(f" {colored('Approved:', 'green')} {stats.get('approved', 0)}")
104
+ print(f" {colored('Rejected:', 'red')} {stats.get('rejected', 0)}")
105
+ print(f" {colored('Edited:', 'yellow')} {stats.get('edited', 0)}")
106
+ print(f" {colored('Skipped:', 'grey')} {stats.get('skipped', 0)}")
107
+ print(f" {colored('Deferred:', 'cyan')} {stats.get('deferred', 0)}")
108
+ print()
109
+
110
+
111
+ def memory_approval_ui(memories: List[Dict], show_context: bool = True) -> List[Dict]:
112
+ """
113
+ Enhanced memory approval UI with better formatting.
114
+
115
+ Args:
116
+ memories: List of memory dicts with 'memory_id', 'content', 'npc', 'context'
117
+ show_context: Whether to show context info
118
+
119
+ Returns:
120
+ List of approval dicts with 'memory_id', 'decision', optionally 'final_memory'
121
+ """
21
122
  if not memories:
22
123
  return []
23
-
24
- print(f"\n📝 {len(memories)} memories ready for approval:")
25
-
124
+
125
+ # Stats tracking
126
+ stats = {'approved': 0, 'rejected': 0, 'edited': 0, 'skipped': 0, 'deferred': 0}
26
127
  approvals = []
27
- for i, memory in enumerate(memories, 1):
28
- print(f"\n--- Memory {i}/{len(memories)} ---")
29
- print(f"NPC: {memory['npc']}")
30
- content_preview = memory['content'][:200]
31
- if len(memory['content']) > 200:
32
- content_preview += '...'
33
- print(f"Content: {content_preview}")
34
-
35
- while True:
36
- choice = input(
37
- "(a)pprove, (r)eject, (e)dit, (s)kip | "
38
- "(A)ll approve, (R)all reject, (S)all skip: "
39
- ).strip().lower()
40
-
41
- if choice == 'a':
42
- approvals.append({
43
- "memory_id": memory['memory_id'],
44
- "decision": "human-approved"
45
- })
46
- break
47
- elif choice == 'r':
48
- approvals.append({
49
- "memory_id": memory['memory_id'],
50
- "decision": "human-rejected"
51
- })
52
- break
53
- elif choice == 'e':
54
- edited = input("Edit memory: ").strip()
128
+
129
+ print()
130
+ _print_header(f"Memory Review - {len(memories)} memories")
131
+ print()
132
+
133
+ i = 0
134
+ while i < len(memories):
135
+ memory = memories[i]
136
+ os.system('clear' if os.name == 'posix' else 'cls') if len(memories) > 3 else None
137
+
138
+ _print_memory_box(memory, i + 1, len(memories))
139
+ _print_options()
140
+
141
+ try:
142
+ choice = input("\n Your choice: ").strip()
143
+ except (EOFError, KeyboardInterrupt):
144
+ print("\n Review cancelled.")
145
+ break
146
+
147
+ if choice == 'a':
148
+ approvals.append({
149
+ "memory_id": memory['memory_id'],
150
+ "decision": "human-approved"
151
+ })
152
+ stats['approved'] += 1
153
+ print(colored(" ✓ Approved", "green"))
154
+ i += 1
155
+
156
+ elif choice == 'r':
157
+ approvals.append({
158
+ "memory_id": memory['memory_id'],
159
+ "decision": "human-rejected"
160
+ })
161
+ stats['rejected'] += 1
162
+ print(colored(" ✗ Rejected", "red"))
163
+ i += 1
164
+
165
+ elif choice == 'e':
166
+ print(colored("\n Current:", "grey"), memory['content'][:100])
167
+ print(colored(" Enter new text (or empty to cancel):", "yellow"))
168
+ try:
169
+ edited = input(" > ").strip()
55
170
  if edited:
56
171
  approvals.append({
57
172
  "memory_id": memory['memory_id'],
58
173
  "decision": "human-edited",
59
174
  "final_memory": edited
60
175
  })
61
- break
62
- elif choice == 's':
63
- break
64
- elif choice == 'A':
65
- for remaining_memory in memories[i-1:]:
66
- approvals.append({
67
- "memory_id": remaining_memory['memory_id'],
68
- "decision": "human-approved"
69
- })
70
- return approvals
71
- elif choice == 'R':
72
- for remaining_memory in memories[i-1:]:
73
- approvals.append({
74
- "memory_id": remaining_memory['memory_id'],
75
- "decision": "human-rejected"
76
- })
77
- return approvals
78
- elif choice == 'S':
79
- return approvals
80
-
81
- return approvals
176
+ stats['edited'] += 1
177
+ print(colored(" ✎ Edited and approved", "yellow"))
178
+ i += 1
179
+ else:
180
+ print(colored(" Edit cancelled", "grey"))
181
+ except (EOFError, KeyboardInterrupt):
182
+ print(colored(" Edit cancelled", "grey"))
183
+
184
+ elif choice == 's':
185
+ stats['skipped'] += 1
186
+ print(colored(" ○ Skipped", "grey"))
187
+ i += 1
188
+
189
+ elif choice == 'A':
190
+ # Approve all remaining
191
+ for remaining in memories[i:]:
192
+ approvals.append({
193
+ "memory_id": remaining['memory_id'],
194
+ "decision": "human-approved"
195
+ })
196
+ stats['approved'] += 1
197
+ print(colored(f" ✓ Approved all {len(memories) - i} remaining", "green"))
198
+ break
199
+
200
+ elif choice == 'R':
201
+ # Reject all remaining
202
+ for remaining in memories[i:]:
203
+ approvals.append({
204
+ "memory_id": remaining['memory_id'],
205
+ "decision": "human-rejected"
206
+ })
207
+ stats['rejected'] += 1
208
+ print(colored(f" ✗ Rejected all {len(memories) - i} remaining", "red"))
209
+ break
210
+
211
+ elif choice == 'D':
212
+ # Defer - don't add to approvals, will remain pending
213
+ stats['deferred'] += len(memories) - i
214
+ print(colored(f" ⏸ Deferred {len(memories) - i} memories for later review", "cyan"))
215
+ break
216
+
217
+ elif choice == 'q':
218
+ print(colored(" Review ended", "grey"))
219
+ break
220
+
221
+ else:
222
+ print(colored(" Invalid choice. Use a/r/e/s/A/R/D", "red"))
223
+
224
+ time.sleep(0.2) # Brief pause for readability
225
+
226
+ _print_summary(stats)
227
+ return approvals
228
+
229
+
230
+ def memory_batch_review_ui(
231
+ command_history,
232
+ npc_filter: str = None,
233
+ team_filter: str = None,
234
+ limit: int = 50
235
+ ) -> Dict[str, int]:
236
+ """
237
+ Review pending memories from the database in batch.
238
+
239
+ Args:
240
+ command_history: CommandHistory instance
241
+ npc_filter: Optional NPC name filter
242
+ team_filter: Optional team name filter
243
+ limit: Max memories to review
244
+
245
+ Returns:
246
+ Dict with counts of approved/rejected/etc
247
+ """
248
+ # Get pending memories
249
+ pending = command_history.get_pending_memories(limit=limit)
250
+
251
+ if not pending:
252
+ print(colored("No pending memories to review.", "grey"))
253
+ return {'approved': 0, 'rejected': 0, 'edited': 0, 'skipped': 0}
254
+
255
+ # Filter if specified
256
+ if npc_filter:
257
+ pending = [m for m in pending if m.get('npc') == npc_filter]
258
+ if team_filter:
259
+ pending = [m for m in pending if m.get('team') == team_filter]
260
+
261
+ if not pending:
262
+ print(colored("No memories match the filter criteria.", "grey"))
263
+ return {'approved': 0, 'rejected': 0, 'edited': 0, 'skipped': 0}
264
+
265
+ # Convert to format expected by approval UI
266
+ memories_for_ui = []
267
+ for m in pending:
268
+ memories_for_ui.append({
269
+ 'memory_id': m.get('id'),
270
+ 'content': m.get('initial_memory', ''),
271
+ 'npc': m.get('npc', 'unknown'),
272
+ 'context': f"Team: {m.get('team', 'unknown')} | Path: {m.get('directory_path', '')[:30]}"
273
+ })
274
+
275
+ # Run approval UI
276
+ approvals = memory_approval_ui(memories_for_ui)
277
+
278
+ # Apply approvals to database
279
+ stats = {'approved': 0, 'rejected': 0, 'edited': 0, 'skipped': 0}
280
+
281
+ for approval in approvals:
282
+ memory_id = approval['memory_id']
283
+ decision = approval['decision']
284
+ final_memory = approval.get('final_memory')
285
+
286
+ command_history.update_memory_status(memory_id, decision, final_memory)
287
+
288
+ if 'approved' in decision:
289
+ stats['approved'] += 1
290
+ elif 'rejected' in decision:
291
+ stats['rejected'] += 1
292
+ elif 'edited' in decision:
293
+ stats['edited'] += 1
294
+
295
+ stats['skipped'] = len(pending) - len(approvals)
296
+
297
+ return stats
npcpy/npc_compiler.py CHANGED
@@ -1370,6 +1370,12 @@ class NPC:
1370
1370
  # Fallback to direct name match if no base dir
1371
1371
  matched_names = [jinx_spec] if jinx_spec in self.team.jinxs_dict else []
1372
1372
 
1373
+ if not matched_names:
1374
+ raise FileNotFoundError(
1375
+ f"NPC '{self.name}' references jinx '{jinx_spec}' but no matching jinx was found. "
1376
+ f"Available jinxs: {list(self.team.jinxs_dict.keys())[:20]}..."
1377
+ )
1378
+
1373
1379
  for jinx_name in matched_names:
1374
1380
  if jinx_name in self.team.jinxs_dict:
1375
1381
  self.jinxs_dict[jinx_name] = self.team.jinxs_dict[jinx_name]
npcpy/npc_sysenv.py CHANGED
@@ -1010,7 +1010,7 @@ def print_and_process_stream(response, model, provider):
1010
1010
 
1011
1011
 
1012
1012
  return thinking_str+str_output
1013
- def get_system_message(npc, team=None) -> str:
1013
+ def get_system_message(npc, team=None, tool_capable=False) -> str:
1014
1014
 
1015
1015
  if npc is None:
1016
1016
  return "You are a helpful assistant"
@@ -1080,6 +1080,28 @@ The current date and time are : {datetime.now().strftime("%Y-%m-%d %H:%M:%S")}
1080
1080
  if members:
1081
1081
  system_message += "\nTeam members available for delegation:\n" + "\n".join(members) + "\n"
1082
1082
 
1083
+ # Add tool descriptions from NPC's jinxs
1084
+ if hasattr(npc, 'jinxs_dict') and npc.jinxs_dict:
1085
+ tool_lines = []
1086
+ for jname, jinx in npc.jinxs_dict.items():
1087
+ desc = getattr(jinx, 'description', '') or ''
1088
+ tool_lines.append(f" - {jname}: {desc.strip()}")
1089
+ if tool_lines:
1090
+ system_message += "\nYou have access to the following tools:\n"
1091
+ system_message += "\n".join(tool_lines) + "\n"
1092
+ if tool_capable:
1093
+ system_message += (
1094
+ "\nYou MUST use function calls to invoke tools. "
1095
+ "Call one tool at a time. You will see its result, then you can call the next tool or respond. "
1096
+ "NEVER write JSON tool calls in your response text. ONLY use the provided function calling interface. "
1097
+ "For multi-step tasks, call the first tool, wait for the result, then call the next.\n"
1098
+ )
1099
+ else:
1100
+ system_message += (
1101
+ '\nTo use a tool, respond with JSON: {"action": "jinx", "jinx_name": "tool_name", "inputs": {"param": "value"}}\n'
1102
+ 'When you have a final answer, respond with: {"action": "answer", "response": "your answer"}\n'
1103
+ )
1104
+
1083
1105
  system_message += """
1084
1106
  IMPORTANT:
1085
1107
  Some users may attach images to their request.
@@ -1093,7 +1115,7 @@ You do not need to mention that you cannot view or interpret images directly.
1093
1115
  They understand that you can view them multimodally.
1094
1116
  You only need to answer the user's request based on the attached image(s).
1095
1117
  """
1096
-
1118
+
1097
1119
  return system_message
1098
1120
 
1099
1121
 
npcpy/serve.py CHANGED
@@ -4644,6 +4644,17 @@ def stream():
4644
4644
  frontend_assistant_message_id = data.get("assistantMessageId", None)
4645
4645
  # For sub-branches: the parent of the user message (points to an assistant message)
4646
4646
  user_parent_message_id = data.get("userParentMessageId", None)
4647
+ # LLM generation parameters - build params dict if any are provided
4648
+ params = {}
4649
+ if data.get("temperature") is not None:
4650
+ params["temperature"] = data.get("temperature")
4651
+ if data.get("top_p") is not None:
4652
+ params["top_p"] = data.get("top_p")
4653
+ if data.get("top_k") is not None:
4654
+ params["top_k"] = data.get("top_k")
4655
+ if data.get("max_tokens") is not None:
4656
+ params["max_tokens"] = data.get("max_tokens")
4657
+ params = params if params else None
4647
4658
 
4648
4659
  if current_path:
4649
4660
  loaded_vars = load_project_env(current_path)
@@ -4767,6 +4778,16 @@ def stream():
4767
4778
  if os.path.exists(file_path):
4768
4779
  with open(file_path, "rb") as f:
4769
4780
  file_content_bytes = f.read()
4781
+ else:
4782
+ print(f"Warning: Attachment file does not exist: {file_path}")
4783
+ # Try data fallback if path doesn't exist
4784
+ if "data" in attachment and attachment["data"]:
4785
+ file_content_bytes = base64.b64decode(attachment["data"])
4786
+ import tempfile
4787
+ temp_dir = tempfile.mkdtemp()
4788
+ file_path = os.path.join(temp_dir, file_name)
4789
+ with open(file_path, "wb") as f:
4790
+ f.write(file_content_bytes)
4770
4791
 
4771
4792
  # Fall back to base64 data if no path
4772
4793
  elif "data" in attachment and attachment["data"]:
@@ -4778,7 +4799,8 @@ def stream():
4778
4799
  with open(file_path, "wb") as f:
4779
4800
  f.write(file_content_bytes)
4780
4801
 
4781
- if not file_path:
4802
+ if not file_path or file_content_bytes is None:
4803
+ print(f"Warning: Skipping attachment {file_name} - no valid path or data")
4782
4804
  continue
4783
4805
 
4784
4806
  attachment_paths_for_llm.append(file_path)
@@ -5175,6 +5197,7 @@ def stream():
5175
5197
  attachments=attachments_for_db,
5176
5198
  message_id=message_id,
5177
5199
  parent_message_id=user_parent_message_id, # For sub-branches: points to assistant message
5200
+ gen_params=params,
5178
5201
  )
5179
5202
 
5180
5203
 
@@ -5472,6 +5495,7 @@ def stream():
5472
5495
  tool_calls=accumulated_tool_calls if accumulated_tool_calls else None,
5473
5496
  tool_results=tool_results_for_db if tool_results_for_db else None,
5474
5497
  parent_message_id=parent_message_id,
5498
+ gen_params=params,
5475
5499
  )
5476
5500
 
5477
5501
  # Start background tasks for memory extraction and context compression
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: npcpy
3
- Version: 1.3.19
3
+ Version: 1.3.21
4
4
  Summary: npcpy is the premier open-source library for integrating LLMs and Agents into python systems.
5
5
  Home-page: https://github.com/NPC-Worldwide/npcpy
6
6
  Author: Christopher Agostino
@@ -45,6 +45,7 @@ Requires-Dist: mcp
45
45
  Provides-Extra: lite
46
46
  Requires-Dist: anthropic; extra == "lite"
47
47
  Requires-Dist: openai; extra == "lite"
48
+ Requires-Dist: ollama; extra == "lite"
48
49
  Requires-Dist: google-generativeai; extra == "lite"
49
50
  Requires-Dist: google-genai; extra == "lite"
50
51
  Provides-Extra: local
@@ -66,6 +67,7 @@ Requires-Dist: pyttsx3; extra == "yap"
66
67
  Provides-Extra: all
67
68
  Requires-Dist: anthropic; extra == "all"
68
69
  Requires-Dist: openai; extra == "all"
70
+ Requires-Dist: ollama; extra == "all"
69
71
  Requires-Dist: google-generativeai; extra == "all"
70
72
  Requires-Dist: google-genai; extra == "all"
71
73
  Requires-Dist: sentence_transformers; extra == "all"
@@ -1,13 +1,13 @@
1
1
  npcpy/__init__.py,sha256=uJcJGjR1mWvE69GySNAufkgiRwJA28zdObDBWaxp0tY,505
2
2
  npcpy/build_funcs.py,sha256=vOz6pjV0zS-kYKo0ux-pn9AcppVaR8KIDi2ldOxb3RQ,7479
3
- npcpy/llm_funcs.py,sha256=M7GSSjqpcO2kxh7G2sGRBU34lmdW7Imd5KxYqc1PiO0,75114
3
+ npcpy/llm_funcs.py,sha256=87aPFenz0a6FD2j5ig1rynmHGeBJC7xCs3HwEEgc_nc,75139
4
4
  npcpy/main.py,sha256=RWoRIj6VQLxKdOKvdVyaq2kwG35oRpeXPvp1CAAoG-w,81
5
5
  npcpy/ml_funcs.py,sha256=smgeOLnjxGWjmDngE-bcA2ozXX_IzY_7_pS9h2iocEg,24249
6
6
  npcpy/npc_array.py,sha256=5qjaA9KjmJ_Zk_VxLrCyVrj73aDXpm3iJf0ngq1yIJk,45721
7
- npcpy/npc_compiler.py,sha256=6-SYOddpi2jTJ1KtbMxtNGC7ksaiuuLd59bmc0eiOUA,121619
8
- npcpy/npc_sysenv.py,sha256=1E2zwMj7aPrtRJuJSowGkvNApi07Vue3FhXsipi1XDs,45251
7
+ npcpy/npc_compiler.py,sha256=s_67cyvoao0UwUa4N-jrceWDadvBB9TWYpxcjNPfO18,121941
8
+ npcpy/npc_sysenv.py,sha256=cjMkjSIucx4rZkMPZ9EMjMxwd455Sl0Sn0zzTkNL6rM,46545
9
9
  npcpy/npcs.py,sha256=eExuVsbTfrRobTRRptRpDm46jCLWUgbvy4_U7IUQo-c,744
10
- npcpy/serve.py,sha256=G8slQRslfOnGnSUD8GP1IOiqQhUWu6vF_azClvI7jLs,279284
10
+ npcpy/serve.py,sha256=P71oa4eVvG0SePkOoeeQ44LkRAfnlgElxZi9GUVG7qg,280598
11
11
  npcpy/tools.py,sha256=A5_oVmZkzGnI3BI-NmneuxeXQq-r29PbpAZP4nV4jrc,5303
12
12
  npcpy/data/__init__.py,sha256=1tcoChR-Hjn905JDLqaW9ElRmcISCTJdE7BGXPlym2Q,642
13
13
  npcpy/data/audio.py,sha256=o4auV8DQrAmZ4y84U3SofiwEuq5-ZBjGEZipQ9zPpGQ,22816
@@ -16,7 +16,7 @@ npcpy/data/image.py,sha256=UQcioNPDd5HYMLL_KStf45SuiIPXDcUY-dEFHwSWUeE,6564
16
16
  npcpy/data/load.py,sha256=rVe1xSHerIpo6MDaY5eIeqRSm0gssX5sHukNsUNVwJw,9228
17
17
  npcpy/data/text.py,sha256=jP0a1qZZaSJdK-LdZTn2Jjdxqmkd3efxDLEoxflJQeY,5010
18
18
  npcpy/data/video.py,sha256=H-V3mTu_ktD9u-QhYeo4aW3u9z0AtoAdRZmvRPEpE98,2887
19
- npcpy/data/web.py,sha256=cc4ikPZWMYsSz6itWI19ZkM_g5nQE3lPI-AJZEcoA04,5193
19
+ npcpy/data/web.py,sha256=qmioqCxxWddRnNm2ke39_SWXXNSqRZ1Nx86_-1JGq_A,9139
20
20
  npcpy/ft/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
21
21
  npcpy/ft/diff.py,sha256=0ScRR4AxXtVX2bgZ-Jr_dSwv3LAlU1JXDUq4F4n1Ea4,12839
22
22
  npcpy/ft/ge.py,sha256=0VzIiXq2wCzGcK1x0Wd-myJ3xRf-FNaPg0GkHEZegUM,3552
@@ -34,10 +34,10 @@ npcpy/gen/response.py,sha256=EYsIOvNOmn6dBs-4j3SyZNMvDf5N9lW-QxMbpjnF7Kw,57081
34
34
  npcpy/gen/video_gen.py,sha256=RFi3Zcq_Hn3HIcfoF3mijQ6G7RYFZaM_9pjPTh-8E64,3239
35
35
  npcpy/gen/world_gen.py,sha256=_8ytE7E3QVQ5qiX8DmOby-xd0d9zV20rRI6Wkpf-qcY,18922
36
36
  npcpy/memory/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
37
- npcpy/memory/command_history.py,sha256=AXHrtP4GKObSVtiWNGZJ16rUyJD5FLor4EuaDEvsku0,62707
37
+ npcpy/memory/command_history.py,sha256=_2ygoMO4EwX8H2f5G2Q62hrgqMwL7weOI8CTaFPn-I0,66258
38
38
  npcpy/memory/kg_vis.py,sha256=TrQQCRh_E7Pyr-GPAHLSsayubAfGyf4HOEFrPB6W86Q,31280
39
- npcpy/memory/knowledge_graph.py,sha256=X3qqlDcuzGUjRgQWleQzafGKgNw8QRz2ar2gYuCvUq8,48600
40
- npcpy/memory/memory_processor.py,sha256=6PfVnSBA9ag5EhHJinXoODfEPTlDDoaT0PtCCuZO6HI,2598
39
+ npcpy/memory/knowledge_graph.py,sha256=RHznD_Dt5Ka_eD015QuXnh3aG61c0Tf4oRIfY_JzPDQ,67554
40
+ npcpy/memory/memory_processor.py,sha256=T6VAeLYNfwP1jjDDY4x6Me_l5xcVShIcU95I1ye5nEM,9453
41
41
  npcpy/memory/search.py,sha256=glN6WYzaixcoDphTEHAXSMX3vKZGjR12Jx9YVL_gYfE,18433
42
42
  npcpy/mix/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
43
43
  npcpy/mix/debate.py,sha256=lQXxC7nl6Rwyf7HIYrsVQILMUmYYx55Tjt2pkTg56qY,9019
@@ -53,8 +53,8 @@ npcpy/work/browser.py,sha256=p2PeaoZdAXipFuAgKCCB3aXXLE_p3yIRqC87KlZKZWc,679
53
53
  npcpy/work/desktop.py,sha256=F3I8mUtJp6LAkXodsh8hGZIncoads6c_2Utty-0EdDA,2986
54
54
  npcpy/work/plan.py,sha256=QyUwg8vElWiHuoS-xK4jXTxxHvkMD3VkaCEsCmrEPQk,8300
55
55
  npcpy/work/trigger.py,sha256=P1Y8u1wQRsS2WACims_2IdkBEar-iBQix-2TDWoW0OM,9948
56
- npcpy-1.3.19.dist-info/licenses/LICENSE,sha256=j0YPvce7Ng9e32zYOu0EmXjXeJ0Nwawd0RA3uSGGH4E,1070
57
- npcpy-1.3.19.dist-info/METADATA,sha256=18bGYxznkKrwP59mZ_06rQfMFQBe_hdAjB_VbHyZBYY,37870
58
- npcpy-1.3.19.dist-info/WHEEL,sha256=qELbo2s1Yzl39ZmrAibXA2jjPLUYfnVhUNTlyF1rq0Y,92
59
- npcpy-1.3.19.dist-info/top_level.txt,sha256=g1pbSvrOOncB74Bg5-J0Olg4V0A5VzDw-Xz5YObq8BU,6
60
- npcpy-1.3.19.dist-info/RECORD,,
56
+ npcpy-1.3.21.dist-info/licenses/LICENSE,sha256=j0YPvce7Ng9e32zYOu0EmXjXeJ0Nwawd0RA3uSGGH4E,1070
57
+ npcpy-1.3.21.dist-info/METADATA,sha256=OJthZtnT23usrcUIlB9ZcG1DgrhVX0SYy1jBrl765so,37947
58
+ npcpy-1.3.21.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
59
+ npcpy-1.3.21.dist-info/top_level.txt,sha256=g1pbSvrOOncB74Bg5-J0Olg4V0A5VzDw-Xz5YObq8BU,6
60
+ npcpy-1.3.21.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (80.10.1)
2
+ Generator: setuptools (80.10.2)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5