npcpy 1.2.32__py3-none-any.whl → 1.2.34__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: npcpy
3
- Version: 1.2.32
3
+ Version: 1.2.34
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
@@ -185,62 +185,117 @@ for tool_call in response['tool_results']:
185
185
  Here is an example for setting up an agent team to use Jinja Execution (Jinxs) templates that are processed entirely with prompts, allowing you to use them with models that do or do not possess tool calling support.
186
186
 
187
187
  ```python
188
+
188
189
  from npcpy.npc_compiler import NPC, Team, Jinx
189
190
  from npcpy.tools import auto_tools
190
191
  import os
192
+ from jinja2 import Environment, Undefined, DictLoader # Import necessary Jinja2 components for Jinx code
191
193
 
192
-
193
-
194
+ # --- REVISED file_reader_jinx ---
194
195
  file_reader_jinx = Jinx(jinx_data={
195
196
  "jinx_name": "file_reader",
196
- "description": "Read a file and summarize its contents",
197
+ "description": "Read a file and optionally summarize its contents using an LLM.",
197
198
  "inputs": ["filename"],
198
199
  "steps": [
199
200
  {
200
- "name": "read_file",
201
+ "name": "read_file_content",
201
202
  "engine": "python",
202
- "code": """
203
+ "code": '''
203
204
  import os
204
- with open(os.path.abspath('{{ filename }}'), 'r') as f:
205
- content = f.read()
206
- output= content
207
- """
205
+ from jinja2 import Environment, Undefined, DictLoader # Local import for Jinx step
206
+
207
+ # The 'filename' input to the file_reader jinx might be a Jinja template string like "{{ source_filename }}"
208
+ # or a direct filename. We need to render it using the current execution context.
209
+
210
+ # Get the Jinja environment from the NPC if available, otherwise create a default one.
211
+ # The 'npc' variable is available in the Jinx execution context.
212
+ # We need to ensure 'npc' exists before trying to access its 'jinja_env'.
213
+ execution_jinja_env = npc.jinja_env if npc else Environment(loader=DictLoader({}), undefined=Undefined)
214
+
215
+ # Render the filename. The current 'context' should contain the variables needed for rendering.
216
+ # For declarative calls, the parent Jinx's inputs (like 'source_filename') will be in this context.
217
+ # We also need to ensure the value from context['filename'] is treated as a template string.
218
+ filename_template = execution_jinja_env.from_string(context['filename'])
219
+ rendered_filename = filename_template.render(**context)
220
+
221
+ file_path_abs = os.path.abspath(rendered_filename)
222
+ try:
223
+ with open(file_path_abs, 'r') as f:
224
+ content = f.read()
225
+ context['file_raw_content'] = content # Store raw content in context for later use
226
+ output = content # Output of this step is the raw content
227
+ except FileNotFoundError:
228
+ output = f"Error: File not found at {file_path_abs}"
229
+ context['file_raw_content'] = output # Store error message for consistency
230
+ except Exception as e:
231
+ output = f"Error reading file {file_path_abs}: {e}"
232
+ context['file_raw_content'] = output # Store error message for consistency
233
+ '''
208
234
  },
209
235
  {
210
- "name": "summarize_content",
211
- "engine": "natural",
212
- "code": """
213
- Summarize the content of the file: {{ read_file }}.
214
- """
236
+ "name": "summarize_file_content",
237
+ "engine": "python",
238
+ "code": '''
239
+ # Check if the previous step encountered an error
240
+ if "Error" not in context['file_raw_content']:
241
+ prompt = f"Summarize the following content concisely, highlighting key themes and points: {context['file_raw_content']}"
242
+ llm_result = npc.get_llm_response(prompt, tool_choice=False) # FIX: Passed prompt positionally
243
+ output = llm_result.get('response', 'Failed to generate summary due to LLM error.')
244
+ else:
245
+ output = "Skipping summary due to previous file reading error."
246
+ '''
215
247
  }
216
248
  ]
217
249
  })
218
250
 
219
-
220
- # Define a jinx for literary research
251
+ # --- REVISED literary_research_jinx ---
221
252
  literary_research_jinx = Jinx(jinx_data={
222
253
  "jinx_name": "literary_research",
223
- "description": "Research a literary topic, analyze files, and summarize findings",
224
- "inputs": ["topic"],
254
+ "description": "Research a literary topic, read a specific file, analyze, and synthesize findings.",
255
+ "inputs": ["topic", "source_filename"],
225
256
  "steps": [
226
257
  {
227
- "name": "gather_info",
228
- "engine": "natural",
229
- "code": """
230
- Research the topic: {{ topic }}.
231
- Summarize the main themes and historical context.
232
- """
258
+ "name": "initial_llm_research",
259
+ "engine": "python",
260
+ "code": '''
261
+ prompt = f"Research the topic: {context['topic']}. Summarize the main themes, key authors, and historical context. Be thorough."
262
+ llm_result = npc.get_llm_response(prompt, tool_choice=False) # FIX: Passed prompt positionally
263
+ context['research_summary'] = llm_result.get('response', 'No initial LLM research found.')
264
+ output = context['research_summary']
265
+ '''
266
+ },
267
+ {
268
+ "name": "read_and_process_source_file",
269
+ "engine": "file_reader",
270
+ "filename": "{{ source_filename }}" # This is passed as a string template to file_reader
233
271
  },
234
272
  {
235
- "name": "final_summary",
236
- "engine": "natural",
237
- "code": """
238
- Based on the research in. {{gather_info}}, write a concise, creative summary.
239
- """
273
+ "name": "final_synthesis_and_creative_writing",
274
+ "engine": "python",
275
+ "code": '''
276
+ # Access outputs from previous steps.
277
+ research_summary = context['initial_llm_research']
278
+ # The output of a declarative jinx call (like 'file_reader') is stored under its step name.
279
+ # The actual content we want is the 'output' of the *last step* within that sub-jinx.
280
+ file_summary = context['read_and_process_source_file'].get('output', 'No file summary available.')
281
+
282
+ prompt = f"""Based on the following information:
283
+ 1. Comprehensive Research Summary:
284
+ {research_summary}
285
+
286
+ 2. Key Insights from Source File:
287
+ {file_summary}
288
+
289
+ Integrate these findings and write a concise, creative, and poetically styled summary of the literary topic '{context['topic']}'. Emphasize unique perspectives or connections between the research and the file content, as if written by a master of magical realism.
290
+ """
291
+ llm_result = npc.get_llm_response(prompt, tool_choice=False) # FIX: Passed prompt positionally
292
+ output = llm_result.get('response', 'Failed to generate final creative summary.')
293
+ '''
240
294
  }
241
295
  ]
242
296
  })
243
297
 
298
+ # --- NPC Definitions (unchanged) ---
244
299
  ggm = NPC(
245
300
  name='Gabriel Garcia Marquez',
246
301
  primary_directive='You are Gabriel Garcia Marquez, master of magical realism. Research, analyze, and write with poetic flair.',
@@ -263,16 +318,24 @@ borges = NPC(
263
318
  provider='ollama',
264
319
  )
265
320
 
266
- # Set up a team with a forenpc that orchestrates the other npcs
267
- lit_team = Team(npcs=[ggm, isabel], forenpc=borges, jinxs={'literary_research': literary_research_jinx, 'file_reader': file_reader_jinx},
321
+ # --- Team Setup ---
322
+ lit_team = Team(
323
+ npcs=[ggm, isabel],
324
+ forenpc=borges,
325
+ jinxs=[literary_research_jinx, file_reader_jinx],
268
326
  )
269
327
 
270
- # Example: Orchestrate a jinx workflow
328
+ # --- Orchestration Example ---
271
329
  result = lit_team.orchestrate(
272
- "Research the topic of magical realism, read ./test_data/magical_realism.txt and summarize the findings"
330
+ "Research the topic of magical realism, using the file './test_data/magical_realism.txt' as a primary source, and provide a comprehensive, creative summary."
273
331
  )
332
+
333
+ print("\n--- Orchestration Result Summary ---")
274
334
  print(result['debrief']['summary'])
275
335
 
336
+ print("\n--- Full Orchestration Output ---")
337
+ print(result['output'])
338
+
276
339
  ```
