Skip to content

Commit

Permalink
Merge pull request #645 from guardrails-ai/history-nomenclature
Browse files Browse the repository at this point in the history
Update History Nomenclature
  • Loading branch information
zsimjee authored Mar 15, 2024
2 parents 9f1bc17 + 89be7c7 commit 59a00ba
Show file tree
Hide file tree
Showing 17 changed files with 224 additions and 124 deletions.
67 changes: 50 additions & 17 deletions guardrails/classes/history/call.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from rich.panel import Panel
from rich.pretty import pretty_repr
from rich.tree import Tree
from typing_extensions import deprecated

from guardrails.classes.generic.stack import Stack
from guardrails.classes.history.call_inputs import CallInputs
Expand Down Expand Up @@ -198,10 +199,19 @@ def parsed_outputs(self) -> Stack[Union[str, Dict]]:
return Stack(*[i.outputs.parsed_output for i in self.iterations])

@property
@deprecated(
"""'Call.validation_output' is deprecated and will be removed in \
versions 0.5.0 and beyond. Use 'validation_response' instead."""
)
def validation_output(self) -> Optional[Union[str, Dict, ReAsk]]:
"""The cumulative validation output across all current iterations.
return self.validation_response

@property
def validation_response(self) -> Optional[Union[str, Dict, ReAsk]]:
"""The aggregated responses from the validation process across all
iterations within the current call.
Could contain ReAsks.
This value could contain ReAsks.
"""
number_of_iterations = self.iterations.length

Expand All @@ -216,43 +226,66 @@ def validation_output(self) -> Optional[Union[str, Dict, ReAsk]]:
if (
self.inputs.full_schema_reask
or number_of_iterations < 2
or isinstance(self.iterations.last.validation_output, ReAsk) # type: ignore
or isinstance(self.iterations.last.validation_output, str) # type: ignore
or isinstance(
self.iterations.last.validation_response, ReAsk # type: ignore
)
or isinstance(self.iterations.last.validation_response, str) # type: ignore
):
return self.iterations.last.validation_output # type: ignore
return self.iterations.last.validation_response # type: ignore

current_index = 1
# We've already established that there are iterations,
# hence the type ignores
merged_validation_output = (
self.iterations.first.validation_output # type: ignore
merged_validation_responses = (
self.iterations.first.validation_response # type: ignore
)
while current_index < number_of_iterations:
current_validation_output = self.iterations.at(
current_index
).validation_output # type: ignore
merged_validation_output = merge_reask_output(
merged_validation_output, current_validation_output
).validation_response # type: ignore
merged_validation_responses = merge_reask_output(
merged_validation_responses, current_validation_output
)
current_index = current_index + 1

return merged_validation_output
return merged_validation_responses

@property
def fixed_output(self) -> Optional[Union[str, Dict]]:
"""The cumulative validation output across all current iterations with
any automatic fixes applied."""
return sub_reasks_with_fixed_values(self.validation_output)
"""The cumulative output from the validation process across all current
iterations with any automatic fixes applied.
Could still contain ReAsks if a fix was not available.
"""
return sub_reasks_with_fixed_values(self.validation_response)

@property
def validated_output(self) -> Optional[Union[str, Dict]]:
"""The output from the LLM after undergoing validation.
def guarded_output(self) -> Optional[Union[str, Dict]]:
"""The complete validated output after all stages of validation are
completed.
This property contains the aggregate validated output after all
validation stages have been completed. Some values in the
validated output may be "fixed" values that were corrected
during validation.
This will only have a value if the Guard is in a passing state.
"""
if self.status == pass_status:
return self.fixed_output

@property
@deprecated(
"""'Call.validated_output' is deprecated and will be removed in \
versions 0.5.0 and beyond. Use 'guarded_output' instead."""
)
def validated_output(self) -> Optional[Union[str, Dict]]:
"""The output from the LLM after undergoing validation.
This will only have a value if the Guard is in a passing state.
"""
return self.guarded_output

