npcpy 1.3.1__py3-none-any.whl → 1.3.2__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/gen/response.py +6 -5
- npcpy/npc_compiler.py +39 -35
- npcpy/serve.py +28 -13
- {npcpy-1.3.1.dist-info → npcpy-1.3.2.dist-info}/METADATA +1 -1
- {npcpy-1.3.1.dist-info → npcpy-1.3.2.dist-info}/RECORD +8 -8
- {npcpy-1.3.1.dist-info → npcpy-1.3.2.dist-info}/WHEEL +0 -0
- {npcpy-1.3.1.dist-info → npcpy-1.3.2.dist-info}/licenses/LICENSE +0 -0
- {npcpy-1.3.1.dist-info → npcpy-1.3.2.dist-info}/top_level.txt +0 -0
npcpy/gen/response.py
CHANGED
|
@@ -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
|
-
|
|
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", {})
|
npcpy/npc_compiler.py
CHANGED
|
@@ -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
|
-
#
|
|
482
|
-
#
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
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
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
#
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
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.
|
npcpy/serve.py
CHANGED
|
@@ -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
|
-
|
|
3905
|
-
|
|
3906
|
-
|
|
3907
|
-
|
|
3908
|
-
|
|
3909
|
-
|
|
3910
|
-
|
|
3911
|
-
|
|
3912
|
-
|
|
3913
|
-
|
|
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":
|
|
3922
|
-
"model":
|
|
3923
|
-
"choices": [{"index": 0, "delta": {"content": chunk_content, "role":
|
|
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:
|
|
@@ -3,10 +3,10 @@ npcpy/llm_funcs.py,sha256=KJpjN6q5iW_qdUfgt4tzYENCAu86376io8eFZ7wp76Y,78081
|
|
|
3
3
|
npcpy/main.py,sha256=RWoRIj6VQLxKdOKvdVyaq2kwG35oRpeXPvp1CAAoG-w,81
|
|
4
4
|
npcpy/ml_funcs.py,sha256=UI7k7JR4XOH_VXR-xxLaO4r9Kyx_jBaEnp3TUIY7ZLQ,22657
|
|
5
5
|
npcpy/npc_array.py,sha256=fVTxcMiXV-lvltmuwaRnTU9D3ikPq3-7k5wzp7MA5OY,40224
|
|
6
|
-
npcpy/npc_compiler.py,sha256=
|
|
6
|
+
npcpy/npc_compiler.py,sha256=X2BjMqKL7hbS37PPkSDGgZSF_PF_GNVGLd92ePRNRwQ,111868
|
|
7
7
|
npcpy/npc_sysenv.py,sha256=rtE3KrXvIuOEpMq1CW5eK5K0o3f6mXagNXCeMnhHob4,36736
|
|
8
8
|
npcpy/npcs.py,sha256=eExuVsbTfrRobTRRptRpDm46jCLWUgbvy4_U7IUQo-c,744
|
|
9
|
-
npcpy/serve.py,sha256=
|
|
9
|
+
npcpy/serve.py,sha256=Nxigo7NR189RFDhdh3whVeWEyjTaopCzXfk6HsTJP4A,176384
|
|
10
10
|
npcpy/tools.py,sha256=A5_oVmZkzGnI3BI-NmneuxeXQq-r29PbpAZP4nV4jrc,5303
|
|
11
11
|
npcpy/data/__init__.py,sha256=1tcoChR-Hjn905JDLqaW9ElRmcISCTJdE7BGXPlym2Q,642
|
|
12
12
|
npcpy/data/audio.py,sha256=3qryGXnWHa4JFMonjuX-lf0fCrF8jmbHe7mHAuOdua0,12397
|
|
@@ -29,7 +29,7 @@ npcpy/gen/audio_gen.py,sha256=w4toESu7nmli1T5FOwRRCGC_QK9W-SMWknYYkbRv9jE,635
|
|
|
29
29
|
npcpy/gen/embeddings.py,sha256=QStTJ2ELiC379OEZsLEgGGIIFD267Y8zQchs7HRn2Zg,2089
|
|
30
30
|
npcpy/gen/image_gen.py,sha256=VflU_wJsKWJarOVwZtL2M8ymDFfKNz8WX66Rwk4obeo,21778
|
|
31
31
|
npcpy/gen/ocr.py,sha256=rgmXWHrCYX1Po-qG_LrNFbVYEZ8aaupxFTgparcoB_Y,6554
|
|
32
|
-
npcpy/gen/response.py,sha256=
|
|
32
|
+
npcpy/gen/response.py,sha256=8oZRRmoh85RyR6sgGsk-H6cpXcCjkBsn-8Wix0mW3bE,40101
|
|
33
33
|
npcpy/gen/video_gen.py,sha256=RFi3Zcq_Hn3HIcfoF3mijQ6G7RYFZaM_9pjPTh-8E64,3239
|
|
34
34
|
npcpy/memory/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
35
35
|
npcpy/memory/command_history.py,sha256=vWzZ4F4o0XOSHn50SkdP885jG1aZIZvfcPAh8EZWlQk,54497
|
|
@@ -50,8 +50,8 @@ npcpy/work/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
|
50
50
|
npcpy/work/desktop.py,sha256=F3I8mUtJp6LAkXodsh8hGZIncoads6c_2Utty-0EdDA,2986
|
|
51
51
|
npcpy/work/plan.py,sha256=QyUwg8vElWiHuoS-xK4jXTxxHvkMD3VkaCEsCmrEPQk,8300
|
|
52
52
|
npcpy/work/trigger.py,sha256=P1Y8u1wQRsS2WACims_2IdkBEar-iBQix-2TDWoW0OM,9948
|
|
53
|
-
npcpy-1.3.
|
|
54
|
-
npcpy-1.3.
|
|
55
|
-
npcpy-1.3.
|
|
56
|
-
npcpy-1.3.
|
|
57
|
-
npcpy-1.3.
|
|
53
|
+
npcpy-1.3.2.dist-info/licenses/LICENSE,sha256=j0YPvce7Ng9e32zYOu0EmXjXeJ0Nwawd0RA3uSGGH4E,1070
|
|
54
|
+
npcpy-1.3.2.dist-info/METADATA,sha256=KmIJEKnatu027fuhR2XJQs7kNlrqaVgzTqG4eKQECCc,37884
|
|
55
|
+
npcpy-1.3.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
56
|
+
npcpy-1.3.2.dist-info/top_level.txt,sha256=g1pbSvrOOncB74Bg5-J0Olg4V0A5VzDw-Xz5YObq8BU,6
|
|
57
|
+
npcpy-1.3.2.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|