weakincentives 0.3.0__py3-none-any.whl → 0.4.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.

Potentially problematic release.


This version of weakincentives might be problematic. Click here for more details.

@@ -35,5 +35,5 @@ __all__ = [
35
35
  ]
36
36
 
37
37
 
38
- def __dir__() -> list[str]: # pragma: no cover - convenience for REPL
38
+ def __dir__() -> list[str]:
39
39
  return sorted({*globals().keys(), *__all__})
@@ -27,5 +27,6 @@ __all__ = [
27
27
  ]
28
28
 
29
29
 
30
- def __dir__() -> list[str]: # pragma: no cover - convenience for REPL
31
- return sorted({*globals().keys(), *(__all__)})
30
+ def __dir__() -> list[str]:
31
+ extra_exports = {"LiteLLMAdapter", "OpenAIAdapter"}
32
+ return sorted({*globals().keys(), *(__all__), *extra_exports})
@@ -19,7 +19,7 @@ from dataclasses import dataclass, field
19
19
 
20
20
  from ..prompt import MarkdownSection, Prompt
21
21
  from ..session import Session
22
- from ..tools import PlanningToolsSection, VfsToolsSection
22
+ from ..tools import AstevalSection, PlanningToolsSection, VfsToolsSection
23
23
  from .code_review_tools import build_tools
24
24
 
25
25
 
@@ -72,10 +72,12 @@ def build_code_review_prompt(session: Session) -> Prompt[ReviewResponse]:
72
72
  - `vfs_list_directory` lists directories and files staged in the virtual
73
73
  filesystem snapshot.
74
74
  - `vfs_read_file` reads staged file contents.
75
- - `vfs_write_file` stages ASCII edits before applying them to the host
75
+ - `vfs_write_file` stages UTF-8 edits before applying them to the host
76
76
  workspace.
77
77
  - `vfs_delete_entry` removes staged files or directories that are no
78
78
  longer needed.
79
+ - `evaluate_python` runs Python snippets inside a sandbox with
80
+ optional staged file reads and writes.
79
81
  If the task requires information beyond these capabilities, ask the
80
82
  user for clarification rather than guessing.
81
83
 
@@ -97,6 +99,7 @@ def build_code_review_prompt(session: Session) -> Prompt[ReviewResponse]:
97
99
  )
98
100
  planning_section = PlanningToolsSection(session=session)
99
101
  vfs_section = VfsToolsSection(session=session)
102
+ asteval_section = AstevalSection(session=session)
100
103
  user_turn_section = MarkdownSection[ReviewTurnParams](
101
104
  title="Review Request",
102
105
  template="${request}",
@@ -106,7 +109,13 @@ def build_code_review_prompt(session: Session) -> Prompt[ReviewResponse]:
106
109
  ns="examples/code-review",
107
110
  key="code-review-session",
108
111
  name="code_review_agent",
109
- sections=[guidance_section, planning_section, vfs_section, user_turn_section],
112
+ sections=[
113
+ guidance_section,
114
+ planning_section,
115
+ vfs_section,
116
+ asteval_section,
117
+ user_turn_section,
118
+ ],
110
119
  )
111
120
 
112
121
 
@@ -95,7 +95,7 @@ class CodeReviewSession:
95
95
  indent=2,
96
96
  )
97
97
  if response.text:
98
- return response.text
98
+ return response.text # pragma: no cover - convenience path for plain text
99
99
  return "(no response from assistant)"
100
100
 
101
101
  def render_tool_history(self) -> str:
@@ -126,11 +126,15 @@ class CodeReviewSession:
126
126
  f" → {event.result.message}"
127
127
  )
128
128
  if payload:
129
- print(f" payload: {payload}")
129
+ print(
130
+ f" payload: {payload}"
131
+ ) # pragma: no cover - console output only
130
132
  latest = select_latest(self._session, ToolCallLog)
131
133
  if latest is not None:
132
134
  count = len(select_all(self._session, ToolCallLog))
133
- print(f" (session recorded this call as #{count})")
135
+ print( # pragma: no cover - console output only
136
+ f" (session recorded this call as #{count})"
137
+ )
134
138
 
