From f94038ff066bc68558442344efd3bccfafc0b2f0 Mon Sep 17 00:00:00 2001 From: Netsanet Gebremedhin Date: Wed, 15 Oct 2025 02:12:01 -0400 Subject: [PATCH 01/12] docs(signature): add Google-style docstring for with_instructions (refs #8926) --- dspy/signatures/signature.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/dspy/signatures/signature.py b/dspy/signatures/signature.py index 236a774ddd..b4f132f727 100644 --- a/dspy/signatures/signature.py +++ b/dspy/signatures/signature.py @@ -245,6 +245,25 @@ class Signature(BaseModel, metaclass=SignatureMeta): @classmethod def with_instructions(cls, instructions: str) -> type["Signature"]: + """Return a new Signature class with identical fields and new instructions. + + This method does not mutate ``cls``. It constructs a fresh Signature + class using the current fields and the provided ``instructions``. + + Args: + instructions (str): Instruction text to attach to the new signature. + + Returns: + type[Signature]: A new Signature class whose fields match ``cls.fields`` + and whose instructions equal ``instructions``. + + Example: + >>> NewSig = MySig.with_instructions("Translate to French.") + >>> NewSig is MySig + False + >>> NewSig.instructions + 'Translate to French.' + """ return Signature(cls.fields, instructions) @classmethod From df58997019e2187ce2d7b5ae46db9765a85101a1 Mon Sep 17 00:00:00 2001 From: Netsanet Gebremedhin Date: Wed, 15 Oct 2025 10:19:42 -0400 Subject: [PATCH 02/12] docs(signature): docstrings for prepend/append/insert/delete (Fixes #8944) --- dspy/signatures/signature.py | 64 +++++++++++++++++++++++++++++++++++- 1 file changed, 63 insertions(+), 1 deletion(-) diff --git a/dspy/signatures/signature.py b/dspy/signatures/signature.py index b4f132f727..f76bb8db42 100644 --- a/dspy/signatures/signature.py +++ b/dspy/signatures/signature.py @@ -294,14 +294,55 @@ def with_updated_fields(cls, name: str, type_: type | None = None, **kwargs: dic @classmethod def prepend(cls, name, field, type_=None) -> type["Signature"]: + """Insert a field at the start of the appropriate section. + + Equivalent to ``insert(0, name, field, type_)``. The section (inputs vs + outputs) is determined by ``field.json_schema_extra["__dspy_field_type"]``. + If ``type_`` is omitted, the field's existing ``annotation`` is used; if + missing, ``str`` is used. + + Args: + name (str): Field name to add. + field: InputField or OutputField instance to insert. + type_ (type | None): Optional explicit type annotation. + + Returns: + type[Signature]: A new Signature class with the field inserted first. + """ return cls.insert(0, name, field, type_) @classmethod def append(cls, name, field, type_=None) -> type["Signature"]: + """Insert a field at the end of the appropriate section. + + Equivalent to ``insert(-1, name, field, type_)``. The section (inputs vs + outputs) is determined by ``field.json_schema_extra["__dspy_field_type"]``. + If ``type_`` is omitted, the field's existing ``annotation`` is used; if + missing, ``str`` is used. + + Args: + name (str): Field name to add. + field: InputField or OutputField instance to insert. + type_ (type | None): Optional explicit type annotation. + + Returns: + type[Signature]: A new Signature class with the field appended. + """ return cls.insert(-1, name, field, type_) @classmethod def delete(cls, name) -> type["Signature"]: + """Return a new Signature class without the given field. + + If ``name`` is not present, the fields are unchanged (no error raised). + + Args: + name (str): Field name to remove. + + Returns: + type[Signature]: A new Signature class with the field removed (or unchanged + if the field was absent). + """ fields = dict(cls.fields) fields.pop(name, None) @@ -309,7 +350,28 @@ def delete(cls, name) -> type["Signature"]: return Signature(fields, cls.instructions) @classmethod - def insert(cls, index: int, name: str, field, type_: type | None = None) -> type["Signature"]: + def insert( + cls, index: int, name: str, field, type_: type | None = None + ) -> type["Signature"]: + """Insert a field at a specific position among inputs or outputs. + + The target section (inputs vs outputs) is determined by + ``field.json_schema_extra["__dspy_field_type"]``. Negative indices are + supported (e.g., ``-1`` appends). If ``type_`` is omitted, the field's + existing ``annotation`` is used; if that is missing, ``str`` is used. + + Args: + index (int): Insertion position within the chosen section; negatives append. + name (str): Field name to add. + field: InputField or OutputField instance to insert. + type_ (type | None): Optional explicit type annotation. + + Returns: + type[Signature]: A new Signature class with the field inserted. + + Raises: + ValueError: If ``index`` falls outside the valid range for the chosen section. + """ # It's possible to set the type as annotation=type in pydantic.Field(...) # But this may be annoying for users, so we allow them to pass the type if type_ is None: From 8d98e44d1bb8245b78acf543d1ef9ebd13e02553 Mon Sep 17 00:00:00 2001 From: Netsanet Gebremedhin Date: Wed, 15 Oct 2025 20:12:23 -0400 Subject: [PATCH 03/12] docs(signature): unify backticks and finalize with_instructions docstring per style guide --- dspy/signatures/signature.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dspy/signatures/signature.py b/dspy/signatures/signature.py index f76bb8db42..89f2e2e44d 100644 --- a/dspy/signatures/signature.py +++ b/dspy/signatures/signature.py @@ -247,14 +247,14 @@ class Signature(BaseModel, metaclass=SignatureMeta): def with_instructions(cls, instructions: str) -> type["Signature"]: """Return a new Signature class with identical fields and new instructions. - This method does not mutate ``cls``. It constructs a fresh Signature + This method does not mutate `cls`. It constructs a fresh Signature class using the current fields and the provided ``instructions``. Args: instructions (str): Instruction text to attach to the new signature. Returns: - type[Signature]: A new Signature class whose fields match ``cls.fields`` + type[Signature]: A new Signature class whose fields match `cls.fields` and whose instructions equal ``instructions``. Example: From ea9924f8b01d371a5f0131b40120886312e7ccbb Mon Sep 17 00:00:00 2001 From: Netsanet Gebremedhin Date: Wed, 15 Oct 2025 20:20:04 -0400 Subject: [PATCH 04/12] docs(signature): fix mkdocs rendering and make example runnable per review feedback --- dspy/signatures/signature.py | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/dspy/signatures/signature.py b/dspy/signatures/signature.py index 89f2e2e44d..f75a57137d 100644 --- a/dspy/signatures/signature.py +++ b/dspy/signatures/signature.py @@ -258,11 +258,17 @@ class using the current fields and the provided ``instructions``. and whose instructions equal ``instructions``. Example: - >>> NewSig = MySig.with_instructions("Translate to French.") - >>> NewSig is MySig - False - >>> NewSig.instructions - 'Translate to French.' + ``` + from dspy.signatures import Signature, InputField, OutputField + + class MySig(Signature): + input_text: str = InputField(desc="Input text") + output_text: str = OutputField(desc="Output text") + + NewSig = MySig.with_instructions("Translate to French.") + assert NewSig is not MySig + assert NewSig.instructions == "Translate to French." + ``` """ return Signature(cls.fields, instructions) From a45cd226976f5f32d0418c1c69d30d76a601db63 Mon Sep 17 00:00:00 2001 From: Netsanet Gebremedhin Date: Sat, 18 Oct 2025 13:33:04 -0400 Subject: [PATCH 05/12] Single backtick: instructions. --- dspy/signatures/signature.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dspy/signatures/signature.py b/dspy/signatures/signature.py index f75a57137d..9186629c32 100644 --- a/dspy/signatures/signature.py +++ b/dspy/signatures/signature.py @@ -255,7 +255,7 @@ class using the current fields and the provided ``instructions``. Returns: type[Signature]: A new Signature class whose fields match `cls.fields` - and whose instructions equal ``instructions``. + and whose instructions equal `instructions`. Example: ``` From 0301de54f0724ee604b94852a93bd99e61b1c919 Mon Sep 17 00:00:00 2001 From: Netsanet Gebremedhin Date: Sat, 18 Oct 2025 14:43:45 -0400 Subject: [PATCH 06/12] unify backticks and remove ambigous statements. --- dspy/signatures/signature.py | 43 ++++++++++++++++++------------------ 1 file changed, 21 insertions(+), 22 deletions(-) diff --git a/dspy/signatures/signature.py b/dspy/signatures/signature.py index 9186629c32..dcdf62c441 100644 --- a/dspy/signatures/signature.py +++ b/dspy/signatures/signature.py @@ -254,7 +254,7 @@ class using the current fields and the provided ``instructions``. instructions (str): Instruction text to attach to the new signature. Returns: - type[Signature]: A new Signature class whose fields match `cls.fields` + A new Signature class whose fields match `cls.fields` and whose instructions equal `instructions`. Example: @@ -300,39 +300,38 @@ def with_updated_fields(cls, name: str, type_: type | None = None, **kwargs: dic @classmethod def prepend(cls, name, field, type_=None) -> type["Signature"]: - """Insert a field at the start of the appropriate section. + """Insert a field at index 0 of the `inputs` or `outputs` section. - Equivalent to ``insert(0, name, field, type_)``. The section (inputs vs - outputs) is determined by ``field.json_schema_extra["__dspy_field_type"]``. - If ``type_`` is omitted, the field's existing ``annotation`` is used; if - missing, ``str`` is used. + Equivalent to `insert(0, name, field, type_)`. The section (inputs vs outputs) + is determined by `field.json_schema_extra["__dspy_field_type"]`. Args: name (str): Field name to add. - field: InputField or OutputField instance to insert. + field: `InputField` or `OutputField` instance to insert. type_ (type | None): Optional explicit type annotation. Returns: - type[Signature]: A new Signature class with the field inserted first. + A new `Signature` class with the field inserted first. """ return cls.insert(0, name, field, type_) @classmethod def append(cls, name, field, type_=None) -> type["Signature"]: - """Insert a field at the end of the appropriate section. + """Insert a field at the end of the `inputs` or `outputs` section. + + Equivalent to `insert(-1, name, field, type_)`. The section (inputs vs + outputs) is determined by `field.json_schema_extra["__dspy_field_type"]`. + + If `type_` is `None`, the effective type is resolved by `insert`. - Equivalent to ``insert(-1, name, field, type_)``. The section (inputs vs - outputs) is determined by ``field.json_schema_extra["__dspy_field_type"]``. - If ``type_`` is omitted, the field's existing ``annotation`` is used; if - missing, ``str`` is used. Args: name (str): Field name to add. - field: InputField or OutputField instance to insert. + field: `InputField` or `OutputField` instance to insert. type_ (type | None): Optional explicit type annotation. Returns: - type[Signature]: A new Signature class with the field appended. + A new Signature class with the field appended. """ return cls.insert(-1, name, field, type_) @@ -340,13 +339,13 @@ def append(cls, name, field, type_=None) -> type["Signature"]: def delete(cls, name) -> type["Signature"]: """Return a new Signature class without the given field. - If ``name`` is not present, the fields are unchanged (no error raised). + If `name` is not present, the fields are unchanged (no error raised). Args: name (str): Field name to remove. Returns: - type[Signature]: A new Signature class with the field removed (or unchanged + A new Signature class with the field removed (or unchanged if the field was absent). """ fields = dict(cls.fields) @@ -362,9 +361,9 @@ def insert( """Insert a field at a specific position among inputs or outputs. The target section (inputs vs outputs) is determined by - ``field.json_schema_extra["__dspy_field_type"]``. Negative indices are - supported (e.g., ``-1`` appends). If ``type_`` is omitted, the field's - existing ``annotation`` is used; if that is missing, ``str`` is used. + `field.json_schema_extra["__dspy_field_type"]`. Negative indices are + supported (e.g., `-1` appends). If `type_` is omitted, the field's + existing `annotation` is used; if that is missing, `str` is used. Args: index (int): Insertion position within the chosen section; negatives append. @@ -373,10 +372,10 @@ def insert( type_ (type | None): Optional explicit type annotation. Returns: - type[Signature]: A new Signature class with the field inserted. + A new Signature class with the field inserted. Raises: - ValueError: If ``index`` falls outside the valid range for the chosen section. + ValueError: If `index` falls outside the valid range for the chosen section. """ # It's possible to set the type as annotation=type in pydantic.Field(...) # But this may be annoying for users, so we allow them to pass the type From 118904b44772d355d45f4f0681232af0a93039eb Mon Sep 17 00:00:00 2001 From: Netsanet Gebremedhin Date: Sat, 18 Oct 2025 17:18:11 -0400 Subject: [PATCH 07/12] docs(signature): add runnable code examples for prepend, append, insert, and delete; completes Signature class docstrings (fixes issues #8942 and #8944) --- dspy/signatures/signature.py | 72 +++++++++++++++++++++++++++++++++--- 1 file changed, 67 insertions(+), 5 deletions(-) diff --git a/dspy/signatures/signature.py b/dspy/signatures/signature.py index dcdf62c441..13bdd5d329 100644 --- a/dspy/signatures/signature.py +++ b/dspy/signatures/signature.py @@ -258,12 +258,12 @@ class using the current fields and the provided ``instructions``. and whose instructions equal `instructions`. Example: - ``` - from dspy.signatures import Signature, InputField, OutputField + ```python + import dspy - class MySig(Signature): - input_text: str = InputField(desc="Input text") - output_text: str = OutputField(desc="Output text") + class MySig(dspy.Signature): + input_text: str = dspy.InputField(desc="Input text") + output_text: str = dspy.OutputField(desc="Output text") NewSig = MySig.with_instructions("Translate to French.") assert NewSig is not MySig @@ -312,6 +312,19 @@ def prepend(cls, name, field, type_=None) -> type["Signature"]: Returns: A new `Signature` class with the field inserted first. + + Example: + ```python + import dspy + + class MySig(dspy.Signature): + input_text: str = dspy.InputField(desc="Input sentence") + output_text: str = dspy.OutputField(desc="Translated sentence") + + NewSig = MySig.prepend("context", dspy.InputField(desc="Context for translation")) + print(list(NewSig.fields.keys())) + assert NewSig is not MySig + ``` """ return cls.insert(0, name, field, type_) @@ -332,6 +345,20 @@ def append(cls, name, field, type_=None) -> type["Signature"]: Returns: A new Signature class with the field appended. + + Example: + ```python + import dspy + + class MySig(dspy.Signature): + input_text: str = dspy.InputField(desc="Input sentence") + output_text: str = dspy.OutputField(desc="Translated sentence") + + NewSig = MySig.append("confidence", dspy.OutputField(desc="Translation confidence")) + print(list(NewSig.fields.keys())) + + assert NewSig is not MySig + ``` """ return cls.insert(-1, name, field, type_) @@ -347,6 +374,24 @@ def delete(cls, name) -> type["Signature"]: Returns: A new Signature class with the field removed (or unchanged if the field was absent). + + Example: + ```python + import dspy + + class MySig(dspy.Signature): + input_text: str = dspy.InputField(desc="Input sentence") + temp_field: str = dspy.InputField(desc="Temporary debug field") + output_text: str = dspy.OutputField(desc="Translated sentence") + + NewSig = MySig.delete("temp_field") + print(list(NewSig.fields.keys())) + + Unchanged = NewSig.delete("nonexistent") + print(list(Unchanged.fields.keys())) + + assert NewSig is not MySig + ``` """ fields = dict(cls.fields) @@ -376,6 +421,23 @@ def insert( Raises: ValueError: If `index` falls outside the valid range for the chosen section. + + Example: + ```python + import dspy + + class MySig(dspy.Signature): + input_text: str = dspy.InputField(desc="Input sentence") + output_text: str = dspy.OutputField(desc="Translated sentence") + + NewSig = MySig.insert(0, "context", dspy.InputField(desc="Context for translation")) + print(list(NewSig.fields.keys())) + + NewSig2 = NewSig.insert(-1, "confidence", dspy.OutputField(desc="Translation confidence")) + print(list(NewSig2.fields.keys())) + + assert NewSig2 is not MySig + ``` """ # It's possible to set the type as annotation=type in pydantic.Field(...) # But this may be annoying for users, so we allow them to pass the type From d8138f82f68f7d5b5da4ed4e60bfd4b89a425b59 Mon Sep 17 00:00:00 2001 From: Netsanet Gebremedhin Date: Mon, 20 Oct 2025 19:59:20 -0400 Subject: [PATCH 08/12] Removed double backticks. --- dspy/signatures/signature.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dspy/signatures/signature.py b/dspy/signatures/signature.py index 13bdd5d329..871c0f072f 100644 --- a/dspy/signatures/signature.py +++ b/dspy/signatures/signature.py @@ -248,7 +248,7 @@ def with_instructions(cls, instructions: str) -> type["Signature"]: """Return a new Signature class with identical fields and new instructions. This method does not mutate `cls`. It constructs a fresh Signature - class using the current fields and the provided ``instructions``. + class using the current fields and the provided `instructions`. Args: instructions (str): Instruction text to attach to the new signature. From 6da3e9c91b911a16f64628d53ef52f8a0538656f Mon Sep 17 00:00:00 2001 From: Netsanet Gebremedhin Date: Mon, 20 Oct 2025 20:57:57 -0400 Subject: [PATCH 09/12] Removes the unnecessary assert statements. --- dspy/signatures/signature.py | 7 ------- 1 file changed, 7 deletions(-) diff --git a/dspy/signatures/signature.py b/dspy/signatures/signature.py index 871c0f072f..6d67ba9912 100644 --- a/dspy/signatures/signature.py +++ b/dspy/signatures/signature.py @@ -323,7 +323,6 @@ class MySig(dspy.Signature): NewSig = MySig.prepend("context", dspy.InputField(desc="Context for translation")) print(list(NewSig.fields.keys())) - assert NewSig is not MySig ``` """ return cls.insert(0, name, field, type_) @@ -356,8 +355,6 @@ class MySig(dspy.Signature): NewSig = MySig.append("confidence", dspy.OutputField(desc="Translation confidence")) print(list(NewSig.fields.keys())) - - assert NewSig is not MySig ``` """ return cls.insert(-1, name, field, type_) @@ -389,8 +386,6 @@ class MySig(dspy.Signature): Unchanged = NewSig.delete("nonexistent") print(list(Unchanged.fields.keys())) - - assert NewSig is not MySig ``` """ fields = dict(cls.fields) @@ -435,8 +430,6 @@ class MySig(dspy.Signature): NewSig2 = NewSig.insert(-1, "confidence", dspy.OutputField(desc="Translation confidence")) print(list(NewSig2.fields.keys())) - - assert NewSig2 is not MySig ``` """ # It's possible to set the type as annotation=type in pydantic.Field(...) From e178fed8c5aea6257c193f1e7f788db7b6a7c50a Mon Sep 17 00:00:00 2001 From: Netsanet Gebremedhin Date: Mon, 20 Oct 2025 20:59:39 -0400 Subject: [PATCH 10/12] Fixes the unintentional formatting change. --- dspy/signatures/signature.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/dspy/signatures/signature.py b/dspy/signatures/signature.py index 6d67ba9912..59797dd2eb 100644 --- a/dspy/signatures/signature.py +++ b/dspy/signatures/signature.py @@ -395,9 +395,7 @@ class MySig(dspy.Signature): return Signature(fields, cls.instructions) @classmethod - def insert( - cls, index: int, name: str, field, type_: type | None = None - ) -> type["Signature"]: + def insert(cls, index: int, name: str, field, type_: type | None = None) -> type["Signature"]: """Insert a field at a specific position among inputs or outputs. The target section (inputs vs outputs) is determined by From b97cb635db02f0240ff6c402949bb02c4de1b642 Mon Sep 17 00:00:00 2001 From: Netsanet Gebremedhin Date: Tue, 21 Oct 2025 12:51:46 -0400 Subject: [PATCH 11/12] docs(signature): clarify insert docstring per reviewer feedback --- dspy/signatures/signature.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/dspy/signatures/signature.py b/dspy/signatures/signature.py index 59797dd2eb..43912af77c 100644 --- a/dspy/signatures/signature.py +++ b/dspy/signatures/signature.py @@ -398,9 +398,7 @@ class MySig(dspy.Signature): def insert(cls, index: int, name: str, field, type_: type | None = None) -> type["Signature"]: """Insert a field at a specific position among inputs or outputs. - The target section (inputs vs outputs) is determined by - `field.json_schema_extra["__dspy_field_type"]`. Negative indices are - supported (e.g., `-1` appends). If `type_` is omitted, the field's + Negative indices are supported (e.g., `-1` appends). If `type_` is omitted, the field's existing `annotation` is used; if that is missing, `str` is used. Args: From 95c7c86a0316661a73ff74f42e4ececcb56288e5 Mon Sep 17 00:00:00 2001 From: chenmoneygithub Date: Tue, 21 Oct 2025 13:31:22 -0700 Subject: [PATCH 12/12] nit --- dspy/signatures/signature.py | 25 ++++++++++--------------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/dspy/signatures/signature.py b/dspy/signatures/signature.py index 43912af77c..b490aed64c 100644 --- a/dspy/signatures/signature.py +++ b/dspy/signatures/signature.py @@ -302,13 +302,11 @@ def with_updated_fields(cls, name: str, type_: type | None = None, **kwargs: dic def prepend(cls, name, field, type_=None) -> type["Signature"]: """Insert a field at index 0 of the `inputs` or `outputs` section. - Equivalent to `insert(0, name, field, type_)`. The section (inputs vs outputs) - is determined by `field.json_schema_extra["__dspy_field_type"]`. - Args: name (str): Field name to add. field: `InputField` or `OutputField` instance to insert. - type_ (type | None): Optional explicit type annotation. + type_ (type | None): Optional explicit type annotation. If `type_` is `None`, the effective type is + resolved by `insert`. Returns: A new `Signature` class with the field inserted first. @@ -331,16 +329,11 @@ class MySig(dspy.Signature): def append(cls, name, field, type_=None) -> type["Signature"]: """Insert a field at the end of the `inputs` or `outputs` section. - Equivalent to `insert(-1, name, field, type_)`. The section (inputs vs - outputs) is determined by `field.json_schema_extra["__dspy_field_type"]`. - - If `type_` is `None`, the effective type is resolved by `insert`. - - Args: name (str): Field name to add. field: `InputField` or `OutputField` instance to insert. - type_ (type | None): Optional explicit type annotation. + type_ (type | None): Optional explicit type annotation. If `type_` is `None`, the effective type is + resolved by `insert`. Returns: A new Signature class with the field appended. @@ -369,8 +362,7 @@ def delete(cls, name) -> type["Signature"]: name (str): Field name to remove. Returns: - A new Signature class with the field removed (or unchanged - if the field was absent). + A new Signature class with the field removed (or unchanged if the field was absent). Example: ```python @@ -384,6 +376,7 @@ class MySig(dspy.Signature): NewSig = MySig.delete("temp_field") print(list(NewSig.fields.keys())) + # No error is raised if the field is not present Unchanged = NewSig.delete("nonexistent") print(list(Unchanged.fields.keys())) ``` @@ -412,7 +405,7 @@ def insert(cls, index: int, name: str, field, type_: type | None = None) -> type Raises: ValueError: If `index` falls outside the valid range for the chosen section. - + Example: ```python import dspy @@ -567,7 +560,9 @@ class MyType: # program of thought and teleprompters, so we just silently default to string. if type_ is None: type_ = str - if not isinstance(type_, (type, typing._GenericAlias, types.GenericAlias, typing._SpecialForm, types.UnionType)): + if not isinstance( + type_, (type, typing._GenericAlias, types.GenericAlias, typing._SpecialForm, types.UnionType) + ): raise ValueError(f"Field types must be types, but received: {type_} of type {type(type_)}.") if not isinstance(field, FieldInfo): raise ValueError(f"Field values must be Field instances, but received: {field}.")