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.
- nighthawk/__init__.py +48 -0
- nighthawk/backends/__init__.py +0 -0
- nighthawk/backends/base.py +95 -0
- nighthawk/backends/claude_code_cli.py +342 -0
- nighthawk/backends/claude_code_sdk.py +325 -0
- nighthawk/backends/codex.py +352 -0
- nighthawk/backends/mcp_boundary.py +129 -0
- nighthawk/backends/mcp_server.py +226 -0
- nighthawk/backends/tool_bridge.py +240 -0
- nighthawk/configuration.py +193 -0
- nighthawk/errors.py +25 -0
- nighthawk/identifier_path.py +35 -0
- nighthawk/json_renderer.py +216 -0
- nighthawk/natural/__init__.py +0 -0
- nighthawk/natural/blocks.py +279 -0
- nighthawk/natural/decorator.py +302 -0
- nighthawk/natural/transform.py +346 -0
- nighthawk/runtime/__init__.py +0 -0
- nighthawk/runtime/async_bridge.py +50 -0
- nighthawk/runtime/prompt.py +344 -0
- nighthawk/runtime/runner.py +462 -0
- nighthawk/runtime/scoping.py +288 -0
- nighthawk/runtime/step_context.py +171 -0
- nighthawk/runtime/step_contract.py +231 -0
- nighthawk/runtime/step_executor.py +360 -0
- nighthawk/runtime/tool_calls.py +99 -0
- nighthawk/tools/__init__.py +0 -0
- nighthawk/tools/assignment.py +246 -0
- nighthawk/tools/contracts.py +72 -0
- nighthawk/tools/execution.py +83 -0
- nighthawk/tools/provided.py +80 -0
- nighthawk/tools/registry.py +212 -0
- nighthawk_python-0.1.0.dist-info/METADATA +111 -0
- nighthawk_python-0.1.0.dist-info/RECORD +36 -0
- nighthawk_python-0.1.0.dist-info/WHEEL +4 -0
- nighthawk_python-0.1.0.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,462 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import inspect
|
|
4
|
+
import uuid
|
|
5
|
+
from dataclasses import dataclass
|
|
6
|
+
from types import FrameType
|
|
7
|
+
from typing import TypedDict
|
|
8
|
+
|
|
9
|
+
from pydantic import TypeAdapter
|
|
10
|
+
|
|
11
|
+
from ..errors import ExecutionError, NaturalParseError
|
|
12
|
+
from ..identifier_path import parse_identifier_path
|
|
13
|
+
from ..natural.blocks import parse_frontmatter, validate_frontmatter_deny
|
|
14
|
+
from .async_bridge import run_coroutine_synchronously
|
|
15
|
+
from .scoping import RUN_ID, SCOPE_ID, STEP_ID, get_execution_context, span
|
|
16
|
+
from .step_context import (
|
|
17
|
+
_MISSING,
|
|
18
|
+
StepContext,
|
|
19
|
+
ToolResultRenderingPolicy,
|
|
20
|
+
get_python_cell_scope_stack,
|
|
21
|
+
get_python_name_scope_stack,
|
|
22
|
+
get_step_context_stack,
|
|
23
|
+
resolve_name_in_step_context,
|
|
24
|
+
)
|
|
25
|
+
from .step_contract import (
|
|
26
|
+
RaiseStepOutcome,
|
|
27
|
+
ReturnStepOutcome,
|
|
28
|
+
StepOutcome,
|
|
29
|
+
)
|
|
30
|
+
from .step_executor import AsyncStepExecutor, StepExecutor, SyncStepExecutor
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def _split_frontmatter(
|
|
34
|
+
processed_natural_program: str,
|
|
35
|
+
) -> tuple[str, tuple[str, ...]]:
|
|
36
|
+
"""Parse frontmatter, validate deny directives, and return stripped program + denied kinds."""
|
|
37
|
+
try:
|
|
38
|
+
program_without_frontmatter, frontmatter = parse_frontmatter(processed_natural_program)
|
|
39
|
+
except NaturalParseError as e:
|
|
40
|
+
raise ExecutionError(str(e)) from e
|
|
41
|
+
try:
|
|
42
|
+
denied_step_kinds = validate_frontmatter_deny(frontmatter)
|
|
43
|
+
except NaturalParseError as e:
|
|
44
|
+
raise ExecutionError(str(e)) from e
|
|
45
|
+
return program_without_frontmatter, denied_step_kinds
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def _compute_allowed_step_kinds(is_in_loop: bool, denied_step_kinds: tuple[str, ...]) -> tuple[str, ...]:
|
|
49
|
+
base_allowed_kinds: list[str] = ["pass", "return", "raise"]
|
|
50
|
+
if is_in_loop:
|
|
51
|
+
base_allowed_kinds.extend(["break", "continue"])
|
|
52
|
+
return tuple(kind for kind in base_allowed_kinds if kind not in denied_step_kinds)
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def _build_step_globals(
|
|
56
|
+
python_globals: dict[str, object],
|
|
57
|
+
) -> dict[str, object]:
|
|
58
|
+
step_globals: dict[str, object] = dict(python_globals)
|
|
59
|
+
if "__builtins__" not in step_globals:
|
|
60
|
+
step_globals["__builtins__"] = __builtins__
|
|
61
|
+
return step_globals
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def _build_step_locals(
|
|
65
|
+
python_locals: dict[str, object],
|
|
66
|
+
) -> dict[str, object]:
|
|
67
|
+
step_locals: dict[str, object] = {}
|
|
68
|
+
step_context_stack = get_step_context_stack()
|
|
69
|
+
if step_context_stack:
|
|
70
|
+
step_locals.update(step_context_stack[-1].step_locals)
|
|
71
|
+
step_locals.update(python_locals)
|
|
72
|
+
return step_locals
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def _resolve_input_bindings(
|
|
76
|
+
input_binding_names: list[str],
|
|
77
|
+
*,
|
|
78
|
+
python_locals: dict[str, object],
|
|
79
|
+
python_globals: dict[str, object],
|
|
80
|
+
caller_frame: FrameType,
|
|
81
|
+
) -> dict[str, tuple[object, str]]:
|
|
82
|
+
"""Resolve each input binding using Python LEGB rules.
|
|
83
|
+
|
|
84
|
+
Returns a mapping of binding name to (resolved_value, resolution_kind).
|
|
85
|
+
"""
|
|
86
|
+
local_variable_name_set = set(caller_frame.f_code.co_varnames)
|
|
87
|
+
local_variable_name_set.update(caller_frame.f_code.co_cellvars)
|
|
88
|
+
free_variable_name_set = set(caller_frame.f_code.co_freevars)
|
|
89
|
+
|
|
90
|
+
python_cell_scope_stack = get_python_cell_scope_stack()
|
|
91
|
+
python_name_scope_stack = get_python_name_scope_stack()
|
|
92
|
+
|
|
93
|
+
python_builtins = python_globals.get("__builtins__", __builtins__)
|
|
94
|
+
|
|
95
|
+
def resolve_one(binding_name: str) -> tuple[object, str]:
|
|
96
|
+
if binding_name in python_locals:
|
|
97
|
+
return python_locals[binding_name], "locals"
|
|
98
|
+
|
|
99
|
+
for scope in reversed(python_cell_scope_stack):
|
|
100
|
+
if binding_name not in scope:
|
|
101
|
+
continue
|
|
102
|
+
cell = scope[binding_name]
|
|
103
|
+
try:
|
|
104
|
+
return cell.cell_contents, "cell_scope"
|
|
105
|
+
except ValueError:
|
|
106
|
+
break
|
|
107
|
+
|
|
108
|
+
if binding_name in local_variable_name_set:
|
|
109
|
+
raise UnboundLocalError(f"cannot access local variable {binding_name!r} where it is not associated with a value")
|
|
110
|
+
|
|
111
|
+
if binding_name in free_variable_name_set:
|
|
112
|
+
error = NameError(f"cannot access free variable {binding_name!r} where it is not associated with a value in enclosing scope")
|
|
113
|
+
error.name = binding_name
|
|
114
|
+
raise error
|
|
115
|
+
|
|
116
|
+
for scope in reversed(python_name_scope_stack):
|
|
117
|
+
if binding_name in scope:
|
|
118
|
+
return scope[binding_name], "name_scope"
|
|
119
|
+
|
|
120
|
+
if binding_name in python_globals:
|
|
121
|
+
return python_globals[binding_name], "globals"
|
|
122
|
+
|
|
123
|
+
if isinstance(python_builtins, dict) and binding_name in python_builtins:
|
|
124
|
+
return python_builtins[binding_name], "builtins"
|
|
125
|
+
|
|
126
|
+
if hasattr(python_builtins, binding_name):
|
|
127
|
+
return getattr(python_builtins, binding_name), "builtins"
|
|
128
|
+
|
|
129
|
+
error = NameError(f"name {binding_name!r} is not defined")
|
|
130
|
+
error.name = binding_name
|
|
131
|
+
raise error
|
|
132
|
+
|
|
133
|
+
binding_name_to_value_and_resolution_kind: dict[str, tuple[object, str]] = {}
|
|
134
|
+
for binding_name in input_binding_names:
|
|
135
|
+
binding_name_to_value_and_resolution_kind[binding_name] = resolve_one(binding_name)
|
|
136
|
+
return binding_name_to_value_and_resolution_kind
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
class StepEnvelope(TypedDict):
|
|
140
|
+
"""Envelope returned by Runner.run_step / run_step_async."""
|
|
141
|
+
|
|
142
|
+
step_outcome: StepOutcome
|
|
143
|
+
input_bindings: dict[str, object]
|
|
144
|
+
bindings: dict[str, object]
|
|
145
|
+
return_value: object | None
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
@dataclass(frozen=True)
|
|
149
|
+
class _StepPreparation:
|
|
150
|
+
"""Result of preparing a Natural block for execution."""
|
|
151
|
+
|
|
152
|
+
processed_program: str
|
|
153
|
+
allowed_step_kinds: tuple[str, ...]
|
|
154
|
+
step_context: StepContext
|
|
155
|
+
input_binding_name_to_value: dict[str, object]
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
class Runner:
|
|
159
|
+
def __init__(self, step_executor: StepExecutor) -> None:
|
|
160
|
+
self.step_executor = step_executor
|
|
161
|
+
|
|
162
|
+
def _parse_and_coerce_return_value(self, value: object, return_annotation: object) -> object:
|
|
163
|
+
try:
|
|
164
|
+
adapted = TypeAdapter(return_annotation)
|
|
165
|
+
return adapted.validate_python(value)
|
|
166
|
+
except Exception as e:
|
|
167
|
+
raise ExecutionError(f"Return value validation failed: {e}") from e
|
|
168
|
+
|
|
169
|
+
def _resolve_reference_path(self, step_context: StepContext, return_reference_path: str) -> object:
|
|
170
|
+
parsed_path = parse_identifier_path(return_reference_path)
|
|
171
|
+
if parsed_path is None:
|
|
172
|
+
raise ExecutionError(f"Invalid return_reference_path: {return_reference_path!r}")
|
|
173
|
+
|
|
174
|
+
root_name = parsed_path[0]
|
|
175
|
+
if root_name not in step_context.step_locals:
|
|
176
|
+
raise ExecutionError(f"Unknown root name in return_reference_path: {root_name}")
|
|
177
|
+
current = step_context.step_locals[root_name]
|
|
178
|
+
|
|
179
|
+
for part in parsed_path[1:]:
|
|
180
|
+
try:
|
|
181
|
+
current = getattr(current, part)
|
|
182
|
+
except Exception as e:
|
|
183
|
+
raise ExecutionError(f"Failed to resolve return_reference_path segment {part!r} in {return_reference_path!r}") from e
|
|
184
|
+
|
|
185
|
+
return current
|
|
186
|
+
|
|
187
|
+
def _prepare_step_execution(
|
|
188
|
+
self,
|
|
189
|
+
natural_program: str,
|
|
190
|
+
input_binding_names: list[str],
|
|
191
|
+
output_binding_names: list[str],
|
|
192
|
+
binding_name_to_type: dict[str, object],
|
|
193
|
+
is_in_loop: bool,
|
|
194
|
+
*,
|
|
195
|
+
caller_frame: FrameType,
|
|
196
|
+
) -> _StepPreparation:
|
|
197
|
+
python_locals = caller_frame.f_locals
|
|
198
|
+
python_globals = caller_frame.f_globals
|
|
199
|
+
|
|
200
|
+
processed_without_frontmatter, denied_step_kinds = _split_frontmatter(natural_program)
|
|
201
|
+
processed_without_frontmatter = processed_without_frontmatter.lstrip("\n")
|
|
202
|
+
|
|
203
|
+
allowed_step_kinds = _compute_allowed_step_kinds(is_in_loop, denied_step_kinds)
|
|
204
|
+
|
|
205
|
+
step_globals = _build_step_globals(python_globals)
|
|
206
|
+
step_locals = _build_step_locals(python_locals)
|
|
207
|
+
|
|
208
|
+
resolved_bindings = _resolve_input_bindings(
|
|
209
|
+
input_binding_names,
|
|
210
|
+
python_locals=python_locals,
|
|
211
|
+
python_globals=python_globals,
|
|
212
|
+
caller_frame=caller_frame,
|
|
213
|
+
)
|
|
214
|
+
|
|
215
|
+
for binding_name, (value, resolution_kind) in resolved_bindings.items():
|
|
216
|
+
if resolution_kind in ("locals", "cell_scope", "name_scope"):
|
|
217
|
+
step_locals[binding_name] = value
|
|
218
|
+
|
|
219
|
+
tool_result_rendering_policy = getattr(self.step_executor, "tool_result_rendering_policy", None)
|
|
220
|
+
if tool_result_rendering_policy is not None and not isinstance(tool_result_rendering_policy, ToolResultRenderingPolicy):
|
|
221
|
+
raise ExecutionError("Step executor tool_result_rendering_policy must be ToolResultRenderingPolicy when provided")
|
|
222
|
+
|
|
223
|
+
binding_commit_targets = set(output_binding_names)
|
|
224
|
+
read_binding_names = frozenset(input_binding_names) - binding_commit_targets
|
|
225
|
+
|
|
226
|
+
step_context = StepContext(
|
|
227
|
+
step_id=str(uuid.uuid4()),
|
|
228
|
+
step_globals=step_globals,
|
|
229
|
+
step_locals=step_locals,
|
|
230
|
+
binding_commit_targets=binding_commit_targets,
|
|
231
|
+
read_binding_names=read_binding_names,
|
|
232
|
+
binding_name_to_type=binding_name_to_type,
|
|
233
|
+
tool_result_rendering_policy=tool_result_rendering_policy,
|
|
234
|
+
)
|
|
235
|
+
|
|
236
|
+
input_binding_name_to_value = {name: value for name, (value, _) in resolved_bindings.items()}
|
|
237
|
+
return _StepPreparation(
|
|
238
|
+
processed_program=processed_without_frontmatter,
|
|
239
|
+
allowed_step_kinds=allowed_step_kinds,
|
|
240
|
+
step_context=step_context,
|
|
241
|
+
input_binding_name_to_value=input_binding_name_to_value,
|
|
242
|
+
)
|
|
243
|
+
|
|
244
|
+
def _validate_raise_outcome(
|
|
245
|
+
self,
|
|
246
|
+
step_context: StepContext,
|
|
247
|
+
step_outcome: RaiseStepOutcome,
|
|
248
|
+
) -> None:
|
|
249
|
+
if step_outcome.raise_error_type is not None:
|
|
250
|
+
resolved_raise_error_type = resolve_name_in_step_context(step_context, step_outcome.raise_error_type)
|
|
251
|
+
if resolved_raise_error_type is _MISSING:
|
|
252
|
+
raise ExecutionError(f"Invalid raise_error_type: {step_outcome.raise_error_type!r}: {step_outcome.raise_message}")
|
|
253
|
+
if not isinstance(resolved_raise_error_type, type) or not issubclass(resolved_raise_error_type, BaseException):
|
|
254
|
+
raise ExecutionError(f"Invalid raise_error_type: {step_outcome.raise_error_type!r}: {step_outcome.raise_message}")
|
|
255
|
+
raise resolved_raise_error_type(step_outcome.raise_message)
|
|
256
|
+
|
|
257
|
+
raise ExecutionError(f"Execution failed: {step_outcome.raise_message}")
|
|
258
|
+
|
|
259
|
+
def _apply_bindings_and_validate_kind(
|
|
260
|
+
self,
|
|
261
|
+
*,
|
|
262
|
+
step_context: StepContext,
|
|
263
|
+
step_outcome: StepOutcome,
|
|
264
|
+
bindings: dict[str, object],
|
|
265
|
+
allowed_step_kinds: tuple[str, ...],
|
|
266
|
+
) -> str:
|
|
267
|
+
step_outcome_kind = step_outcome.kind
|
|
268
|
+
if step_outcome_kind not in allowed_step_kinds:
|
|
269
|
+
raise ExecutionError(f"Step '{step_outcome_kind}' is not allowed for this step. Allowed kinds: {allowed_step_kinds}")
|
|
270
|
+
step_context.step_locals.update(bindings)
|
|
271
|
+
return step_outcome_kind
|
|
272
|
+
|
|
273
|
+
def _finalize_step(
|
|
274
|
+
self,
|
|
275
|
+
*,
|
|
276
|
+
preparation: _StepPreparation,
|
|
277
|
+
step_outcome: StepOutcome,
|
|
278
|
+
bindings: dict[str, object],
|
|
279
|
+
return_annotation: object,
|
|
280
|
+
) -> StepEnvelope:
|
|
281
|
+
"""Sync finalization: validate kind, resolve return value, handle raise."""
|
|
282
|
+
step_outcome_kind = self._apply_bindings_and_validate_kind(
|
|
283
|
+
step_context=preparation.step_context,
|
|
284
|
+
step_outcome=step_outcome,
|
|
285
|
+
bindings=bindings,
|
|
286
|
+
allowed_step_kinds=preparation.allowed_step_kinds,
|
|
287
|
+
)
|
|
288
|
+
|
|
289
|
+
return_value: object | None = None
|
|
290
|
+
if step_outcome_kind == "return":
|
|
291
|
+
assert isinstance(step_outcome, ReturnStepOutcome)
|
|
292
|
+
resolved = self._resolve_reference_path(
|
|
293
|
+
preparation.step_context,
|
|
294
|
+
step_outcome.return_reference_path,
|
|
295
|
+
)
|
|
296
|
+
if inspect.isawaitable(resolved):
|
|
297
|
+
raise ExecutionError("Sync Natural function cannot return an awaitable value. Use async def and await the function call.")
|
|
298
|
+
return_value = self._parse_and_coerce_return_value(resolved, return_annotation)
|
|
299
|
+
|
|
300
|
+
if step_outcome_kind == "raise":
|
|
301
|
+
assert isinstance(step_outcome, RaiseStepOutcome)
|
|
302
|
+
self._validate_raise_outcome(preparation.step_context, step_outcome)
|
|
303
|
+
|
|
304
|
+
return StepEnvelope(
|
|
305
|
+
step_outcome=step_outcome,
|
|
306
|
+
input_bindings=dict(preparation.input_binding_name_to_value),
|
|
307
|
+
bindings=bindings,
|
|
308
|
+
return_value=return_value,
|
|
309
|
+
)
|
|
310
|
+
|
|
311
|
+
async def _run_step_async_impl(
|
|
312
|
+
self,
|
|
313
|
+
natural_program: str,
|
|
314
|
+
input_binding_names: list[str],
|
|
315
|
+
output_binding_names: list[str],
|
|
316
|
+
binding_name_to_type: dict[str, object],
|
|
317
|
+
return_annotation: object,
|
|
318
|
+
is_in_loop: bool,
|
|
319
|
+
*,
|
|
320
|
+
caller_frame: FrameType,
|
|
321
|
+
) -> StepEnvelope:
|
|
322
|
+
preparation = self._prepare_step_execution(
|
|
323
|
+
natural_program,
|
|
324
|
+
input_binding_names,
|
|
325
|
+
output_binding_names,
|
|
326
|
+
binding_name_to_type,
|
|
327
|
+
is_in_loop,
|
|
328
|
+
caller_frame=caller_frame,
|
|
329
|
+
)
|
|
330
|
+
execution_context = get_execution_context()
|
|
331
|
+
|
|
332
|
+
with span(
|
|
333
|
+
"nighthawk.step",
|
|
334
|
+
**{
|
|
335
|
+
RUN_ID: execution_context.run_id,
|
|
336
|
+
SCOPE_ID: execution_context.scope_id,
|
|
337
|
+
STEP_ID: preparation.step_context.step_id,
|
|
338
|
+
},
|
|
339
|
+
):
|
|
340
|
+
step_executor = self.step_executor
|
|
341
|
+
|
|
342
|
+
if isinstance(step_executor, AsyncStepExecutor):
|
|
343
|
+
step_outcome, bindings = await step_executor.run_step_async(
|
|
344
|
+
processed_natural_program=preparation.processed_program,
|
|
345
|
+
step_context=preparation.step_context,
|
|
346
|
+
binding_names=output_binding_names,
|
|
347
|
+
allowed_step_kinds=preparation.allowed_step_kinds,
|
|
348
|
+
)
|
|
349
|
+
elif isinstance(step_executor, SyncStepExecutor):
|
|
350
|
+
step_outcome, bindings = step_executor.run_step(
|
|
351
|
+
processed_natural_program=preparation.processed_program,
|
|
352
|
+
step_context=preparation.step_context,
|
|
353
|
+
binding_names=output_binding_names,
|
|
354
|
+
allowed_step_kinds=preparation.allowed_step_kinds,
|
|
355
|
+
)
|
|
356
|
+
else:
|
|
357
|
+
raise ExecutionError("Step executor must define run_step_async(...) or run_step(...)")
|
|
358
|
+
|
|
359
|
+
step_outcome_kind = self._apply_bindings_and_validate_kind(
|
|
360
|
+
step_context=preparation.step_context,
|
|
361
|
+
step_outcome=step_outcome,
|
|
362
|
+
bindings=bindings,
|
|
363
|
+
allowed_step_kinds=preparation.allowed_step_kinds,
|
|
364
|
+
)
|
|
365
|
+
|
|
366
|
+
return_value: object | None = None
|
|
367
|
+
if step_outcome_kind == "return":
|
|
368
|
+
assert isinstance(step_outcome, ReturnStepOutcome)
|
|
369
|
+
resolved = self._resolve_reference_path(
|
|
370
|
+
preparation.step_context,
|
|
371
|
+
step_outcome.return_reference_path,
|
|
372
|
+
)
|
|
373
|
+
if inspect.isawaitable(resolved):
|
|
374
|
+
resolved = await resolved
|
|
375
|
+
return_value = self._parse_and_coerce_return_value(resolved, return_annotation)
|
|
376
|
+
|
|
377
|
+
if step_outcome_kind == "raise":
|
|
378
|
+
assert isinstance(step_outcome, RaiseStepOutcome)
|
|
379
|
+
self._validate_raise_outcome(preparation.step_context, step_outcome)
|
|
380
|
+
|
|
381
|
+
return StepEnvelope(
|
|
382
|
+
step_outcome=step_outcome,
|
|
383
|
+
input_bindings=dict(preparation.input_binding_name_to_value),
|
|
384
|
+
bindings=bindings,
|
|
385
|
+
return_value=return_value,
|
|
386
|
+
)
|
|
387
|
+
|
|
388
|
+
def run_step(
|
|
389
|
+
self,
|
|
390
|
+
natural_program: str,
|
|
391
|
+
input_binding_names: list[str],
|
|
392
|
+
output_binding_names: list[str],
|
|
393
|
+
binding_name_to_type: dict[str, object],
|
|
394
|
+
return_annotation: object,
|
|
395
|
+
is_in_loop: bool,
|
|
396
|
+
*,
|
|
397
|
+
caller_frame: FrameType,
|
|
398
|
+
) -> StepEnvelope:
|
|
399
|
+
preparation = self._prepare_step_execution(
|
|
400
|
+
natural_program,
|
|
401
|
+
input_binding_names,
|
|
402
|
+
output_binding_names,
|
|
403
|
+
binding_name_to_type,
|
|
404
|
+
is_in_loop,
|
|
405
|
+
caller_frame=caller_frame,
|
|
406
|
+
)
|
|
407
|
+
|
|
408
|
+
if isinstance(self.step_executor, SyncStepExecutor):
|
|
409
|
+
execution_context = get_execution_context()
|
|
410
|
+
with span(
|
|
411
|
+
"nighthawk.step",
|
|
412
|
+
**{
|
|
413
|
+
RUN_ID: execution_context.run_id,
|
|
414
|
+
SCOPE_ID: execution_context.scope_id,
|
|
415
|
+
STEP_ID: preparation.step_context.step_id,
|
|
416
|
+
},
|
|
417
|
+
):
|
|
418
|
+
step_outcome, bindings = self.step_executor.run_step(
|
|
419
|
+
processed_natural_program=preparation.processed_program,
|
|
420
|
+
step_context=preparation.step_context,
|
|
421
|
+
binding_names=output_binding_names,
|
|
422
|
+
allowed_step_kinds=preparation.allowed_step_kinds,
|
|
423
|
+
)
|
|
424
|
+
return self._finalize_step(
|
|
425
|
+
preparation=preparation,
|
|
426
|
+
step_outcome=step_outcome,
|
|
427
|
+
bindings=bindings,
|
|
428
|
+
return_annotation=return_annotation,
|
|
429
|
+
)
|
|
430
|
+
|
|
431
|
+
return run_coroutine_synchronously(
|
|
432
|
+
lambda: self._run_step_async_impl(
|
|
433
|
+
natural_program,
|
|
434
|
+
input_binding_names,
|
|
435
|
+
output_binding_names,
|
|
436
|
+
binding_name_to_type,
|
|
437
|
+
return_annotation,
|
|
438
|
+
is_in_loop,
|
|
439
|
+
caller_frame=caller_frame,
|
|
440
|
+
)
|
|
441
|
+
)
|
|
442
|
+
|
|
443
|
+
async def run_step_async(
|
|
444
|
+
self,
|
|
445
|
+
natural_program: str,
|
|
446
|
+
input_binding_names: list[str],
|
|
447
|
+
output_binding_names: list[str],
|
|
448
|
+
binding_name_to_type: dict[str, object],
|
|
449
|
+
return_annotation: object,
|
|
450
|
+
is_in_loop: bool,
|
|
451
|
+
*,
|
|
452
|
+
caller_frame: FrameType,
|
|
453
|
+
) -> StepEnvelope:
|
|
454
|
+
return await self._run_step_async_impl(
|
|
455
|
+
natural_program,
|
|
456
|
+
input_binding_names,
|
|
457
|
+
output_binding_names,
|
|
458
|
+
binding_name_to_type,
|
|
459
|
+
return_annotation,
|
|
460
|
+
is_in_loop,
|
|
461
|
+
caller_frame=caller_frame,
|
|
462
|
+
)
|