fix: Add API token input and improve file uploader
Browse files**API Token Support:**
- Add password-protected input field for HuggingFace API token
- Display clear instructions with link to get token
- Check token presence before translation attempts
- Support both user-provided tokens and Space secrets
- Better error messages for missing/invalid tokens
**File Uploader Improvements:**
- Add 20MB file size limit with validation
- Improve error handling with descriptive messages
- Add file extension preservation for temp files
- Auto-cleanup of temporary files after loading
- Better success/warning messages
**UX Enhancements:**
- Clear visual sections with dividers
- Emoji indicators for status
- Direct links to HF token settings
- Informative help text
This resolves the "api_key required" error and improves file upload reliability.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- apertus_ui.py +82 -28
|
@@ -116,26 +116,66 @@ selected_lang = st.selectbox("Language / Langue / Idioma",
|
|
| 116 |
# Sidebar for Coptic tools
|
| 117 |
with st.sidebar:
|
| 118 |
st.header("Coptic Tools")
|
| 119 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 120 |
# Lexicon file uploader
|
| 121 |
-
|
| 122 |
-
|
| 123 |
-
|
|
|
|
|
|
|
|
|
|
| 124 |
|
| 125 |
# Load lexicon
|
| 126 |
if lexicon_file:
|
| 127 |
-
|
| 128 |
-
|
| 129 |
-
|
| 130 |
-
|
| 131 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 132 |
else:
|
| 133 |
# Try to load the comprehensive lexicon if available
|
| 134 |
comprehensive_lexicon_path = "Comprehensive_Coptic_Lexicon-v1.2-2020.xml"
|
| 135 |
if os.path.exists(comprehensive_lexicon_path):
|
| 136 |
coptic_lexicon = load_coptic_lexicon(comprehensive_lexicon_path)
|
| 137 |
if coptic_lexicon:
|
| 138 |
-
st.info(f"Loaded Comprehensive Coptic Lexicon: {len(coptic_lexicon)} entries")
|
| 139 |
else:
|
| 140 |
coptic_lexicon = {}
|
| 141 |
else:
|
|
@@ -231,20 +271,22 @@ with st.sidebar:
|
|
| 231 |
# This is much faster and doesn't require GPU
|
| 232 |
MODEL_NAME = "swiss-ai/Apertus-8B-Instruct-2509"
|
| 233 |
|
| 234 |
-
|
| 235 |
-
|
| 236 |
-
"""Initialize HuggingFace Inference API client"""
|
| 237 |
try:
|
| 238 |
-
|
| 239 |
-
|
| 240 |
-
|
| 241 |
-
|
| 242 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 243 |
except Exception as e:
|
| 244 |
-
st.
|
| 245 |
-
return
|
| 246 |
-
|
| 247 |
-
inference_client = get_inference_client()
|
| 248 |
|
| 249 |
# Chat interface
|
| 250 |
if "messages" not in st.session_state:
|
|
@@ -257,10 +299,22 @@ for message in st.session_state.messages:
|
|
| 257 |
|
| 258 |
# User input
|
| 259 |
if prompt := st.chat_input("Type your message..."):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 260 |
# Add Coptic-specific prompt prefix if applicable
|
| 261 |
if selected_lang in ['cop', 'cop-sa', 'cop-bo'] and 'analysis_type' in locals():
|
| 262 |
full_prompt = f"{COPTIC_PROMPTS[analysis_type]} {prompt}"
|
| 263 |
-
|
| 264 |
# Add lexicon context for lexicon lookup
|
| 265 |
if analysis_type == 'lexicon_lookup' and coptic_lexicon:
|
| 266 |
words_in_prompt = prompt.split()
|
|
@@ -268,17 +322,17 @@ if prompt := st.chat_input("Type your message..."):
|
|
| 268 |
for word in words_in_prompt:
|
| 269 |
if word in coptic_lexicon:
|
| 270 |
lexicon_matches.append(f"{word} = {coptic_lexicon[word]}")
|
| 271 |
-
|
| 272 |
if lexicon_matches:
|
| 273 |
full_prompt += f"\n\nLexicon entries found: {'; '.join(lexicon_matches)}"
|
| 274 |
else:
|
| 275 |
full_prompt = prompt
|
| 276 |
-
|
| 277 |
st.session_state.messages.append({"role": "user", "content": full_prompt})
|
| 278 |
-
|
| 279 |
with st.chat_message("user"):
|
| 280 |
st.markdown(full_prompt)
|
| 281 |
-
|
| 282 |
# Generate response using HuggingFace Inference API
|
| 283 |
with st.chat_message("assistant"):
|
| 284 |
try:
|
|
@@ -309,4 +363,4 @@ if prompt := st.chat_input("Type your message..."):
|
|
| 309 |
|
| 310 |
except Exception as e:
|
| 311 |
st.error(f"❌ Error generating response: {str(e)}")
|
| 312 |
-
st.info("💡
|
|
|
|
| 116 |
# Sidebar for Coptic tools
|
| 117 |
with st.sidebar:
|
| 118 |
st.header("Coptic Tools")
|
| 119 |
+
|
| 120 |
+
# HuggingFace API Token input
|
| 121 |
+
st.subheader("🔑 API Configuration")
|
| 122 |
+
hf_token_input = st.text_input(
|
| 123 |
+
"HuggingFace API Token",
|
| 124 |
+
type="password",
|
| 125 |
+
help="Required for Apertus-8B translation. Get your token at: https://huggingface.co/settings/tokens"
|
| 126 |
+
)
|
| 127 |
+
if hf_token_input:
|
| 128 |
+
st.success("✅ API token configured")
|
| 129 |
+
else:
|
| 130 |
+
st.warning("⚠️ Translation requires an API token")
|
| 131 |
+
st.markdown("[Get your free HF token →](https://huggingface.co/settings/tokens)")
|
| 132 |
+
|
| 133 |
+
st.divider()
|
| 134 |
+
|
| 135 |
# Lexicon file uploader
|
| 136 |
+
st.subheader("📚 Lexicon Upload")
|
| 137 |
+
lexicon_file = st.file_uploader(
|
| 138 |
+
"Upload Coptic Lexicon (optional)",
|
| 139 |
+
type=['txt', 'tsv', 'csv', 'xml'],
|
| 140 |
+
help="Supports: Text (TAB/pipe separated), XML (TEI format), CSV\nNote: Comprehensive lexicon is pre-loaded"
|
| 141 |
+
)
|
| 142 |
|
| 143 |
# Load lexicon
|
| 144 |
if lexicon_file:
|
| 145 |
+
try:
|
| 146 |
+
# Check file size (max 20MB)
|
| 147 |
+
file_size = len(lexicon_file.getvalue())
|
| 148 |
+
if file_size > 20 * 1024 * 1024:
|
| 149 |
+
st.error("❌ File too large (max 20MB)")
|
| 150 |
+
coptic_lexicon = {}
|
| 151 |
+
else:
|
| 152 |
+
# Save uploaded file temporarily
|
| 153 |
+
temp_path = f"temp_lexicon.{lexicon_file.name.split('.')[-1]}"
|
| 154 |
+
with open(temp_path, "wb") as f:
|
| 155 |
+
f.write(lexicon_file.getbuffer())
|
| 156 |
+
|
| 157 |
+
coptic_lexicon = load_coptic_lexicon(temp_path)
|
| 158 |
+
|
| 159 |
+
if coptic_lexicon:
|
| 160 |
+
st.success(f"✅ Loaded {len(coptic_lexicon)} lexicon entries from {lexicon_file.name}")
|
| 161 |
+
else:
|
| 162 |
+
st.warning("⚠️ File uploaded but no valid entries found")
|
| 163 |
+
coptic_lexicon = {}
|
| 164 |
+
|
| 165 |
+
# Clean up temp file
|
| 166 |
+
if os.path.exists(temp_path):
|
| 167 |
+
os.remove(temp_path)
|
| 168 |
+
except Exception as e:
|
| 169 |
+
st.error(f"❌ Error loading file: {str(e)}")
|
| 170 |
+
st.info("💡 Supported formats: Plain text (TAB/pipe separated), XML (TEI), CSV")
|
| 171 |
+
coptic_lexicon = {}
|
| 172 |
else:
|
| 173 |
# Try to load the comprehensive lexicon if available
|
| 174 |
comprehensive_lexicon_path = "Comprehensive_Coptic_Lexicon-v1.2-2020.xml"
|
| 175 |
if os.path.exists(comprehensive_lexicon_path):
|
| 176 |
coptic_lexicon = load_coptic_lexicon(comprehensive_lexicon_path)
|
| 177 |
if coptic_lexicon:
|
| 178 |
+
st.info(f"📚 Loaded Comprehensive Coptic Lexicon: {len(coptic_lexicon)} entries")
|
| 179 |
else:
|
| 180 |
coptic_lexicon = {}
|
| 181 |
else:
|
|
|
|
| 271 |
# This is much faster and doesn't require GPU
|
| 272 |
MODEL_NAME = "swiss-ai/Apertus-8B-Instruct-2509"
|
| 273 |
|
| 274 |
+
def get_inference_client(token=None):
|
| 275 |
+
"""Initialize HuggingFace Inference API client with provided token"""
|
|
|
|
| 276 |
try:
|
| 277 |
+
if token:
|
| 278 |
+
client = InferenceClient(token=token)
|
| 279 |
+
return client
|
| 280 |
+
else:
|
| 281 |
+
# Try to get token from Space secrets as fallback
|
| 282 |
+
if hasattr(st, 'secrets') and 'HF_TOKEN' in st.secrets:
|
| 283 |
+
client = InferenceClient(token=st.secrets['HF_TOKEN'])
|
| 284 |
+
return client
|
| 285 |
+
else:
|
| 286 |
+
return None
|
| 287 |
except Exception as e:
|
| 288 |
+
st.error(f"Error initializing inference client: {e}")
|
| 289 |
+
return None
|
|
|
|
|
|
|
| 290 |
|
| 291 |
# Chat interface
|
| 292 |
if "messages" not in st.session_state:
|
|
|
|
| 299 |
|
| 300 |
# User input
|
| 301 |
if prompt := st.chat_input("Type your message..."):
|
| 302 |
+
# Check if API token is available
|
| 303 |
+
if not hf_token_input:
|
| 304 |
+
st.error("⚠️ Please enter your HuggingFace API token in the sidebar to use translation.")
|
| 305 |
+
st.stop()
|
| 306 |
+
|
| 307 |
+
# Initialize inference client with user token
|
| 308 |
+
inference_client = get_inference_client(hf_token_input)
|
| 309 |
+
|
| 310 |
+
if not inference_client:
|
| 311 |
+
st.error("❌ Failed to initialize inference client. Please check your API token.")
|
| 312 |
+
st.stop()
|
| 313 |
+
|
| 314 |
# Add Coptic-specific prompt prefix if applicable
|
| 315 |
if selected_lang in ['cop', 'cop-sa', 'cop-bo'] and 'analysis_type' in locals():
|
| 316 |
full_prompt = f"{COPTIC_PROMPTS[analysis_type]} {prompt}"
|
| 317 |
+
|
| 318 |
# Add lexicon context for lexicon lookup
|
| 319 |
if analysis_type == 'lexicon_lookup' and coptic_lexicon:
|
| 320 |
words_in_prompt = prompt.split()
|
|
|
|
| 322 |
for word in words_in_prompt:
|
| 323 |
if word in coptic_lexicon:
|
| 324 |
lexicon_matches.append(f"{word} = {coptic_lexicon[word]}")
|
| 325 |
+
|
| 326 |
if lexicon_matches:
|
| 327 |
full_prompt += f"\n\nLexicon entries found: {'; '.join(lexicon_matches)}"
|
| 328 |
else:
|
| 329 |
full_prompt = prompt
|
| 330 |
+
|
| 331 |
st.session_state.messages.append({"role": "user", "content": full_prompt})
|
| 332 |
+
|
| 333 |
with st.chat_message("user"):
|
| 334 |
st.markdown(full_prompt)
|
| 335 |
+
|
| 336 |
# Generate response using HuggingFace Inference API
|
| 337 |
with st.chat_message("assistant"):
|
| 338 |
try:
|
|
|
|
| 363 |
|
| 364 |
except Exception as e:
|
| 365 |
st.error(f"❌ Error generating response: {str(e)}")
|
| 366 |
+
st.info("💡 Please verify your API token is valid and has not expired.")
|