npcpy 1.3.1__tar.gz → 1.3.2__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 (76) hide show
  1. {npcpy-1.3.1/npcpy.egg-info → npcpy-1.3.2}/PKG-INFO +1 -1
  2. {npcpy-1.3.1 → npcpy-1.3.2}/npcpy/gen/response.py +6 -5
  3. {npcpy-1.3.1 → npcpy-1.3.2}/npcpy/npc_compiler.py +39 -35
  4. {npcpy-1.3.1 → npcpy-1.3.2}/npcpy/serve.py +28 -13
  5. {npcpy-1.3.1 → npcpy-1.3.2/npcpy.egg-info}/PKG-INFO +1 -1
  6. {npcpy-1.3.1 → npcpy-1.3.2}/setup.py +1 -1
  7. {npcpy-1.3.1 → npcpy-1.3.2}/LICENSE +0 -0
  8. {npcpy-1.3.1 → npcpy-1.3.2}/MANIFEST.in +0 -0
  9. {npcpy-1.3.1 → npcpy-1.3.2}/README.md +0 -0
  10. {npcpy-1.3.1 → npcpy-1.3.2}/npcpy/__init__.py +0 -0
  11. {npcpy-1.3.1 → npcpy-1.3.2}/npcpy/data/__init__.py +0 -0
  12. {npcpy-1.3.1 → npcpy-1.3.2}/npcpy/data/audio.py +0 -0
  13. {npcpy-1.3.1 → npcpy-1.3.2}/npcpy/data/data_models.py +0 -0
  14. {npcpy-1.3.1 → npcpy-1.3.2}/npcpy/data/image.py +0 -0
  15. {npcpy-1.3.1 → npcpy-1.3.2}/npcpy/data/load.py +0 -0
  16. {npcpy-1.3.1 → npcpy-1.3.2}/npcpy/data/text.py +0 -0
  17. {npcpy-1.3.1 → npcpy-1.3.2}/npcpy/data/video.py +0 -0
  18. {npcpy-1.3.1 → npcpy-1.3.2}/npcpy/data/web.py +0 -0
  19. {npcpy-1.3.1 → npcpy-1.3.2}/npcpy/ft/__init__.py +0 -0
  20. {npcpy-1.3.1 → npcpy-1.3.2}/npcpy/ft/diff.py +0 -0
  21. {npcpy-1.3.1 → npcpy-1.3.2}/npcpy/ft/ge.py +0 -0
  22. {npcpy-1.3.1 → npcpy-1.3.2}/npcpy/ft/memory_trainer.py +0 -0
  23. {npcpy-1.3.1 → npcpy-1.3.2}/npcpy/ft/model_ensembler.py +0 -0
  24. {npcpy-1.3.1 → npcpy-1.3.2}/npcpy/ft/rl.py +0 -0
  25. {npcpy-1.3.1 → npcpy-1.3.2}/npcpy/ft/sft.py +0 -0
  26. {npcpy-1.3.1 → npcpy-1.3.2}/npcpy/ft/usft.py +0 -0
  27. {npcpy-1.3.1 → npcpy-1.3.2}/npcpy/gen/__init__.py +0 -0
  28. {npcpy-1.3.1 → npcpy-1.3.2}/npcpy/gen/audio_gen.py +0 -0
  29. {npcpy-1.3.1 → npcpy-1.3.2}/npcpy/gen/embeddings.py +0 -0
  30. {npcpy-1.3.1 → npcpy-1.3.2}/npcpy/gen/image_gen.py +0 -0
  31. {npcpy-1.3.1 → npcpy-1.3.2}/npcpy/gen/ocr.py +0 -0
  32. {npcpy-1.3.1 → npcpy-1.3.2}/npcpy/gen/video_gen.py +0 -0
  33. {npcpy-1.3.1 → npcpy-1.3.2}/npcpy/llm_funcs.py +0 -0
  34. {npcpy-1.3.1 → npcpy-1.3.2}/npcpy/main.py +0 -0
  35. {npcpy-1.3.1 → npcpy-1.3.2}/npcpy/memory/__init__.py +0 -0
  36. {npcpy-1.3.1 → npcpy-1.3.2}/npcpy/memory/command_history.py +0 -0
  37. {npcpy-1.3.1 → npcpy-1.3.2}/npcpy/memory/kg_vis.py +0 -0
  38. {npcpy-1.3.1 → npcpy-1.3.2}/npcpy/memory/knowledge_graph.py +0 -0
  39. {npcpy-1.3.1 → npcpy-1.3.2}/npcpy/memory/memory_processor.py +0 -0
  40. {npcpy-1.3.1 → npcpy-1.3.2}/npcpy/memory/search.py +0 -0
  41. {npcpy-1.3.1 → npcpy-1.3.2}/npcpy/mix/__init__.py +0 -0
  42. {npcpy-1.3.1 → npcpy-1.3.2}/npcpy/mix/debate.py +0 -0
  43. {npcpy-1.3.1 → npcpy-1.3.2}/npcpy/ml_funcs.py +0 -0
  44. {npcpy-1.3.1 → npcpy-1.3.2}/npcpy/npc_array.py +0 -0
  45. {npcpy-1.3.1 → npcpy-1.3.2}/npcpy/npc_sysenv.py +0 -0
  46. {npcpy-1.3.1 → npcpy-1.3.2}/npcpy/npcs.py +0 -0
  47. {npcpy-1.3.1 → npcpy-1.3.2}/npcpy/sql/__init__.py +0 -0
  48. {npcpy-1.3.1 → npcpy-1.3.2}/npcpy/sql/ai_function_tools.py +0 -0
  49. {npcpy-1.3.1 → npcpy-1.3.2}/npcpy/sql/database_ai_adapters.py +0 -0
  50. {npcpy-1.3.1 → npcpy-1.3.2}/npcpy/sql/database_ai_functions.py +0 -0
  51. {npcpy-1.3.1 → npcpy-1.3.2}/npcpy/sql/model_runner.py +0 -0
  52. {npcpy-1.3.1 → npcpy-1.3.2}/npcpy/sql/npcsql.py +0 -0
  53. {npcpy-1.3.1 → npcpy-1.3.2}/npcpy/sql/sql_model_compiler.py +0 -0
  54. {npcpy-1.3.1 → npcpy-1.3.2}/npcpy/tools.py +0 -0
  55. {npcpy-1.3.1 → npcpy-1.3.2}/npcpy/work/__init__.py +0 -0
  56. {npcpy-1.3.1 → npcpy-1.3.2}/npcpy/work/desktop.py +0 -0
  57. {npcpy-1.3.1 → npcpy-1.3.2}/npcpy/work/plan.py +0 -0
  58. {npcpy-1.3.1 → npcpy-1.3.2}/npcpy/work/trigger.py +0 -0
  59. {npcpy-1.3.1 → npcpy-1.3.2}/npcpy.egg-info/SOURCES.txt +0 -0
  60. {npcpy-1.3.1 → npcpy-1.3.2}/npcpy.egg-info/dependency_links.txt +0 -0
  61. {npcpy-1.3.1 → npcpy-1.3.2}/npcpy.egg-info/requires.txt +0 -0
  62. {npcpy-1.3.1 → npcpy-1.3.2}/npcpy.egg-info/top_level.txt +0 -0
  63. {npcpy-1.3.1 → npcpy-1.3.2}/setup.cfg +0 -0
  64. {npcpy-1.3.1 → npcpy-1.3.2}/tests/test_audio.py +0 -0
  65. {npcpy-1.3.1 → npcpy-1.3.2}/tests/test_command_history.py +0 -0
  66. {npcpy-1.3.1 → npcpy-1.3.2}/tests/test_image.py +0 -0
  67. {npcpy-1.3.1 → npcpy-1.3.2}/tests/test_llm_funcs.py +0 -0
  68. {npcpy-1.3.1 → npcpy-1.3.2}/tests/test_load.py +0 -0
  69. {npcpy-1.3.1 → npcpy-1.3.2}/tests/test_npc_array.py +0 -0
  70. {npcpy-1.3.1 → npcpy-1.3.2}/tests/test_npc_compiler.py +0 -0
  71. {npcpy-1.3.1 → npcpy-1.3.2}/tests/test_npcsql.py +0 -0
  72. {npcpy-1.3.1 → npcpy-1.3.2}/tests/test_response.py +0 -0
  73. {npcpy-1.3.1 → npcpy-1.3.2}/tests/test_serve.py +0 -0
  74. {npcpy-1.3.1 → npcpy-1.3.2}/tests/test_text.py +0 -0
  75. {npcpy-1.3.1 → npcpy-1.3.2}/tests/test_tools.py +0 -0
  76. {npcpy-1.3.1 → npcpy-1.3.2}/tests/test_web.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: npcpy
