Spaces:
Running
Running
milwright
commited on
Commit
·
7a2e6d1
1
Parent(s):
8679929
remove unused support_docs.py module
Browse files- support_docs.py +0 -401
support_docs.py
DELETED
|
@@ -1,401 +0,0 @@
|
|
| 1 |
-
"""
|
| 2 |
-
Support documentation module with accordion-style help sections
|
| 3 |
-
"""
|
| 4 |
-
|
| 5 |
-
import gradio as gr
|
| 6 |
-
from datetime import datetime
|
| 7 |
-
|
| 8 |
-
|
| 9 |
-
def create_support_docs():
|
| 10 |
-
"""Create the support documentation interface with accordion menus"""
|
| 11 |
-
|
| 12 |
-
with gr.Column():
|
| 13 |
-
gr.Markdown("# ChatUI Helper Documentation")
|
| 14 |
-
gr.Markdown("Welcome to ChatUI Helper! This tool helps you create customizable AI chat interfaces for deployment on HuggingFace Spaces.")
|
| 15 |
-
|
| 16 |
-
with gr.Accordion("📖 Quick Start Guide", open=True):
|
| 17 |
-
gr.Markdown("""
|
| 18 |
-
## Overview
|
| 19 |
-
|
| 20 |
-
1. **Configure** your assistant using templates or custom settings
|
| 21 |
-
2. **Preview** your configuration to test it works
|
| 22 |
-
3. **Generate** your deployment package
|
| 23 |
-
4. **Deploy** to HuggingFace Spaces
|
| 24 |
-
|
| 25 |
-
**Requirements:**
|
| 26 |
-
- HuggingFace account (free)
|
| 27 |
-
- OpenRouter API key
|
| 28 |
-
- Basic understanding of AI assistants
|
| 29 |
-
""")
|
| 30 |
-
|
| 31 |
-
with gr.Accordion("📝 Step 1: Configure & Preview Your Space", open=False):
|
| 32 |
-
gr.Markdown("""
|
| 33 |
-
### Configuration Tab Overview
|
| 34 |
-
|
| 35 |
-
Configure your assistant through various sections in the Configuration tab, then test it in the Preview tab before generating your deployment package.
|
| 36 |
-
""")
|
| 37 |
-
|
| 38 |
-
with gr.Accordion("Step 1a: Templates & Identity", open=False):
|
| 39 |
-
gr.Markdown("""
|
| 40 |
-
**Quick Start Templates**
|
| 41 |
-
- Choose from pre-configured academic templates (Socratic Research Chat, STEM Adventure Games, etc.)
|
| 42 |
-
- Or select "None (Custom)" for a blank slate
|
| 43 |
-
|
| 44 |
-
**Space Identity**
|
| 45 |
-
- **Assistant Name**: Give your assistant a memorable name
|
| 46 |
-
- **Tagline**: Brief description (max 60 characters) for HuggingFace YAML frontmatter
|
| 47 |
-
- **Description**: Full markdown description for the README
|
| 48 |
-
- **Theme**: Select from Gradio themes (Default, Soft, Glass, Monochrome, Base)
|
| 49 |
-
""")
|
| 50 |
-
|
| 51 |
-
with gr.Accordion("Step 1b: System Configuration", open=False):
|
| 52 |
-
gr.Markdown("""
|
| 53 |
-
**System Prompt**
|
| 54 |
-
- Define your assistant's role, context, and behavior
|
| 55 |
-
- Set guardrails and creative constraints
|
| 56 |
-
- Specify audience and terms of engagement
|
| 57 |
-
|
| 58 |
-
**Model Selection**
|
| 59 |
-
- Choose from available models (Gemini, Claude, GPT, Mistral, etc.)
|
| 60 |
-
- Or enter a custom model ID
|
| 61 |
-
|
| 62 |
-
**Language Support**
|
| 63 |
-
- Configure language through system prompt instructions
|
| 64 |
-
- Assistant can be instructed to respond exclusively in any language
|
| 65 |
-
- See "Language Learning Partner" template for Italian example
|
| 66 |
-
|
| 67 |
-
**Sampling Parameters**
|
| 68 |
-
- **Temperature** (0-2): Controls response variability
|
| 69 |
-
- **Max Tokens** (50-4096): Limits response length
|
| 70 |
-
""")
|
| 71 |
-
|
| 72 |
-
with gr.Accordion("Step 1c: Example Prompts", open=False):
|
| 73 |
-
gr.Markdown("""
|
| 74 |
-
**Example Prompts**
|
| 75 |
-
- Add 3-5 starter prompts that showcase your assistant's capabilities
|
| 76 |
-
- These appear as quick-start buttons for users
|
| 77 |
-
- Help users understand how to interact with your assistant
|
| 78 |
-
- Use the ➕/➖ buttons to add or remove examples
|
| 79 |
-
""")
|
| 80 |
-
|
| 81 |
-
with gr.Accordion("Step 1d: URL Grounding", open=False):
|
| 82 |
-
gr.Markdown("""
|
| 83 |
-
**Grounding URLs**
|
| 84 |
-
- Add up to 10 reference URLs to provide context
|
| 85 |
-
- **Primary Sources (URLs 1-2)**: Always loaded, up to 8000 characters each
|
| 86 |
-
- **Secondary Sources (URLs 3+)**: Supplementary context, up to 2500 characters each
|
| 87 |
-
- URLs are fetched and included in the system prompt
|
| 88 |
-
- Assistant will cite specific URLs when using grounded information
|
| 89 |
-
- Use the ➕/➖ buttons to manage URLs
|
| 90 |
-
""")
|
| 91 |
-
|
| 92 |
-
with gr.Accordion("Step 1e: API Configuration", open=False):
|
| 93 |
-
gr.Markdown("""
|
| 94 |
-
**Required Secrets**
|
| 95 |
-
- **API_KEY**: Your OpenRouter API key (must start with `sk-or-`)
|
| 96 |
-
- Get from: https://openrouter.ai/keys
|
| 97 |
-
- Configure in HuggingFace Space settings
|
| 98 |
-
|
| 99 |
-
**Optional Secrets**
|
| 100 |
-
- **HF_TOKEN** (Recommended): Enables configuration editor in deployed Space
|
| 101 |
-
- Get from: https://huggingface.co/settings/tokens
|
| 102 |
-
- Select "write" permissions when creating
|
| 103 |
-
- Allows updating configuration without redeployment
|
| 104 |
-
|
| 105 |
-
- **ACCESS_CODE**: Password-protect your Space
|
| 106 |
-
- Set any password value
|
| 107 |
-
- Users will need this code to access
|
| 108 |
-
- Leave unset for public access
|
| 109 |
-
""")
|
| 110 |
-
|
| 111 |
-
with gr.Accordion("Step 1f: Upload Configuration", open=False):
|
| 112 |
-
gr.Markdown("""
|
| 113 |
-
**Upload Existing Configuration**
|
| 114 |
-
- Have a previous `config.json` file?
|
| 115 |
-
- Use the "Upload Configuration" accordion
|
| 116 |
-
- Drag and drop or click to upload
|
| 117 |
-
- All settings will be restored automatically
|
| 118 |
-
""")
|
| 119 |
-
|
| 120 |
-
with gr.Accordion("Step 1g: Preview Your Assistant", open=False):
|
| 121 |
-
gr.Markdown("""
|
| 122 |
-
**Testing in Preview Tab**
|
| 123 |
-
- Click "💬 Preview Configuration" to prepare your assistant
|
| 124 |
-
- Switch to the Preview tab
|
| 125 |
-
- Test with real queries and example prompts
|
| 126 |
-
- Upload files if file upload is enabled
|
| 127 |
-
- Export conversation history as markdown
|
| 128 |
-
|
| 129 |
-
**Preview Features**
|
| 130 |
-
- Real-time API responses (requires API key in environment)
|
| 131 |
-
- Grounding context from configured URLs
|
| 132 |
-
- Language-specific responses
|
| 133 |
-
- File upload handling
|
| 134 |
-
- Conversation export
|
| 135 |
-
""")
|
| 136 |
-
|
| 137 |
-
with gr.Accordion("🗳️ Step 2: Generate & Deploy", open=False):
|
| 138 |
-
gr.Markdown("""
|
| 139 |
-
### Deployment Package & HuggingFace Space Setup
|
| 140 |
-
|
| 141 |
-
**Package Contents:**
|
| 142 |
-
- `app.py`: Complete Gradio application
|
| 143 |
-
- `requirements.txt`: Python dependencies
|
| 144 |
-
- `config.json`: Configuration backup
|
| 145 |
-
- `README.md`: Deployment instructions
|
| 146 |
-
|
| 147 |
-
**Deployment Overview:** Generate Package → Create Space → Upload Files → Configure Secrets → Monitor Build
|
| 148 |
-
""")
|
| 149 |
-
|
| 150 |
-
with gr.Accordion("Step 2a: Generate & Create Space", open=False):
|
| 151 |
-
gr.Markdown("""
|
| 152 |
-
**Generate Deployment Package**
|
| 153 |
-
1. Click "🗳️ Generate Deployment Package" in Configuration tab
|
| 154 |
-
2. Download the generated ZIP file
|
| 155 |
-
3. Extract files: `app.py`, `config.json`, `requirements.txt`, `README.md`
|
| 156 |
-
|
| 157 |
-
**Create New Space**
|
| 158 |
-
1. Go to [huggingface.co/spaces](https://huggingface.co/spaces)
|
| 159 |
-
2. Click "Create new Space"
|
| 160 |
-
3. Name your Space
|
| 161 |
-
4. Select **Gradio** SDK
|
| 162 |
-
5. Choose **Blank** template
|
| 163 |
-
6. Select hardware (CPU Basic is free)
|
| 164 |
-
7. Click "Create Space"
|
| 165 |
-
""")
|
| 166 |
-
|
| 167 |
-
with gr.Accordion("Step 2b: Upload Files", open=False):
|
| 168 |
-
gr.Markdown("""
|
| 169 |
-
**Upload Project Files**
|
| 170 |
-
1. Navigate to the **Files** tab in your new Space
|
| 171 |
-
2. Click "Add file" → "Upload files"
|
| 172 |
-
3. Select all 4 files from your extracted package:
|
| 173 |
-
- `README.md`
|
| 174 |
-
- `app.py`
|
| 175 |
-
- `config.json`
|
| 176 |
-
- `requirements.txt`
|
| 177 |
-
4. Commit directly to main branch
|
| 178 |
-
5. Your Space will automatically start building
|
| 179 |
-
""")
|
| 180 |
-
|
| 181 |
-
with gr.Accordion("Step 2c: Configure Secrets", open=False):
|
| 182 |
-
gr.Markdown("""
|
| 183 |
-
**Navigate to Settings**
|
| 184 |
-
1. Click the **Settings** tab in your Space
|
| 185 |
-
2. Find **Variables and secrets** section
|
| 186 |
-
3. Click **New secret**
|
| 187 |
-
|
| 188 |
-
**Add Required Secret**
|
| 189 |
-
- **Name**: `API_KEY` (or your configured variable name)
|
| 190 |
-
- **Value**: Your OpenRouter API key
|
| 191 |
-
- Must start with `sk-or-`
|
| 192 |
-
|
| 193 |
-
**Add Optional Secrets (if desired)**
|
| 194 |
-
- **HF_TOKEN**: Your HuggingFace token with write permissions
|
| 195 |
-
- Enables the configuration editor in your Space
|
| 196 |
-
- Get from: https://huggingface.co/settings/tokens
|
| 197 |
-
|
| 198 |
-
- **ACCESS_CODE**: Any password value
|
| 199 |
-
- Restricts access to authorized users
|
| 200 |
-
- Do NOT set an empty value
|
| 201 |
-
- Either set a code or don't create the secret
|
| 202 |
-
""")
|
| 203 |
-
|
| 204 |
-
with gr.Accordion("Step 2d: Verify & Iterate", open=False):
|
| 205 |
-
gr.Markdown("""
|
| 206 |
-
**Monitor Build Process**
|
| 207 |
-
1. Return to the **App** tab
|
| 208 |
-
2. Watch for "Building..." status
|
| 209 |
-
3. Check build logs for any errors
|
| 210 |
-
4. Wait 1-3 minutes for completion
|
| 211 |
-
5. Space will show "Running" when ready
|
| 212 |
-
|
| 213 |
-
**Test Your Deployment**
|
| 214 |
-
1. Try the example prompts
|
| 215 |
-
2. Test different types of queries
|
| 216 |
-
3. Verify grounding URLs are working
|
| 217 |
-
4. Check language responses (if configured)
|
| 218 |
-
|
| 219 |
-
**Iterate Configuration (with HF_TOKEN)**
|
| 220 |
-
1. Go to ⚙️ Configuration tab in your Space
|
| 221 |
-
2. Enter your HF_TOKEN to authenticate
|
| 222 |
-
3. Modify settings as needed
|
| 223 |
-
4. Click "Save Configuration"
|
| 224 |
-
5. Space will automatically rebuild
|
| 225 |
-
6. Test new configuration
|
| 226 |
-
""")
|
| 227 |
-
|
| 228 |
-
with gr.Accordion("🔧 Troubleshooting", open=False):
|
| 229 |
-
gr.Markdown("""
|
| 230 |
-
### Common Issues and Solutions
|
| 231 |
-
|
| 232 |
-
Find solutions to common problems organized by category.
|
| 233 |
-
""")
|
| 234 |
-
|
| 235 |
-
with gr.Accordion("Build Errors", open=False):
|
| 236 |
-
gr.Markdown("""
|
| 237 |
-
**Requirements Compatibility**
|
| 238 |
-
- Ensure Gradio version ≥ 5.39.0 in requirements.txt
|
| 239 |
-
- Check all dependencies are spelled correctly
|
| 240 |
-
- Verify version numbers are compatible
|
| 241 |
-
|
| 242 |
-
**Common Build Failures**
|
| 243 |
-
- Missing dependencies: Add to requirements.txt
|
| 244 |
-
- Import errors: Check module names and casing
|
| 245 |
-
- Syntax errors: Review app.py for Python syntax issues
|
| 246 |
-
""")
|
| 247 |
-
|
| 248 |
-
with gr.Accordion("API Errors", open=False):
|
| 249 |
-
gr.Markdown("""
|
| 250 |
-
**API Key Issues**
|
| 251 |
-
- Verify API_KEY is set in HuggingFace Secrets
|
| 252 |
-
- Ensure key starts with 'sk-or-' for OpenRouter
|
| 253 |
-
- Check for extra spaces or quotes in the secret value
|
| 254 |
-
|
| 255 |
-
**API Response Errors**
|
| 256 |
-
- 401 Unauthorized: Invalid API key
|
| 257 |
-
- 402 Payment Required: No API credits remaining
|
| 258 |
-
- 429 Rate Limited: Too many requests, wait and retry
|
| 259 |
-
- 500 Server Error: OpenRouter service issue
|
| 260 |
-
""")
|
| 261 |
-
|
| 262 |
-
with gr.Accordion("Access Control Issues", open=False):
|
| 263 |
-
gr.Markdown("""
|
| 264 |
-
**ACCESS_CODE Problems**
|
| 265 |
-
- Code must match exactly (case-sensitive)
|
| 266 |
-
- Don't include quotes around the password
|
| 267 |
-
- Check for trailing spaces in the secret
|
| 268 |
-
- To disable: Delete the ACCESS_CODE secret entirely
|
| 269 |
-
|
| 270 |
-
**Authentication Flow**
|
| 271 |
-
- Users enter code on first visit
|
| 272 |
-
- Code is stored in browser session
|
| 273 |
-
- Clear cookies to re-prompt for code
|
| 274 |
-
""")
|
| 275 |
-
|
| 276 |
-
with gr.Accordion("Preview Tab Not Working", open=False):
|
| 277 |
-
gr.Markdown("""
|
| 278 |
-
**Local Testing**
|
| 279 |
-
- Set API_KEY in your local .env file
|
| 280 |
-
- Format: `API_KEY=sk-or-your-key-here`
|
| 281 |
-
- Restart the app after adding environment variable
|
| 282 |
-
|
| 283 |
-
**Preview Issues**
|
| 284 |
-
- Click "💬 Preview Configuration" first
|
| 285 |
-
- Check browser console (F12) for JavaScript errors
|
| 286 |
-
- Ensure all required fields are filled
|
| 287 |
-
- Try refreshing the page
|
| 288 |
-
""")
|
| 289 |
-
|
| 290 |
-
with gr.Accordion("Configuration Status Shows ❌", open=False):
|
| 291 |
-
gr.Markdown("""
|
| 292 |
-
**Status Check Failures**
|
| 293 |
-
- Red X means configuration issue detected
|
| 294 |
-
- Usually indicates missing or invalid API key
|
| 295 |
-
|
| 296 |
-
**Resolution Steps**
|
| 297 |
-
1. Verify secret name matches your configuration
|
| 298 |
-
2. Default is API_KEY, check if you changed it
|
| 299 |
-
3. Regenerate API key at openrouter.ai/keys
|
| 300 |
-
4. Update secret in HuggingFace Space settings
|
| 301 |
-
5. Wait 30 seconds and refresh
|
| 302 |
-
""")
|
| 303 |
-
|
| 304 |
-
with gr.Accordion("Space Not Loading", open=False):
|
| 305 |
-
gr.Markdown("""
|
| 306 |
-
**Infinite Loading**
|
| 307 |
-
- Check Space logs for error messages
|
| 308 |
-
- Common cause: Missing required secrets
|
| 309 |
-
- Verify all files uploaded correctly
|
| 310 |
-
|
| 311 |
-
**Gradio App Errors**
|
| 312 |
-
- Component version mismatches
|
| 313 |
-
- Update to latest Gradio version
|
| 314 |
-
- Check for deprecated components
|
| 315 |
-
- Review migration guides for Gradio 5.x
|
| 316 |
-
""")
|
| 317 |
-
|
| 318 |
-
with gr.Accordion("📚 Additional Resources", open=False):
|
| 319 |
-
gr.Markdown("""
|
| 320 |
-
### Documentation Links
|
| 321 |
-
|
| 322 |
-
**HuggingFace**
|
| 323 |
-
- [Spaces Overview](https://huggingface.co/docs/hub/spaces-overview)
|
| 324 |
-
- [Gradio on Spaces](https://huggingface.co/docs/hub/spaces-gradio)
|
| 325 |
-
- [Environment Variables](https://huggingface.co/docs/hub/spaces-overview#managing-secrets)
|
| 326 |
-
|
| 327 |
-
**OpenRouter**
|
| 328 |
-
- [API Keys](https://openrouter.ai/keys)
|
| 329 |
-
- [Model Comparison](https://openrouter.ai/models)
|
| 330 |
-
- [Pricing](https://openrouter.ai/docs#models)
|
| 331 |
-
|
| 332 |
-
**Gradio**
|
| 333 |
-
- [Chat Interface](https://gradio.app/docs/chatinterface)
|
| 334 |
-
- [Components](https://gradio.app/docs/)
|
| 335 |
-
- [Sharing Apps](https://gradio.app/sharing-your-app/)
|
| 336 |
-
|
| 337 |
-
**Community Support**
|
| 338 |
-
- [HuggingFace Forums](https://discuss.huggingface.co/)
|
| 339 |
-
- [Gradio Discord](https://discord.gg/feTf9x3ZSB)
|
| 340 |
-
""")
|
| 341 |
-
|
| 342 |
-
def export_conversation_to_markdown(conversation_history, config_metadata=None):
|
| 343 |
-
"""Export conversation history to markdown format with configuration metadata"""
|
| 344 |
-
if not conversation_history:
|
| 345 |
-
return "No conversation to export."
|
| 346 |
-
|
| 347 |
-
markdown_content = f"""# Conversation Export
|
| 348 |
-
Generated on: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}
|
| 349 |
-
|
| 350 |
-
"""
|
| 351 |
-
|
| 352 |
-
# Add configuration metadata if provided
|
| 353 |
-
if config_metadata:
|
| 354 |
-
markdown_content += """## Configuration Information
|
| 355 |
-
|
| 356 |
-
"""
|
| 357 |
-
|
| 358 |
-
# Add basic configuration details
|
| 359 |
-
if config_metadata.get('model'):
|
| 360 |
-
markdown_content += f"**Model:** {config_metadata['model']}\n"
|
| 361 |
-
if config_metadata.get('temperature'):
|
| 362 |
-
markdown_content += f"**Temperature:** {config_metadata['temperature']}\n"
|
| 363 |
-
if config_metadata.get('max_tokens'):
|
| 364 |
-
markdown_content += f"**Max Tokens:** {config_metadata['max_tokens']}\n"
|
| 365 |
-
|
| 366 |
-
# Add URL grounding information
|
| 367 |
-
grounding_urls = []
|
| 368 |
-
for i in range(1, 5):
|
| 369 |
-
url = config_metadata.get(f'url{i}')
|
| 370 |
-
if url and url.strip():
|
| 371 |
-
grounding_urls.append(url.strip())
|
| 372 |
-
|
| 373 |
-
if grounding_urls:
|
| 374 |
-
markdown_content += f"\n**URL Grounding ({len(grounding_urls)} URLs):**\n"
|
| 375 |
-
for i, url in enumerate(grounding_urls, 1):
|
| 376 |
-
markdown_content += f"- URL {i}: {url}\n"
|
| 377 |
-
|
| 378 |
-
# Add feature flags
|
| 379 |
-
if config_metadata.get('enable_dynamic_urls'):
|
| 380 |
-
markdown_content += f"\n**Dynamic URL Fetching:** Enabled\n"
|
| 381 |
-
|
| 382 |
-
# Add system prompt
|
| 383 |
-
if config_metadata.get('system_prompt'):
|
| 384 |
-
system_prompt = config_metadata['system_prompt']
|
| 385 |
-
markdown_content += f"\n**System Prompt:**\n```\n{system_prompt}\n```\n"
|
| 386 |
-
|
| 387 |
-
markdown_content += "\n---\n\n"
|
| 388 |
-
else:
|
| 389 |
-
markdown_content += "---\n\n"
|
| 390 |
-
|
| 391 |
-
for i, message in enumerate(conversation_history):
|
| 392 |
-
if isinstance(message, dict):
|
| 393 |
-
role = message.get('role', 'unknown')
|
| 394 |
-
content = message.get('content', '')
|
| 395 |
-
|
| 396 |
-
if role == 'user':
|
| 397 |
-
markdown_content += f"## User Message {(i//2) + 1}\n\n{content}\n\n"
|
| 398 |
-
elif role == 'assistant':
|
| 399 |
-
markdown_content += f"## Assistant Response {(i//2) + 1}\n\n{content}\n\n---\n\n"
|
| 400 |
-
|
| 401 |
-
return markdown_content
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|