6
6
import base64
7
7
import os
8
8
import subprocess
9
- from datetime import datetime
9
+ from datetime import datetime , timedelta
10
10
from enum import StrEnum
11
11
from functools import partial
12
12
from pathlib import PosixPath
13
13
from typing import cast
14
14
15
+ import httpx
15
16
import streamlit as st
16
- from anthropic import APIResponse
17
+ from anthropic import RateLimitError
17
18
from anthropic .types import (
18
19
TextBlock ,
19
20
)
20
- from anthropic .types .beta import BetaMessage , BetaTextBlock , BetaToolUseBlock
21
+ from anthropic .types .beta import BetaTextBlock , BetaToolUseBlock
21
22
from anthropic .types .tool_use_block import ToolUseBlock
22
23
from streamlit .delta_generator import DeltaGenerator
23
24
@@ -186,8 +187,8 @@ def _reset_api_provider():
186
187
)
187
188
188
189
# 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 )
191
192
192
193
# render past chats
193
194
if new_message :
@@ -278,16 +279,20 @@ def save_to_storage(filename: str, data: str) -> None:
278
279
279
280
280
281
def _api_response_callback (
281
- response : APIResponse [BetaMessage ],
282
+ request : httpx .Request ,
283
+ response : httpx .Response | object | None ,
284
+ error : Exception | None ,
282
285
tab : DeltaGenerator ,
283
- response_state : dict [str , APIResponse [ BetaMessage ]],
286
+ response_state : dict [str , tuple [ httpx . Request , httpx . Response | object | None ]],
284
287
):
285
288
"""
286
289
Handle an API response by storing it to state and rendering it.
287
290
"""
288
291
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 )
291
296
292
297
293
298
def _tool_output_callback (
@@ -299,20 +304,38 @@ def _tool_output_callback(
299
304
300
305
301
306
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 ,
303
311
):
304
312
"""Render an API response to a streamlit tab"""
305
313
with tab :
306
314
with st .expander (f"Request/Response ({ response_id } )" ):
307
315
newline = "\n \n "
308
316
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 ())} "
314
318
)
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:" )
316
339
317
340
318
341
def _render_message (
0 commit comments