135
139
  def _register_tool_history(self) -> None:
136
140
  for result_type in (
@@ -59,7 +59,7 @@ class MarkdownSection[ParamsT: SupportsDataclass](Section[ParamsT]):
59
59
  try:
60
60
  normalized_params = self._normalize_params(params)
61
61
  rendered_body = template.substitute(normalized_params)
62
- except KeyError as error: # pragma: no cover - handled at prompt level
62
+ except KeyError as error:
63
63
  missing = error.args[0]
64
64
  raise PromptRenderError(
65
65
  "Missing placeholder during render.",
@@ -46,7 +46,7 @@ class RenderedPrompt[OutputT]:
46
46
  default=_EMPTY_TOOL_PARAM_DESCRIPTIONS
47
47
  )
48
48
 
49
- def __str__(self) -> str: # pragma: no cover - convenience for logging
49
+ def __str__(self) -> str:
50
50
  return self.text
51
51
 
52
52
  @property
@@ -71,11 +71,11 @@ def _clone_dataclass(instance: SupportsDataclass) -> SupportsDataclass:
71
71
 
72
72
 
73
73
  def _format_specialization_argument(argument: object | None) -> str:
74
- if argument is None: # pragma: no cover - defensive formatting
74
+ if argument is None:
75
75
  return "?"
76
76
  if isinstance(argument, type):
77
77
  return argument.__name__
78
- return repr(argument) # pragma: no cover - fallback for debugging
78
+ return repr(argument)
79
79
 
80
80
 
81
81
  @dataclass(frozen=True, slots=True)
@@ -314,7 +314,7 @@ class Prompt[OutputT]:
314
314
  if candidate is None or container is None:
315
315
  return None, None, None
316
316
 
317
- if not isinstance(candidate, type): # pragma: no cover - defensive guard
317
+ if not isinstance(candidate, type):
318
318
  candidate_type = cast(type[Any], type(candidate))
319
319
  raise PromptValidationError(
320
320
  "Prompt output type must be a dataclass.",
@@ -333,7 +333,7 @@ class Prompt[OutputT]:
333
333
 
334
334
  def _build_response_format_params(self) -> ResponseFormatParams:
335
335
  container = self._output_container
336
- if container is None: # pragma: no cover - defensive guard
336
+ if container is None:
337
337
  raise RuntimeError(
338
338
  "Output container missing during response format construction."
339
339
  )
@@ -458,7 +458,7 @@ class Prompt[OutputT]:
458
458
  dataclass_type=params_type,
459
459
  placeholder=error.placeholder,
460
460
  ) from error
461
- except Exception as error: # pragma: no cover - defensive guard
461
+ except Exception as error:
462
462
  raise PromptRenderError(
463
463
  "Section rendering failed.",
464
464
  section_path=node.path,
@@ -519,7 +519,7 @@ class Prompt[OutputT]:
519
519
  else:
520
520
  try:
521
521
  enabled = node.section.is_enabled(section_params)
522
- except Exception as error: # pragma: no cover - defensive guard
522
+ except Exception as error:
523
523
  raise PromptRenderError(
524
524
  "Section enabled predicate failed.",
525
525
  section_path=node.path,
@@ -110,7 +110,7 @@ def parse_structured_output[PayloadT](
110
110
  parsed_items.append(parsed_item)
111
111
  return cast(PayloadT, parsed_items)
112
112
 
113
- raise OutputParseError( # pragma: no cover - defensive guard
113
+ raise OutputParseError(
114
114
  "Unknown output container declared.",
115
115
  dataclass_type=dataclass_type,
116
116
  )
@@ -141,7 +141,7 @@ def _extract_json_payload(text: str, dataclass_type: type[Any]) -> object:
141
141
  continue
142
142
  try:
143
143
  payload, _ = decoder.raw_decode(text, index)
144
- except json.JSONDecodeError: # pragma: no cover - defensive fallback
144
+ except json.JSONDecodeError:
145
145
  continue
146
146
  return payload
147
147
 
@@ -51,7 +51,7 @@ class Tool[ParamsT: SupportsDataclass, ResultT: SupportsDataclass]:
51
51
  )
52
52
  if params_type is None or result_type is None:
53
53
  origin = getattr(self, "__orig_class__", None)
54
- if origin is not None: # pragma: no cover - defensive fallback
54
+ if origin is not None: # pragma: no cover - interpreter-specific path
55
55
  args = get_args(origin)
56
56
  if len(args) == 2 and all(isinstance(arg, type) for arg in args):
57
57
  params_type = cast(type[SupportsDataclass], args[0])
@@ -161,7 +161,7 @@ class Tool[ParamsT: SupportsDataclass, ResultT: SupportsDataclass]:
161
161
 
162
162
  try:
163
163
  hints = get_type_hints(handler, include_extras=True)
164
- except Exception: # pragma: no cover - fallback for invalid hints
164
+ except Exception:
165
165
  hints = {}
166
166
 
167
167
  annotation = hints.get(parameter.name, parameter.annotation)
@@ -12,6 +12,7 @@
12
12
 
13
13
  from __future__ import annotations
14
14
 
15
+ # pyright: reportUnknownArgumentType=false, reportUnknownVariableType=false, reportUnknownMemberType=false, reportUnknownParameterType=false, reportUnnecessaryIsInstance=false, reportCallIssue=false, reportArgumentType=false, reportPossiblyUnboundVariable=false
15
16
  import dataclasses
16
17
  import re
17
18
  from collections.abc import Callable, Iterable, Mapping, Sequence, Sized
@@ -57,10 +58,7 @@ def _ordered_values(values: Iterable[object]) -> list[object]:
57
58
 
58
59
  items = list(values)
59
60
  if isinstance(values, (set, frozenset)):
60
- try:
61
- return sorted(items)
62
- except TypeError:
63
- return sorted(items, key=repr)
61
+ return sorted(items, key=repr)
64
62
  return items
65
63
 
66
64
 
@@ -101,7 +99,7 @@ def _merge_annotated_meta(
101
99
  base = args[0]
102
100
  for extra in args[1:]:
103
101
  if isinstance(extra, Mapping):
104
- merged.update(extra)
102
+ merged.update(cast(Mapping[str, object], extra))
105
103
  return base, merged
106
104
 
107
105
 
@@ -191,14 +189,16 @@ def _apply_constraints(value: object, meta: Mapping[str, object], path: str) ->
191
189
 
192
190
  members = meta.get("in") or meta.get("enum")
193
191
  if isinstance(members, Iterable) and not isinstance(members, (str, bytes)):
194
- options = _ordered_values(members)
192
+ options_iter = cast(Iterable[object], members)
193
+ options = _ordered_values(options_iter)
195
194
  normalized_options = [_normalize_option(option) for option in options]
196
195
  if result not in normalized_options:
197
196
  _fail(f"must be one of {normalized_options}")
198
197
 
199
198
  not_members = meta.get("not_in")
200
199
  if isinstance(not_members, Iterable) and not isinstance(not_members, (str, bytes)):
201
- forbidden = _ordered_values(not_members)
200
+ forbidden_iter = cast(Iterable[object], not_members)
201
+ forbidden = _ordered_values(forbidden_iter)
202
202
  normalized_forbidden = [_normalize_option(option) for option in forbidden]
203
203
  if result in normalized_forbidden:
204
204
  _fail(f"may not be one of {normalized_forbidden}")
@@ -222,8 +222,9 @@ def _apply_constraints(value: object, meta: Mapping[str, object], path: str) ->
222
222
 
223
223
  converter = meta.get("convert", meta.get("transform"))
224
224
  if converter:
225
+ converter_fn = cast(Callable[[object], object], converter)
225
226
  try:
226
- result = converter(result)
227
+ result = converter_fn(result)
227
228
  except (TypeError, ValueError) as error:
228
229
  raise type(error)(f"{path}: {error}") from error
229
230
  except Exception as error: # pragma: no cover - defensive
@@ -243,7 +244,7 @@ def _coerce_to_type(
243
244
  origin = get_origin(base_type)
244
245
  type_name = getattr(base_type, "__name__", type(base_type).__name__)
245
246
 
246
- if base_type in {object, _AnyType}:
247
+ if base_type is object or base_type is _AnyType:
247
248
  return _apply_constraints(value, merged_meta, path)
248
249
 
249
250
  if origin is Union:
@@ -290,7 +291,7 @@ def _coerce_to_type(
290
291
  if value == literal:
291
292
  return _apply_constraints(literal, merged_meta, path)
292
293
  if config.coerce:
293
- literal_type = type(literal)
294
+ literal_type = cast(type[object], type(literal))
294
295
  try:
295
296
  if isinstance(literal, bool) and isinstance(value, str):
296
297
  coerced_literal = _bool_from_str(value)
@@ -313,7 +314,7 @@ def _coerce_to_type(
313
314
  return _apply_constraints(value, merged_meta, path)
314
315
  if not isinstance(value, Mapping):
315
316
  type_name = getattr(
316
- dataclass_type, "__name__", dataclass_type.__class__.__name__
317
+ dataclass_type, "__name__", type(dataclass_type).__name__
317
318
  )
318
319
  raise TypeError(f"{path}: expected mapping for dataclass {type_name}")
319
320
  try:
@@ -353,7 +354,7 @@ def _coerce_to_type(
353
354
  if isinstance(value, str):
354
355
  value = [value]
355
356
  elif isinstance(value, Iterable):
356
- value = list(value)
357
+ value = list(cast(Iterable[object], value))
357
358
  else:
358
359
  raise TypeError(f"{path}: expected set")
359
360
  else:
@@ -367,7 +368,7 @@ def _coerce_to_type(
367
368
  if isinstance(value, str): # pragma: no cover - handled by earlier coercion
368
369
  items = [value]
369
370
  elif isinstance(value, Iterable):
370
- items = list(value)
371
+ items = list(cast(Iterable[object], value))
371
372
  else: # pragma: no cover - defensive guard
372
373
  raise TypeError(f"{path}: expected iterable")
373
374
  args = get_args(base_type)
@@ -402,8 +403,9 @@ def _coerce_to_type(
402
403
  key_type, value_type = (
403
404
  get_args(base_type) if get_args(base_type) else (object, object)
404
405
  )
406
+ mapping_value = cast(Mapping[object, object], value)
405
407
  result_dict: dict[object, object] = {}
406
- for key, item in value.items():
408
+ for key, item in mapping_value.items():
407
409
  coerced_key = _coerce_to_type(key, key_type, None, f"{path} keys", config)
408
410
  coerced_value = _coerce_to_type(
409
411
  item, value_type, None, f"{path}[{coerced_key}]", config
@@ -95,6 +95,11 @@ class Session:
95
95
 
96
96
  return cast(tuple[S, ...], self._state.get(slice_type, ()))
97
97
 
98
+ def seed_slice[S](self, slice_type: type[S], values: Iterable[S]) -> None:
99
+ """Initialize or replace the stored tuple for the provided type."""
100
+
101
+ self._state[slice_type] = tuple(values)
102
+
98
103
  def _on_tool_invoked(self, event: object) -> None:
99
104
  if isinstance(event, ToolInvoked):
100
105
  self._handle_tool_invoked(event)
@@ -14,6 +14,13 @@
14
14
 
15
15
  from __future__ import annotations
16
16
 
17
+ from .asteval import (
18
+ AstevalSection,
19
+ EvalFileRead,
20
+ EvalFileWrite,
21
+ EvalParams,
22
+ EvalResult,
23
+ )
17
24
  from .errors import ToolValidationError
18
25
  from .planning import (
19
26
  AddStep,
@@ -44,6 +51,11 @@ from .vfs import (
44
51
 
45
52
  __all__ = [
46
53
  "ToolValidationError",
54
+ "AstevalSection",
55
+ "EvalParams",
56
+ "EvalResult",
57
+ "EvalFileRead",
58
+ "EvalFileWrite",
47
59
  "Plan",
48
60
  "PlanStep",
49
61
  "PlanStatus",