typesafeainate/dspy-typesafeify

Add a decorator for dspy Signatures that automatically uses TypeSafe where relevant

10

stars

4,466

commits

Python

primary language

Sep 15, 2026

updated

README

typesafeify: a Typesafe decorator for DSPy

This is a deliberately stripped-down proof-of-concept fork of DSPy. It exists to test one idea in isolation: can a normal DSPy signature opt into Typesafe's typed inference path with one decorator, while application code keeps constructing and calling dspy.Predict exactly as before?

Observed before-and-after benchmark for the Typesafe decorator

The meaningful integration diff is intentionally tiny:

 from typing import Literal

 import dspy
+from typesafe_dspy import typesafeify

+@typesafeify(score_fields={"severity_score": [1, 5]})
 class SupportTicketTriage(dspy.Signature):
     ticket: dict = dspy.InputField()
     customer_impacting: bool = dspy.OutputField()
     owner_team: Literal["api-platform", "billing", "checkout", "infra", "support-ops"] = dspy.OutputField()
     severity_score: float = dspy.OutputField()
     internal_summary: str = dspy.OutputField()

The caller does not learn a new predictor API:

predictor = dspy.Predict(SupportTicketTriage)
result = predictor(**ticket_context)

What happens after adding the decorator

@typesafeify(...) reads the signature's output annotations and builds a hybrid execution plan.

flowchart LR
    A[Same DSPy signature] --> B{Output annotation}
    B -->|bool, Literal, configured score| C[One Typesafe request]
    B -->|freeform string| D[DSPy generative LM]
    C -->|trusted typed results| D
    C --> E[Same DSPy Prediction]
    D --> E
Signature outputPlain DSPyWith @typesafeify(...)
customer_impacting, needs_human_nowGenerated by the DSPy LMTypesafe Noul probabilities, thresholded into bool
owner_team, severity_bandGenerated by the DSPy LMTypesafe Choice decisions with per-option probabilities
severity_scoreGenerated by the DSPy LMTypesafe Score, mapped back onto the declared 1–5 scale
internal_summaryGenerated by the DSPy LMGenerated by the DSPy LM after the five typed results are known

The demo keeps separate baseline and decorated signature files only to make a controlled before/after run possible. Before sending either request, it proves that both signatures have identical fields and instructions.

Run the comparison

Install the published Typesafe SDK and this fork's development dependencies:

uv sync --extra typesafe

Set OPENAI_API_KEY and TYPESAFE_API_KEY, then run the same three tickets through both paths. Luna uses medium reasoning by default.

OPENAI_MODEL=gpt-5.6-luna \
  uv run python examples/typesafe_dspy_ticket_triage/run_demo.py \
  --limit 3 \
  --show-document

The command prints the execution plan, exact signature-parity check, typed results and probabilities, field-by-field output differences, freeform text, timing, token usage, and total modeled cost for every case.

Before/after benchmark

Across the three observed cases, the decorated path averaged 1.958 seconds versus 2.329 seconds for plain DSPy: 15.9% faster, or a projected 37.1 seconds saved per 100 sequential calls. Average modeled cost fell from $0.000377 to $0.000263 per ticket, a 30.1% reduction.

The cost calculation will use these explicit inputs:

InputRateStatus
Typesafe input tokens$0.042 / 1M tokensSupplied estimate; replace with a public pricing source when available
GPT-5.6 Luna input tokens$0.20 / 1M tokensPublished OpenAI rate
GPT-5.6 Luna output and reasoning tokens$1.20 / 1M tokensPublished OpenAI rate

The modeled totals use every priced category in the table: Luna input, Luna output plus reasoning, and Typesafe input. Typesafe output-token usage is reported by the demo but is not assigned a cost because the supplied Typesafe rate covers input tokens only.

Proof-of-concept layout

This fork is an experiment, not a replacement distribution for upstream DSPy. For DSPy itself, use the upstream project and documentation.

Contributors

(top 30 of 407)

okhat

1,129 commits

arnavsinghvi11

402 commits

isaacbmiller

293 commits

chenmoneygithub

265 commits

typesafeainate/dspy-typesafeify

