skilled-proposer 0.1.0__tar.gz

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.
Files changed (30) hide show
  1. skilled_proposer-0.1.0/.github/workflows/test.yml +19 -0
  2. skilled_proposer-0.1.0/.gitignore +9 -0
  3. skilled_proposer-0.1.0/LICENSE +21 -0
  4. skilled_proposer-0.1.0/PKG-INFO +130 -0
  5. skilled_proposer-0.1.0/README.md +109 -0
  6. skilled_proposer-0.1.0/docs/superpowers/plans/2026-07-20-skilled-proposer-v0.1.0.md +1420 -0
  7. skilled_proposer-0.1.0/docs/superpowers/specs/2026-07-20-skilled-proposer-packaging-design.md +130 -0
  8. skilled_proposer-0.1.0/pyproject.toml +34 -0
  9. skilled_proposer-0.1.0/skills/prompt-engineering/SKILL.md +314 -0
  10. skilled_proposer-0.1.0/skills/prompt-engineering/models/anthropic.md +102 -0
  11. skilled_proposer-0.1.0/skills/prompt-engineering/models/deepseek.md +78 -0
  12. skilled_proposer-0.1.0/skills/prompt-engineering/models/google.md +112 -0
  13. skilled_proposer-0.1.0/skills/prompt-engineering/models/meta.md +90 -0
  14. skilled_proposer-0.1.0/skills/prompt-engineering/models/openai.md +104 -0
  15. skilled_proposer-0.1.0/skills/prompt-engineering/models/other.md +112 -0
  16. skilled_proposer-0.1.0/skills/prompt-engineering/models/qwen.md +75 -0
  17. skilled_proposer-0.1.0/skills/prompt-engineering/models/xai.md +90 -0
  18. skilled_proposer-0.1.0/skills/prompt-engineering/references/eval-design.md +138 -0
  19. skilled_proposer-0.1.0/skills/prompt-engineering/references/techniques.md +497 -0
  20. skilled_proposer-0.1.0/skills/prompt-engineering/templates/iteration-log.md +88 -0
  21. skilled_proposer-0.1.0/src/skilled_proposer/__init__.py +19 -0
  22. skilled_proposer-0.1.0/src/skilled_proposer/proposer.py +259 -0
  23. skilled_proposer-0.1.0/src/skilled_proposer/signatures.py +110 -0
  24. skilled_proposer-0.1.0/src/skilled_proposer/skill.py +90 -0
  25. skilled_proposer-0.1.0/tests/test_length.py +78 -0
  26. skilled_proposer-0.1.0/tests/test_proposer.py +116 -0
  27. skilled_proposer-0.1.0/tests/test_public_api.py +14 -0
  28. skilled_proposer-0.1.0/tests/test_signatures.py +38 -0
  29. skilled_proposer-0.1.0/tests/test_skill.py +98 -0
  30. skilled_proposer-0.1.0/uv.lock +2458 -0