@property
def reasks(self) -> Stack[ReAsk]:
"""Reasks generated during validation that could not be automatically
Expand Down Expand Up @@ -355,7 +388,7 @@ def tree(self) -> Tree:
:-1
]
validated_outcome_panel = Panel(
pretty_repr(self.validated_output),
pretty_repr(self.guarded_output),
title="Validated Output",
style="on #F0FFF0",
)
Expand Down
39 changes: 35 additions & 4 deletions guardrails/classes/history/iteration.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from rich.panel import Panel
from rich.pretty import pretty_repr
from rich.table import Table
from typing_extensions import deprecated

from guardrails.classes.generic.stack import Stack
from guardrails.classes.history.inputs import Inputs
Expand Down Expand Up @@ -76,21 +77,51 @@ def parsed_output(self) -> Optional[Union[str, Dict]]:
return self.outputs.parsed_output

@property
def validation_response(self) -> Optional[Union[ReAsk, str, Dict]]:
"""The response from a single stage of validation.
Validation response is the output of a single stage of validation
and could be a combination of valid output and reasks.
Note that a Guard may run validation multiple times if reasks occur.
To access the final output after all steps of validation are completed,
check out `Call.guarded_output`."
"""
return self.outputs.validation_response

@property
@deprecated(
"""'Iteration.validation_output' is deprecated and will be removed in \
versions 0.5.0 and beyond. Use 'validation_response' instead."""
)
def validation_output(self) -> Optional[Union[ReAsk, str, Dict]]:
"""The output from the validation process.
Could be a combination of valid output and ReAsks
"""
return self.outputs.validation_output
return self.validation_response

@property
def guarded_output(self) -> Optional[Union[str, Dict]]:
"""Any valid values after undergoing validation.
Some values in the validated output may be "fixed" values that
were corrected during validation. This property may be a partial
structure if field level reasks occur.
"""
return self.outputs.guarded_output

@property
@deprecated(
"""'Iteration.validated_output' is deprecated and will be removed in \
versions 0.5.0 and beyond. Use 'guarded_output' instead."""
)
def validated_output(self) -> Optional[Union[str, Dict]]:
"""The valid output from the LLM after undergoing validation.
Could be only a partial structure if field level reasks occur.
Could contain fixed values.
"""
return self.outputs.validated_output
return self.outputs.guarded_output

@property
def reasks(self) -> Sequence[ReAsk]:
Expand Down Expand Up @@ -167,7 +198,7 @@ def create_msg_history_table(
self.raw_output or "", title="Raw LLM Output", style="on #F5F5DC"
),
Panel(
pretty_repr(self.validation_output),
pretty_repr(self.validation_response),
title="Validated Output",
style="on #F0FFF0",
),
Expand All @@ -184,7 +215,7 @@ def create_msg_history_table(
self.raw_output or "", title="Raw LLM Output", style="on #F5F5DC"
),
Panel(
pretty_repr(self.validation_output),
pretty_repr(self.validation_response),
title="Validated Output",
style="on #F0FFF0",
),
Expand Down
38 changes: 28 additions & 10 deletions guardrails/classes/history/outputs.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from typing import Dict, List, Optional, Sequence, Union

from pydantic import Field
from typing_extensions import deprecated

from guardrails.constants import error_status, fail_status, not_run_status, pass_status
from guardrails.utils.llm_response import LLMResponse
Expand All @@ -22,13 +23,14 @@ class Outputs(ArbitraryModel):
"as it was passed into validation.",
default=None,
)
validation_output: Optional[Union[str, ReAsk, Dict]] = Field(
description="The output from the validation process.", default=None
validation_response: Optional[Union[str, ReAsk, Dict]] = Field(
description="The response from the validation process.", default=None
)
validated_output: Optional[Union[str, Dict]] = Field(
description="The valid output after validation."
"Could be only a partial structure if field level reasks occur."
"Could contain fixed values.",
guarded_output: Optional[Union[str, Dict]] = Field(
description="""Any valid values after undergoing validation.
Some values may be "fixed" values that were corrected during validation.
This property may be a partial structure if field level reasks occur.""",
default=None,
)
reasks: Sequence[ReAsk] = Field(
Expand All @@ -54,8 +56,8 @@ def _all_empty(self) -> bool:
return (
self.llm_response_info is None
and self.parsed_output is None
and self.validation_output is None
and self.validated_output is None
and self.validation_response is None
and self.guarded_output is None
and len(self.reasks) == 0
and len(self.validator_logs) == 0
and self.error is None
Expand Down Expand Up @@ -93,8 +95,24 @@ def status(self) -> str:
return error_status
elif not all_reasks_have_fixes:
return fail_status
elif self.validated_output is None and isinstance(
self.validation_output, ReAsk
elif self.guarded_output is None and isinstance(
self.validation_response, ReAsk
):
return fail_status
return pass_status

@property
@deprecated(
"""'Outputs.validation_output' is deprecated and will be removed in \
versions 0.5.0 and beyond. Use 'validation_response' instead."""
)
def validation_output(self) -> Optional[Union[str, ReAsk, Dict]]:
return self.validation_response

@property
@deprecated(
"""'Outputs.validated_output' is deprecated and will be removed in \
versions 0.5.0 and beyond. Use 'guarded_output' instead."""
)
def validated_output(self) -> Optional[Union[str, ReAsk, Dict]]:
return self.guarded_output
4 changes: 2 additions & 2 deletions guardrails/classes/validation_outcome.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,11 +51,11 @@ class ValidationOutcome(ArbitraryModel, Generic[OT]):
def from_guard_history(cls, call: Call):
"""Create a ValidationOutcome from a history Call object."""
last_iteration = call.iterations.last or Iteration()
last_output = last_iteration.validation_output or last_iteration.parsed_output
last_output = last_iteration.validation_response or last_iteration.parsed_output
validation_passed = call.status == pass_status
reask = last_output if isinstance(last_output, ReAsk) else None
error = call.error
output = cast(OT, call.validated_output)
output = cast(OT, call.guarded_output)
return cls(
raw_llm_output=call.raw_outputs.last,
validated_output=output,
Expand Down
26 changes: 13 additions & 13 deletions guardrails/run.py
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,7 @@ def __call__(self, call_log: Call, prompt_params: Optional[Dict] = None) -> Call
msg_history,
) = self.prepare_to_loop(
iteration.reasks,
call_log.validation_output,
call_log.validation_response,
output_schema,
prompt_params=prompt_params,
include_instructions=include_instructions,
Expand Down Expand Up @@ -291,13 +291,13 @@ def step(
validated_output = self.validate(
iteration, index, parsed_output, output_schema
)
iteration.outputs.validation_output = validated_output
iteration.outputs.validation_response = validated_output

# Introspect: inspect validated output for reasks.
reasks, valid_output = self.introspect(
index, validated_output, output_schema
)
iteration.outputs.validated_output = valid_output
iteration.outputs.guarded_output = valid_output

iteration.outputs.reasks = reasks

Expand All @@ -323,7 +323,7 @@ def validate_msg_history(
validated_msg_history = msg_history_schema.validate(
iteration, msg_str, self.metadata, disable_tracer=self._disable_tracer
)
iteration.outputs.validation_output = validated_msg_history
iteration.outputs.validation_response = validated_msg_history
if isinstance(validated_msg_history, ReAsk):
raise ValidationError(
f"Message history validation failed: " f"{validated_msg_history}"
Expand Down Expand Up @@ -366,7 +366,7 @@ def validate_prompt(
self.metadata,
disable_tracer=self._disable_tracer,
)
iteration.outputs.validation_output = validated_prompt
iteration.outputs.validation_response = validated_prompt
if validated_prompt is None:
raise ValidationError("Prompt validation failed")
if isinstance(validated_prompt, ReAsk):
Expand All @@ -390,7 +390,7 @@ def validate_instructions(
self.metadata,
disable_tracer=self._disable_tracer,
)
iteration.outputs.validation_output = validated_instructions
iteration.outputs.validation_response = validated_instructions
if validated_instructions is None:
raise ValidationError("Instructions validation failed")
if isinstance(validated_instructions, ReAsk):
Expand Down Expand Up @@ -718,7 +718,7 @@ async def async_run(
msg_history,
) = self.prepare_to_loop(
iteration.reasks,
call_log.validation_output,
call_log.validation_response,
output_schema,
prompt_params=prompt_params,
)
Expand Down Expand Up @@ -816,13 +816,13 @@ async def async_step(
validated_output = await self.async_validate(
iteration, index, parsed_output, output_schema
)
iteration.outputs.validation_output = validated_output
iteration.outputs.validation_response = validated_output

# Introspect: inspect validated output for reasks.
reasks, valid_output = self.introspect(
index, validated_output, output_schema
)
iteration.outputs.validated_output = valid_output
iteration.outputs.guarded_output = valid_output

iteration.outputs.reasks = reasks

Expand Down Expand Up @@ -975,7 +975,7 @@ async def async_prepare(
validated_prompt = await prompt_schema.async_validate(
iteration, prompt.source, self.metadata
)
iteration.outputs.validation_output = validated_prompt
iteration.outputs.validation_response = validated_prompt
if validated_prompt is None:
raise ValidationError("Prompt validation failed")
if isinstance(validated_prompt, ReAsk):
Expand All @@ -994,7 +994,7 @@ async def async_prepare(
validated_instructions = await instructions_schema.async_validate(
iteration, instructions.source, self.metadata
)
iteration.outputs.validation_output = validated_instructions
iteration.outputs.validation_response = validated_instructions
if validated_instructions is None:
raise ValidationError("Instructions validation failed")
if isinstance(validated_instructions, ReAsk):
Expand Down Expand Up @@ -1194,8 +1194,8 @@ def step(
# Finally, add to logs
iteration.outputs.raw_output = fragment
iteration.outputs.parsed_output = parsed_fragment
iteration.outputs.validation_output = validated_fragment
iteration.outputs.validated_output = valid_op
iteration.outputs.validation_response = validated_fragment
iteration.outputs.guarded_output = valid_op

def get_chunk_text(self, chunk: Any, api: Union[PromptCallableBase, None]) -> str:
"""Get the text from a chunk."""
Expand Down
2 changes: 1 addition & 1 deletion guardrails/utils/misc.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ def generate_test_artifacts(
f.write(llm_output or "")

# Save the validated response.
validated_output = logs.validated_output
validated_output = logs.guarded_output
with open(
os.path.join(artifact_dir, f"validated_response_{on_fail_type}{ext}.py"),
"w",
Expand Down
Loading

0 comments on commit 59a00ba

Please sign in to comment.