Add a decorator for dspy Signatures that automatically uses TypeSafe where relevant

10

stars

4,466

commits

Python

primary language

Sep 15, 2026

updated

README

typesafeify: a Typesafe decorator for DSPy

This is a deliberately stripped-down proof-of-concept fork of DSPy. It exists to test one idea in isolation: can a normal DSPy signature opt into Typesafe's typed inference path with one decorator, while application code keeps constructing and calling dspy.Predict exactly as before?

Observed before-and-after benchmark for the Typesafe decorator

The meaningful integration diff is intentionally tiny:

 from typing import Literal

 import dspy
+from typesafe_dspy import typesafeify

+@typesafeify(score_fields={"severity_score": [1, 5]})
 class SupportTicketTriage(dspy.Signature):
     ticket: dict = dspy.InputField()
     customer_impacting: bool = dspy.OutputField()
     owner_team: Literal["api-platform", "billing", "checkout", "infra", "support-ops"] = dspy.OutputField()
     severity_score: float = dspy.OutputField()
     internal_summary: str = dspy.OutputField()

The caller does not learn a new predictor API:

predictor = dspy.Predict(SupportTicketTriage)
result = predictor(**ticket_context)

What happens after adding the decorator

@typesafeify(...) reads the signature's output annotations and builds a hybrid execution plan.

flowchart LR
    A[Same DSPy signature] --> B{Output annotation}
    B -->|bool, Literal, configured score| C[One Typesafe request]
    B -->|freeform string| D[DSPy generative LM]
    C -->|trusted typed results| D
    C --> E[Same DSPy Prediction]
    D --> E
Signature outputPlain DSPyWith @typesafeify(...)
customer_impacting, needs_human_nowGenerated by the DSPy LMTypesafe Noul probabilities, thresholded into bool
owner_team, severity_bandGenerated by the DSPy LMTypesafe Choice decisions with per-option probabilities
severity_scoreGenerated by the DSPy LMTypesafe Score, mapped back onto the declared 1–5 scale
internal_summaryGenerated by the DSPy LMGenerated by the DSPy LM after the five typed results are known

The demo keeps separate baseline and decorated signature files only to make a controlled before/after run possible. Before sending either request, it proves that both signatures have identical fields and instructions.

Run the comparison

Install the published Typesafe SDK and this fork's development dependencies:

uv sync --extra typesafe

Set OPENAI_API_KEY and TYPESAFE_API_KEY, then run the same three tickets through both paths. Luna uses medium reasoning by default.

OPENAI_MODEL=gpt-5.6-luna \
  uv run python examples/typesafe_dspy_ticket_triage/run_demo.py \
  --limit 3 \
  --show-document

The command prints the execution plan, exact signature-parity check, typed results and probabilities, field-by-field output differences, freeform text, timing, token usage, and total modeled cost for every case.

Before/after benchmark

Across the three observed cases, the decorated path averaged 1.958 seconds versus 2.329 seconds for plain DSPy: 15.9% faster, or a projected 37.1 seconds saved per 100 sequential calls. Average modeled cost fell from $0.000377 to $0.000263 per ticket, a 30.1% reduction.

The cost calculation will use these explicit inputs:

InputRateStatus
Typesafe input tokens$0.042 / 1M tokensSupplied estimate; replace with a public pricing source when available
GPT-5.6 Luna input tokens$0.20 / 1M tokensPublished OpenAI rate
GPT-5.6 Luna output and reasoning tokens$1.20 / 1M tokensPublished OpenAI rate

The modeled totals use every priced category in the table: Luna input, Luna output plus reasoning, and Typesafe input. Typesafe output-token usage is reported by the demo but is not assigned a cost because the supplied Typesafe rate covers input tokens only.

Proof-of-concept layout

This fork is an experiment, not a replacement distribution for upstream DSPy. For DSPy itself, use the upstream project and documentation.

See what people are saying

Contributors

(top 30 of 407)

okhat

1,129 commits

arnavsinghvi11

402 commits

isaacbmiller

293 commits

chenmoneygithub

265 commits

Languages

Python

99.7%