Skip to content

Commit de7c82d

Browse files
Route native compilation through verified DSPy TypeSafe adapter
1 parent 67a53ee commit de7c82d

17 files changed

Lines changed: 658 additions & 21 deletions

‎AGENTS.md‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@
44

55
- This is a compiler/runtime library. DSPy and GEPA belong to compile time.
66
Importing `typewright` or running a frozen program must not import either.
7+
Native compile-time evaluation uses the qualified DSPy TypeSafe wrapper;
8+
frozen execution retains the direct SDK backend. The owner approved this
9+
boundary on 2026-10-03; do not move DSPy into frozen execution.
710
- Target Jev's native typed API. Choice probabilities, Noul P(true), Score
811
expectations, and vendor confidence are distinct values.
912
- Keep the source contract fixed: output names/types, Choice labels, Score scale

‎README.md‎

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,14 @@ flowchart LR
4848
DSPy and GEPA are used only at compile time. Importing `typewright` or running a
4949
frozen program does not import either of them.
5050

51+
Native `compile --backend typesafe` and research selection use the experimental
52+
DSPy TypeSafe wrapper qualified with **DSPy 3.4.0 / SDK 0.7.0**. Install
53+
`.[compile]` (and `.[optimize]` for the generative teacher/GEPA). Frozen `run`,
54+
evaluation and held-out research execution retain the direct SDK backend and
55+
need only `.[live]`. This owner-approved boundary keeps DSPy out of runtime;
56+
offline equivalence tests do not establish live provider operation. Programmatic
57+
compilers can explicitly supply `DSPyTypeSafeBackend` to `ManagedBackend`.
58+
5159
## Where Typewright fits
5260