3
- Version: 1.3.1
3
+ Version: 1.3.2
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
@@ -346,7 +346,12 @@ def get_ollama_response(
346
346
  res = ollama.chat(**api_params, options=options)
347
347
  result["raw_response"] = res
348
348
 
349
- # Extract usage from ollama response
349
+ if stream:
350
+ # Return immediately for streaming - don't check 'in' on generator as it consumes it
351
+ result["response"] = res
352
+ return result
353
+
354
+ # Extract usage from ollama response (only for non-streaming)
350
355
  if hasattr(res, 'prompt_eval_count') or 'prompt_eval_count' in res:
351
356
  input_tokens = getattr(res, 'prompt_eval_count', None) or res.get('prompt_eval_count', 0) or 0
352
357
  output_tokens = getattr(res, 'eval_count', None) or res.get('eval_count', 0) or 0
@@ -354,10 +359,6 @@ def get_ollama_response(
354
359
  "input_tokens": input_tokens,
355
360
  "output_tokens": output_tokens,
356
361
  }
357
-
358
- if stream:
359
- result["response"] = res
360
- return result
361
362
  else:
362
363
 
363
364
  message = res.get("message", {})
@@ -468,8 +468,8 @@ class Jinx:
468
468
  }
469
469
 
470
470
  def render_first_pass(
471
- self,
472
- jinja_env_for_macros: Environment,
471
+ self,
472
+ jinja_env_for_macros: Environment,
473
473
  all_jinx_callables: Dict[str, Callable]
474
474
  ):
