Skip to content

Commit e7e77b4

Browse files
committed
prettify errors
1 parent 6271405 commit e7e77b4

File tree

2 files changed

+69
-28
lines changed

2 files changed

+69
-28
lines changed

computer-use-demo/computer_use_demo/loop.py

Lines changed: 30 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,22 @@
88
from enum import StrEnum
99
from typing import Any, cast
1010

11-
from anthropic import Anthropic, AnthropicBedrock, AnthropicVertex, APIResponse
11+
import httpx
12+
from anthropic import (
13+
Anthropic,
14+
AnthropicBedrock,
15+
AnthropicVertex,
16+
APIError,
17+
APIResponseValidationError,
18+
APIStatusError,
19+
)
1220
from anthropic.types import (
1321
ToolResultBlockParam,
1422
)
1523
from anthropic.types.beta import (
1624
BetaContentBlock,
1725
BetaContentBlockParam,
1826
BetaImageBlockParam,
19-
BetaMessage,
2027
BetaMessageParam,
2128
BetaTextBlockParam,
2229
BetaToolResultBlockParam,
@@ -70,7 +77,9 @@ async def sampling_loop(
7077
messages: list[BetaMessageParam],
7178
output_callback: Callable[[BetaContentBlock], None],
7279
tool_output_callback: Callable[[ToolResult, str], None],
73-
api_response_callback: Callable[[APIResponse[BetaMessage]], None],
80+
api_response_callback: Callable[
81+
[httpx.Request, httpx.Response | object | None, Exception | None], None
82+
],
7483
api_key: str,
7584
only_n_most_recent_images: int | None = None,
7685
max_tokens: int = 4096,
@@ -102,16 +111,25 @@ async def sampling_loop(
102111
# we use raw_response to provide debug information to streamlit. Your
103112
# implementation may be able call the SDK directly with:
104113
# `response = client.messages.create(...)` instead.
105-
raw_response = client.beta.messages.with_raw_response.create(
106-
max_tokens=max_tokens,
107-
messages=messages,
108-
model=model,
109-
system=system,
110-
tools=tool_collection.to_params(),
111-
betas=["computer-use-2024-10-22"],
112-
)
114+
try:
115+
raw_response = client.beta.messages.with_raw_response.create(
116+
max_tokens=max_tokens,
117+
messages=messages,
118+
model=model,
119+
system=system,
120+
tools=tool_collection.to_params(),
121+
betas=["computer-use-2024-10-22"],
122+
)
123+
except (APIStatusError, APIResponseValidationError) as e:
124+
api_response_callback(e.request, e.response, e)
125+
return messages
126+
except APIError as e:
127+
api_response_callback(e.request, e.body, e)
128+
return messages
113129

114-
api_response_callback(cast(APIResponse[BetaMessage], raw_response))
130+
api_response_callback(
131+
raw_response.http_response.request, raw_response.http_response, None
132+
)
115133

116134
response = raw_response.parse()
117135

computer-use-demo/computer_use_demo/streamlit.py

Lines changed: 39 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -6,18 +6,19 @@
66
import base64
77
import os
88
import subprocess
9-
from datetime import datetime
9+
from datetime import datetime, timedelta
1010
from enum import StrEnum
1111
from functools import partial
1212
from pathlib import PosixPath
1313
from typing import cast
1414

15+
import httpx
1516
import streamlit as st
16-
from anthropic import APIResponse
17+
from anthropic import RateLimitError
1718
from anthropic.types import (
1819
TextBlock,
1920
)
20-
from anthropic.types.beta import BetaMessage, BetaTextBlock, BetaToolUseBlock
21+
from anthropic.types.beta import BetaTextBlock, BetaToolUseBlock
2122
from anthropic.types.tool_use_block import ToolUseBlock
2223
from streamlit.delta_generator import DeltaGenerator
2324

@@ -186,8 +187,8 @@ def _reset_api_provider():
186187
)
187188

188189
# render past http exchanges
189-
for identity, response in st.session_state.responses.items():
190-
_render_api_response(response, identity, http_logs)
190+
for identity, (request, response) in st.session_state.responses.items():
191+
_render_api_response(request, response, identity, http_logs)
191192

192193
# render past chats
193194
if new_message:
@@ -278,16 +279,20 @@ def save_to_storage(filename: str, data: str) -> None:
278279

279280

280281
def _api_response_callback(
281-
response: APIResponse[BetaMessage],
282+
request: httpx.Request,
283+
response: httpx.Response | object | None,
284+
error: Exception | None,
282285
tab: DeltaGenerator,
283-
response_state: dict[str, APIResponse[BetaMessage]],
286+
response_state: dict[str, tuple[httpx.Request, httpx.Response | object | None]],
284287
):
285288
"""
286289
Handle an API response by storing it to state and rendering it.
287290
"""
288291
response_id = datetime.now().isoformat()
289-
response_state[response_id] = response
290-
_render_api_response(response, response_id, tab)
292+
response_state[response_id] = (request, response)
293+
if error:
294+
_render_error(error)
295+
_render_api_response(request, response, response_id, tab)
291296

292297

293298
def _tool_output_callback(
@@ -299,20 +304,38 @@ def _tool_output_callback(
299304

300305

301306
def _render_api_response(
302-
response: APIResponse[BetaMessage], response_id: str, tab: DeltaGenerator
307+
request: httpx.Request,
308+
response: httpx.Response | object | None,
309+
response_id: str,
310+
tab: DeltaGenerator,
303311
):
304312
"""Render an API response to a streamlit tab"""
305313
with tab:
306314
with st.expander(f"Request/Response ({response_id})"):
307315
newline = "\n\n"
308316
st.markdown(
309-
f"`{response.http_request.method} {response.http_request.url}`{newline}{newline.join(f'`{k}: {v}`' for k, v in response.http_request.headers.items())}"
310-
)
311-
st.json(response.http_request.read().decode())
312-
st.markdown(
313-
f"`{response.http_response.status_code}`{newline}{newline.join(f'`{k}: {v}`' for k, v in response.headers.items())}"
317+
f"`{request.method} {request.url}`{newline}{newline.join(f'`{k}: {v}`' for k, v in request.headers.items())}"
314318
)
315-
st.json(response.http_response.text)
319+
st.json(request.read().decode())
320+
st.markdown("---")
321+
if isinstance(response, httpx.Response):
322+
st.markdown(
323+
f"`{response.status_code}`{newline}{newline.join(f'`{k}: {v}`' for k, v in response.headers.items())}"
324+
)
325+
st.json(response.text)
326+
else:
327+
st.write(response)
328+
329+
330+
def _render_error(error: Exception):
331+
if isinstance(error, RateLimitError):
332+
body = "You have been rate limited."
333+
if retry_after := error.response.headers.get("retry-after"):
334+
body += f" **Retry after {str(timedelta(seconds=int(retry_after)))} (HH:MM:SS).**"
335+
body += f"\n\n{error.message}"
336+
else:
337+
body = str(error)
338+
st.error(f"**{error.__class__.__name__}**\n\n{body}", icon=":material/error:")
316339

317340

318341
def _render_message(

0 commit comments

Comments
 (0)