5361
DSPy 3.4 added experimental, native Jev support: a `TypeSafe` client behind the
@@ -72,7 +80,7 @@ experimental upstream features that may change. Typewright's structural search a
7280
hierarchies are implemented but **not yet shown to improve accuracy**; the checks
7381
above are about process and safety, not measured gains. Planned alignment with
7482
DSPy: compatibility verification ([#90](https://github.com/Jev-Engineering/TypeWright/issues/90))
75-
and evaluating DSPy's Jev adapter at compile time ([#91](https://github.com/Jev-Engineering/TypeWright/issues/91)).
83+
and the qualified compile-only DSPy Jev adapter ([#91](https://github.com/Jev-Engineering/TypeWright/issues/91)).
7684

7785
## Quickstart
7886

‎SETUP_PROMPT.md‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,12 @@ the README's one-request draft smoke check with `--backend typesafe --allow-paid
9898
model, and usage without exposing credentials. Do not silently substitute mock
9999
results when live calls fail.
100100

101+
Native compile/selection uses the experimental DSPy TypeSafe wrapper, qualified
102+
with DSPy 3.4.0 and SDK 0.7.0. Install `.[compile,optimize]` for live optimization;
103+
the generative teacher independently retains its supported 3.3.1 floor. Frozen
104+
runtime remains the direct SDK (`.[live]`) with no DSPy import. Offline transport
105+
equivalence is not authentication or live provider evidence.
106+
101107
Live DSPy/GEPA optimization additionally needs an explicitly selected real teacher
102108
model and provider credentials, permission to send the projected training data to
103109
that provider, four disjoint splits, and agreed request/signature/token budgets.

‎docs/ARCHITECTURE.md‎

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,21 @@ and limits are copied by the compiler, then the full graph is validated.
7373
`Revise` updates requested instruction/rubric entries. All use `dspy.Predict` inside an explicit
7474
`dspy.context(lm=...)`. A user-selected teacher provider is mandatory.
7575

76+
The owner-approved native evaluation boundary is separate from that generative
77+
teacher. Compile and selection entrypoints use `DSPyTypeSafeBackend`, a lazy
78+
compile-only wrapper over `dspy.experimental.TypeSafe`, qualified against exactly
79+
DSPy 3.4.0 and SDK 0.7.0. It passes raw native question dictionaries and retains
80+
the complete SDK response envelope, including model, usage and Score legend;
81+
it disables retries, upstream caches, history, callbacks and usage tracking.
82+
`ManagedBackend` remains the sole pre-dispatch budget, identity, cache and leaf
83+
receipt owner. Unknown cost remains null. Frozen execution and held-out research
84+
requests use the direct `TypeSafeBackend`; importing or executing a frozen
85+
program never imports DSPy. Both paths have offline native payload/response and
86+
hierarchy reservation, settlement, resume and replay equivalence tests. Private
87+
experimental hooks require the exact version guard; they are not live
88+
qualification. Callers injecting a backend into the Python compiler retain
89+
ownership of that explicit choice.
90+
7691
Standalone GEPA receives a candidate mapping `str -> str`. Each value is the JSON
7792
encoding of a TypeSafe entry; the entry itself may be a string, object, list, or
7893
null. Keys identify question instructions and indexed criteria/Score levels.

‎docs/INTEGRATIONS.md‎

Lines changed: 49 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@ Documentation inspection date: **2026-09-19**. These are the implementation targ
77
| Python | >=3.11 | Core library, CLI, tests. |
88
| TypeSafe SDK | `typesafe-sdk==0.7.0` | Native `TypeSafeClient.system_one`. |
99
| Target model | `jev-1.13.0` | Versioned ID, not a moving alias. |
10-
| DSPy | `dspy[litellm]>=3.3.1,<3.5` (verified: 3.3.1 and 3.4.0) | `Signature`, `Predict`, explicit teacher `LM/context`. |
10+
| DSPy teacher | `dspy[litellm]>=3.3.1,<3.5` (verified: 3.3.1 and 3.4.0) | `Signature`, `Predict`, explicit teacher `LM/context`. |
11+
| Native compile client | `dspy[typesafe]==3.4.0`, SDK 0.7.0 (`compile` extra) | Experimental TypeSafe wrapper; absent from frozen runtime. |
1112
| GEPA | `gepa==0.1.4` | Standalone adapter and custom component proposer. |
1213

1314
These are not claims that all dependencies were installed or live-tested in the
@@ -21,20 +22,22 @@ installing an arbitrary newest version.
2122
The supported range is `>=3.3.1,<3.5`, with `gepa==0.1.4` pinned (DSPy 3.4.0 resolves
2223
the same GEPA). Only 3.3.1 and 3.4.0 were exercised; later 3.4.x patch releases are
2324
accepted by the range but unverified. The upper bound is evidence-based: DSPy 3.4
24-
deprecates overriding `BaseLM.forward()` (which the teacher meter does) and schedules
25-
its removal in 3.5. See `BUILD_REPORT.md` for the executed results.
25+
deprecates overriding `BaseLM.forward()` and schedules its removal in 3.5.
26+
The #93 replacement uses the supported engine on 3.4; only the supported 3.3
27+
legacy branch still meters forward. See `BUILD_REPORT.md` for the executed results.
2628

2729
- Engine: DSPy 3.4 `engine="auto"` prefers native lm15 execution. The native response
2830
has no LiteLLM `_hidden_params`, so the teacher's SDK cost estimate would silently
29-
become "unknown". `DSPyTeacher` therefore passes `engine="litellm"` whenever
30-
`dspy.LM` accepts `engine` (3.3.x has no such argument and always uses LiteLLM).
31+
become "unknown". the #90 checkpoint therefore selected `engine="litellm"`. The #93 engine now
32+
observes the raw LiteLLM response before conversion (3.3 retains its legacy path).
3133
- Unchanged in 3.4.0 (checked offline against a loopback test double): caches off,
3234
`disable_history=True` leaves no history, signature-call and LM-forward ceilings count,
3335
`S1_TEACHER_API_BASE`/`S1_TEACHER_API_KEY` reach the request, consent flags are untouched.
3436
- Pydantic: `dspy` 3.4.0 itself requires `pydantic>=2.11.0`, so the `optimize` extra
3537
resolves to it automatically. The base install stays `pydantic>=2.10,<3`.
36-
- Not covered: `dspy[typesafe]`, `TypeSafe`/`Noul`/`Choice`/`Score` decision types and
37-
`ReAnchor` are not used or tested here.
38+
- The #90 checkpoint did not cover the native DSPy client. The #91 qualification
39+
below covers raw native transport, not DSPy Predict decision-type translation
40+
or ReAnchor. Those remain unused.
3841
- The documentation inspection date above was not changed; this verification used the
3942
installed packages, not a fresh read of the primary documentation.
4043

@@ -121,3 +124,42 @@ the pinned `jev-1.13.0` only under the operator's own equivalence confirmation,
121124
not an independent attestation.
122125
Unexpected returned model identities fail. None of these routes has been verified
123126
live in this repository. See `examples/ai-gateway/README.md` for caps and setup.
127+
128+
## Compile-only TypeSafe qualification (#91)
129+
130+
Owner decision on 2026-10-03: use the DSPy client for native compile-time
131+
evaluation and retain the direct SDK for frozen runtime. Install
132+
`python -m pip install -e '.[compile]'`; combine with `optimize` when a generative
133+
teacher/GEPA is required. The teacher's 3.3.1 floor remains supported independently;
134+
the experimental native wrapper refuses all versions except DSPy 3.4.0 and
135+
typesafe-sdk 0.7.0. This deliberately fails closed on unverified patch releases.
136+
137+
Gap analysis against actual pinned upstream code, rechecked 2026-10-03:
138+
139+
| Upstream behavior | Qualified wrapper |
140+
| --- | --- |
141+
| Defaults to `jev-latest`; no returned-model equality check | Versioned explicit model; ManagedBackend validates exact returned identity. |
142+
| SDK kwargs omit RetryPolicy (SDK default is two retries) | Explicit RetryPolicy(max_retries=0), fixed timeout. |
143+
| Cache defaults on; history may retain raw state/questions/response | Per-client cache off; history/callbacks/tracker disabled; no upstream response retention. |
144+
| Response mapping drops Score legend; public result drops model/usage envelope | Raw native dictionaries and full SDK JSON envelope preserved. |
145+
| No paid consent or our attempt ledger | Explicit allow_paid; ManagedBackend pre-dispatch budget/cache/identity and per-leaf reservations/settlement. |
146+
| No billed cost evidence | Dollar cost remains null. |
147+
148+
The wrapper uses exact-version experimental `_sdk_kwargs`, `_response` and
149+
`_finish` hooks. Native client traffic never goes through a generative Predict
150+
program. Compile CLI and flat/hierarchy research selection choose this backend;
151+
frozen run, evaluate and held-out study phases use the direct SDK. Callers of
152+
the Python compiler explicitly supply their backend. The direct SDK backend is
153+
retained for runtime, rather than deprecated away. The live hierarchy manifest
154+
binds separate compile and runtime identities; the code/manifest change needs
155+
a fresh digest-bound review before paid selection. It cannot revive old consent.
156+
157+
Installed-package socket-forbidden doubles verify payload/envelope equivalence,
158+
real SDK constructor arguments, consent/version/model refusal, zero retries,
159+
cache/attempt accounting, chain/conditional/diamond/nested graph receipts,
160+
interrupted resume and zero-dispatch replay. These are offline mechanics, not
161+
live inference, authenticity, measured gains or production qualification.
162+
163+
Primary sources: [pinned TypeSafe client](https://github.com/stanfordnlp/dspy/blob/3.4.0/dspy/clients/typesafe.py),
164+
[official Jev tutorial](https://dspy.ai/current/tutorials/jev_decisions/),
165+
[SDK usage](https://docs.typesafe.ai/sdk/python/usage).

‎docs/SECURITY.md‎

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,16 @@
33
## Data flows
44

55
A native TypeSafe request contains the projected state and all compiled questions.
6+
Compile/selection uses the qualified DSPy TypeSafe wrapper; frozen and held-out
7+
execution uses the direct SDK. The wrapper preserves native dictionaries and the
8+
full response identity, sets SDK retries to zero and disables upstream cache,
9+
history, callbacks and usage tracking. It does not route native state through a
10+
generative teacher. Entrypoints and provider backends enforce explicit consent;
11+
`ManagedBackend` owns pre-dispatch reservations, cache identity and evidence
12+
settlement on both paths. Experimental DSPy hooks
13+
are guarded to exactly 3.4.0 with SDK 0.7.0; drift fails closed. A reviewed live
14+
hierarchy manifest now binds separate compile and runtime backend identities,
15+
so an old manifest cannot approve the changed selection path by accident.
616
A DSPy teacher call contains the source declaration, current plan/components, and
717
selected projected training examples or training error traces with labels. The
818
teacher may therefore see sensitive task data. Consent to TypeSafe is not assumed

‎docs/SOURCES.md‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,3 +63,12 @@ The source release intentionally uses a standalone GEPA adapter rather than
6363
pretending Jev provides a general text-generation API. No upstream performance
6464
claim is asserted for this framework. Metrics from the bundled fixture are
6565
synthetic software test output only.
66+
67+
## Compile-only native client review, 2026-10-03
68+
69+
- Exact-version upstream TypeSafe hooks: https://github.com/stanfordnlp/dspy/blob/3.4.0/dspy/clients/typesafe.py
70+
- Native Jev tutorial: https://dspy.ai/current/tutorials/jev_decisions/
71+
72+
The wrapper is qualified against installed DSPy 3.4.0 and SDK 0.7.0 with
73+
socket-forbidden native doubles. Direct SDK frozen execution is retained by
74+
owner decision. No live inference, data sharing or measured gain follows.

‎pyproject.toml‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,11 @@ dependencies = ["pydantic>=2.10,<3", "PyYAML>=6.0,<7"]
1414

1515
[project.optional-dependencies]
1616
live = ["typesafe-sdk==0.7.0"]
17+
compile = ["dspy[typesafe]==3.4.0", "typesafe-sdk==0.7.0"]
1718
optimize = ["dspy[litellm]>=3.3.1,<3.5", "gepa==0.1.4"]
1819
research = ["numpy>=1.26,<3", "matplotlib>=3.8,<4"]
1920
dev = ["pytest>=8,<10", "pytest-cov>=5,<8", "ruff>=0.11,<1", "build>=1,<2"]
20-
all = ["typesafe-sdk==0.7.0", "dspy[litellm]>=3.3.1,<3.5", "gepa==0.1.4", "pytest>=8,<10", "pytest-cov>=5,<8"]
21+
all = ["typesafe-sdk==0.7.0", "dspy[litellm,typesafe]==3.4.0", "gepa==0.1.4", "pytest>=8,<10", "pytest-cov>=5,<8"]
2122

2223
[project.scripts]
2324
typewright = "typewright.cli:main"

‎src/typewright/banking77.py‎

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -332,8 +332,13 @@ def plan(splits, seeds, search_calls, proposals, max_chars):
332332
"matching_note": "Equal budget ceilings, not equal realized tokens or proposal counts. Report actual use."}
333333

334334

335-
def make_backend(live, limit):
336-
return ManagedBackend(TypeSafeBackend(allow_paid=True) if live else MockBackend(), max_calls=limit, cache=None)
335+
def make_backend(live, limit, *, compile_time=False):
336+
if live and compile_time:
337+
from .dspy_typesafe_backend import DSPyTypeSafeBackend
338+
provider = DSPyTypeSafeBackend(allow_paid=True)
339+
else:
340+
provider = TypeSafeBackend(allow_paid=True) if live else MockBackend()
341+
return ManagedBackend(provider, max_calls=limit, cache=None)
337342

338343

339344
def select_experiment(source, splits, dataset, out, *, seeds, search_calls, proposals,
@@ -539,7 +544,7 @@ def main(argv=None):
539544
if args.command == "select" and live and (not args.share_feedback or not args.teacher_model):
540545
parser.error("Live selection also requires --share-feedback and an explicit --teacher-model")
541546
def backend_factory(limit):
542-
return make_backend(live, limit)
547+
return make_backend(live, limit, compile_time=args.command == "select")
543548
if args.command == "select":
544549
def teacher_factory(limit):
545550
return DSPyTeacher(args.teacher_model, allow_paid=True, share_feedback=True, max_calls=limit,

‎src/typewright/cli.py‎

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,8 +54,13 @@ def make_backend(args):
5454
raise ConfigurationError("Choose --cache or --no-cache, not both.")
5555
if args.max_calls < 1 or args.request_timeout <= 0:
5656
raise ConfigurationError("Request budget and timeout must be positive.")
57-
provider = MockBackend() if args.backend == "mock" else TypeSafeBackend(
58-
allow_paid=args.allow_paid, timeout=args.request_timeout)
57+
if args.backend == "mock":
58+
provider = MockBackend()
59+
elif getattr(args, "command", None) == "compile":
60+
from .dspy_typesafe_backend import DSPyTypeSafeBackend
61+
provider = DSPyTypeSafeBackend(allow_paid=args.allow_paid, timeout=args.request_timeout)
62+
else:
63+
provider = TypeSafeBackend(allow_paid=args.allow_paid, timeout=args.request_timeout)
5964
cache = None if args.no_cache else AnswerCache(args.cache)
6065
return ManagedBackend(provider, max_calls=args.max_calls, cache=cache)
6166

0 commit comments

Comments
 (0)