weakincentives 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.
- weakincentives/__init__.py +67 -0
- weakincentives/adapters/__init__.py +37 -0
- weakincentives/adapters/_names.py +32 -0
- weakincentives/adapters/_provider_protocols.py +69 -0
- weakincentives/adapters/_tool_messages.py +80 -0
- weakincentives/adapters/core.py +102 -0
- weakincentives/adapters/litellm.py +254 -0
- weakincentives/adapters/openai.py +254 -0
- weakincentives/adapters/shared.py +1021 -0
- weakincentives/cli/__init__.py +23 -0
- weakincentives/cli/wink.py +58 -0
- weakincentives/dbc/__init__.py +412 -0
- weakincentives/deadlines.py +58 -0
- weakincentives/prompt/__init__.py +105 -0
- weakincentives/prompt/_generic_params_specializer.py +64 -0
- weakincentives/prompt/_normalization.py +48 -0
- weakincentives/prompt/_overrides_protocols.py +33 -0
- weakincentives/prompt/_types.py +34 -0
- weakincentives/prompt/chapter.py +146 -0
- weakincentives/prompt/composition.py +281 -0
- weakincentives/prompt/errors.py +57 -0
- weakincentives/prompt/markdown.py +108 -0
- weakincentives/prompt/overrides/__init__.py +59 -0
- weakincentives/prompt/overrides/_fs.py +164 -0
- weakincentives/prompt/overrides/inspection.py +141 -0
- weakincentives/prompt/overrides/local_store.py +275 -0
- weakincentives/prompt/overrides/validation.py +534 -0
- weakincentives/prompt/overrides/versioning.py +269 -0
- weakincentives/prompt/prompt.py +353 -0
- weakincentives/prompt/protocols.py +103 -0
- weakincentives/prompt/registry.py +375 -0
- weakincentives/prompt/rendering.py +288 -0
- weakincentives/prompt/response_format.py +60 -0
- weakincentives/prompt/section.py +166 -0
- weakincentives/prompt/structured_output.py +179 -0
- weakincentives/prompt/tool.py +397 -0
- weakincentives/prompt/tool_result.py +30 -0
- weakincentives/py.typed +0 -0
- weakincentives/runtime/__init__.py +82 -0
- weakincentives/runtime/events/__init__.py +126 -0
- weakincentives/runtime/events/_types.py +110 -0
- weakincentives/runtime/logging.py +284 -0
- weakincentives/runtime/session/__init__.py +46 -0
- weakincentives/runtime/session/_slice_types.py +24 -0
- weakincentives/runtime/session/_types.py +55 -0
- weakincentives/runtime/session/dataclasses.py +29 -0
- weakincentives/runtime/session/protocols.py +34 -0
- weakincentives/runtime/session/reducer_context.py +40 -0
- weakincentives/runtime/session/reducers.py +82 -0
- weakincentives/runtime/session/selectors.py +56 -0
- weakincentives/runtime/session/session.py +387 -0
- weakincentives/runtime/session/snapshots.py +310 -0
- weakincentives/serde/__init__.py +19 -0
- weakincentives/serde/_utils.py +240 -0
- weakincentives/serde/dataclass_serde.py +55 -0
- weakincentives/serde/dump.py +189 -0
- weakincentives/serde/parse.py +417 -0
- weakincentives/serde/schema.py +260 -0
- weakincentives/tools/__init__.py +154 -0
- weakincentives/tools/_context.py +38 -0
- weakincentives/tools/asteval.py +853 -0
- weakincentives/tools/errors.py +26 -0
- weakincentives/tools/planning.py +831 -0
- weakincentives/tools/podman.py +1655 -0
- weakincentives/tools/subagents.py +346 -0
- weakincentives/tools/vfs.py +1390 -0
- weakincentives/types/__init__.py +35 -0
- weakincentives/types/json.py +45 -0
- weakincentives-0.9.0.dist-info/METADATA +775 -0
- weakincentives-0.9.0.dist-info/RECORD +73 -0
- weakincentives-0.9.0.dist-info/WHEEL +4 -0
- weakincentives-0.9.0.dist-info/entry_points.txt +2 -0
- weakincentives-0.9.0.dist-info/licenses/LICENSE +201 -0
|
@@ -0,0 +1,397 @@
|
|
|
1
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
2
|
+
# you may not use this file except in compliance with the License.
|
|
3
|
+
# You may obtain a copy of the License at
|
|
4
|
+
#
|
|
5
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
6
|
+
#
|
|
7
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
8
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
9
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
10
|
+
# See the License for the specific language governing permissions and
|
|
11
|
+
# limitations under the License.
|
|
12
|
+
|
|
13
|
+
from __future__ import annotations
|
|
14
|
+
|
|
15
|
+
import inspect
|
|
16
|
+
import re
|
|
17
|
+
from collections.abc import Callable
|
|
18
|
+
from collections.abc import Sequence as SequenceABC
|
|
19
|
+
from dataclasses import dataclass, field
|
|
20
|
+
from typing import (
|
|
21
|
+
TYPE_CHECKING,
|
|
22
|
+
Annotated,
|
|
23
|
+
Any,
|
|
24
|
+
Final,
|
|
25
|
+
Literal,
|
|
26
|
+
Protocol,
|
|
27
|
+
TypeVar,
|
|
28
|
+
cast,
|
|
29
|
+
get_args,
|
|
30
|
+
get_origin,
|
|
31
|
+
get_type_hints,
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
from ..deadlines import Deadline
|
|
35
|
+
from ._types import SupportsDataclass, SupportsToolResult
|
|
36
|
+
from .errors import PromptValidationError
|
|
37
|
+
from .tool_result import ToolResult
|
|
38
|
+
|
|
39
|
+
_NAME_PATTERN: Final[re.Pattern[str]] = re.compile(r"^[a-z0-9_-]{1,64}$")
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
if TYPE_CHECKING:
|
|
43
|
+
from ..runtime.events._types import EventBus
|
|
44
|
+
from ..runtime.session.protocols import SessionProtocol
|
|
45
|
+
from .protocols import (
|
|
46
|
+
PromptProtocol,
|
|
47
|
+
ProviderAdapterProtocol,
|
|
48
|
+
RenderedPromptProtocol,
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
ParamsT_contra = TypeVar("ParamsT_contra", bound=SupportsDataclass, contravariant=True)
|
|
52
|
+
ResultT_co = TypeVar("ResultT_co", bound=SupportsToolResult)
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
@dataclass(slots=True, frozen=True)
|
|
56
|
+
class ToolContext:
|
|
57
|
+
"""Immutable container exposing prompt execution state to handlers."""
|
|
58
|
+
|
|
59
|
+
prompt: PromptProtocol[Any]
|
|
60
|
+
rendered_prompt: RenderedPromptProtocol[Any] | None
|
|
61
|
+
adapter: ProviderAdapterProtocol[Any]
|
|
62
|
+
session: SessionProtocol
|
|
63
|
+
event_bus: EventBus
|
|
64
|
+
deadline: Deadline | None = None
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def _normalize_specialization(item: object) -> tuple[object, object]:
|
|
68
|
+
if not isinstance(item, tuple):
|
|
69
|
+
raise TypeError("Tool[...] expects two type arguments (ParamsT, ResultT).")
|
|
70
|
+
normalized = cast(SequenceABC[object], item)
|
|
71
|
+
if len(normalized) != 2:
|
|
72
|
+
raise TypeError("Tool[...] expects two type arguments (ParamsT, ResultT).")
|
|
73
|
+
return normalized[0], normalized[1]
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
class ToolHandler(Protocol[ParamsT_contra, ResultT_co]):
|
|
77
|
+
"""Callable protocol implemented by tool handlers."""
|
|
78
|
+
|
|
79
|
+
def __call__(
|
|
80
|
+
self, params: ParamsT_contra, *, context: ToolContext
|
|
81
|
+
) -> ToolResult[ResultT_co]: ...
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
@dataclass(slots=True)
|
|
85
|
+
class Tool[ParamsT: SupportsDataclass, ResultT: SupportsToolResult]:
|
|
86
|
+
"""Describe a callable tool exposed by prompt sections."""
|
|
87
|
+
|
|
88
|
+
name: str
|
|
89
|
+
description: str
|
|
90
|
+
handler: ToolHandler[ParamsT, ResultT] | None
|
|
91
|
+
params_type: type[ParamsT] = field(init=False, repr=False)
|
|
92
|
+
result_type: type[SupportsDataclass] = field(init=False, repr=False)
|
|
93
|
+
result_container: Literal["object", "array"] = field(
|
|
94
|
+
init=False,
|
|
95
|
+
repr=False,
|
|
96
|
+
)
|
|
97
|
+
_result_annotation: ResultT = field(init=False, repr=False)
|
|
98
|
+
accepts_overrides: bool = True
|
|
99
|
+
|
|
100
|
+
def __post_init__(self) -> None:
|
|
101
|
+
params_attr = getattr(self, "params_type", None)
|
|
102
|
+
params_type: type[SupportsDataclass] | None = (
|
|
103
|
+
params_attr if isinstance(params_attr, type) else None
|
|
104
|
+
)
|
|
105
|
+
raw_result_annotation = getattr(self, "_result_annotation", None)
|
|
106
|
+
if params_type is None or raw_result_annotation is None:
|
|
107
|
+
origin = getattr(self, "__orig_class__", None)
|
|
108
|
+
if origin is not None: # pragma: no cover - interpreter-specific path
|
|
109
|
+
args = get_args(origin)
|
|
110
|
+
if len(args) == 2:
|
|
111
|
+
params_arg, result_arg = args
|
|
112
|
+
if isinstance(params_arg, type):
|
|
113
|
+
params_type = params_arg
|
|
114
|
+
raw_result_annotation = cast(ResultT, result_arg)
|
|
115
|
+
if params_type is None or raw_result_annotation is None:
|
|
116
|
+
raise PromptValidationError(
|
|
117
|
+
"Tool must be instantiated with concrete type arguments.",
|
|
118
|
+
placeholder="type_arguments",
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
result_type, result_container = self._normalize_result_annotation(
|
|
122
|
+
raw_result_annotation,
|
|
123
|
+
params_type,
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
self.params_type = cast(type[ParamsT], params_type)
|
|
127
|
+
self.result_type = result_type
|
|
128
|
+
self.result_container = result_container
|
|
129
|
+
self._result_annotation = raw_result_annotation
|
|
130
|
+
|
|
131
|
+
raw_name = self.name
|
|
132
|
+
stripped_name = raw_name.strip()
|
|
133
|
+
if raw_name != stripped_name:
|
|
134
|
+
normalized_name = stripped_name
|
|
135
|
+
raise PromptValidationError(
|
|
136
|
+
"Tool name must not contain surrounding whitespace.",
|
|
137
|
+
dataclass_type=params_type,
|
|
138
|
+
placeholder=normalized_name,
|
|
139
|
+
)
|
|
140
|
+
|
|
141
|
+
name_clean = raw_name
|
|
142
|
+
if not name_clean:
|
|
143
|
+
raise PromptValidationError(
|
|
144
|
+
"Tool name must match the OpenAI function name constraints (1-64 lowercase ASCII letters, digits, underscores, or hyphens).",
|
|
145
|
+
dataclass_type=params_type,
|
|
146
|
+
placeholder=stripped_name,
|
|
147
|
+
)
|
|
148
|
+
if len(name_clean) > 64 or not _NAME_PATTERN.fullmatch(name_clean):
|
|
149
|
+
raise PromptValidationError(
|
|
150
|
+
"Tool name must match the OpenAI function name constraints (pattern: ^[a-z0-9_-]{1,64}$).",
|
|
151
|
+
dataclass_type=params_type,
|
|
152
|
+
placeholder=name_clean,
|
|
153
|
+
)
|
|
154
|
+
|
|
155
|
+
description_clean = self.description.strip()
|
|
156
|
+
if not description_clean or len(description_clean) > 200:
|
|
157
|
+
raise PromptValidationError(
|
|
158
|
+
"Tool description must be 1-200 ASCII characters.",
|
|
159
|
+
dataclass_type=params_type,
|
|
160
|
+
placeholder="description",
|
|
161
|
+
)
|
|
162
|
+
try:
|
|
163
|
+
_ = description_clean.encode("ascii")
|
|
164
|
+
except UnicodeEncodeError as error:
|
|
165
|
+
raise PromptValidationError(
|
|
166
|
+
"Tool description must be ASCII.",
|
|
167
|
+
dataclass_type=params_type,
|
|
168
|
+
placeholder="description",
|
|
169
|
+
) from error
|
|
170
|
+
|
|
171
|
+
handler = self.handler
|
|
172
|
+
if handler is not None:
|
|
173
|
+
self._validate_handler(
|
|
174
|
+
handler,
|
|
175
|
+
params_type,
|
|
176
|
+
raw_result_annotation,
|
|
177
|
+
)
|
|
178
|
+
|
|
179
|
+
self.name = name_clean
|
|
180
|
+
self.description = description_clean
|
|
181
|
+
|
|
182
|
+
def _validate_handler(
|
|
183
|
+
self,
|
|
184
|
+
handler: object,
|
|
185
|
+
params_type: type[SupportsDataclass],
|
|
186
|
+
result_annotation: object,
|
|
187
|
+
) -> None:
|
|
188
|
+
if not callable(handler):
|
|
189
|
+
raise PromptValidationError(
|
|
190
|
+
"Tool handler must be callable.",
|
|
191
|
+
dataclass_type=params_type,
|
|
192
|
+
placeholder="handler",
|
|
193
|
+
)
|
|
194
|
+
|
|
195
|
+
callable_handler = cast(Callable[..., ToolResult[ResultT]], handler)
|
|
196
|
+
signature = inspect.signature(callable_handler)
|
|
197
|
+
parameters = list(signature.parameters.values())
|
|
198
|
+
|
|
199
|
+
if len(parameters) != 2:
|
|
200
|
+
raise PromptValidationError(
|
|
201
|
+
"Tool handler must accept exactly one positional argument and the keyword-only 'context' parameter.",
|
|
202
|
+
dataclass_type=params_type,
|
|
203
|
+
placeholder="handler",
|
|
204
|
+
)
|
|
205
|
+
|
|
206
|
+
parameter = parameters[0]
|
|
207
|
+
context_parameter = parameters[1]
|
|
208
|
+
|
|
209
|
+
if parameter.kind not in (
|
|
210
|
+
inspect.Parameter.POSITIONAL_ONLY,
|
|
211
|
+
inspect.Parameter.POSITIONAL_OR_KEYWORD,
|
|
212
|
+
):
|
|
213
|
+
raise PromptValidationError(
|
|
214
|
+
"Tool handler parameter must be positional.",
|
|
215
|
+
dataclass_type=params_type,
|
|
216
|
+
placeholder="handler",
|
|
217
|
+
)
|
|
218
|
+
|
|
219
|
+
if context_parameter.kind is not inspect.Parameter.KEYWORD_ONLY:
|
|
220
|
+
raise PromptValidationError(
|
|
221
|
+
"Tool handler must declare a keyword-only 'context' parameter.",
|
|
222
|
+
dataclass_type=params_type,
|
|
223
|
+
placeholder="handler",
|
|
224
|
+
)
|
|
225
|
+
if context_parameter.default is not inspect.Signature.empty:
|
|
226
|
+
raise PromptValidationError(
|
|
227
|
+
"Tool handler 'context' parameter must not define a default value.",
|
|
228
|
+
dataclass_type=params_type,
|
|
229
|
+
placeholder="handler",
|
|
230
|
+
)
|
|
231
|
+
if context_parameter.name != "context":
|
|
232
|
+
raise PromptValidationError(
|
|
233
|
+
"Tool handler must name the keyword-only context parameter 'context'.",
|
|
234
|
+
dataclass_type=params_type,
|
|
235
|
+
placeholder="handler",
|
|
236
|
+
)
|
|
237
|
+
|
|
238
|
+
try:
|
|
239
|
+
hints = get_type_hints(callable_handler, include_extras=True)
|
|
240
|
+
except Exception:
|
|
241
|
+
hints = {}
|
|
242
|
+
|
|
243
|
+
annotation = hints.get(parameter.name, parameter.annotation)
|
|
244
|
+
if annotation is inspect.Parameter.empty:
|
|
245
|
+
raise PromptValidationError(
|
|
246
|
+
"Tool handler parameter must be annotated with ParamsT.",
|
|
247
|
+
dataclass_type=params_type,
|
|
248
|
+
placeholder="handler",
|
|
249
|
+
)
|
|
250
|
+
if get_origin(annotation) is Annotated:
|
|
251
|
+
annotation = get_args(annotation)[0]
|
|
252
|
+
if annotation is not params_type:
|
|
253
|
+
raise PromptValidationError(
|
|
254
|
+
"Tool handler parameter annotation must match ParamsT.",
|
|
255
|
+
dataclass_type=params_type,
|
|
256
|
+
placeholder="handler",
|
|
257
|
+
)
|
|
258
|
+
|
|
259
|
+
context_annotation = hints.get(
|
|
260
|
+
context_parameter.name, context_parameter.annotation
|
|
261
|
+
)
|
|
262
|
+
if context_annotation is inspect.Parameter.empty:
|
|
263
|
+
raise PromptValidationError(
|
|
264
|
+
"Tool handler must annotate the 'context' parameter with ToolContext.",
|
|
265
|
+
dataclass_type=params_type,
|
|
266
|
+
placeholder="handler",
|
|
267
|
+
)
|
|
268
|
+
if get_origin(context_annotation) is Annotated:
|
|
269
|
+
context_annotation = get_args(context_annotation)[0]
|
|
270
|
+
if context_annotation is not ToolContext:
|
|
271
|
+
raise PromptValidationError(
|
|
272
|
+
"Tool handler 'context' annotation must be ToolContext.",
|
|
273
|
+
dataclass_type=params_type,
|
|
274
|
+
placeholder="handler",
|
|
275
|
+
)
|
|
276
|
+
|
|
277
|
+
return_annotation = hints.get("return", signature.return_annotation)
|
|
278
|
+
if return_annotation is inspect.Signature.empty:
|
|
279
|
+
raise PromptValidationError(
|
|
280
|
+
"Tool handler must annotate its return value with ToolResult[ResultT].",
|
|
281
|
+
dataclass_type=params_type,
|
|
282
|
+
placeholder="return",
|
|
283
|
+
)
|
|
284
|
+
if get_origin(return_annotation) is Annotated:
|
|
285
|
+
return_annotation = get_args(return_annotation)[0]
|
|
286
|
+
|
|
287
|
+
origin = get_origin(return_annotation)
|
|
288
|
+
if origin is ToolResult:
|
|
289
|
+
result_args_raw = get_args(return_annotation)
|
|
290
|
+
if result_args_raw and self._matches_result_annotation(
|
|
291
|
+
result_args_raw[0],
|
|
292
|
+
result_annotation,
|
|
293
|
+
):
|
|
294
|
+
return
|
|
295
|
+
raise PromptValidationError(
|
|
296
|
+
"Tool handler return annotation must be ToolResult[ResultT].",
|
|
297
|
+
dataclass_type=params_type,
|
|
298
|
+
placeholder="return",
|
|
299
|
+
)
|
|
300
|
+
|
|
301
|
+
@staticmethod
|
|
302
|
+
def _normalize_result_annotation(
|
|
303
|
+
annotation: ResultT,
|
|
304
|
+
params_type: type[SupportsDataclass],
|
|
305
|
+
) -> tuple[type[SupportsDataclass], Literal["object", "array"]]:
|
|
306
|
+
if isinstance(annotation, type):
|
|
307
|
+
return cast(type[SupportsDataclass], annotation), "object"
|
|
308
|
+
|
|
309
|
+
origin = get_origin(annotation)
|
|
310
|
+
if origin not in {list, tuple, SequenceABC}:
|
|
311
|
+
raise PromptValidationError(
|
|
312
|
+
"Tool ResultT must be a dataclass type or a sequence of dataclasses.",
|
|
313
|
+
dataclass_type=params_type,
|
|
314
|
+
placeholder="ResultT",
|
|
315
|
+
)
|
|
316
|
+
|
|
317
|
+
args = get_args(annotation)
|
|
318
|
+
element: object | None = None
|
|
319
|
+
if origin is tuple:
|
|
320
|
+
if len(args) != 2 or args[1] is not Ellipsis:
|
|
321
|
+
raise PromptValidationError(
|
|
322
|
+
"Variadic Tuple[ResultT, ...] is required for Tool sequence results.",
|
|
323
|
+
dataclass_type=params_type,
|
|
324
|
+
placeholder="ResultT",
|
|
325
|
+
)
|
|
326
|
+
element = args[0]
|
|
327
|
+
elif len(args) == 1:
|
|
328
|
+
element = args[0]
|
|
329
|
+
|
|
330
|
+
if not isinstance(element, type):
|
|
331
|
+
raise PromptValidationError(
|
|
332
|
+
"Tool ResultT must be a dataclass type or a sequence of dataclasses.",
|
|
333
|
+
dataclass_type=params_type,
|
|
334
|
+
placeholder="ResultT",
|
|
335
|
+
)
|
|
336
|
+
|
|
337
|
+
return cast(type[SupportsDataclass], element), "array"
|
|
338
|
+
|
|
339
|
+
@staticmethod
|
|
340
|
+
def _matches_result_annotation(candidate: object, expected: object) -> bool:
|
|
341
|
+
if candidate is expected:
|
|
342
|
+
return True
|
|
343
|
+
|
|
344
|
+
candidate_origin = get_origin(candidate)
|
|
345
|
+
expected_origin = get_origin(expected)
|
|
346
|
+
|
|
347
|
+
if candidate_origin is None or expected_origin is None:
|
|
348
|
+
return False
|
|
349
|
+
|
|
350
|
+
sequence_origins = {list, tuple, SequenceABC}
|
|
351
|
+
if candidate_origin in sequence_origins and expected_origin in sequence_origins:
|
|
352
|
+
candidate_args = get_args(candidate)
|
|
353
|
+
expected_args = get_args(expected)
|
|
354
|
+
candidate_element = (
|
|
355
|
+
candidate_args[0]
|
|
356
|
+
if candidate_origin is not tuple
|
|
357
|
+
else candidate_args[0]
|
|
358
|
+
if len(candidate_args) == 2
|
|
359
|
+
else None
|
|
360
|
+
)
|
|
361
|
+
expected_element = (
|
|
362
|
+
expected_args[0]
|
|
363
|
+
if expected_origin is not tuple
|
|
364
|
+
else expected_args[0]
|
|
365
|
+
if len(expected_args) == 2
|
|
366
|
+
else None
|
|
367
|
+
)
|
|
368
|
+
return candidate_element is expected_element
|
|
369
|
+
|
|
370
|
+
return False
|
|
371
|
+
|
|
372
|
+
@classmethod
|
|
373
|
+
def __class_getitem__(
|
|
374
|
+
cls, item: object
|
|
375
|
+
) -> type[Tool[SupportsDataclass, SupportsToolResult]]:
|
|
376
|
+
params_candidate, result_candidate = _normalize_specialization(item)
|
|
377
|
+
if not isinstance(params_candidate, type):
|
|
378
|
+
raise TypeError("Tool ParamsT type argument must be a type.")
|
|
379
|
+
params_type = cast(type[SupportsDataclass], params_candidate)
|
|
380
|
+
result_annotation = cast(ResultT, result_candidate)
|
|
381
|
+
|
|
382
|
+
class _SpecializedTool(cls):
|
|
383
|
+
def __post_init__(self) -> None:
|
|
384
|
+
self.params_type = cast(type[ParamsT], params_type)
|
|
385
|
+
self._result_annotation = result_annotation
|
|
386
|
+
super().__post_init__()
|
|
387
|
+
|
|
388
|
+
_SpecializedTool.__name__ = cls.__name__
|
|
389
|
+
_SpecializedTool.__qualname__ = cls.__qualname__
|
|
390
|
+
_SpecializedTool.__module__ = cls.__module__
|
|
391
|
+
return cast(
|
|
392
|
+
"type[Tool[SupportsDataclass, SupportsToolResult]]",
|
|
393
|
+
_SpecializedTool,
|
|
394
|
+
)
|
|
395
|
+
|
|
396
|
+
|
|
397
|
+
__all__ = ["Tool", "ToolContext", "ToolHandler", "ToolResult"]
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
2
|
+
# you may not use this file except in compliance with the License.
|
|
3
|
+
# You may obtain a copy of the License at
|
|
4
|
+
#
|
|
5
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
6
|
+
#
|
|
7
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
8
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
9
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
10
|
+
# See the License for the specific language governing permissions and
|
|
11
|
+
# limitations under the License.
|
|
12
|
+
|
|
13
|
+
"""Shared result container returned by tool handlers."""
|
|
14
|
+
|
|
15
|
+
from __future__ import annotations
|
|
16
|
+
|
|
17
|
+
from dataclasses import dataclass
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
@dataclass(slots=True)
|
|
21
|
+
class ToolResult[ResultValueT]:
|
|
22
|
+
"""Structured response emitted by a tool handler."""
|
|
23
|
+
|
|
24
|
+
message: str
|
|
25
|
+
value: ResultValueT | None
|
|
26
|
+
success: bool = True
|
|
27
|
+
exclude_value_from_context: bool = False
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
__all__ = ["ToolResult"]
|
weakincentives/py.typed
ADDED
|
File without changes
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
2
|
+
# you may not use this file except in compliance with the License.
|
|
3
|
+
# You may obtain a copy of the License at
|
|
4
|
+
#
|
|
5
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
6
|
+
#
|
|
7
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
8
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
9
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
10
|
+
# See the License for the specific language governing permissions and
|
|
11
|
+
# limitations under the License.
|
|
12
|
+
|
|
13
|
+
"""Runtime primitives for :mod:`weakincentives`."""
|
|
14
|
+
|
|
15
|
+
from __future__ import annotations
|
|
16
|
+
|
|
17
|
+
from . import events, session
|
|
18
|
+
from .events import (
|
|
19
|
+
EventBus,
|
|
20
|
+
HandlerFailure,
|
|
21
|
+
InProcessEventBus,
|
|
22
|
+
PromptExecuted,
|
|
23
|
+
PublishResult,
|
|
24
|
+
ToolInvoked,
|
|
25
|
+
)
|
|
26
|
+
from .logging import StructuredLogger, configure_logging, get_logger
|
|
27
|
+
from .session import (
|
|
28
|
+
DataEvent,
|
|
29
|
+
ReducerContext,
|
|
30
|
+
ReducerContextProtocol,
|
|
31
|
+
ReducerEvent,
|
|
32
|
+
Session,
|
|
33
|
+
SessionProtocol,
|
|
34
|
+
Snapshot,
|
|
35
|
+
SnapshotProtocol,
|
|
36
|
+
SnapshotRestoreError,
|
|
37
|
+
SnapshotSerializationError,
|
|
38
|
+
TypedReducer,
|
|
39
|
+
append,
|
|
40
|
+
build_reducer_context,
|
|
41
|
+
replace_latest,
|
|
42
|
+
select_all,
|
|
43
|
+
select_latest,
|
|
44
|
+
select_where,
|
|
45
|
+
upsert_by,
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
__all__ = [
|
|
49
|
+
"DataEvent",
|
|
50
|
+
"EventBus",
|
|
51
|
+
"HandlerFailure",
|
|
52
|
+
"InProcessEventBus",
|
|
53
|
+
"PromptExecuted",
|
|
54
|
+
"PublishResult",
|
|
55
|
+
"ReducerContext",
|
|
56
|
+
"ReducerContextProtocol",
|
|
57
|
+
"ReducerEvent",
|
|
58
|
+
"Session",
|
|
59
|
+
"SessionProtocol",
|
|
60
|
+
"Snapshot",
|
|
61
|
+
"SnapshotProtocol",
|
|
62
|
+
"SnapshotRestoreError",
|
|
63
|
+
"SnapshotSerializationError",
|
|
64
|
+
"StructuredLogger",
|
|
65
|
+
"ToolInvoked",
|
|
66
|
+
"TypedReducer",
|
|
67
|
+
"append",
|
|
68
|
+
"build_reducer_context",
|
|
69
|
+
"configure_logging",
|
|
70
|
+
"events",
|
|
71
|
+
"get_logger",
|
|
72
|
+
"replace_latest",
|
|
73
|
+
"select_all",
|
|
74
|
+
"select_latest",
|
|
75
|
+
"select_where",
|
|
76
|
+
"session",
|
|
77
|
+
"upsert_by",
|
|
78
|
+
]
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def __dir__() -> list[str]:
|
|
82
|
+
return sorted({*globals().keys(), *__all__}) # pragma: no cover - convenience shim
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
2
|
+
# you may not use this file except in compliance with the License.
|
|
3
|
+
# You may obtain a copy of the License at
|
|
4
|
+
#
|
|
5
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
6
|
+
#
|
|
7
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
8
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
9
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
10
|
+
# See the License for the specific language governing permissions and
|
|
11
|
+
# limitations under the License.
|
|
12
|
+
|
|
13
|
+
"""In-process event primitives for adapter telemetry."""
|
|
14
|
+
|
|
15
|
+
from __future__ import annotations
|
|
16
|
+
|
|
17
|
+
from dataclasses import dataclass, field
|
|
18
|
+
from datetime import datetime
|
|
19
|
+
from threading import RLock
|
|
20
|
+
from typing import TYPE_CHECKING
|
|
21
|
+
from uuid import UUID, uuid4
|
|
22
|
+
|
|
23
|
+
from ...prompt._types import SupportsDataclass
|
|
24
|
+
from ..logging import StructuredLogger, get_logger
|
|
25
|
+
from ._types import EventBus, EventHandler, HandlerFailure, PublishResult, ToolInvoked
|
|
26
|
+
|
|
27
|
+
if TYPE_CHECKING:
|
|
28
|
+
from ...adapters._names import AdapterName
|
|
29
|
+
from ...adapters.core import PromptResponse
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def _describe_handler(handler: EventHandler) -> str:
|
|
33
|
+
module_name = getattr(handler, "__module__", None)
|
|
34
|
+
qualname = getattr(handler, "__qualname__", None)
|
|
35
|
+
if isinstance(qualname, str):
|
|
36
|
+
prefix = f"{module_name}." if isinstance(module_name, str) else ""
|
|
37
|
+
return f"{prefix}{qualname}"
|
|
38
|
+
return repr(handler) # pragma: no cover - defensive fallback
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
logger: StructuredLogger = get_logger(__name__, context={"component": "event_bus"})
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
class InProcessEventBus:
|
|
45
|
+
"""Process-local event bus that delivers events synchronously."""
|
|
46
|
+
|
|
47
|
+
def __init__(self) -> None:
|
|
48
|
+
super().__init__()
|
|
49
|
+
self._handlers: dict[type[object], list[EventHandler]] = {}
|
|
50
|
+
self._lock = RLock()
|
|
51
|
+
|
|
52
|
+
def subscribe(self, event_type: type[object], handler: EventHandler) -> None:
|
|
53
|
+
with self._lock:
|
|
54
|
+
handlers = self._handlers.setdefault(event_type, [])
|
|
55
|
+
handlers.append(handler)
|
|
56
|
+
|
|
57
|
+
def publish(self, event: object) -> PublishResult:
|
|
58
|
+
with self._lock:
|
|
59
|
+
handlers = tuple(self._handlers.get(type(event), ()))
|
|
60
|
+
invoked: list[EventHandler] = []
|
|
61
|
+
failures: list[HandlerFailure] = []
|
|
62
|
+
for handler in handlers:
|
|
63
|
+
invoked.append(handler)
|
|
64
|
+
try:
|
|
65
|
+
handler(event)
|
|
66
|
+
except Exception as error:
|
|
67
|
+
logger.exception(
|
|
68
|
+
"Error delivering event.",
|
|
69
|
+
event="event_delivery_failed",
|
|
70
|
+
context={
|
|
71
|
+
"handler": _describe_handler(handler),
|
|
72
|
+
"event_type": type(event).__name__,
|
|
73
|
+
},
|
|
74
|
+
)
|
|
75
|
+
failures.append(HandlerFailure(handler=handler, error=error))
|
|
76
|
+
|
|
77
|
+
return PublishResult(
|
|
78
|
+
event=event,
|
|
79
|
+
handlers_invoked=tuple(invoked),
|
|
80
|
+
errors=tuple(failures),
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
@dataclass(slots=True, frozen=True)
|
|
85
|
+
class PromptExecuted:
|
|
86
|
+
"""Event emitted after an adapter finishes evaluating a prompt."""
|
|
87
|
+
|
|
88
|
+
prompt_name: str
|
|
89
|
+
adapter: AdapterName
|
|
90
|
+
result: PromptResponse[object]
|
|
91
|
+
session_id: UUID | None
|
|
92
|
+
created_at: datetime
|
|
93
|
+
value: SupportsDataclass | None = None
|
|
94
|
+
event_id: UUID = field(default_factory=uuid4)
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
@dataclass(slots=True, frozen=True)
|
|
98
|
+
class PromptRendered:
|
|
99
|
+
"""Event emitted immediately before dispatching a rendered prompt."""
|
|
100
|
+
|
|
101
|
+
prompt_ns: str
|
|
102
|
+
prompt_key: str
|
|
103
|
+
prompt_name: str | None
|
|
104
|
+
adapter: AdapterName
|
|
105
|
+
session_id: UUID | None
|
|
106
|
+
render_inputs: tuple[SupportsDataclass, ...]
|
|
107
|
+
rendered_prompt: str
|
|
108
|
+
created_at: datetime
|
|
109
|
+
event_id: UUID = field(default_factory=uuid4)
|
|
110
|
+
|
|
111
|
+
@property
|
|
112
|
+
def value(self) -> SupportsDataclass:
|
|
113
|
+
"""Expose the dataclass instance for reducer compatibility."""
|
|
114
|
+
|
|
115
|
+
return self
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
__all__ = [
|
|
119
|
+
"EventBus",
|
|
120
|
+
"HandlerFailure",
|
|
121
|
+
"InProcessEventBus",
|
|
122
|
+
"PromptExecuted",
|
|
123
|
+
"PromptRendered",
|
|
124
|
+
"PublishResult",
|
|
125
|
+
"ToolInvoked",
|
|
126
|
+
]
|