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.

@@ -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=MOVuBbGbGA2O_QS69wH8E17bkRc_fHQ2Z0MGD-79ngE,1092
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=loPj9FVLCSm2zZu8-8GV9h_Usc3lPISzojPOlS2Us8k,948
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=VkLS3ZNcyxWQLX6VxH8MmTWbKCjDxNFnjye2nbhMcK8,4316
10
- weakincentives/examples/code_review_session.py,sha256=ArCrBiakJb3DlcNjVw2s420zdVOsdT8Pq_fQaTItGyE,5236
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=KUqxKfRWvASNzAyLno7M0Z0d7TWx8jc6qiWaWImBiHY,3460
16
- weakincentives/prompt/prompt.py,sha256=tO-u9ZdjlzdTSjc6obvhniV3564ry7yilHZhmJVUf00,22962
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=v3Ton3DpJbXYOf-itpUrUJtIad9j58oHmZ1UeMCRviY,5286
20
- weakincentives/prompt/tool.py,sha256=_a5vKUfH6mBYtHWbf1KFk4RlXr6O9endzB4yjh9tTiE,9156
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=9ZYYGyl0PCRLweYx9XQk8vKKLiSiY_3mf_P7BmsCxOE,37327
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=RnLG9KRXdpj3PRVlXwTa501qNdUzeTaXc5s2G4hLlL8,5782
28
- weakincentives/tools/__init__.py,sha256=B5U8Ujh4--3CwssNSUApkC5KCn5z_NqbHR2w_jdgcyc,1524
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=3u9v3CDkqcKmuNFId8Bov00Mb5IxcJvH9mb5yTE2zo4,20582
32
- weakincentives-0.3.0.dist-info/METADATA,sha256=jfUtPG9WpJcyXTwU3jeJ51uzC37C4dfpuVx3TGha9rs,6965
33
- weakincentives-0.3.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
34
- weakincentives-0.3.0.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
35
- weakincentives-0.3.0.dist-info/RECORD,,
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,,