Repository navigation
Add field_markers opt-out to ChatAdapter #10324
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 10 commits
0e5d550
ba6aa95
4095576
c0deadb
9393276
9fac4a0
18fd19a
c230e8b
901b2e5
42239ab
c2eff0c
3fe6a98
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -47,6 +47,7 @@ def __init__( | |
| native_response_types: list[type[type]] | None = None, | ||
| use_json_adapter_fallback: bool = True, | ||
| parallel_tool_calls: bool | None = None, | ||
| field_markers : bool = True, | ||
| ): | ||
| """ | ||
| Args: | ||
|
|
@@ -58,6 +59,10 @@ def __init__( | |
| Configuration errors, engine failures and programming bugs propagate. Defaults to True. | ||
| parallel_tool_calls: Whether to request provider-side parallel tool-call generation when native function | ||
| calling is active. If None, the adapter does not set the provider option. | ||
| field_markers: Whether to wrap each field in `[[ ## field_name ## ]]` markers in prompts and | ||
| completions. Defaults to True. Set to False to reduce token overhead — only supported for | ||
| signatures with a single output field, since markers are what let the parser separate | ||
| multiple output fields in the raw completion text | ||
| """ | ||
| super().__init__( | ||
| callbacks=callbacks, | ||
|
|
@@ -66,15 +71,24 @@ def __init__( | |
| native_response_types=native_response_types, | ||
| ) | ||
| self.use_json_adapter_fallback = use_json_adapter_fallback | ||
| self.field_markers = field_markers | ||
|
|
||
| def _validate_field_markers(self, signature: type[Signature]) -> None: | ||
| if not self.field_markers and len(signature.output_fields) > 1: | ||
| raise ValueError( | ||
| "field_markers=False is only supported for signatures with a single output field, " | ||
| f"but got {len(signature.output_fields)} output fields: " | ||
| f"{list(signature.output_fields.keys())}. Field markers are required to separate " | ||
| "multiple output fields in the raw completion text. Use JSONAdapter instead if you " | ||
| "need both low token overhead and multiple output fields." | ||
| ) | ||
|
|
||
| def _make_json_adapter_fallback(self): | ||
| from dspy.adapters.json_adapter import JSONAdapter | ||
|
|
||
| return JSONAdapter( | ||
| use_native_function_calling=self.use_native_function_calling, | ||
| parallel_tool_calls=self.parallel_tool_calls, | ||
| ) | ||
|
|
||
| def __call__( | ||
| self, | ||
| lm: BaseLM, | ||
|
|
@@ -83,34 +97,32 @@ def __call__( | |
| demos: list[dict[str, Any]], | ||
| inputs: dict[str, Any], | ||
| ) -> list[dict[str, Any]]: | ||
| with adapter_fallback_scope() as progress: | ||
| try: | ||
| return super().__call__(lm, lm_kwargs, signature, demos, inputs) | ||
| except AdapterParseError: | ||
| from dspy.adapters.json_adapter import JSONAdapter | ||
|
|
||
| if progress.emitted or isinstance(self, JSONAdapter) or not self.use_json_adapter_fallback: | ||
| raise | ||
| return self._make_json_adapter_fallback()(lm, lm_kwargs, signature, demos, inputs) | ||
|
|
||
| async def acall( | ||
| self, | ||
| processed_signature = self._call_preprocess(lm, lm_kwargs, signature, inputs) | ||
| self._validate_field_markers(processed_signature) | ||
| with adapter_fallback_scope() as progress: | ||
| try: | ||
| return super().__call__(lm, lm_kwargs, signature, demos, inputs) | ||
| except AdapterParseError: | ||
| from dspy.adapters.json_adapter import JSONAdapter | ||
| if progress.emitted or isinstance(self, JSONAdapter) or not self.use_json_adapter_fallback: | ||
| raise | ||
| return self._make_json_adapter_fallback()(lm, lm_kwargs, signature, demos, inputs) | ||
| lm: BaseLM, | ||
| lm_kwargs: dict[str, Any], | ||
| signature: type[Signature], | ||
| demos: list[dict[str, Any]], | ||
| inputs: dict[str, Any], | ||
| ) -> list[dict[str, Any]]: | ||
|
Comment on lines
110
to
115
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The fallback changes leave ArtifactsExecuted adapter parser and import check source
Previous review revision parser output
PR head parser and import failure output
|
||
| with adapter_fallback_scope() as progress: | ||
| try: | ||
| return await super().acall(lm, lm_kwargs, signature, demos, inputs) | ||
| except AdapterParseError: | ||
| from dspy.adapters.json_adapter import JSONAdapter | ||
|
|
||
| if progress.emitted or isinstance(self, JSONAdapter) or not self.use_json_adapter_fallback: | ||
| raise | ||
| return await self._make_json_adapter_fallback().acall(lm, lm_kwargs, signature, demos, inputs) | ||
|
|
||
| processed_signature = self._call_preprocess(lm, lm_kwargs, signature, inputs) | ||
| self._validate_field_markers(processed_signature) | ||
| with adapter_fallback_scope() as progress: | ||
| try: | ||
| return await super().acall(lm, lm_kwargs, signature, demos, inputs) | ||
| except AdapterParseError: | ||
| from dspy.adapters.json_adapter import JSONAdapter | ||
| if progress.emitted or isinstance(self, JSONAdapter) or not self.use_json_adapter_fallback: | ||
| raise | ||
| return await self._make_json_adapter_fallback().acall(lm, lm_kwargs, signature, demos, inputs) | ||
| def format_field_description(self, signature: type[Signature]) -> str: | ||
| return ( | ||
| f"Your input fields are:\n{get_field_description_string(signature.input_fields)}\n" | ||
|
|
@@ -123,8 +135,11 @@ def format_field_structure(self, signature: type[Signature]) -> str: | |
| `[[ ## field_name ## ]]`. An arbitrary field `completed` ([[ ## completed ## ]]) is added to the end of the | ||
| output fields section to indicate the end of the output fields. | ||
| """ | ||
| parts = [] | ||
| parts.append("All interactions will be structured in the following way, with the appropriate values filled in.") | ||
| if not self.field_markers: | ||
| parts = [] | ||
| parts.append(format_signature_fields_for_instructions(signature.input_fields)) | ||
| parts.append(format_signature_fields_for_instructions(signature.output_fields)) | ||
|
greptile-apps[bot] marked this conversation as resolved.
Outdated
|
||
| return "\n\n".join(parts).strip() | ||
|
|
||
| def format_signature_fields_for_instructions(fields: dict[str, FieldInfo]): | ||
| return self.format_field_with_value( | ||
|
|
@@ -134,6 +149,7 @@ def format_signature_fields_for_instructions(fields: dict[str, FieldInfo]): | |
| }, | ||
| ) | ||
|
|
||
| parts = [] | ||
| parts.append(format_signature_fields_for_instructions(signature.input_fields)) | ||
| parts.append(format_signature_fields_for_instructions(signature.output_fields)) | ||
| parts.append("[[ ## completed ## ]]\n") | ||
|
|
@@ -157,8 +173,10 @@ def format_user_message_content( | |
| if k in inputs: | ||
| value = inputs.get(k) | ||
| formatted_field_value = format_field_value(field_info=v, value=value) | ||
| messages.append(f"[[ ## {k} ## ]]\n{formatted_field_value}") | ||
|
|
||
| if self.field_markers: | ||
| messages.append(f"[[ ## {k} ## ]]\n{formatted_field_value}") | ||
| else: | ||
| messages.append(formatted_field_value) | ||
| if main_request: | ||
| output_requirements = self.user_message_output_requirements(signature) | ||
| if output_requirements is not None: | ||
|
|
@@ -193,6 +211,12 @@ def type_info(v): | |
| else: | ||
| return "" | ||
|
|
||
| if not self.field_markers: | ||
| if not signature.output_fields: | ||
| return None | ||
| (f, v), = signature.output_fields.items() | ||
| return f"Respond with the value for `{f}`{type_info(v)}. Do not include any other text." | ||
|
Comment on lines
+213
to
+217
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
The changed Artifacts
|
||
|
|
||
| message = "Respond with the corresponding output fields, starting with the field " | ||
| message += ", then ".join(f"`[[ ## {f} ## ]]`{type_info(v)}" for f, v in signature.output_fields.items()) | ||
| message += ", and then ending with the marker for `[[ ## completed ## ]]`." | ||
|
|
@@ -210,10 +234,25 @@ def format_assistant_message_content( | |
| for k, v in signature.output_fields.items() | ||
| }, | ||
| ) | ||
| assistant_message_content += "\n\n[[ ## completed ## ]]\n" | ||
| if self.field_markers: | ||
| assistant_message_content += "\n\n[[ ## completed ## ]]\n" | ||
| return assistant_message_content | ||
|
|
||
| def parse(self, signature: type[Signature], completion: str) -> dict[str, Any]: | ||
| if not self.field_markers: | ||
| if not signature.output_fields: | ||
| return {} | ||
| (field_name, field_info), = signature.output_fields.items() | ||
|
greptile-apps[bot] marked this conversation as resolved.
|
||
| try: | ||
| return {field_name: parse_value(completion.strip(), field_info.annotation)} | ||
| except Exception as e: | ||
| raise AdapterParseError( | ||
| adapter_name="ChatAdapter", | ||
| signature=signature, | ||
| lm_response=completion, | ||
| message=f"Failed to parse field {field_name} with value {completion!r} from the LM response. Error message: {e}", | ||
| ) | ||
|
|
||
| sections = [(None, [])] | ||
|
|
||
| for line in completion.splitlines(): | ||
|
|
@@ -267,8 +306,10 @@ def format_field_with_value(self, fields_with_values: dict[FieldInfoWithName, An | |
| output = [] | ||
| for field, field_value in fields_with_values.items(): | ||
| formatted_field_value = format_field_value(field_info=field.info, value=field_value) | ||
| output.append(f"[[ ## {field.name} ## ]]\n{formatted_field_value}") | ||
|
|
||
| if self.field_markers: | ||
| output.append(f"[[ ## {field.name} ## ]]\n{formatted_field_value}") | ||
| else: | ||
| output.append(formatted_field_value) | ||
| return "\n\n".join(output).strip() | ||
|
|
||
| def format_finetune_data( | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.