475
475
  """
@@ -478,40 +478,44 @@ class Jinx:
478
478
  then expands nested Jinx calls (e.g., {{ sh(...) }} or engine: jinx_name)
479
479
  and inline macros.
480
480
  """
481
- # 1. Join the list of raw steps (which are individual YAML lines) into a single string.
482
- # This single string is the complete Jinja template for the 'steps' section.
483
- raw_steps_template_string = "\n".join(self._raw_steps)
484
-
485
- # 2. Render this single string as a Jinja template.
486
- # Jinja will now process the {% for %} and {% if %} directives,
487
- # dynamically generating the YAML structure.
488
- try:
489
- steps_template = jinja_env_for_macros.from_string(raw_steps_template_string)
490
- # Pass globals (like num_tasks, include_greeting from Jinx inputs)
491
- # to the Jinja rendering context for structural templating.
492
- rendered_steps_yaml_string = steps_template.render(**jinja_env_for_macros.globals)
493
- except Exception as e:
494
- # In a real Jinx, this would go to a proper logger.
495
- # For this context, we handle the error gracefully.
496
- # self._log_debug(f"Warning: Error during first-pass templating of Jinx '{self.jinx_name}' steps YAML: {e}")
497
- self.steps = list(self._raw_steps) # Fallback to original raw steps
498
- return
481
+ # Check if steps are already parsed dicts (common case when loaded from YAML)
482
+ # If so, skip the YAML string join/parse cycle and use them directly
483
+ if self._raw_steps and isinstance(self._raw_steps[0], dict):
484
+ structurally_expanded_steps = list(self._raw_steps)
485
+ else:
486
+ # 1. Join the list of raw steps (which are individual YAML lines) into a single string.
487
+ # This single string is the complete Jinja template for the 'steps' section.
488
+ raw_steps_template_string = "\n".join(self._raw_steps)
499
489
 
