nighthawk-python 0.8.0__py3-none-any.whl → 0.9.0__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.
nighthawk/__init__.py CHANGED
@@ -3,7 +3,6 @@ from __future__ import annotations
3
3
  from .configuration import (
4
4
  StepContextLimits,
5
5
  StepExecutorConfiguration,
6
- StepExecutorConfigurationPatch,
7
6
  StepPromptTemplates,
8
7
  )
9
8
  from .errors import (
@@ -32,7 +31,6 @@ __all__ = [
32
31
  "StepContextLimits",
33
32
  "StepExecutor",
34
33
  "StepExecutorConfiguration",
35
- "StepExecutorConfigurationPatch",
36
34
  "StepPromptTemplates",
37
35
  "ToolEvaluationError",
38
36
  "ToolRegistrationError",
@@ -160,44 +160,3 @@ class StepExecutorConfiguration(BaseModel):
160
160
  return tiktoken.encoding_for_model(model_name)
161
161
  except Exception:
162
162
  return tiktoken.get_encoding("o200k_base")
163
-
164
-
165
- class StepExecutorConfigurationPatch(BaseModel):
166
- """Partial override for StepExecutorConfiguration.
167
-
168
- Non-None fields replace the corresponding fields in the target configuration.
169
-
170
- Attributes:
171
- model: Model identifier override.
172
- model_settings: Model settings override. Accepts a dict or a
173
- backend-specific BaseModel instance (auto-converted to dict).
174
- prompts: Prompt templates override.
175
- context_limits: Context limits override.
176
- json_renderer_style: JSON renderer style override.
177
- tokenizer_encoding: Tokenizer encoding override.
178
- system_prompt_suffix_fragments: System prompt suffix fragments override.
179
- user_prompt_suffix_fragments: User prompt suffix fragments override.
180
- """
181
-
182
- model_config = ConfigDict(extra="forbid", frozen=True)
183
-
184
- model: str | None = None
185
- model_settings: dict[str, Any] | BaseModel | None = None
186
-
187
- @field_validator("model_settings", mode="before")
188
- @classmethod
189
- def _normalize_model_settings(cls, value: Any) -> dict[str, Any] | None:
190
- if isinstance(value, BaseModel):
191
- return value.model_dump()
192
- return value
193
-
194
- prompts: StepPromptTemplates | None = None
195
- context_limits: StepContextLimits | None = None
196
- json_renderer_style: JsonRendererStyle | None = None
197
- tokenizer_encoding: str | None = None
198
- system_prompt_suffix_fragments: tuple[str, ...] | None = None
199
- user_prompt_suffix_fragments: tuple[str, ...] | None = None
200
-
201
- def apply_to(self, configuration: StepExecutorConfiguration) -> StepExecutorConfiguration:
202
- """Apply non-None fields to the given configuration and return a new copy."""
203
- return configuration.model_copy(update=self.model_dump(exclude_none=True))
@@ -2,17 +2,17 @@ from __future__ import annotations
2
2
 
3
3
  import importlib.metadata
4
4
  import threading
5
- from collections.abc import Iterator, Mapping
5
+ from collections.abc import Iterator, Mapping, Sequence
6
6
  from contextlib import contextmanager
7
7
  from contextvars import ContextVar
8
8
  from copy import copy
9
9
  from dataclasses import dataclass, replace
10
- from typing import TYPE_CHECKING, Any
10
+ from typing import TYPE_CHECKING, Any, Literal
11
11
 
12
12
  from opentelemetry.trace import Span, get_tracer_provider
13
13
  from pydantic_ai.usage import RunUsage
14
14
 
15
- from ..configuration import StepExecutorConfiguration, StepExecutorConfigurationPatch
15
+ from ..configuration import StepExecutorConfiguration
16
16
  from ..errors import NighthawkError
17
17
  from ..tools.registry import tool_scope
18
18
  from ..ulid import generate_ulid
@@ -295,24 +295,28 @@ def run(
295
295
  @contextmanager
296
296
  def scope(
297
297
  *,
298
+ mode: Literal["inherit", "replace"] = "inherit",
298
299
  step_executor_configuration: StepExecutorConfiguration | None = None,
299
- step_executor_configuration_patch: StepExecutorConfigurationPatch | None = None,
300
300
  step_executor: StepExecutor | None = None,
301
- system_prompt_suffix_fragment: str | None = None,
302
- user_prompt_suffix_fragment: str | None = None,
301
+ system_prompt_suffix_fragments: Sequence[str] | None = None,
302
+ user_prompt_suffix_fragments: Sequence[str] | None = None,
303
303
  implicit_references: ImplicitReferenceNameToValue | None = None,
304
304
  ) -> Iterator[StepExecutor]:
305
- """Open a nested scope that can override the step executor or its configuration.
305
+ """Open a nested scope that can override the active execution context.
306
306
 
307
- Must be called inside an active run context. Creates a new scope_id while inheriting the run_id from the parent context.
307
+ Must be called inside an active run context. Creates a new ``scope_id`` while inheriting the ``run_id`` from the parent context.
308
308
 
309
309
  Args:
310
+ mode: Scope composition mode. ``"inherit"`` appends/merges values into the current scope. ``"replace"`` fully replaces provided list/dict values.
311
+ In both modes, ``None`` means no change.
310
312
  step_executor_configuration: Full replacement configuration for the step executor.
311
- step_executor_configuration_patch: Partial override applied on top of the current configuration.
312
313
  step_executor: Replacement step executor for this scope.
313
- system_prompt_suffix_fragment: Additional text appended to the system prompt.
314
- user_prompt_suffix_fragment: Additional text appended to the user prompt.
315
- implicit_references: Additional implicit global references merged into this scope and inherited by nested scopes.
314
+ system_prompt_suffix_fragments: Additional system prompt suffix fragments.
315
+ In ``mode="inherit"``, fragments are appended. In ``mode="replace"``, provided fragments fully replace the current fragments.
316
+ user_prompt_suffix_fragments: Additional user prompt suffix fragments.
317
+ In ``mode="inherit"``, fragments are appended. In ``mode="replace"``, provided fragments fully replace the current fragments.
318
+ implicit_references: Implicit global references for this scope.
319
+ In ``mode="inherit"``, values are merged with conflict checks. In ``mode="replace"``, provided mappings fully replace the current mapping.
316
320
 
317
321
  Yields:
318
322
  The step executor active within this scope.
@@ -321,9 +325,9 @@ def scope(
321
325
  ```python
322
326
  with nighthawk.run(executor):
323
327
  with nighthawk.scope(
324
- step_executor_configuration_patch=StepExecutorConfigurationPatch(
325
- model="openai:gpt-5.4-mini",
326
- ),
328
+ mode="replace",
329
+ implicit_references={},
330
+ system_prompt_suffix_fragments=["Use concise answers."],
327
331
  ) as scoped_executor:
328
332
  result = my_natural_function()
329
333
  ```
@@ -332,31 +336,13 @@ def scope(
332
336
  current_execution_context = get_execution_context()
333
337
 
334
338
  next_step_executor = current_step_executor
335
-
336
339
  if step_executor is not None:
337
340
  next_step_executor = step_executor
338
341
 
339
- has_configuration_update = any(
340
- value is not None
341
- for value in (
342
- step_executor_configuration,
343
- step_executor_configuration_patch,
344
- )
345
- )
346
-
347
- if has_configuration_update:
348
- current_agent_step_executor = _resolve_agent_step_executor(next_step_executor)
349
- next_configuration = current_agent_step_executor.configuration
350
-
351
- if step_executor_configuration is not None:
352
- next_configuration = step_executor_configuration
353
-
354
- if step_executor_configuration_patch is not None:
355
- next_configuration = step_executor_configuration_patch.apply_to(next_configuration)
356
-
342
+ if step_executor_configuration is not None:
357
343
  next_step_executor = _replace_step_executor_with_configuration(
358
344
  next_step_executor,
359
- configuration=next_configuration,
345
+ configuration=step_executor_configuration,
360
346
  )
361
347
 
362
348
  next_execution_context = replace(
@@ -364,27 +350,36 @@ def scope(
364
350
  scope_id=generate_ulid(),
365
351
  )
366
352
 
367
- next_system_fragments = _system_prompt_suffix_fragments_var.get()
368
- next_user_fragments = _user_prompt_suffix_fragments_var.get()
353
+ next_system_prompt_suffix_fragments = _system_prompt_suffix_fragments_var.get()
354
+ next_user_prompt_suffix_fragments = _user_prompt_suffix_fragments_var.get()
369
355
  next_implicit_reference_name_to_value = _implicit_reference_name_to_value_var.get()
370
356
 
371
- if system_prompt_suffix_fragment is not None:
372
- next_system_fragments = (*next_system_fragments, system_prompt_suffix_fragment)
357
+ if system_prompt_suffix_fragments is not None:
358
+ if mode == "inherit":
359
+ next_system_prompt_suffix_fragments = (*next_system_prompt_suffix_fragments, *system_prompt_suffix_fragments)
360
+ else:
361
+ next_system_prompt_suffix_fragments = tuple(system_prompt_suffix_fragments)
373
362
 
374
- if user_prompt_suffix_fragment is not None:
375
- next_user_fragments = (*next_user_fragments, user_prompt_suffix_fragment)
363
+ if user_prompt_suffix_fragments is not None:
364
+ if mode == "inherit":
365
+ next_user_prompt_suffix_fragments = (*next_user_prompt_suffix_fragments, *user_prompt_suffix_fragments)
366
+ else:
367
+ next_user_prompt_suffix_fragments = tuple(user_prompt_suffix_fragments)
376
368
 
377
369
  if implicit_references is not None:
378
- next_implicit_reference_name_to_value = _merge_implicit_reference_name_to_value_with_conflict_check(
379
- next_implicit_reference_name_to_value,
380
- implicit_references,
381
- )
370
+ if mode == "inherit":
371
+ next_implicit_reference_name_to_value = _merge_implicit_reference_name_to_value_with_conflict_check(
372
+ next_implicit_reference_name_to_value,
373
+ implicit_references,
374
+ )
375
+ else:
376
+ next_implicit_reference_name_to_value = dict(implicit_references)
382
377
 
383
378
  with tool_scope():
384
379
  step_executor_token = _step_executor_var.set(next_step_executor)
385
380
  execution_context_token = _execution_context_var.set(next_execution_context)
386
- system_fragments_token = _system_prompt_suffix_fragments_var.set(next_system_fragments)
387
- user_fragments_token = _user_prompt_suffix_fragments_var.set(next_user_fragments)
381
+ system_fragments_token = _system_prompt_suffix_fragments_var.set(next_system_prompt_suffix_fragments)
382
+ user_fragments_token = _user_prompt_suffix_fragments_var.set(next_user_prompt_suffix_fragments)
388
383
  implicit_reference_name_to_value_token = _implicit_reference_name_to_value_var.set(next_implicit_reference_name_to_value)
389
384
  try:
390
385
  with span(
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: nighthawk-python
3
- Version: 0.8.0
3
+ Version: 0.9.0
4
4
  Summary: A Python library where Python controls flow and LLMs or coding agents reason within constrained Natural blocks.
5
5
  Project-URL: Repository, https://github.com/kurusugawa-computer/nighthawk-python
6
6
  Project-URL: Documentation, https://kurusugawa-computer.github.io/nighthawk-python/
@@ -1,5 +1,5 @@
1
- nighthawk/__init__.py,sha256=AYep0vIYT0t5p6Qb-GyhEY5u0Kmp9nP6-jRZhkgWDn4,1382
2
- nighthawk/configuration.py,sha256=KKSpVnXd46Fg7l15xLZkdptnvhoy2xZqXry_zM4w_K0,8046
1
+ nighthawk/__init__.py,sha256=YqE9InkNzbW12v9Bh3qF4INLUHfJfmNT_hPqmE_kNb8,1308
2
+ nighthawk/configuration.py,sha256=1gtdLxcDMMnvz9t2fBZP-U-MHW54IHWmInSPiByTaGo,6286
3
3
  nighthawk/errors.py,sha256=CSw_VfIoppCE2SdN2zMtVbMGu_wFwD628v8ZAVxeHIY,602
4
4
  nighthawk/identifier_path.py,sha256=Io3VcT8BlxAASHQUSUclrRcCwnRKTtMDPSR1bpaDN6s,984
5
5
  nighthawk/json_renderer.py,sha256=ixpUYTsVVKOp-iMdHldw7-6I3JxelXOQD8vK2KVXwbw,7640
@@ -29,7 +29,7 @@ nighthawk/runtime/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU
29
29
  nighthawk/runtime/async_bridge.py,sha256=k1s7e5dJEMNZJd0xHqkqAY7VkE7tOBzJf6nAEJH_Xh0,1478
30
30
  nighthawk/runtime/prompt.py,sha256=fod1f8wS2xKoc3RM44eB8wrKwPmap9K1gwLw4EzUSP4,22669
31
31
  nighthawk/runtime/runner.py,sha256=saVkEeMkVfz1cm_AVaz8I23Y5OiWuWthqwv-z_4kwwY,23121
32
- nighthawk/runtime/scoping.py,sha256=jWvlNYMJ25cv3n0uA60vDkR9O-BBjREw1JVx6I9ChvY,14614
32
+ nighthawk/runtime/scoping.py,sha256=oa3SqjxctFQtTTZNYFj-scvf5Q0_zaVtluh8V1h8pqE,14986
33
33
  nighthawk/runtime/step_context.py,sha256=-MafRMUV9fo0IBTGnQfAF72IyQy90ZoUS9SK77OVgPc,5529
34
34
  nighthawk/runtime/step_contract.py,sha256=2rbZpUBbmWGTogAksYerIAZfv2xgBgaNmidWxjJtfmY,6391
35
35
  nighthawk/runtime/step_executor.py,sha256=Y4P7PuGzJT64FIwjhU2gABxoqHf6wPTXMiSFTSfkK6U,12847
@@ -40,7 +40,7 @@ nighthawk/tools/contracts.py,sha256=9bFj88GiRqwyUSfjhZxY3jhtQafdBP3RvDSz6-QCYPs,
40
40
  nighthawk/tools/execution.py,sha256=e1uv09MWOkCoZ5uQu9myUXyhQzM_UziR9yE0raFYtQI,2740
41
41
  nighthawk/tools/provided.py,sha256=gsZ7xcrNmEmwcvdkQi4YndTAIcfpwY1jTxpKJsYU4QQ,2079
42
42
  nighthawk/tools/registry.py,sha256=wsZznwI2xPTflN6tfVE8je5keNVqBHHtTPd9bq0Cu7g,6425
43
- nighthawk_python-0.8.0.dist-info/METADATA,sha256=ZY11lioIPP53PYIiGEZkJjUGZ5GoJJ6zk3FEl7OzUVU,6192
44
- nighthawk_python-0.8.0.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
45
- nighthawk_python-0.8.0.dist-info/licenses/LICENSE,sha256=R8tjrX79o5Pbi_l_aFPPWDbTZkmz4is0CxmzMMs3f0A,1076
46
- nighthawk_python-0.8.0.dist-info/RECORD,,
43
+ nighthawk_python-0.9.0.dist-info/METADATA,sha256=DIyU5rb7e0BvfBfC7hbiw8vc8SIeDQRapJ1XTM0gDg0,6192
44
+ nighthawk_python-0.9.0.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
45
+ nighthawk_python-0.9.0.dist-info/licenses/LICENSE,sha256=R8tjrX79o5Pbi_l_aFPPWDbTZkmz4is0CxmzMMs3f0A,1076
46
+ nighthawk_python-0.9.0.dist-info/RECORD,,