@@ -0,0 +1,19 @@
1
+ name: test
2
+
3
+ on:
4
+ push:
5
+ pull_request:
6
+
7
+ jobs:
8
+ test:
9
+ runs-on: ubuntu-latest
10
+ strategy:
11
+ matrix:
12
+ python-version: ["3.10", "3.11", "3.12", "3.13"]
13
+ steps:
14
+ - uses: actions/checkout@v4
15
+ - uses: astral-sh/setup-uv@v5
16
+ with:
17
+ python-version: ${{ matrix.python-version }}
18
+ - run: uv sync
19
+ - run: uv run pytest -v
@@ -0,0 +1,9 @@
1
+ __pycache__/
2
+ *.py[cod]
3
+ *.egg-info/
4
+ dist/
5
+ build/
6
+ .venv/
7
+ .pytest_cache/
8
+ .ruff_cache/
9
+ .DS_Store
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 cmpnd
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,130 @@
1
+ Metadata-Version: 2.4
2
+ Name: skilled-proposer
3
+ Version: 0.1.0
4
+ Summary: A GEPA instruction proposer for DSPy that writes generalizable, skill-informed instructions
5
+ Project-URL: Homepage, https://github.com/cmpnd-ai/skilled-proposer
6
+ Project-URL: Repository, https://github.com/cmpnd-ai/skilled-proposer
7
+ Author-email: Drew Breunig <dbreunig@gmail.com>
8
+ License-Expression: MIT
9
+ License-File: LICENSE
10
+ Keywords: dspy,gepa,llm,prompt-optimization
11
+ Classifier: Development Status :: 4 - Beta
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Programming Language :: Python :: 3.10
15
+ Classifier: Programming Language :: Python :: 3.11
16
+ Classifier: Programming Language :: Python :: 3.12
17
+ Classifier: Programming Language :: Python :: 3.13
18
+ Requires-Python: >=3.10
19
+ Requires-Dist: dspy>=3.0
20
+ Description-Content-Type: text/markdown
21
+
22
+ # skilled-proposer
23
+
24
+ A custom instruction proposer for [GEPA](https://dspy.ai/api/optimizers/GEPA/), the reflective prompt optimizer in [DSPy](https://dspy.ai). Drop it into `dspy.GEPA(instruction_proposer=...)` to get instructions that generalize instead of memorizing your training set, informed by reference skills you provide.
25
+
26
+ ## Why
27
+
28
+ GEPA improves a program by asking a reflection model to rewrite each component's instruction based on execution traces and evaluator feedback. The stock proposer tells the reflection model to include "niche and domain specific factual information" from those traces in the new instruction. That helps on benchmarks, but it copies entities, numbers, and answers from your training examples into the prompt, and the prompt then does worse on inputs it has never seen.
29
+
30
+ `SkilledProposer` uses a different meta-prompt. It treats the examples as evidence of weaknesses in the instruction, extracts strategies and decision rules that transfer, and forbids copying example-specific content. It also adds three practical controls:
31
+
32
+ - Skills. Pass SKILL.md files, skill directories, or inline strings. The reflection model gets them as reference material, e.g., a prompting guide for your student model.
33
+ - Extra guidance. A plain string applied to every proposal.
34
+ - Length budgets. Cap the proposed instruction by words or tokens. The cap is enforced by a prompt constraint, then a compression pass, then truncation.
35
+
36
+ ## Install
37
+
38
+ ```bash
39
+ pip install skilled-proposer
40
+ ```
41
+
42
+ Requires Python 3.10 or newer and dspy 3.0 or newer.
43
+
44
+ ## Quickstart
45
+
46
+ ```python
47
+ import dspy
48
+ from skilled_proposer import SkilledProposer
49
+
50
+ proposer = SkilledProposer(
51
+ skills=["./skills/prompt-engineering"],
52
+ additional_instructions="Write instructions in imperative voice.",
53
+ max_words=300,
54
+ )
55
+
56
+ optimizer = dspy.GEPA(
57
+ metric=metric,
58
+ reflection_lm=dspy.LM("openai/gpt-5", temperature=1.0, max_tokens=32000),
59
+ instruction_proposer=proposer,
60
+ auto="medium",
61
+ )
62
+
63
+ optimized = optimizer.compile(program, trainset=train, valset=val)
64
+ ```
65
+
66
+ ## Skills
67
+
68
+ A skill is reference material for the reflection model. Each entry in `skills` can be:
69
+
70
+ - a path to a directory that contains a `SKILL.md` (the [Agent Skills](https://agentskills.io) layout)
71
+ - a path to a markdown file
72
+ - an inline string
73
+ - a `Skill(name=..., content=..., description=...)` object
74
+
75
+ If the file starts with YAML frontmatter, `name` and `description` are read from it and the block is stripped from the content. This repo ships an example at [`skills/prompt-engineering`](skills/prompt-engineering), a prompt optimization guide the reflection model can apply when rewriting instructions.
76
+
77
+ ### Skills with subfolders
78
+
79
+ Loading a skill directory reads only its `SKILL.md`. Files in subfolders such as `models/` or `references/` are not loaded. This is deliberate. An agent browsing a skill can open those files when it needs them, but GEPA calls the proposer in a plain LM call with no filesystem, many times per run. What the reflection model should see is also known before the run starts, e.g., you know which student model you are optimizing. So you, the developer, pick the extra files and pass them alongside the parent skill:
80
+
81
+ ```python
82
+ proposer = SkilledProposer(
83
+ skills=[
84
+ "./skills/prompt-engineering", # reads SKILL.md
85
+ "./skills/prompt-engineering/models/openai.md", # guidance for the student model
86
+ ],
87
+ )
88
+ ```
89
+
90
+ Each entry becomes its own `<skill>` block in the reflection prompt. Pass only the files that apply to your run. Inlining a whole skill folder would grow every proposal call for no benefit.
91
+
92
+ ## Options
93
+
94
+ ```python
95
+ SkilledProposer(
96
+ skills=None, # skills, paths, or inline strings
97
+ additional_instructions=None, # guidance applied to every proposal
98
+ base_instructions=None, # replace the built-in meta-prompt
99
+ max_words=None, # word cap on proposed instructions
100
+ max_tokens=None, # token cap on proposed instructions
101
+ prompt_model=None, # LM for the standalone gepa package
102
+ max_examples=None, # cap reflective examples per component
103
+ on_error="keep", # "keep" or "raise"
104
+ )
105
+ ```
106
+
107
+ - `base_instructions` replaces the whole meta-prompt, including the anti-overfitting rules. If you still want those rules, include equivalent text in your replacement.
108
+ - `on_error="keep"` logs a failed proposal and keeps the current instruction, so a long GEPA run survives a flaky call. Use `on_error="raise"` during development so failures surface.
109
+ - `max_tokens` counts tokens with litellm's tokenizer when it can resolve your model name, and falls back to about 4 characters per token.
110
+
111
+ ## Using the standalone gepa package
112
+
113
+ `dspy.GEPA` runs the proposer inside the reflection model's context, so you do not pass a model. The standalone [gepa](https://github.com/gepa-ai/gepa) package does not set a DSPy context, so pass the model yourself:
114
+
115
+ ```python
116
+ proposer = SkilledProposer(
117
+ skills=["./skills/prompt-engineering"],
118
+ prompt_model=dspy.LM("openai/gpt-5", temperature=1.0, max_tokens=32000),
119
+ )
120
+ ```
121
+
122
+ Then pass `proposer` wherever gepa accepts a `ProposalFn`.
123
+
124
+ ## Limits
125
+
126
+ - v0.1 is text only. Rich values such as `dspy.Image` are stringified in the reflective examples, so the reflection model cannot see them. Multimodal support is planned for v0.2.
127
+
128
+ ## License
129
+
130
+ MIT
@@ -0,0 +1,109 @@
1
+ # skilled-proposer
2
+
3
+ A custom instruction proposer for [GEPA](https://dspy.ai/api/optimizers/GEPA/), the reflective prompt optimizer in [DSPy](https://dspy.ai). Drop it into `dspy.GEPA(instruction_proposer=...)` to get instructions that generalize instead of memorizing your training set, informed by reference skills you provide.
4
+
5
+ ## Why
6
+
7
+ GEPA improves a program by asking a reflection model to rewrite each component's instruction based on execution traces and evaluator feedback. The stock proposer tells the reflection model to include "niche and domain specific factual information" from those traces in the new instruction. That helps on benchmarks, but it copies entities, numbers, and answers from your training examples into the prompt, and the prompt then does worse on inputs it has never seen.
8
+
9
+ `SkilledProposer` uses a different meta-prompt. It treats the examples as evidence of weaknesses in the instruction, extracts strategies and decision rules that transfer, and forbids copying example-specific content. It also adds three practical controls:
10
+
11
+ - Skills. Pass SKILL.md files, skill directories, or inline strings. The reflection model gets them as reference material, e.g., a prompting guide for your student model.
12
+ - Extra guidance. A plain string applied to every proposal.
13
+ - Length budgets. Cap the proposed instruction by words or tokens. The cap is enforced by a prompt constraint, then a compression pass, then truncation.
14
+
15
+ ## Install
16
+
17
+ ```bash
18
+ pip install skilled-proposer
19
+ ```
20
+
21
+ Requires Python 3.10 or newer and dspy 3.0 or newer.
22
+
23
+ ## Quickstart
24
+
25
+ ```python
26
+ import dspy
27
+ from skilled_proposer import SkilledProposer
28
+
29
+ proposer = SkilledProposer(
30
+ skills=["./skills/prompt-engineering"],
31
+ additional_instructions="Write instructions in imperative voice.",
32
+ max_words=300,
33
+ )
34
+
35
+ optimizer = dspy.GEPA(
36
+ metric=metric,
37
+ reflection_lm=dspy.LM("openai/gpt-5", temperature=1.0, max_tokens=32000),
38
+ instruction_proposer=proposer,
39
+ auto="medium",
40
+ )
41
+
42
+ optimized = optimizer.compile(program, trainset=train, valset=val)
43
+ ```
44
+
45
+ ## Skills
46
+
47
+ A skill is reference material for the reflection model. Each entry in `skills` can be:
48
+
49
+ - a path to a directory that contains a `SKILL.md` (the [Agent Skills](https://agentskills.io) layout)
50
+ - a path to a markdown file
51
+ - an inline string
52
+ - a `Skill(name=..., content=..., description=...)` object
53
+
54
+ If the file starts with YAML frontmatter, `name` and `description` are read from it and the block is stripped from the content. This repo ships an example at [`skills/prompt-engineering`](skills/prompt-engineering), a prompt optimization guide the reflection model can apply when rewriting instructions.
55
+
56
+ ### Skills with subfolders
57
+
58
+ Loading a skill directory reads only its `SKILL.md`. Files in subfolders such as `models/` or `references/` are not loaded. This is deliberate. An agent browsing a skill can open those files when it needs them, but GEPA calls the proposer in a plain LM call with no filesystem, many times per run. What the reflection model should see is also known before the run starts, e.g., you know which student model you are optimizing. So you, the developer, pick the extra files and pass them alongside the parent skill:
59
+
60
+ ```python
61
+ proposer = SkilledProposer(
62
+ skills=[
63
+ "./skills/prompt-engineering", # reads SKILL.md
64
+ "./skills/prompt-engineering/models/openai.md", # guidance for the student model
65
+ ],
66
+ )
67
+ ```
68
+
69
+ Each entry becomes its own `<skill>` block in the reflection prompt. Pass only the files that apply to your run. Inlining a whole skill folder would grow every proposal call for no benefit.
70
+
71
+ ## Options
72
+
73
+ ```python
74
+ SkilledProposer(
75
+ skills=None, # skills, paths, or inline strings
76
+ additional_instructions=None, # guidance applied to every proposal
77
+ base_instructions=None, # replace the built-in meta-prompt
78
+ max_words=None, # word cap on proposed instructions
79
+ max_tokens=None, # token cap on proposed instructions
80
+ prompt_model=None, # LM for the standalone gepa package
81
+ max_examples=None, # cap reflective examples per component
82
+ on_error="keep", # "keep" or "raise"
83
+ )
84
+ ```
85
+
86
+ - `base_instructions` replaces the whole meta-prompt, including the anti-overfitting rules. If you still want those rules, include equivalent text in your replacement.
87
+ - `on_error="keep"` logs a failed proposal and keeps the current instruction, so a long GEPA run survives a flaky call. Use `on_error="raise"` during development so failures surface.
88
+ - `max_tokens` counts tokens with litellm's tokenizer when it can resolve your model name, and falls back to about 4 characters per token.
89
+
90
+ ## Using the standalone gepa package
91
+
92
+ `dspy.GEPA` runs the proposer inside the reflection model's context, so you do not pass a model. The standalone [gepa](https://github.com/gepa-ai/gepa) package does not set a DSPy context, so pass the model yourself:
93
+
94
+ ```python
95
+ proposer = SkilledProposer(
96
+ skills=["./skills/prompt-engineering"],
97
+ prompt_model=dspy.LM("openai/gpt-5", temperature=1.0, max_tokens=32000),
98
+ )
99
+ ```
100
+
101
+ Then pass `proposer` wherever gepa accepts a `ProposalFn`.
102
+
103
+ ## Limits
104
+
105
+ - v0.1 is text only. Rich values such as `dspy.Image` are stringified in the reflective examples, so the reflection model cannot see them. Multimodal support is planned for v0.2.
106
+
107
+ ## License
108
+
109
+ MIT