Show code cell source
# %load -r :19 ../_init.py
import pathlib
import sammo
from sammo.runners import OpenAIChat
from sammo.base import Template, EvaluationScore
from sammo.components import Output, GenerateText, ForEach, Union
from sammo.extractors import ExtractRegex
from sammo.data import DataTable
import json
import requests
API_CONFIG_FILE = pathlib.Path().cwd().parent.parent / "config" / "personal.openai"
API_CONFIG = ""
if API_CONFIG_FILE.exists():
API_CONFIG = API_CONFIG_FILE
if not API_CONFIG:
raise ValueError('Please set API_CONFIG to {"api_key": "YOUR_KEY"}')
_ = sammo.setup_logger("WARNING") # we're only interested in warnings for now
Handling failures#
There are two main types of failures that can happen during the execution of a metaprompt β network failures
and processing failures.
By default, SAMMO
will retry the network request a few times before giving up.
Network request failures#
The two most common network request failures are timeouts and rejected requests (mostly due to rate limiting).
Timeout errors#
Letβs simulate a timeout error.
runner = OpenAIChat(
model_id="gpt-3.5-turbo-16k",
api_config=API_CONFIG,
cache=CACHE_FILE,
timeout=0.01,
)
Output(GenerateText("Generate a 5000 word essay about horses.")).run(
runner, progress_callback=False
)
09:14:34,419: TimeoutError: {'messages': [{'role': 'user', 'content': 'Generate a 5000 word essay about horses.'}], 'max_tokens': None, 'temperature': 0}
09:14:34,438: TimeoutError: {'messages': [{'role': 'user', 'content': 'Generate a 5000 word essay about horses.'}], 'max_tokens': None, 'temperature': 0}
09:14:34,438: Failed to generate text: TimeoutError()
+---------+-------------------------------------------------------------+
| input | output |
+=========+=============================================================+
| None | TimeoutResult(value='TimeoutError()'..., parent=TextResult) |
+---------+-------------------------------------------------------------+
Constants: None
Here, we can see that SAMMO
re-tried it once and then returned a TimeoutResult
.
To customize how these are handled, you can specify the following parameters:
timeout
: The timeout for the network request. Defaults to 60 seconds.max_timeout_retries
: The maximum number of times to retry a network request in case of a timeout. Defaults to 1.
Network Errors#
Letβs see how SAMMO
behaves under network errors.
import openai
runner = OpenAIChat(
model_id="gpt-1",
api_config=API_CONFIG,
cache=CACHE_FILE,
max_retries=1,
retry_on=(openai.error.InvalidRequestError,),
)
Output(
GenerateText("Generate a 5000 word essay about horses.", on_error="empty_result")
).run(runner, progress_callback=False)
09:14:34,615: Failed to generate text: InvalidRequestError(message='The model `gpt-1` does not exist', param=None, code='model_not_found', http_status=404, request_id=None)
+---------+-----------------------------------------------------------+
| input | output |
+=========+===========================================================+
| None | EmptyResult(value="InvalidRequestError(message='The model |
| | `gpt-1` does not exist', param=None, |
| | code='model_not_found',..., parent=TextResult) |
+---------+-----------------------------------------------------------+
Constants: None
Here, SAMMO
returns an empty result after retrying once.
Component failures#
Component failures typically are errors that occur when the code in a component cannot be run correctly. This often happens when the LLM output cannot be parsed correctly or contains the wrong number of rows
for a minibatch. By default, these failures are raised in order to have the user make an explicit decision on how
exceptions should handled, with the exception of GenerateText
which returns an empty result since those failures are very common.
Note
A little different from typical Python, SAMMO
encourages developers to not let errors bubble up but catch them where they happen. This makes it easier to locate errors in the pipeline.
To manage exceptions, you can specify the following parameter when creating certain Component
instances:
on_error
: Choose betweenraise
(default) orempty_result
(other options might be available). Ifempty_result
is chosen, the component will return anEmptyResult
object instead of raising an exception.
Note
Components that are expected to always complete have no on_error
option.
Example: Parsing error#
The input string here is invalid JSON, so after a failed parse attempt, we will get a EmptyResult
.
from sammo.extractors import ParseJSON
from sammo.base import VerbatimText
parsed = Output(ParseJSON("{[}", parse_fragments="whole", on_error="empty_result")).run(
runner, progress_callback=False
)
parsed
09:14:34,649: Error extracting from [TextResult(value='{[}'..., parent=NoneType)]: Expecting property name enclosed in double quotes: line 1 column 2 (char 1)
+---------+-----------------------------------------+
| input | output |
+=========+=========================================+
| None | EmptyResult(value=None..., parent=list) |
+---------+-----------------------------------------+
Constants: None
What to do with an EmptyResult
#
When we have empty results in the final output, it is up to the developer to decide how these cases should be handled.
A common case is to replace all empty results with a sensible default value, e.g., 0 when numbers are required.
parsed.outputs.normalized_values(on_empty=0)
[0]
Alternatively, we can simply filter them out.
parsed.outputs.nonempty_values()
[]