500
- # 3. Parse the rendered YAML string back into a list of step dictionaries.
501
- # This step will now correctly interpret the YAML structure generated by Jinja.
502
- try:
503
- structurally_expanded_steps = yaml.safe_load(rendered_steps_yaml_string)
504
- if not isinstance(structurally_expanded_steps, list):
505
- # Handle cases where the rendered YAML might be empty or not a list
506
- if structurally_expanded_steps is None:
507
- structurally_expanded_steps = []
508
- else:
509
- raise ValueError(f"Rendered steps YAML did not result in a list: {type(structurally_expanded_steps)}")
510
- self.steps = structurally_expanded_steps
511
- except Exception as e:
512
- # self._log_debug(f"Warning: Error re-parsing structurally expanded steps YAML for Jinx '{self.jinx_name}': {e}")
513
- self.steps = list(self._raw_steps) # Fallback
514
- return
490
+ # 2. Render this single string as a Jinja template.
491
+ # Jinja will now process the {% for %} and {% if %} directives,
492
+ # dynamically generating the YAML structure.
493
+ try:
494
+ steps_template = jinja_env_for_macros.from_string(raw_steps_template_string)
495
+ # Pass globals (like num_tasks, include_greeting from Jinx inputs)
496
+ # to the Jinja rendering context for structural templating.
497
+ rendered_steps_yaml_string = steps_template.render(**jinja_env_for_macros.globals)
498
+ except Exception as e:
499
+ # In a real Jinx, this would go to a proper logger.
500
+ # For this context, we handle the error gracefully.
501
+ # self._log_debug(f"Warning: Error during first-pass templating of Jinx '{self.jinx_name}' steps YAML: {e}")
502
+ self.steps = list(self._raw_steps) # Fallback to original raw steps
503
+ return
504
+
505
+ # 3. Parse the rendered YAML string back into a list of step dictionaries.
506
+ # This step will now correctly interpret the YAML structure generated by Jinja.
507
+ try:
508
+ structurally_expanded_steps = yaml.safe_load(rendered_steps_yaml_string)
509
+ if not isinstance(structurally_expanded_steps, list):
510
+ # Handle cases where the rendered YAML might be empty or not a list
511
+ if structurally_expanded_steps is None:
512
+ structurally_expanded_steps = []
513
+ else:
514
+ raise ValueError(f"Rendered steps YAML did not result in a list: {type(structurally_expanded_steps)}")
515
+ except Exception as e:
516
+ # self._log_debug(f"Warning: Error re-parsing structurally expanded steps YAML for Jinx '{self.jinx_name}': {e}")
517
+ self.steps = list(self._raw_steps) # Fallback
518
+ return
515
519
 
516
520
  # 4. Now, iterate through these `structurally_expanded_steps` to expand
517
521
  # declarative Jinx calls (engine: jinx_name) and inline macros.
@@ -3901,26 +3901,41 @@ def stream():
3901
3901
  print('.', end="", flush=True)
3902
3902
  dot_count += 1
3903
3903
  if "hf.co" in model or provider == 'ollama' and 'gpt-oss' not in model:
