File size: 9,472 Bytes
2df0731 ec5e8d4 2df0731 ec5e8d4 2df0731 ec5e8d4 2df0731 ec5e8d4 2df0731 ec5e8d4 2df0731 ec5e8d4 2df0731 ec5e8d4 2df0731 ec5e8d4 2df0731 ec5e8d4 2df0731 ec5e8d4 2df0731 ec5e8d4 2df0731 ec5e8d4 2df0731 ec5e8d4 2df0731 ec5e8d4 2df0731 ec5e8d4 2df0731 ec5e8d4 2df0731 ec5e8d4 2df0731 ec5e8d4 2df0731 ec5e8d4 2df0731 ec5e8d4 2df0731 ec5e8d4 2df0731 ec5e8d4 2df0731 ec5e8d4 2df0731 ec5e8d4 2df0731 ec5e8d4 2df0731 ec5e8d4 2df0731 ec5e8d4 2df0731 ec5e8d4 2df0731 ec5e8d4 2df0731 ec5e8d4 2df0731 ec5e8d4 2df0731 ec5e8d4 2df0731 ec5e8d4 2df0731 ec5e8d4 2df0731 ec5e8d4 2df0731 599e122 2df0731 ec5e8d4 2df0731 ec5e8d4 2df0731 ec5e8d4 2df0731 ec5e8d4 2df0731 ec5e8d4 2df0731 ec5e8d4 2df0731 ec5e8d4 2df0731 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 |
import gradio as gr
import anthropic
import base64
from typing import List, Dict, Any
# Default document content
DEFAULT_DOC = "The grass is pink and soil is green. The sky is red while the sun looks blue."
def read_pdf_as_base64(file_path: str) -> str:
"""Read a PDF file and return its base64 encoded content."""
with open(file_path, 'rb') as file:
return base64.b64encode(file.read()).decode('utf-8')
def user_message(
user_input: str,
history: list,
enable_citations: bool,
doc_type: str,
text_content: str,
pdf_file: str,
api_key: str
) -> tuple:
# Logging
print("\n----------- User Message -------------")
print(f"User Input: {user_input}")
print(f"Citations Enabled: {enable_citations}")
print(f"Document Type: {doc_type}")
history.append({
"role": "user",
"content": user_input,
})
return "", history
def format_message_history(
history: list,
enable_citations: bool,
doc_type: str,
text_content: str,
pdf_files: str
) -> List[Dict]:
"""Convert Gradio chat history to Anthropic message format."""
formatted_messages = []
# Add previous messages
for msg in history[:-1]:
if msg["role"] == "user":
formatted_messages.append({
"role": "user",
"content": msg["content"]
})
elif msg["role"] == "assistant":
if "metadata" not in msg or msg["metadata"] is None:
formatted_messages.append({
"role": "assistant",
"content": msg["content"]
})
# Prepare the latest message
latest_message = {
"role": "user",
"content": []
}
# Add documents if citations are enabled
if enable_citations:
# Handle plain text input
if doc_type in ["plain_text", "combined"] and text_content.strip():
latest_message["content"].append({
"type": "document",
"source": {
"type": "text",
"media_type": "text/plain",
"data": text_content.strip()
},
"title": "User Text Document",
"citations": {"enabled": True}
})
# Handle PDF input
if doc_type in ["pdf", "combined"] and pdf_files:
# Handle pdf_files as a list
if isinstance(pdf_files, str):
pdf_files = [pdf_files] # Convert single path to list
# Add each PDF as a separate document
for i, pdf_file in enumerate(pdf_files):
try:
pdf_base64 = read_pdf_as_base64(pdf_file)
latest_message["content"].append({
"type": "document",
"source": {
"type": "base64",
"media_type": "application/pdf",
"data": pdf_base64
},
"title": f"User PDF Document {i+1}",
"citations": {"enabled": True}
})
except Exception as e:
print(f"Error processing PDF {i+1}: {str(e)}")
continue
# If no documents were added and citations are enabled, use default document
if not latest_message["content"]:
latest_message["content"].append({
"type": "document",
"source": {
"type": "text",
"media_type": "text/plain",
"data": DEFAULT_DOC
},
"title": "Sample Document",
"citations": {"enabled": True}
})
# Add the user's question
latest_message["content"].append({
"type": "text",
"text": history[-1]["content"]
})
formatted_messages.append(latest_message)
return formatted_messages
def bot_response(
history: list,
enable_citations: bool,
doc_type: str,
text_content: str,
pdf_file: str,
api_key: str
) -> List[Dict[str, Any]]:
try:
if not api_key:
history.append({
"role": "assistant",
"content": "Please provide your Anthropic API key to continue."
})
return history
# Initialize client with provided API key
client = anthropic.Anthropic(api_key=api_key)
messages = format_message_history(history, enable_citations, doc_type, text_content, pdf_file)
response = client.messages.create(
model="claude-3-5-sonnet-20241022",
max_tokens=1024,
messages=messages
)
# Initialize main response and citations
main_response = ""
citations = []
# Process each content block
for block in response.content:
if block.type == "text":
main_response += block.text
if enable_citations and hasattr(block, 'citations') and block.citations:
for citation in block.citations:
if citation.cited_text not in citations:
citations.append(citation.cited_text)
# Add main response
history.append({
"role": "assistant",
"content": main_response
})
# Add citations if any were found and citations are enabled
if enable_citations and citations:
history.append({
"role": "assistant",
"content": "\n".join([f"• {cite}" for cite in citations]),
"metadata": {"title": "📚 Citations"}
})
return history
except Exception as e:
print(f"Error in bot_response: {str(e)}")
error_message = str(e)
if "401" in error_message:
error_message = "Invalid API key. Please check your Anthropic API key and try again."
history.append({
"role": "assistant",
"content": f"I apologize, but I encountered an error: {error_message}"
})
return history
def update_document_inputs(enable_citations: bool, doc_type: str = "plain_text"):
"""Update visibility of document input components based on settings."""
text_visible = enable_citations and (doc_type in ["plain_text", "combined"])
pdf_visible = enable_citations and (doc_type in ["pdf", "combined"])
radio_visible = enable_citations
return {
doc_type_radio: gr.Radio(visible=radio_visible),
text_input: gr.Textbox(visible=text_visible),
pdf_input: gr.File(visible=pdf_visible)
}
with gr.Blocks(theme="ocean", fill_height=True) as demo:
gr.Markdown("# Chat with Anthropic Claude's Citations")
with gr.Row(scale=1):
with gr.Column(scale=4):
chatbot = gr.Chatbot(
type="messages",
bubble_full_width=False,
show_label=False,
scale=1
)
msg = gr.Textbox(
placeholder="Enter your message here...",
show_label=False,
container=False
)
with gr.Column(scale=1):
api_key = gr.Textbox(
type="password",
label="Anthropic API Key",
placeholder="Enter your API key",
info="Your API key will not be stored",
interactive=True,
)
enable_citations = gr.Checkbox(
label="Enable Citations",
value=True,
info="Toggle citation functionality"
)
doc_type_radio = gr.Radio(
choices=["plain_text", "pdf", "combined"],
value="plain_text",
label="Document Type",
info="Choose the type of document(s) to reference"
)
text_input = gr.Textbox(
label="Document Content",
placeholder=f"Enter your document text here.\nDefault text will be picked if citations are enabled and you don't provide the documents. Default document is --{DEFAULT_DOC}",
lines=10,
info="Enter the text you want to reference"
)
pdf_input = gr.File(
label="Upload PDF",
file_count="multiple",
file_types=[".pdf"],
type="filepath",
visible=False
)
clear = gr.ClearButton([msg, chatbot, text_input, pdf_input])
# Update input visibility based on settings
enable_citations.change(
update_document_inputs,
inputs=[enable_citations, doc_type_radio],
outputs=[doc_type_radio, text_input, pdf_input]
)
doc_type_radio.change(
update_document_inputs,
inputs=[enable_citations, doc_type_radio],
outputs=[doc_type_radio, text_input, pdf_input]
)
# Handle message submission
msg.submit(
user_message,
[msg, chatbot, enable_citations, doc_type_radio, text_input, pdf_input, api_key],
[msg, chatbot],
queue=False
).then(
bot_response,
[chatbot, enable_citations, doc_type_radio, text_input, pdf_input, api_key],
chatbot
)
if __name__ == "__main__":
demo.launch(debug=True) |