277
340
  ```
278
341
  • Action chosen: pass_to_npc
@@ -1,10 +1,10 @@
1
1
  npcpy/__init__.py,sha256=9imxFtK74_6Rw9rz0kyMnZYl_voPb569tkTlYLt0Urg,131
2
- npcpy/llm_funcs.py,sha256=qC-WmNvUl3YAS1u-xPXh1YDlr2e9cv80_wXK6wCr7TA,85546
2
+ npcpy/llm_funcs.py,sha256=RtZAtX1_nvfn-X3IHVyQggDBXDzGxKEix_sS_iliNN0,87172
3
3
  npcpy/main.py,sha256=RWoRIj6VQLxKdOKvdVyaq2kwG35oRpeXPvp1CAAoG-w,81
4
- npcpy/npc_compiler.py,sha256=j3JYZPKPLi42HAEA_i3Cp5GBGGUcpzBk8OEzZEvxzY4,89458
4
+ npcpy/npc_compiler.py,sha256=p__rbudWTbhOKJyHxaalP0I33HEfohWfwqQ9Q5q4ROY,99091
5
5
  npcpy/npc_sysenv.py,sha256=t9AswM-9_P2NaGsnlzTMc2hUfdSthi9ofbud6F1G7LM,35974
6
6
  npcpy/npcs.py,sha256=eExuVsbTfrRobTRRptRpDm46jCLWUgbvy4_U7IUQo-c,744
7
- npcpy/serve.py,sha256=P01tYsY1ctq408nn-t3sLPGuGJg5KoaApy4gNECDRgo,118007
7
+ npcpy/serve.py,sha256=555kHrKEsQGVfJpisdShIDxekb39Z7X86z2bt5EdqRM,121575
8
8
  npcpy/tools.py,sha256=A5_oVmZkzGnI3BI-NmneuxeXQq-r29PbpAZP4nV4jrc,5303
9
9
  npcpy/data/__init__.py,sha256=1tcoChR-Hjn905JDLqaW9ElRmcISCTJdE7BGXPlym2Q,642
10
10
  npcpy/data/audio.py,sha256=goon4HfsYgx0bI-n1lhkrzWPrJoejJlycXcB0P62pyk,11280
@@ -29,7 +29,7 @@ npcpy/gen/image_gen.py,sha256=mAlLG9jo9RnuuMU0jJVV0CpIgHqdizU9sfC6A0w5kKE,15599
29
29
  npcpy/gen/response.py,sha256=6iAOi4hxUxkTZ1d2suBUASOssT6pQnr3HFwZWrvmATg,31925
30
30
  npcpy/gen/video_gen.py,sha256=RFi3Zcq_Hn3HIcfoF3mijQ6G7RYFZaM_9pjPTh-8E64,3239
31
31
  npcpy/memory/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
32
- npcpy/memory/command_history.py,sha256=2VdmNW5VRpMrOkbdrMsgn5p3mvuJHNnzGHnIUEM8XMI,46279
32
+ npcpy/memory/command_history.py,sha256=xdDu1LNPtdKRUX-kcOz7fL5XoimGu76HBeTQLTiMQ7w,47173
33
33
  npcpy/memory/kg_vis.py,sha256=TrQQCRh_E7Pyr-GPAHLSsayubAfGyf4HOEFrPB6W86Q,31280
34
34
  npcpy/memory/knowledge_graph.py,sha256=2XpIlsyPdAOnzQ6kkwP6MWPGwL3P6V33_3suNJYMMJE,48681
35
35
  npcpy/memory/memory_processor.py,sha256=6PfVnSBA9ag5EhHJinXoODfEPTlDDoaT0PtCCuZO6HI,2598
@@ -47,8 +47,8 @@ npcpy/work/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
47
47
  npcpy/work/desktop.py,sha256=F3I8mUtJp6LAkXodsh8hGZIncoads6c_2Utty-0EdDA,2986
48
48
  npcpy/work/plan.py,sha256=QyUwg8vElWiHuoS-xK4jXTxxHvkMD3VkaCEsCmrEPQk,8300
49
49
  npcpy/work/trigger.py,sha256=P1Y8u1wQRsS2WACims_2IdkBEar-iBQix-2TDWoW0OM,9948
50
- npcpy-1.2.32.dist-info/licenses/LICENSE,sha256=j0YPvce7Ng9e32zYOu0EmXjXeJ0Nwawd0RA3uSGGH4E,1070
51
- npcpy-1.2.32.dist-info/METADATA,sha256=Bii6xZThq-8YL-AByfstXOd6xYhtzH4deF4QQZ44LAk,29895
52
- npcpy-1.2.32.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
53
- npcpy-1.2.32.dist-info/top_level.txt,sha256=g1pbSvrOOncB74Bg5-J0Olg4V0A5VzDw-Xz5YObq8BU,6
54
- npcpy-1.2.32.dist-info/RECORD,,
50
+ npcpy-1.2.34.dist-info/licenses/LICENSE,sha256=j0YPvce7Ng9e32zYOu0EmXjXeJ0Nwawd0RA3uSGGH4E,1070
51
+ npcpy-1.2.34.dist-info/METADATA,sha256=tEmBrassMmZLHnvB4zXS0Cr8qj0Of-wFfLkdUjFqy2Y,33537
52
+ npcpy-1.2.34.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
53
+ npcpy-1.2.34.dist-info/top_level.txt,sha256=g1pbSvrOOncB74Bg5-J0Olg4V0A5VzDw-Xz5YObq8BU,6
54
+ npcpy-1.2.34.dist-info/RECORD,,
File without changes