3904
- chunk_content = response_chunk["message"]["content"] if "message" in response_chunk and "content" in response_chunk["message"] else ""
3905
- if "message" in response_chunk and "tool_calls" in response_chunk["message"]:
3906
- for tool_call in response_chunk["message"]["tool_calls"]:
3907
- if "id" in tool_call:
3908
- tool_call_data["id"] = tool_call["id"]
3909
- if "function" in tool_call:
3910
- if "name" in tool_call["function"]:
3911
- tool_call_data["function_name"] = tool_call["function"]["name"]
3912
- if "arguments" in tool_call["function"]:
3913
- arg_val = tool_call["function"]["arguments"]
3904
+ # Ollama returns ChatResponse objects - support both attribute and dict access
3905
+ msg = getattr(response_chunk, "message", None) or response_chunk.get("message", {}) if hasattr(response_chunk, "get") else {}
3906
+ chunk_content = getattr(msg, "content", None) or (msg.get("content") if hasattr(msg, "get") else "") or ""
3907
+ # Extract Ollama thinking/reasoning tokens
3908
+ thinking_content = getattr(msg, "thinking", None) or (msg.get("thinking") if hasattr(msg, "get") else None)
3909
+ # Handle tool calls with robust attribute/dict access
3910
+ tool_calls = getattr(msg, "tool_calls", None) or (msg.get("tool_calls") if hasattr(msg, "get") else None)
3911
+ if tool_calls:
3912
+ for tool_call in tool_calls:
3913
+ tc_id = getattr(tool_call, "id", None) or (tool_call.get("id") if hasattr(tool_call, "get") else None)
3914
+ if tc_id:
3915
+ tool_call_data["id"] = tc_id
3916
+ tc_func = getattr(tool_call, "function", None) or (tool_call.get("function") if hasattr(tool_call, "get") else None)
3917
+ if tc_func:
3918
+ tc_name = getattr(tc_func, "name", None) or (tc_func.get("name") if hasattr(tc_func, "get") else None)
3919
+ if tc_name:
3920
+ tool_call_data["function_name"] = tc_name
3921
+ tc_args = getattr(tc_func, "arguments", None) or (tc_func.get("arguments") if hasattr(tc_func, "get") else None)
3922
+ if tc_args:
3923
+ arg_val = tc_args
3914
3924
  if isinstance(arg_val, dict):
3915
3925
  arg_val = json.dumps(arg_val)
3916
3926
  tool_call_data["arguments"] += arg_val
3917
3927
  if chunk_content:
3918
3928
  complete_response.append(chunk_content)
3929
+ # Extract other fields with robust access
3930
+ created_at = getattr(response_chunk, "created_at", None) or (response_chunk.get("created_at") if hasattr(response_chunk, "get") else None)
3931
+ model_name = getattr(response_chunk, "model", None) or (response_chunk.get("model") if hasattr(response_chunk, "get") else model)
3932
+ msg_role = getattr(msg, "role", None) or (msg.get("role") if hasattr(msg, "get") else "assistant")
3933
+ done_reason = getattr(response_chunk, "done_reason", None) or (response_chunk.get("done_reason") if hasattr(response_chunk, "get") else None)
3919
3934
  chunk_data = {
3920
3935
  "id": None, "object": None,
3921
- "created": response_chunk["created_at"] or datetime.datetime.now(),
3922
- "model": response_chunk["model"],
3923
- "choices": [{"index": 0, "delta": {"content": chunk_content, "role": response_chunk["message"]["role"]}, "finish_reason": response_chunk.get("done_reason")}]
3936
+ "created": created_at or datetime.datetime.now(),
3937
+ "model": model_name,
3938
+ "choices": [{"index": 0, "delta": {"content": chunk_content, "role": msg_role, "reasoning_content": thinking_content}, "finish_reason": done_reason}]
3924
3939
  }
3925
3940
  yield f"data: {json.dumps(chunk_data)}\n\n"
3926
3941
  else:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: npcpy
3
- Version: 1.3.1
3
+ Version: 1.3.2
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
@@ -83,7 +83,7 @@ extra_files = package_files("npcpy/npc_team/")
83
83
 
84
84
  setup(
85
85
  name="npcpy",
86
- version="1.3.1",
86
+ version="1.3.2",
87
87
  packages=find_packages(exclude=["tests*"]),
88
88
  install_requires=base_requirements,
89
89
  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
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
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes