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.
- weakincentives/__init__.py +1 -1
- weakincentives/adapters/__init__.py +3 -2
- weakincentives/examples/code_review_prompt.py +12 -3
- weakincentives/examples/code_review_session.py +7 -3
- weakincentives/prompt/markdown.py +1 -1
- weakincentives/prompt/prompt.py +7 -7
- weakincentives/prompt/structured_output.py +2 -2
- weakincentives/prompt/tool.py +2 -2
- weakincentives/serde/dataclass_serde.py +16 -14
- weakincentives/session/session.py +5 -0
- weakincentives/tools/__init__.py +12 -0
- weakincentives/tools/asteval.py +698 -0
- weakincentives/tools/vfs.py +59 -56
- weakincentives-0.4.0.dist-info/METADATA +490 -0
- {weakincentives-0.3.0.dist-info → weakincentives-0.4.0.dist-info}/RECORD +17 -16
- weakincentives-0.3.0.dist-info/METADATA +0 -231
- {weakincentives-0.3.0.dist-info → weakincentives-0.4.0.dist-info}/WHEEL +0 -0
- {weakincentives-0.3.0.dist-info → weakincentives-0.4.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,490 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: weakincentives
|
|
3
|
+
Version: 0.4.0
|
|
4
|
+
Summary: Tools for developing and optimizing side effect free background agents
|
|
5
|
+
Project-URL: Homepage, https://weakincentives.com/
|
|
6
|
+
Project-URL: Documentation, https://github.com/weakincentives/weakincentives#readme
|
|
7
|
+
Project-URL: Repository, https://github.com/weakincentives/weakincentives
|
|
8
|
+
Project-URL: Issue Tracker, https://github.com/weakincentives/weakincentives/issues
|
|
9
|
+
Author-email: Andrei Savu <andrei@weakincentives.com>
|
|
10
|
+
License: Apache-2.0
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Keywords: agents,ai,background-agents,optimization,side-effect-free,weak-incentives
|
|
13
|
+
Classifier: Development Status :: 3 - Alpha
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: Intended Audience :: Science/Research
|
|
16
|
+
Classifier: License :: OSI Approved :: Apache Software License
|
|
17
|
+
Classifier: Operating System :: OS Independent
|
|
18
|
+
Classifier: Programming Language :: Python :: 3
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
22
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
23
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
24
|
+
Classifier: Typing :: Typed
|
|
25
|
+
Requires-Python: >=3.12
|
|
26
|
+
Requires-Dist: asteval>=1.0.6
|
|
27
|
+
Provides-Extra: litellm
|
|
28
|
+
Requires-Dist: litellm>=1.79.0; extra == 'litellm'
|
|
29
|
+
Provides-Extra: openai
|
|
30
|
+
Requires-Dist: openai>=2.6.1; extra == 'openai'
|
|
31
|
+
Description-Content-Type: text/markdown
|
|
32
|
+
|
|
33
|
+
# Weak Incentives
|
|
34
|
+
|
|
35
|
+
**Lean, typed building blocks for side-effect-free background agents.**
|
|
36
|
+
Compose deterministic prompts, run typed tools, and parse strict JSON replies without
|
|
37
|
+
heavy dependencies. Optional adapters snap in when you need a model provider.
|
|
38
|
+
|
|
39
|
+
## Why now?
|
|
40
|
+
|
|
41
|
+
This library was built out of frustration with LangGraph and DSPy to explore
|
|
42
|
+
better ways to do state and context management when building apps with LLMs
|
|
43
|
+
while allowing the prompts to be automatically optimized.
|
|
44
|
+
|
|
45
|
+
## Highlights
|
|
46
|
+
|
|
47
|
+
- Namespaced prompt trees with deterministic Markdown renders, placeholder
|
|
48
|
+
verification, and tool-aware versioning metadata.
|
|
49
|
+
- Stdlib-only dataclass serde (`parse`, `dump`, `clone`, `schema`) keeps request and
|
|
50
|
+
response types honest end-to-end.
|
|
51
|
+
- Session state container and event bus collect prompt and tool telemetry for
|
|
52
|
+
downstream automation.
|
|
53
|
+
- Built-in planning and virtual filesystem tool suites give agents durable plans and
|
|
54
|
+
sandboxed edits backed by reducers and selectors.
|
|
55
|
+
- Optional OpenAI and LiteLLM adapters integrate structured output parsing, tool
|
|
56
|
+
orchestration, and telemetry hooks.
|
|
57
|
+
|
|
58
|
+
## Requirements
|
|
59
|
+
|
|
60
|
+
- Python 3.12+ (the repository pins 3.14 in `.python-version` for development)
|
|
61
|
+
- [`uv`](https://github.com/astral-sh/uv) CLI
|
|
62
|
+
|
|
63
|
+
## Install
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
uv add weakincentives
|
|
67
|
+
# optional provider adapters
|
|
68
|
+
uv add "weakincentives[openai]"
|
|
69
|
+
uv add "weakincentives[litellm]"
|
|
70
|
+
# cloning the repo? use: uv sync --extra openai --extra litellm
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## Tutorial: Build a Stateful Code-Reviewing Agent
|
|
74
|
+
|
|
75
|
+
Use Weak Incentives to assemble a reproducible reviewer that tracks every
|
|
76
|
+
decision, stages file edits in a sandbox, and evaluates quick calculations when
|
|
77
|
+
the diff raises questions. Compared to LangGraph you do not need to bolt on a
|
|
78
|
+
custom state store—the `Session` captures prompt and tool telemetry out of the
|
|
79
|
+
box. Unlike DSPy, prompt sections already expose versioning and override hooks
|
|
80
|
+
so optimizers can swap instructions without rewriting the runtime.
|
|
81
|
+
|
|
82
|
+
### 1. Model review data and expected outputs
|
|
83
|
+
|
|
84
|
+
Typed dataclasses keep inputs and outputs honest so adapters can emit consistent
|
|
85
|
+
telemetry and structured responses stay predictable.
|
|
86
|
+
|
|
87
|
+
```python
|
|
88
|
+
from dataclasses import dataclass
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
@dataclass
|
|
92
|
+
class PullRequestContext:
|
|
93
|
+
repository: str
|
|
94
|
+
title: str
|
|
95
|
+
body: str
|
|
96
|
+
files_summary: str
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
@dataclass
|
|
100
|
+
class ReviewComment:
|
|
101
|
+
file_path: str
|
|
102
|
+
line: int
|
|
103
|
+
severity: str
|
|
104
|
+
summary: str
|
|
105
|
+
rationale: str
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
@dataclass
|
|
109
|
+
class ReviewBundle:
|
|
110
|
+
comments: tuple[ReviewComment, ...]
|
|
111
|
+
overall_assessment: str
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
### 2. Create a session, surface built-in tool suites, and mount diffs
|
|
115
|
+
|
|
116
|
+
Planning, virtual filesystem, and Python-evaluation sections register reducers on
|
|
117
|
+
the provided session. Introducing them early keeps every evaluation capable of
|
|
118
|
+
multi-step plans, staged edits, and quick calculations. Host mounts feed the
|
|
119
|
+
reviewer precomputed diffs before the run begins so it can read them through the
|
|
120
|
+
virtual filesystem tools without calling back to your orchestrator.
|
|
121
|
+
|
|
122
|
+
```python
|
|
123
|
+
from pathlib import Path
|
|
124
|
+
|
|
125
|
+
from weakincentives.events import InProcessEventBus, PromptExecuted
|
|
126
|
+
from weakincentives.session import Session
|
|
127
|
+
from weakincentives.tools import (
|
|
128
|
+
AstevalSection,
|
|
129
|
+
HostMount,
|
|
130
|
+
PlanningToolsSection,
|
|
131
|
+
VfsPath,
|
|
132
|
+
VfsToolsSection,
|
|
133
|
+
)
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
bus = InProcessEventBus()
|
|
137
|
+
session = Session(bus=bus)
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
diff_root = Path("/srv/agent-mounts")
|
|
141
|
+
diff_root.mkdir(parents=True, exist_ok=True)
|
|
142
|
+
vfs_section = VfsToolsSection(
|
|
143
|
+
session=session,
|
|
144
|
+
allowed_host_roots=(diff_root,),
|
|
145
|
+
mounts=(
|
|
146
|
+
HostMount(
|
|
147
|
+
host_path="octo_widgets/cache-layer.diff",
|
|
148
|
+
mount_path=VfsPath(("diffs", "cache-layer.diff")),
|
|
149
|
+
),
|
|
150
|
+
),
|
|
151
|
+
)
|
|
152
|
+
planning_section = PlanningToolsSection(session=session)
|
|
153
|
+
asteval_section = AstevalSection(session=session)
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
def log_prompt(event: PromptExecuted) -> None:
|
|
157
|
+
print(
|
|
158
|
+
f"Prompt {event.prompt_name} completed with "
|
|
159
|
+
f"{len(event.result.tool_results)} tool calls"
|
|
160
|
+
)
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
bus.subscribe(PromptExecuted, log_prompt)
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
Copy unified diff files into `/srv/agent-mounts` before launching the run. The
|
|
167
|
+
host mount resolves `octo_widgets/cache-layer.diff` relative to that directory
|
|
168
|
+
and exposes it to the agent as `diffs/cache-layer.diff` inside the virtual
|
|
169
|
+
filesystem snapshot.
|
|
170
|
+
|
|
171
|
+
### 3. Compose the prompt with deterministic sections
|
|
172
|
+
|
|
173
|
+
Sections rely on `string.Template`, so prepare readable placeholders up front.
|
|
174
|
+
Combine your review instructions with the built-in tool suites to publish a
|
|
175
|
+
single, auditable prompt tree.
|
|
176
|
+
|
|
177
|
+
```python
|
|
178
|
+
from weakincentives import MarkdownSection, Prompt
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
@dataclass
|
|
182
|
+
class ReviewGuidance:
|
|
183
|
+
severity_scale: str = "minor | major | critical"
|
|
184
|
+
output_schema: str = "ReviewBundle with comments[] and overall_assessment"
|
|
185
|
+
focus_areas: str = (
|
|
186
|
+
"Security regressions, concurrency bugs, test coverage gaps, and"
|
|
187
|
+
" ambiguous logic should be escalated."
|
|
188
|
+
)
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
overview_section = MarkdownSection[PullRequestContext](
|
|
192
|
+
title="Repository Overview",
|
|
193
|
+
key="review.overview",
|
|
194
|
+
template="""
|
|
195
|
+
You are a principal engineer reviewing a pull request.
|
|
196
|
+
Repository: ${repository}
|
|
197
|
+
Title: ${title}
|
|
198
|
+
|
|
199
|
+
Pull request summary:
|
|
200
|
+
${body}
|
|
201
|
+
|
|
202
|
+
Files touched: ${files_summary}
|
|
203
|
+
""",
|
|
204
|
+
)
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
analysis_section = MarkdownSection[ReviewGuidance](
|
|
208
|
+
title="Review Directives",
|
|
209
|
+
key="review.directives",
|
|
210
|
+
template="""
|
|
211
|
+
- Classify findings using this severity scale: ${severity_scale}.
|
|
212
|
+
- Emit output that matches ${output_schema}; missing fields fail the run.
|
|
213
|
+
- Investigation focus:
|
|
214
|
+
${focus_areas}
|
|
215
|
+
- Inspect mounted diffs under `diffs/` with `vfs_read_file` before
|
|
216
|
+
commenting on unfamiliar hunks.
|
|
217
|
+
""",
|
|
218
|
+
default_params=ReviewGuidance(),
|
|
219
|
+
)
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
review_prompt = Prompt[ReviewBundle](
|
|
223
|
+
ns="tutorial/code_review",
|
|
224
|
+
key="review.generate",
|
|
225
|
+
name="code_review_agent",
|
|
226
|
+
sections=(
|
|
227
|
+
overview_section,
|
|
228
|
+
planning_section,
|
|
229
|
+
vfs_section,
|
|
230
|
+
asteval_section,
|
|
231
|
+
analysis_section,
|
|
232
|
+
),
|
|
233
|
+
)
|
|
234
|
+
|
|
235
|
+
|
|
236
|
+
rendered = review_prompt.render(
|
|
237
|
+
PullRequestContext(
|
|
238
|
+
repository="octo/widgets",
|
|
239
|
+
title="Add caching layer",
|
|
240
|
+
body="Introduces memoization to reduce redundant IO while preserving correctness.",
|
|
241
|
+
files_summary="loader.py, cache.py",
|
|
242
|
+
),
|
|
243
|
+
)
|
|
244
|
+
|
|
245
|
+
|
|
246
|
+
print(rendered.text)
|
|
247
|
+
print([tool.name for tool in rendered.tools])
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
### 4. Evaluate the prompt with an adapter
|
|
251
|
+
|
|
252
|
+
Adapters send the rendered prompt to a provider and publish telemetry to the
|
|
253
|
+
event bus. The session subscribed above automatically ingests each
|
|
254
|
+
`PromptExecuted` and `ToolInvoked` event.
|
|
255
|
+
|
|
256
|
+
```python
|
|
257
|
+
from weakincentives.adapters.openai import OpenAIAdapter
|
|
258
|
+
|
|
259
|
+
|
|
260
|
+
adapter = OpenAIAdapter(
|
|
261
|
+
model="gpt-4o-mini",
|
|
262
|
+
client_kwargs={"api_key": "sk-..."},
|
|
263
|
+
)
|
|
264
|
+
|
|
265
|
+
|
|
266
|
+
response = adapter.evaluate(
|
|
267
|
+
review_prompt,
|
|
268
|
+
PullRequestContext(
|
|
269
|
+
repository="octo/widgets",
|
|
270
|
+
title="Add caching layer",
|
|
271
|
+
body="Introduces memoization to reduce redundant IO while preserving correctness.",
|
|
272
|
+
files_summary="loader.py, cache.py",
|
|
273
|
+
),
|
|
274
|
+
bus=bus,
|
|
275
|
+
)
|
|
276
|
+
|
|
277
|
+
|
|
278
|
+
bundle = response.output
|
|
279
|
+
if bundle is None:
|
|
280
|
+
raise RuntimeError("Structured parsing failed")
|
|
281
|
+
|
|
282
|
+
|
|
283
|
+
for comment in bundle.comments:
|
|
284
|
+
print(f"{comment.file_path}:{comment.line} → {comment.summary}")
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
If the model omits a required field, `OpenAIAdapter` raises `PromptEvaluationError`
|
|
288
|
+
with provider context rather than silently degrading.
|
|
289
|
+
|
|
290
|
+
### 5. Mine session state for downstream automation
|
|
291
|
+
|
|
292
|
+
Built-in selectors expose the data collected by reducers that each tool suite
|
|
293
|
+
registered. This gives you ready-to-ship audit logs without building LangGraph
|
|
294
|
+
callbacks or DSPy side channels.
|
|
295
|
+
|
|
296
|
+
```python
|
|
297
|
+
from weakincentives.session import select_all, select_latest
|
|
298
|
+
from weakincentives.tools import Plan, VirtualFileSystem
|
|
299
|
+
|
|
300
|
+
|
|
301
|
+
plan_history = select_all(session, Plan)
|
|
302
|
+
latest_plan = select_latest(session, Plan)
|
|
303
|
+
vfs_snapshot = select_latest(session, VirtualFileSystem)
|
|
304
|
+
|
|
305
|
+
|
|
306
|
+
print(f"Plan steps recorded: {len(plan_history)}")
|
|
307
|
+
if latest_plan:
|
|
308
|
+
for step in latest_plan.steps:
|
|
309
|
+
print(f"- [{step.status}] {step.title}")
|
|
310
|
+
|
|
311
|
+
|
|
312
|
+
if vfs_snapshot:
|
|
313
|
+
for file in vfs_snapshot.files:
|
|
314
|
+
print(f"Staged file {file.path.segments} (version {file.version})")
|
|
315
|
+
```
|
|
316
|
+
|
|
317
|
+
### 6. Override sections with a version store
|
|
318
|
+
|
|
319
|
+
DSPy-style optimizers can persist improved instructions and let the runtime swap
|
|
320
|
+
them in without re-deploying code. Implement the `PromptVersionStore` protocol to
|
|
321
|
+
serve overrides by namespace, key, and tag.
|
|
322
|
+
|
|
323
|
+
```python
|
|
324
|
+
from dataclasses import dataclass
|
|
325
|
+
from weakincentives.prompt.versioning import (
|
|
326
|
+
PromptDescriptor,
|
|
327
|
+
PromptOverride,
|
|
328
|
+
PromptVersionStore,
|
|
329
|
+
)
|
|
330
|
+
|
|
331
|
+
|
|
332
|
+
@dataclass
|
|
333
|
+
class StaticVersionStore(PromptVersionStore):
|
|
334
|
+
override: PromptOverride | None = None
|
|
335
|
+
|
|
336
|
+
|
|
337
|
+
def resolve(
|
|
338
|
+
self,
|
|
339
|
+
descriptor: PromptDescriptor,
|
|
340
|
+
tag: str = "latest",
|
|
341
|
+
) -> PromptOverride | None:
|
|
342
|
+
if (
|
|
343
|
+
self.override
|
|
344
|
+
and self.override.ns == descriptor.ns
|
|
345
|
+
and self.override.prompt_key == descriptor.key
|
|
346
|
+
and self.override.tag == tag
|
|
347
|
+
):
|
|
348
|
+
return self.override
|
|
349
|
+
return None
|
|
350
|
+
|
|
351
|
+
|
|
352
|
+
overrides = PromptOverride(
|
|
353
|
+
ns=review_prompt.ns,
|
|
354
|
+
prompt_key=review_prompt.key,
|
|
355
|
+
tag="assertive-feedback",
|
|
356
|
+
overrides={
|
|
357
|
+
("review.directives",): """
|
|
358
|
+
- Classify findings using this severity scale: minor | major | critical.
|
|
359
|
+
- Always cite the exact diff hunk when raising a major or critical issue.
|
|
360
|
+
- Respond with ReviewBundle JSON. Missing fields terminate the run.
|
|
361
|
+
""",
|
|
362
|
+
},
|
|
363
|
+
)
|
|
364
|
+
|
|
365
|
+
|
|
366
|
+
store = StaticVersionStore(override=overrides)
|
|
367
|
+
rendered_with_override = review_prompt.render_with_overrides(
|
|
368
|
+
PullRequestContext(
|
|
369
|
+
repository="octo/widgets",
|
|
370
|
+
title="Add caching layer",
|
|
371
|
+
body="Introduces memoization to reduce redundant IO while preserving correctness.",
|
|
372
|
+
files_summary="loader.py, cache.py",
|
|
373
|
+
),
|
|
374
|
+
version_store=store,
|
|
375
|
+
tag="assertive-feedback",
|
|
376
|
+
)
|
|
377
|
+
|
|
378
|
+
|
|
379
|
+
print(rendered_with_override.text)
|
|
380
|
+
```
|
|
381
|
+
|
|
382
|
+
Because sections expose stable `(ns, key, path)` identifiers, overrides stay scoped
|
|
383
|
+
to the intended content. That means optimizers can explore new directives without
|
|
384
|
+
risking accidental prompt drift elsewhere in the tree.
|
|
385
|
+
|
|
386
|
+
### 7. Ship it
|
|
387
|
+
|
|
388
|
+
You now have a deterministic reviewer that:
|
|
389
|
+
|
|
390
|
+
1. Enforces typed contracts for inputs, tools, and outputs.
|
|
391
|
+
1. Persists multi-step plans, VFS edits, and evaluation transcripts inside a
|
|
392
|
+
session without custom plumbing.
|
|
393
|
+
1. Supports optimizer-driven overrides that slot cleanly into CI, evaluation
|
|
394
|
+
harnesses, or on-call tuning workflows.
|
|
395
|
+
|
|
396
|
+
Drop the agent into a queue worker, Slack bot, or scheduled job. Every evaluation
|
|
397
|
+
is replayable thanks to the captured session state, so postmortems start with
|
|
398
|
+
facts—not speculation.
|
|
399
|
+
|
|
400
|
+
## Sessions and Built-in Tools
|
|
401
|
+
|
|
402
|
+
Session state turns prompt output and tool calls into durable data. Built-in planning
|
|
403
|
+
and virtual filesystem sections register reducers on the provided session.
|
|
404
|
+
|
|
405
|
+
```python
|
|
406
|
+
from weakincentives.session import Session, select_latest
|
|
407
|
+
from weakincentives.tools import (
|
|
408
|
+
PlanningToolsSection,
|
|
409
|
+
Plan,
|
|
410
|
+
VfsToolsSection,
|
|
411
|
+
VirtualFileSystem,
|
|
412
|
+
)
|
|
413
|
+
|
|
414
|
+
session = Session()
|
|
415
|
+
planning_section = PlanningToolsSection(session=session)
|
|
416
|
+
vfs_section = VfsToolsSection(session=session)
|
|
417
|
+
|
|
418
|
+
prompt = Prompt[ResearchSummary](
|
|
419
|
+
ns="examples/research",
|
|
420
|
+
key="research.session",
|
|
421
|
+
sections=[task_section, planning_section, vfs_section],
|
|
422
|
+
)
|
|
423
|
+
|
|
424
|
+
active_plan = select_latest(session, Plan)
|
|
425
|
+
vfs_snapshot = select_latest(session, VirtualFileSystem)
|
|
426
|
+
```
|
|
427
|
+
|
|
428
|
+
Use `session.select_all(...)` or the helpers in `weakincentives.session` to drive UI
|
|
429
|
+
state, persistence, or audits after each adapter run.
|
|
430
|
+
|
|
431
|
+
## Adapter Integrations
|
|
432
|
+
|
|
433
|
+
Adapters stay optional and only load their dependencies when you import them.
|
|
434
|
+
|
|
435
|
+
```python
|
|
436
|
+
from weakincentives.adapters.openai import OpenAIAdapter
|
|
437
|
+
from weakincentives.events import InProcessEventBus
|
|
438
|
+
from weakincentives.session import Session
|
|
439
|
+
from weakincentives.tools import Plan
|
|
440
|
+
|
|
441
|
+
bus = InProcessEventBus()
|
|
442
|
+
session = Session(bus=bus)
|
|
443
|
+
|
|
444
|
+
adapter = OpenAIAdapter(
|
|
445
|
+
model="gpt-4o-mini",
|
|
446
|
+
client_kwargs={"api_key": "sk-..."},
|
|
447
|
+
)
|
|
448
|
+
|
|
449
|
+
response = adapter.evaluate(
|
|
450
|
+
prompt,
|
|
451
|
+
ResearchGuidance(topic="Ada Lovelace"),
|
|
452
|
+
bus=bus,
|
|
453
|
+
)
|
|
454
|
+
|
|
455
|
+
plan_history = session.select_all(Plan)
|
|
456
|
+
```
|
|
457
|
+
|
|
458
|
+
`InProcessEventBus` publishes `ToolInvoked` and `PromptExecuted` events for the
|
|
459
|
+
session (or any other subscriber) to consume.
|
|
460
|
+
|
|
461
|
+
## Development Setup
|
|
462
|
+
|
|
463
|
+
1. Install Python 3.14 (for example with `pyenv install 3.14.0`).
|
|
464
|
+
|
|
465
|
+
1. Install `uv`, then bootstrap the environment and hooks:
|
|
466
|
+
|
|
467
|
+
```bash
|
|
468
|
+
uv sync
|
|
469
|
+
./install-hooks.sh
|
|
470
|
+
```
|
|
471
|
+
|
|
472
|
+
1. Run checks with `uv run` so everything shares the managed virtualenv:
|
|
473
|
+
|
|
474
|
+
- `make format` / `make format-check`
|
|
475
|
+
- `make lint` / `make lint-fix`
|
|
476
|
+
- `make typecheck` (Ty + Pyright, warnings fail the build)
|
|
477
|
+
- `make test` (pytest via `build/run_pytest.py`, 100% coverage enforced)
|
|
478
|
+
- `make check` (aggregates the quiet checks above plus Bandit, Deptry, pip-audit,
|
|
479
|
+
and markdown linting)
|
|
480
|
+
|
|
481
|
+
## Documentation
|
|
482
|
+
|
|
483
|
+
- `AGENTS.md` — operational handbook and contributor workflow.
|
|
484
|
+
- `specs/` — design docs for prompts, planning tools, and adapters.
|
|
485
|
+
- `ROADMAP.md` — upcoming feature sketches.
|
|
486
|
+
- `docs/api/` — API reference material.
|
|
487
|
+
|
|
488
|
+
## License
|
|
489
|
+
|
|
490
|
+
Apache 2.0 • Status: Alpha (APIs may change between releases)
|
|
@@ -1,35 +1,36 @@
|
|
|
1
|
-
weakincentives/__init__.py,sha256=
|
|
1
|
+
weakincentives/__init__.py,sha256=d6uIlHj5DSwWYQ5Ky9J1qQXaK0vgj-OJeoMQXjfhqyI,1049
|
|
2
2
|
weakincentives/events.py,sha256=s1ddTuCOgKNcRZvL9T7vX7PUOXbifD8Gr1QJiuenA6c,3048
|
|
3
3
|
weakincentives/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
4
|
-
weakincentives/adapters/__init__.py,sha256=
|
|
4
|
+
weakincentives/adapters/__init__.py,sha256=YWZqiAce-auh8sIAdGRcyYAh69uNXgh76Rjm5yZHIEw,977
|
|
5
5
|
weakincentives/adapters/core.py,sha256=Eo1309SsjZPPET_xCIjbSoeSQmakwwLdVXDlv2ZlulI,2101
|
|
6
6
|
weakincentives/adapters/litellm.py,sha256=ZO8B05zZ8JkHirxciCQPZjxNILFWCop3LjvcKU4SBmA,21261
|
|
7
7
|
weakincentives/adapters/openai.py,sha256=LEYih4EcQ7So-b_sTItgRt-6JVRITsfO0aG847TpHNs,20871
|
|
8
8
|
weakincentives/examples/__init__.py,sha256=SNyUBOjzdKl4UkaEk99uSenVnqDvK7m1UnAe27Rhry8,1724
|
|
9
|
-
weakincentives/examples/code_review_prompt.py,sha256=
|
|
10
|
-
weakincentives/examples/code_review_session.py,sha256=
|
|
9
|
+
weakincentives/examples/code_review_prompt.py,sha256=oMHwzQ6EoifvH2oBmo_FxBsY3yklQXFkln7sdswf9nk,4602
|
|
10
|
+
weakincentives/examples/code_review_session.py,sha256=YrfnfIbNMqylz0uvIGIREn4mAv4kcjEmS1OMrK-Yqv8,5434
|
|
11
11
|
weakincentives/examples/code_review_tools.py,sha256=rOTZBxgMZfuy8b_oNCJu31BskeExSa4jWB3GPOSJ88g,11193
|
|
12
12
|
weakincentives/prompt/__init__.py,sha256=gPGFeVN6puN-P1ztqFrobE5zRb3CJ4_JZJSmPatIjUo,1274
|
|
13
13
|
weakincentives/prompt/_types.py,sha256=zvb4KrJyqFGhilmvzvgZzIxpZhm1LRIzc8vwugnZ2jY,925
|
|
14
14
|
weakincentives/prompt/errors.py,sha256=DR3GN54oTnNARkto3mVbIuT6BwhijTP3onLpcB6jdq0,1664
|
|
15
|
-
weakincentives/prompt/markdown.py,sha256=
|
|
16
|
-
weakincentives/prompt/prompt.py,sha256=
|
|
15
|
+
weakincentives/prompt/markdown.py,sha256=DM3rUcqvknwuZB6viojTkrWkePSTy8oI8PILy1mROdg,3414
|
|
16
|
+
weakincentives/prompt/prompt.py,sha256=VrcEnc-RJG3mxMXgufW09pVAO6zFyGj8fuCT9XC3mjg,22676
|
|
17
17
|
weakincentives/prompt/response_format.py,sha256=uYGg2mD6KIXuBDGgx65szV7er804ychb0zuZj9Irtuo,1805
|
|
18
18
|
weakincentives/prompt/section.py,sha256=LdqoZADjsvvSpTvPzG0Unan_N-q1AgPWw-0F2LrWn5Y,4962
|
|
19
|
-
weakincentives/prompt/structured_output.py,sha256=
|
|
20
|
-
weakincentives/prompt/tool.py,sha256=
|
|
19
|
+
weakincentives/prompt/structured_output.py,sha256=SdK645AD3cz_Kkz-50eyO2YhSUmXQ0HzuFvLTftBhCQ,5207
|
|
20
|
+
weakincentives/prompt/tool.py,sha256=XLy5lcWgtsL2ZPnylOoPVhUUJlhBozwobalJofLqMpM,9114
|
|
21
21
|
weakincentives/prompt/versioning.py,sha256=Ur0rLYvVvMuoGxOB-SzYIALMlK6IKbOvxJWf2HsE9lQ,4034
|
|
22
22
|
weakincentives/serde/__init__.py,sha256=jkYZa12RWgXlDzfPVHA6x3Jh0x52SnkVbVD2SpRUOtU,690
|
|
23
|
-
weakincentives/serde/dataclass_serde.py,sha256=
|
|
23
|
+
weakincentives/serde/dataclass_serde.py,sha256=07mJQj08cbaTsrcZ5xvtjlLxzVkUGirHl1k74jPAFQI,37881
|
|
24
24
|
weakincentives/session/__init__.py,sha256=f-JN93x4VXzGJd1oontyZuaRJ1wSN80MQ6qduNG-3ss,1000
|
|
25
25
|
weakincentives/session/reducers.py,sha256=_Wi8brUaGhT9jxfCychu59T_6jo3tvYp5JkhWpSb5ts,1944
|
|
26
26
|
weakincentives/session/selectors.py,sha256=uLYfhVF8ot8W03BQtmj7AzLltrdZ_VAsRsn31gQfWUk,1427
|
|
27
|
-
weakincentives/session/session.py,sha256=
|
|
28
|
-
weakincentives/tools/__init__.py,sha256=
|
|
27
|
+
weakincentives/session/session.py,sha256=jXPq-zQkmUnH6VeKcYxQPVY-Yt9VjV4r_lK3swxvFtQ,5987
|
|
28
|
+
weakincentives/tools/__init__.py,sha256=2MabNTtfvAiZGzQcGA7i_XNj9UqDW9-X7IWoDzVpdVE,1737
|
|
29
|
+
weakincentives/tools/asteval.py,sha256=RwhHLAifA0ZWmMZBtrJQRQFOnh9FEamY-4ztaEK7UBw,25107
|
|
29
30
|
weakincentives/tools/errors.py,sha256=cHD_78WEpwzVjtuiSGtEyMy8u-n-1k0I95fEBoMngbI,765
|
|
30
31
|
weakincentives/tools/planning.py,sha256=qlxulLZgLlt_7eFI4Psb8IGNaea4N6uR_ijch1f7Jc4,17023
|
|
31
|
-
weakincentives/tools/vfs.py,sha256
|
|
32
|
-
weakincentives-0.
|
|
33
|
-
weakincentives-0.
|
|
34
|
-
weakincentives-0.
|
|
35
|
-
weakincentives-0.
|
|
32
|
+
weakincentives/tools/vfs.py,sha256=-3hIs5YOM1T_Lmi0UUULejce83oomM_EZDc5Gnua1SI,20615
|
|
33
|
+
weakincentives-0.4.0.dist-info/METADATA,sha256=NMd3oDspblHXanLHAWH7vzoH-ZYeciG7DXTqIBgsF3A,14728
|
|
34
|
+
weakincentives-0.4.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
35
|
+
weakincentives-0.4.0.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
|
36
|
+
weakincentives-0.4.0.dist-info/RECORD,,
|