nighthawk-python 0.1.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.
@@ -0,0 +1,72 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Literal
4
+
5
+ import tiktoken
6
+ from pydantic import BaseModel
7
+
8
+ from ..json_renderer import JsonRendererStyle, render_json_text
9
+
10
+ type ErrorKind = Literal["invalid_input", "resolution", "execution", "transient", "internal"]
11
+
12
+
13
+ class ToolBoundaryError(Exception):
14
+ def __init__(self, *, kind: ErrorKind, message: str, guidance: str | None = None) -> None:
15
+ super().__init__(message)
16
+ self.kind: ErrorKind = kind
17
+ self.guidance: str | None = guidance
18
+
19
+
20
+ class _Error(BaseModel, extra="forbid"):
21
+ kind: ErrorKind
22
+ message: str
23
+ guidance: str | None = None
24
+
25
+
26
+ class ToolResult[ValueType](BaseModel, extra="forbid"):
27
+ value: ValueType | None
28
+ error: _Error | None
29
+
30
+
31
+ def render_tool_result_json_text(
32
+ *,
33
+ value: object | None,
34
+ error: object | None,
35
+ max_tokens: int,
36
+ encoding: tiktoken.Encoding,
37
+ style: JsonRendererStyle,
38
+ ) -> str:
39
+ """Render a tool result envelope as compact JSON text.
40
+
41
+ Both ``value`` and ``error`` are individually rendered under their own
42
+ token budgets via ``render_json_text``, then assembled into a
43
+ ``{"value": ..., "error": ...}`` envelope using f-string interpolation.
44
+ The sub-values are already valid JSON fragments produced by
45
+ ``render_json_text`` / ``json.dumps``, so the f-string concatenation
46
+ is structurally safe.
47
+ """
48
+ if error is None:
49
+ error_text = "null"
50
+ error_token_count = 0
51
+ value_max_tokens = max_tokens
52
+ else:
53
+ error_max_tokens = int(max_tokens * 0.9)
54
+ error_text, error_token_count = render_json_text(
55
+ error,
56
+ max_tokens=error_max_tokens,
57
+ encoding=encoding,
58
+ style=style,
59
+ )
60
+ value_max_tokens = max(max_tokens - error_token_count, 0)
61
+
62
+ if value is None:
63
+ value_text = "null"
64
+ else:
65
+ value_text, _ = render_json_text(
66
+ value,
67
+ max_tokens=value_max_tokens,
68
+ encoding=encoding,
69
+ style=style,
70
+ )
71
+
72
+ return f'{{"value":{value_text},"error":{error_text}}}'
@@ -0,0 +1,83 @@
1
+ """Tool execution wrappers: normalization, classification, and toolset wrapping."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from collections.abc import Awaitable, Callable
6
+ from typing import Any
7
+
8
+ from pydantic_ai import RunContext
9
+ from pydantic_ai.exceptions import ApprovalRequired, CallDeferred, ModelRetry
10
+ from pydantic_ai.toolsets.abstract import ToolsetTool
11
+ from pydantic_ai.toolsets.wrapper import WrapperToolset
12
+
13
+ from ..json_renderer import JsonableValue, to_jsonable_value
14
+ from .contracts import ErrorKind, ToolBoundaryError, ToolResult, _Error
15
+
16
+
17
+ def _classify_unexpected_exception(exception: BaseException) -> ErrorKind:
18
+ if isinstance(exception, TimeoutError):
19
+ return "transient"
20
+ return "internal"
21
+
22
+
23
+ def _normalize_tool_success(value: object) -> ToolResult[JsonableValue]:
24
+ return ToolResult(value=to_jsonable_value(value), error=None)
25
+
26
+
27
+ def _normalize_tool_failure(*, kind: ErrorKind, message: str, guidance: str | None) -> ToolResult[JsonableValue]:
28
+ return ToolResult(
29
+ value=None,
30
+ error=_Error(
31
+ kind=kind,
32
+ message=message,
33
+ guidance=guidance,
34
+ ),
35
+ )
36
+
37
+
38
+ async def _run_tool_and_normalize(tool_call: Callable[[], Awaitable[object]]) -> ToolResult[JsonableValue]:
39
+ """Execute a tool call and normalize the outcome to a ToolResult.
40
+
41
+ Control-flow exceptions are re-raised unchanged.
42
+ """
43
+
44
+ try:
45
+ value = await tool_call()
46
+ except (ModelRetry, CallDeferred, ApprovalRequired):
47
+ raise
48
+ except ToolBoundaryError as exception:
49
+ return _normalize_tool_failure(
50
+ kind=exception.kind,
51
+ message=str(exception),
52
+ guidance=exception.guidance,
53
+ )
54
+ except TimeoutError:
55
+ raise ModelRetry("Tool execution timed out. Retry.") from None
56
+ except Exception as exception:
57
+ kind = _classify_unexpected_exception(exception)
58
+ return _normalize_tool_failure(
59
+ kind=kind,
60
+ message=str(exception) or "Tool execution failed",
61
+ guidance="The tool execution raised an unexpected error. Retry or report this error.",
62
+ )
63
+
64
+ return _normalize_tool_success(value)
65
+
66
+
67
+ class ToolResultWrapperToolset[DepsType](WrapperToolset[DepsType]):
68
+ def __getattr__(self, name: str) -> object:
69
+ return getattr(self.wrapped, name)
70
+
71
+ async def call_tool(
72
+ self,
73
+ name: str,
74
+ tool_args: dict[str, Any],
75
+ ctx: RunContext[DepsType],
76
+ tool: ToolsetTool[DepsType],
77
+ ) -> ToolResult[JsonableValue]:
78
+ run_context = ctx
79
+
80
+ async def tool_call() -> object:
81
+ return await self.wrapped.call_tool(name, tool_args, run_context, tool)
82
+
83
+ return await _run_tool_and_normalize(tool_call)
@@ -0,0 +1,80 @@
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass
4
+ from typing import Any
5
+
6
+ from pydantic_ai import RunContext
7
+ from pydantic_ai.tools import Tool
8
+
9
+ from ..runtime.step_context import StepContext
10
+ from .assignment import assign_tool, eval_expression
11
+ from .contracts import ToolBoundaryError
12
+
13
+
14
+ @dataclass(frozen=True)
15
+ class ProvidedToolDefinition:
16
+ name: str
17
+ tool: Tool[StepContext]
18
+
19
+
20
+ def _eval_expression_or_raise(run_context: RunContext[StepContext], expression: str) -> object:
21
+ try:
22
+ return eval_expression(run_context.deps, expression)
23
+ except Exception as exception:
24
+ raise ToolBoundaryError(kind="execution", message=str(exception), guidance="Fix the expression and retry.") from exception
25
+
26
+
27
+ def build_provided_tool_definitions() -> list[ProvidedToolDefinition]:
28
+ metadata = {"nighthawk.provided": True}
29
+
30
+ def nh_assign(
31
+ run_context: RunContext[StepContext],
32
+ target_path: str,
33
+ expression: str,
34
+ ) -> dict[str, Any]:
35
+ return assign_tool(
36
+ run_context.deps,
37
+ target_path,
38
+ expression,
39
+ )
40
+
41
+ def nh_eval(run_context: RunContext[StepContext], expression: str) -> object:
42
+ return _eval_expression_or_raise(run_context, expression)
43
+
44
+ # nh_exec intentionally shares the same implementation as nh_eval.
45
+ # Both use Python eval(); the two-tool split exists purely as a semantic
46
+ # signal to the LLM (inspect vs mutate intent), not a runtime distinction.
47
+ # If future requirements demand runtime differentiation (e.g., read-only
48
+ # enforcement for nh_eval), the split provides the structural hook for it.
49
+ def nh_exec(run_context: RunContext[StepContext], expression: str) -> object:
50
+ return _eval_expression_or_raise(run_context, expression)
51
+
52
+ return [
53
+ ProvidedToolDefinition(
54
+ name="nh_assign",
55
+ tool=Tool(
56
+ nh_assign,
57
+ name="nh_assign",
58
+ metadata=metadata,
59
+ description="Rebind a name or set a nested field to a new value. target_path format: name(.field)*.",
60
+ ),
61
+ ),
62
+ ProvidedToolDefinition(
63
+ name="nh_eval",
64
+ tool=Tool(
65
+ nh_eval,
66
+ name="nh_eval",
67
+ metadata=metadata,
68
+ description="Evaluate a Python expression and return the result.",
69
+ ),
70
+ ),
71
+ ProvidedToolDefinition(
72
+ name="nh_exec",
73
+ tool=Tool(
74
+ nh_exec,
75
+ name="nh_exec",
76
+ metadata=metadata,
77
+ description="Execute a Python expression for its side effect (e.g., list.append(), dict.update()). Returns the expression result.",
78
+ ),
79
+ ),
80
+ ]
@@ -0,0 +1,212 @@
1
+ from __future__ import annotations
2
+
3
+ import re
4
+ from collections.abc import Callable, Iterator
5
+ from contextlib import contextmanager
6
+ from contextvars import ContextVar
7
+ from dataclasses import dataclass
8
+ from typing import Any, overload
9
+
10
+ from pydantic_ai.tools import Tool
11
+
12
+ from ..errors import ToolRegistrationError
13
+ from ..runtime.step_context import StepContext
14
+ from .provided import build_provided_tool_definitions
15
+
16
+
17
+ @dataclass(frozen=True)
18
+ class ToolDefinition:
19
+ name: str
20
+ tool: Tool[StepContext]
21
+
22
+
23
+ _builtin_tool_name_to_definition: dict[str, ToolDefinition] = {}
24
+ _builtin_tools_registered = False
25
+
26
+ _global_tool_name_to_definition: dict[str, ToolDefinition] = {}
27
+
28
+ _tool_scope_stack_var: ContextVar[tuple[dict[str, ToolDefinition], ...]] = ContextVar(
29
+ "nighthawk_tool_scope_stack",
30
+ default=(),
31
+ )
32
+
33
+ _call_scope_stack_var: ContextVar[tuple[dict[str, ToolDefinition], ...]] = ContextVar(
34
+ "nighthawk_call_tool_scope_stack",
35
+ default=(),
36
+ )
37
+
38
+ _VALID_NAME_PATTERN = re.compile(r"^[A-Za-z_][A-Za-z0-9_]*$")
39
+
40
+
41
+ def _validate_tool_name(name: str) -> None:
42
+ try:
43
+ name.encode("ascii")
44
+ except UnicodeEncodeError as e:
45
+ raise ToolRegistrationError(f"Tool name must be ASCII: {name!r}") from e
46
+
47
+ if not _VALID_NAME_PATTERN.fullmatch(name):
48
+ raise ToolRegistrationError(f"Tool name must match ^[A-Za-z_][A-Za-z0-9_]*$: {name!r}")
49
+
50
+
51
+ def ensure_builtin_tools_registered() -> None:
52
+ global _builtin_tools_registered
53
+
54
+ if _builtin_tools_registered:
55
+ return
56
+
57
+ for builtin_definition in build_provided_tool_definitions():
58
+ _validate_tool_name(builtin_definition.name)
59
+ if builtin_definition.name in _builtin_tool_name_to_definition:
60
+ raise ToolRegistrationError(f"Duplicate builtin tool name: {builtin_definition.name!r}")
61
+ _builtin_tool_name_to_definition[builtin_definition.name] = ToolDefinition(
62
+ name=builtin_definition.name,
63
+ tool=builtin_definition.tool,
64
+ )
65
+
66
+ _builtin_tools_registered = True
67
+
68
+
69
+ def _visible_tool_definitions() -> dict[str, ToolDefinition]:
70
+ ensure_builtin_tools_registered()
71
+
72
+ merged: dict[str, ToolDefinition] = dict(_builtin_tool_name_to_definition)
73
+ merged.update(_global_tool_name_to_definition)
74
+
75
+ for scope in _tool_scope_stack_var.get():
76
+ merged.update(scope)
77
+
78
+ for scope in _call_scope_stack_var.get():
79
+ merged.update(scope)
80
+
81
+ return merged
82
+
83
+
84
+ def _register_tool_definition(tool_definition: ToolDefinition, *, overwrite: bool) -> None:
85
+ ensure_builtin_tools_registered()
86
+
87
+ name = tool_definition.name
88
+ visible = _visible_tool_definitions()
89
+
90
+ if name in visible and not overwrite:
91
+ raise ToolRegistrationError(f"Tool name conflict: {name!r}. Pass overwrite=True to replace the visible definition.")
92
+
93
+ call_scope_stack = _call_scope_stack_var.get()
94
+ if call_scope_stack:
95
+ call_scope_stack[-1][name] = tool_definition
96
+ return
97
+
98
+ tool_scope_stack = _tool_scope_stack_var.get()
99
+ if tool_scope_stack:
100
+ tool_scope_stack[-1][name] = tool_definition
101
+ return
102
+
103
+ _global_tool_name_to_definition[name] = tool_definition
104
+
105
+
106
+ @contextmanager
107
+ def tool_scope() -> Iterator[None]:
108
+ current = _tool_scope_stack_var.get()
109
+ token = _tool_scope_stack_var.set((*current, {}))
110
+ try:
111
+ yield
112
+ finally:
113
+ _tool_scope_stack_var.reset(token)
114
+
115
+
116
+ @contextmanager
117
+ def call_scope() -> Iterator[None]:
118
+ current = _call_scope_stack_var.get()
119
+ token = _call_scope_stack_var.set((*current, {}))
120
+ try:
121
+ yield
122
+ finally:
123
+ _call_scope_stack_var.reset(token)
124
+
125
+
126
+ def get_visible_tools() -> list[Tool[StepContext]]:
127
+ ensure_builtin_tools_registered()
128
+ visible = dict(_visible_tool_definitions())
129
+ return [definition.tool for definition in visible.values()]
130
+
131
+
132
+ type ToolFunction = Callable[..., Any]
133
+
134
+
135
+ @overload
136
+ def tool(func: ToolFunction, /) -> ToolFunction: ...
137
+
138
+
139
+ @overload
140
+ def tool(
141
+ func: None = None,
142
+ /,
143
+ *,
144
+ name: str | None = None,
145
+ overwrite: bool = False,
146
+ description: str | None = None,
147
+ metadata: dict[str, Any] | None = None,
148
+ ) -> Callable[[ToolFunction], ToolFunction]: ...
149
+
150
+
151
+ def tool(
152
+ func: ToolFunction | None = None,
153
+ /,
154
+ *,
155
+ name: str | None = None,
156
+ overwrite: bool = False,
157
+ description: str | None = None,
158
+ metadata: dict[str, Any] | None = None,
159
+ ) -> ToolFunction | Callable[[ToolFunction], ToolFunction]:
160
+ """Register a Python function as a Nighthawk tool visible to Natural blocks.
161
+
162
+ Args:
163
+ func: The function to register. Can be omitted for use as a bare decorator.
164
+ name: Tool name override. Defaults to the function name.
165
+ overwrite: If True, replace any existing tool with the same name.
166
+ description: Tool description override. Defaults to the function docstring.
167
+ metadata: Arbitrary metadata attached to the tool definition.
168
+
169
+ Raises:
170
+ ToolRegistrationError: If the name conflicts with an existing tool and
171
+ overwrite is False.
172
+
173
+ Example:
174
+ ```python
175
+ @nighthawk.tool
176
+ def lookup_user(user_id: str) -> dict:
177
+ return {"user_id": user_id, "name": "Alice"}
178
+ ```
179
+ """
180
+
181
+ def decorator(inner: ToolFunction) -> ToolFunction:
182
+ ensure_builtin_tools_registered()
183
+
184
+ tool_name = name or inner.__name__
185
+ _validate_tool_name(tool_name)
186
+
187
+ resolved_description = description
188
+ if resolved_description is None:
189
+ resolved_description = inner.__doc__
190
+
191
+ tool_object: Tool[StepContext] = Tool(
192
+ inner,
193
+ name=tool_name,
194
+ description=resolved_description,
195
+ metadata=metadata,
196
+ )
197
+
198
+ tool_definition = ToolDefinition(name=tool_name, tool=tool_object)
199
+ _register_tool_definition(tool_definition, overwrite=overwrite)
200
+ return inner
201
+
202
+ if func is not None:
203
+ return decorator(func)
204
+
205
+ return decorator
206
+
207
+
208
+ def _reset_all_tools_for_tests() -> None:
209
+ global _builtin_tools_registered
210
+ _global_tool_name_to_definition.clear()
211
+ _builtin_tool_name_to_definition.clear()
212
+ _builtin_tools_registered = False
@@ -0,0 +1,111 @@
1
+ Metadata-Version: 2.4
2
+ Name: nighthawk-python
3
+ Version: 0.1.0
4
+ Summary: An experimental Python library that embeds Natural blocks inside Python functions and executes them using an LLM.
5
+ Project-URL: Repository, https://github.com/kurusugawa-computer/nighthawk-python
6
+ Project-URL: Documentation, https://kurusugawa-computer.github.io/nighthawk-python/
7
+ Project-URL: Bug Tracker, https://github.com/kurusugawa-computer/nighthawk-python/issues
8
+ Author-email: "Kurusugawa Computer Inc." <oss@kurusugawa.jp>
9
+ License-Expression: MIT
10
+ License-File: LICENSE
11
+ Keywords: embedded-dsl,interoperability,llm,natural-language,pydantic-ai
12
+ Classifier: Development Status :: 3 - Alpha
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: License :: OSI Approved :: MIT License
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.13
17
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
18
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
19
+ Classifier: Typing :: Typed
20
+ Requires-Python: >=3.13
21
+ Requires-Dist: headson>=0.16.1
22
+ Requires-Dist: pydantic-ai-slim>=1.59
23
+ Requires-Dist: pydantic>=2
24
+ Requires-Dist: pyyaml>=6
25
+ Requires-Dist: tiktoken>=0.12
26
+ Provides-Extra: claude-code-cli
27
+ Requires-Dist: mcp>=1.26; extra == 'claude-code-cli'
28
+ Provides-Extra: claude-code-sdk
29
+ Requires-Dist: claude-agent-sdk>=0.1; extra == 'claude-code-sdk'
30
+ Provides-Extra: codex
31
+ Requires-Dist: mcp>=1.26; extra == 'codex'
32
+ Description-Content-Type: text/markdown
33
+
34
+ [![PyPI](https://img.shields.io/pypi/v/nighthawk-python)](https://pypi.org/project/nighthawk-python)
35
+ ![PyPI - Downloads](https://img.shields.io/pypi/dm/nighthawk-python)
36
+ [![license](https://img.shields.io/github/license/psg-mit/nighthawk-python.svg)](https://github.com/kurusugawa-computer/nighthawk-python/tree/main/LICENSE)
37
+ [![issue resolution](https://img.shields.io/github/issues-closed-raw/kurusugawa-computer/nighthawk-python)](https://github.com/kurusugawa-computer/nighthawk-python/issues)
38
+
39
+ # Nighthawk
40
+
41
+ <div align="center">
42
+ <img src="https://github.com/kurusugawa-computer/nighthawk-python/raw/main/docs/assets/nighthawk_logo-128x128.png" alt="nighthawk-logo" width="128px" margin="10px"></img>
43
+ </div>
44
+
45
+ Nighthawk is an experimental Python library exploring a clear separation between **hard control** (Python code) for strict procedure and deterministic flow, and **soft reasoning** (an LLM) for semantic interpretation inside small embedded "Natural blocks". It is a compact reimplementation of the core ideas of [Nightjar](https://github.com/psg-mit/nightjarpy).
46
+
47
+ ## Quickstart
48
+
49
+ Prerequisites: Python 3.13+
50
+
51
+ Install Nighthawk and a provider:
52
+
53
+ ```bash
54
+ pip install nighthawk-python pydantic-ai-slim[openai]
55
+ ```
56
+
57
+ Save as `quickstart.py`:
58
+
59
+ ```py
60
+ import nighthawk as nh
61
+
62
+ step_executor = nh.AgentStepExecutor.from_configuration(
63
+ configuration=nh.StepExecutorConfiguration(model="openai-responses:gpt-5-mini")
64
+ )
65
+
66
+ with nh.run(step_executor):
67
+
68
+ @nh.natural_function
69
+ def calculate_total(items: str) -> int:
70
+ total = 0
71
+ """natural
72
+ Read <items> and set <:total> to the sum of all quantities mentioned.
73
+ """
74
+ return total
75
+
76
+ print(calculate_total("three apples, a dozen eggs, and 5 oranges"))
77
+ ```
78
+
79
+ Run with your API key:
80
+
81
+ ```bash
82
+ export OPENAI_API_KEY=sk-xxxxxxxxx
83
+ python quickstart.py
84
+ # => 20
85
+ ```
86
+
87
+ For backends, credentials, model identifiers, and detailed guidance, see the [documentation site](https://kurusugawa-computer.github.io/nighthawk-python/).
88
+
89
+ ## Development
90
+
91
+ Run tests:
92
+
93
+ ```bash
94
+ uv run pytest -q
95
+ ```
96
+
97
+ Run an OTel collector UI (otel-tui) for observability:
98
+
99
+ ```bash
100
+ docker run --rm -it -p 4318:4318 --name otel-tui ymtdzzz/otel-tui:latest
101
+ ```
102
+
103
+ Then run integration tests with `OTEL_EXPORTER_OTLP_ENDPOINT` set:
104
+
105
+ ```bash
106
+ OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318 uv run pytest -q tests/integration/test_llm_integration.py
107
+ ```
108
+
109
+ ## References
110
+
111
+ - Nightjar (upstream concept): https://github.com/psg-mit/nightjarpy
@@ -0,0 +1,36 @@
1
+ nighthawk/__init__.py,sha256=4Yt9le9-Nh0xZ9Ty9t5XNZY8_KBTUZtKz7zBe8UadHA,1296
2
+ nighthawk/configuration.py,sha256=igHa9H1eaHT4D8jtpd1i28O3f_CRc8zkwtxQ93WsQyY,7241
3
+ nighthawk/errors.py,sha256=CSw_VfIoppCE2SdN2zMtVbMGu_wFwD628v8ZAVxeHIY,602
4
+ nighthawk/identifier_path.py,sha256=Io3VcT8BlxAASHQUSUclrRcCwnRKTtMDPSR1bpaDN6s,984
5
+ nighthawk/json_renderer.py,sha256=ixpUYTsVVKOp-iMdHldw7-6I3JxelXOQD8vK2KVXwbw,7640
6
+ nighthawk/backends/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
7
+ nighthawk/backends/base.py,sha256=HicDmOFN_CChCDs532dHtiGAiw1wKOsWCx0185u1Dz8,3869
8
+ nighthawk/backends/claude_code_cli.py,sha256=g1Jy1JtBaETyL1lMSSUfwwnPV_ojJTCokdVlG8EQn5A,14436
9
+ nighthawk/backends/claude_code_sdk.py,sha256=-EtGp5J5x8wncWaPJ-REBvtZVfsKQpYv5-F9lTGN-ck,13176
10
+ nighthawk/backends/codex.py,sha256=nSRt6gsssGi-xQ9tD7xvkC8aYH98itYzhIbw4K0Klis,14739
11
+ nighthawk/backends/mcp_boundary.py,sha256=DjBgYI32bRne3d91XxSV56xBrxOjcnJi2nyxGcy613E,4186
12
+ nighthawk/backends/mcp_server.py,sha256=Vw1iRRhrMgFlX5umSLIc0XGlRAljOm9xqoTYBNpbjxI,8142
13
+ nighthawk/backends/tool_bridge.py,sha256=yv4WbxT7OQQaR3OsHuNIPfKhNjsaIIfRFbqv2hNtMro,9288
14
+ nighthawk/natural/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
15
+ nighthawk/natural/blocks.py,sha256=U6VhskMuEVUdWiPqFUBavFlvHgDkDbCYOKeQpdXxXMY,9717
16
+ nighthawk/natural/decorator.py,sha256=3bx4PQ85Odh5sN3_S3N9wNdDmFUWxUgzF_YbfpNS-LM,11134
17
+ nighthawk/natural/transform.py,sha256=_Uctcz65kI7yN_uuWmG-qIujQzgd7sqq-IGdlzEAopg,14950
18
+ nighthawk/runtime/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
19
+ nighthawk/runtime/async_bridge.py,sha256=k1s7e5dJEMNZJd0xHqkqAY7VkE7tOBzJf6nAEJH_Xh0,1478
20
+ nighthawk/runtime/prompt.py,sha256=Q5IfNCj0II415uevGkNJyRZPvEBz16_BRk6KMIxOve4,12254
21
+ nighthawk/runtime/runner.py,sha256=B20ZHt4DCvqsYwicVsKPc9cmitcNUMIzW1zzYRfLwgY,17946
22
+ nighthawk/runtime/scoping.py,sha256=yKYjgSXRp4kXvmOE3ai34_dbAkFhc1VGbwyvri81BmU,9474
23
+ nighthawk/runtime/step_context.py,sha256=CPfCOOc1KWvF_9nzmKOmm2NssL5-FXoDhzI4iWwcomg,5489
24
+ nighthawk/runtime/step_contract.py,sha256=DFKH8wuEA6ZY9MKHw6lwQhD6-cgq6sJAjUBSfOw1_-Y,7312
25
+ nighthawk/runtime/step_executor.py,sha256=7b9mYA-NqkR9CXNyHbZWCYb1q6k8cr_UXoy0CyhZXVY,13028
26
+ nighthawk/runtime/tool_calls.py,sha256=Hepb71WF_k51_PSnXtyiAoSm53kDBbqLsqd7Lh-5nWE,3056
27
+ nighthawk/tools/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
28
+ nighthawk/tools/assignment.py,sha256=Mh-WLK7U5pE3IBoyzXVJfFW5SdAxqUL0O1NSFkiX4ds,8823
29
+ nighthawk/tools/contracts.py,sha256=lLfBue1VF8NMd76GMIp6y71cCx1p4MlVQyNhd4Ju2qk,2096
30
+ nighthawk/tools/execution.py,sha256=e1uv09MWOkCoZ5uQu9myUXyhQzM_UziR9yE0raFYtQI,2740
31
+ nighthawk/tools/provided.py,sha256=6vIBnT33m0KUNQ40xgnhm52wAtV1afPEYVrgIc-ZiUk,2773
32
+ nighthawk/tools/registry.py,sha256=9obdHJmocr9b_DGBVzq8unDQA1jy-OfMWRky0UGsLQA,6141
33
+ nighthawk_python-0.1.0.dist-info/METADATA,sha256=K2XHWwrdiSZ_aVssgQdKLWaTjoPuXqIRYJko4AtK3R4,3967
34
+ nighthawk_python-0.1.0.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
35
+ nighthawk_python-0.1.0.dist-info/licenses/LICENSE,sha256=R8tjrX79o5Pbi_l_aFPPWDbTZkmz4is0CxmzMMs3f0A,1076
36
+ nighthawk_python-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.29.0
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright 2026 Kurusugawa Computer Inc.
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.