|
|
import gradio as gr |
|
|
import os |
|
|
from dotenv import load_dotenv |
|
|
import openai |
|
|
from openai import AsyncOpenAI |
|
|
import asyncio |
|
|
import json |
|
|
import time |
|
|
from datetime import datetime |
|
|
from typing import List, Dict, Any |
|
|
|
|
|
|
|
|
load_dotenv() |
|
|
|
|
|
|
|
|
initial_api_key = os.getenv("OPENAI_API_KEY") |
|
|
|
|
|
|
|
|
current_api_key = initial_api_key |
|
|
current_model = "gpt-4o-mini" |
|
|
client = None |
|
|
conversation_history = [] |
|
|
system_prompt = "You are a helpful, creative, and intelligent AI assistant. You provide accurate, detailed, and engaging responses while being friendly and professional." |
|
|
|
|
|
|
|
|
if initial_api_key: |
|
|
client = AsyncOpenAI(api_key=initial_api_key) |
|
|
|
|
|
|
|
|
|
|
|
DEFAULT_ASSISTANT = "Default Assistant" |
|
|
CUSTOM_PROMPT = "Custom" |
|
|
|
|
|
SYSTEM_PROMPTS = { |
|
|
DEFAULT_ASSISTANT: "You are a helpful, creative, and intelligent AI assistant. You provide accurate, detailed, and engaging responses while being friendly and professional.", |
|
|
"Creative Writer": "You are a creative writing assistant. Help users with storytelling, creative writing, poetry, and imaginative content. Be expressive and inspiring.", |
|
|
"Code Expert": "You are a programming expert. Provide clear, well-commented code solutions, explain programming concepts, and help debug issues. Focus on best practices and clean code.", |
|
|
"Academic Tutor": "You are an academic tutor. Explain complex concepts clearly, provide step-by-step solutions, and help students understand difficult topics across various subjects.", |
|
|
"Business Analyst": "You are a business consultant. Provide strategic insights, analyze market trends, suggest business solutions, and help with professional decision-making.", |
|
|
"Health & Wellness": "You are a health and wellness advisor. Provide general health information, wellness tips, and lifestyle advice. Always remind users to consult healthcare professionals for medical issues.", |
|
|
"Travel Guide": "You are a travel expert. Provide destination recommendations, travel tips, cultural insights, and help plan memorable trips around the world.", |
|
|
"Tech Support": "You are a technical support specialist. Help troubleshoot technology issues, explain technical concepts simply, and provide step-by-step solutions.", |
|
|
} |
|
|
|
|
|
async def get_openai_response_stream(messages, model="gpt-4o-mini", temperature=0.7, max_tokens=2000): |
|
|
"""Get streaming response from OpenAI API""" |
|
|
try: |
|
|
stream = await client.chat.completions.create( |
|
|
model=model, |
|
|
messages=messages, |
|
|
temperature=temperature, |
|
|
max_tokens=max_tokens, |
|
|
stream=True |
|
|
) |
|
|
|
|
|
full_response = "" |
|
|
async for chunk in stream: |
|
|
if chunk.choices[0].delta.content is not None: |
|
|
content = chunk.choices[0].delta.content |
|
|
full_response += content |
|
|
yield full_response |
|
|
except Exception as e: |
|
|
yield f"Error: {str(e)}" |
|
|
|
|
|
def update_conversation_history(user_msg, assistant_msg, system_msg): |
|
|
"""Update the global conversation history""" |
|
|
global conversation_history |
|
|
|
|
|
|
|
|
if not conversation_history or conversation_history[0]["content"] != system_msg: |
|
|
conversation_history = [{"role": "system", "content": system_msg}] |
|
|
|
|
|
|
|
|
conversation_history.append({"role": "user", "content": user_msg}) |
|
|
conversation_history.append({"role": "assistant", "content": assistant_msg}) |
|
|
|
|
|
|
|
|
if len(conversation_history) > 41: |
|
|
conversation_history = conversation_history[:1] + conversation_history[-40:] |
|
|
|
|
|
async def chat_response_stream(message, history, api_key, model, temperature, max_tokens, system_prompt_choice, custom_system_prompt): |
|
|
"""Handle streaming chat response""" |
|
|
global client, current_api_key, current_model, system_prompt |
|
|
|
|
|
|
|
|
if api_key and api_key.strip() != current_api_key: |
|
|
current_api_key = api_key.strip() |
|
|
if current_api_key: |
|
|
client = AsyncOpenAI(api_key=current_api_key) |
|
|
else: |
|
|
client = None |
|
|
elif not api_key and current_api_key: |
|
|
current_api_key = None |
|
|
client = None |
|
|
|
|
|
|
|
|
if client is None: |
|
|
history.append({"role": "user", "content": message}) |
|
|
history.append({"role": "assistant", "content": "🔑 Please provide your OpenAI API key to start the conversation."}) |
|
|
yield history, "" |
|
|
|
|
|
|
|
|
current_model = model |
|
|
|
|
|
|
|
|
if system_prompt_choice == "Custom" and custom_system_prompt.strip(): |
|
|
system_prompt = custom_system_prompt.strip() |
|
|
else: |
|
|
system_prompt = SYSTEM_PROMPTS.get(system_prompt_choice, SYSTEM_PROMPTS["Default Assistant"]) |
|
|
|
|
|
if not message.strip(): |
|
|
history.append({"role": "user", "content": message}) |
|
|
history.append({"role": "assistant", "content": "⚠️ Please enter a message to continue our conversation."}) |
|
|
yield history, "" |
|
|
|
|
|
|
|
|
history.append({"role": "user", "content": message}) |
|
|
history.append({"role": "assistant", "content": "🤔 Thinking..."}) |
|
|
|
|
|
|
|
|
messages = [{"role": "system", "content": system_prompt}] |
|
|
for msg in history[:-1]: |
|
|
messages.append(msg) |
|
|
|
|
|
|
|
|
full_response = "" |
|
|
try: |
|
|
async for partial_response in get_openai_response_stream(messages, current_model, temperature, max_tokens): |
|
|
full_response = partial_response |
|
|
history[-1]["content"] = full_response |
|
|
yield history, "" |
|
|
|
|
|
|
|
|
update_conversation_history(message, full_response, system_prompt) |
|
|
|
|
|
except Exception as e: |
|
|
history[-1]["content"] = f"❌ Error: {str(e)}" |
|
|
yield history, "" |
|
|
|
|
|
def clear_chat(): |
|
|
"""Clear the chat history and conversation memory""" |
|
|
global conversation_history |
|
|
conversation_history = [] |
|
|
return [] |
|
|
|
|
|
def export_conversation(history): |
|
|
"""Export conversation as JSON""" |
|
|
if not history: |
|
|
return None |
|
|
|
|
|
export_data = { |
|
|
"timestamp": datetime.now().isoformat(), |
|
|
"model": current_model, |
|
|
"system_prompt": system_prompt, |
|
|
"conversation": history |
|
|
} |
|
|
|
|
|
filename = f"conversation_{datetime.now().strftime('%Y%m%d_%H%M%S')}.json" |
|
|
with open(filename, 'w', encoding='utf-8') as f: |
|
|
json.dump(export_data, f, indent=2, ensure_ascii=False) |
|
|
|
|
|
return filename |
|
|
|
|
|
def get_status_info(): |
|
|
"""Get current status information""" |
|
|
status = f"🟢 **Active** | Model: {current_model} | Messages: {len(conversation_history)} | Time: {datetime.now().strftime('%H:%M:%S')}" |
|
|
return status |
|
|
|
|
|
def get_model_info(model): |
|
|
"""Get information about the selected model""" |
|
|
model_info = { |
|
|
"gpt-3.5-turbo": "⚡ **GPT-3.5 Turbo** - Fast and efficient for most tasks. Cost-effective choice.", |
|
|
"gpt-4": "🧠 **GPT-4** - Most capable model with superior reasoning. Higher cost but better quality.", |
|
|
"gpt-4-turbo": "🚀 **GPT-4 Turbo** - Latest GPT-4 with improved performance and larger context window.", |
|
|
"gpt-4o": "✨ **GPT-4o** - Optimized for conversation with multimodal capabilities.", |
|
|
"gpt-4o-mini": "💫 **GPT-4o Mini** - Lightweight version of GPT-4o. Great balance of speed and capability." |
|
|
} |
|
|
return model_info.get(model, "📋 Model information not available.") |
|
|
|
|
|
|
|
|
custom_css = """ |
|
|
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap'); |
|
|
|
|
|
:root { |
|
|
--primary-color: #10a37f; |
|
|
--primary-hover: #0d8d6c; |
|
|
--secondary-color: #f7f7f8; |
|
|
--accent-color: #0066cc; |
|
|
--success-color: #10a37f; |
|
|
--warning-color: #ff9500; |
|
|
--error-color: #ff3333; |
|
|
--background-primary: #ffffff; |
|
|
--background-secondary: #f7f7f8; |
|
|
--background-chat: #ffffff; |
|
|
--text-primary: #2d3748; |
|
|
--text-secondary: #4a5568; |
|
|
--text-light: #718096; |
|
|
--border-color: #e2e8f0; |
|
|
--shadow-primary: 0 2px 8px rgba(0, 0, 0, 0.1); |
|
|
--shadow-secondary: 0 1px 3px rgba(0, 0, 0, 0.1); |
|
|
} |
|
|
|
|
|
* { |
|
|
font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; |
|
|
} |
|
|
|
|
|
.gradio-container { |
|
|
background: var(--background-primary); |
|
|
color: var(--text-primary); |
|
|
min-height: 100vh; |
|
|
} |
|
|
|
|
|
/* Header styling */ |
|
|
.header { |
|
|
background: linear-gradient(135deg, var(--primary-color) 0%, var(--accent-color) 100%); |
|
|
color: white; |
|
|
padding: 20px; |
|
|
text-align: center; |
|
|
border-radius: 0 0 20px 20px; |
|
|
margin-bottom: 20px; |
|
|
box-shadow: var(--shadow-primary); |
|
|
} |
|
|
|
|
|
.header h1 { |
|
|
font-size: 2.5em; |
|
|
font-weight: 700; |
|
|
margin: 0; |
|
|
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); |
|
|
} |
|
|
|
|
|
.header p { |
|
|
font-size: 1.1em; |
|
|
margin: 10px 0 0 0; |
|
|
opacity: 0.9; |
|
|
} |
|
|
|
|
|
/* Settings panel styling */ |
|
|
.settings-panel { |
|
|
background: var(--background-secondary); |
|
|
border: 1px solid var(--border-color); |
|
|
border-radius: 12px; |
|
|
padding: 20px; |
|
|
margin-bottom: 20px; |
|
|
box-shadow: var(--shadow-secondary); |
|
|
} |
|
|
|
|
|
.settings-panel h3 { |
|
|
color: var(--text-primary); |
|
|
margin-top: 0; |
|
|
margin-bottom: 15px; |
|
|
font-weight: 600; |
|
|
} |
|
|
|
|
|
/* Chat container styling */ |
|
|
.chat-container { |
|
|
background: var(--background-chat); |
|
|
border: 1px solid var(--border-color); |
|
|
border-radius: 12px; |
|
|
box-shadow: var(--shadow-secondary); |
|
|
overflow: hidden; |
|
|
} |
|
|
|
|
|
/* Status bar styling */ |
|
|
.status-bar { |
|
|
background: linear-gradient(135deg, var(--success-color), var(--accent-color)); |
|
|
color: white; |
|
|
padding: 10px 15px; |
|
|
border-radius: 8px; |
|
|
margin: 10px 0; |
|
|
font-size: 0.9em; |
|
|
font-weight: 500; |
|
|
} |
|
|
|
|
|
/* Model info styling */ |
|
|
.model-info { |
|
|
background: var(--background-secondary); |
|
|
border: 1px solid var(--border-color); |
|
|
border-radius: 8px; |
|
|
padding: 15px; |
|
|
margin: 10px 0; |
|
|
font-size: 0.9em; |
|
|
} |
|
|
|
|
|
/* Button styling */ |
|
|
.gr-button { |
|
|
background: var(--primary-color); |
|
|
color: white; |
|
|
border: none; |
|
|
border-radius: 8px; |
|
|
padding: 10px 20px; |
|
|
font-weight: 500; |
|
|
transition: all 0.2s ease; |
|
|
cursor: pointer; |
|
|
} |
|
|
|
|
|
.gr-button:hover { |
|
|
background: var(--primary-hover); |
|
|
transform: translateY(-1px); |
|
|
} |
|
|
|
|
|
.gr-button[variant="secondary"] { |
|
|
background: var(--warning-color); |
|
|
} |
|
|
|
|
|
.gr-button[variant="secondary"]:hover { |
|
|
background: #e6850e; |
|
|
} |
|
|
|
|
|
/* Input styling */ |
|
|
.gr-textbox, .gr-dropdown, .gr-slider { |
|
|
border: 1px solid var(--border-color); |
|
|
border-radius: 8px; |
|
|
padding: 10px; |
|
|
font-size: 14px; |
|
|
transition: border-color 0.2s ease; |
|
|
} |
|
|
|
|
|
.gr-textbox:focus, .gr-dropdown:focus { |
|
|
border-color: var(--primary-color); |
|
|
outline: none; |
|
|
box-shadow: 0 0 0 3px rgba(16, 163, 127, 0.1); |
|
|
} |
|
|
|
|
|
/* Chatbot styling */ |
|
|
.gr-chatbot { |
|
|
background: transparent; |
|
|
border: none; |
|
|
font-size: 14px; |
|
|
line-height: 1.6; |
|
|
} |
|
|
|
|
|
/* Message styling */ |
|
|
.message { |
|
|
padding: 15px; |
|
|
margin: 8px 0; |
|
|
border-radius: 12px; |
|
|
max-width: 85%; |
|
|
word-wrap: break-word; |
|
|
} |
|
|
|
|
|
.message.user { |
|
|
background: var(--primary-color); |
|
|
color: white; |
|
|
margin-left: auto; |
|
|
margin-right: 0; |
|
|
} |
|
|
|
|
|
.message.assistant { |
|
|
background: var(--background-secondary); |
|
|
color: var(--text-primary); |
|
|
margin-right: auto; |
|
|
margin-left: 0; |
|
|
} |
|
|
|
|
|
/* Advanced controls */ |
|
|
.advanced-controls { |
|
|
background: var(--background-secondary); |
|
|
border: 1px solid var(--border-color); |
|
|
border-radius: 8px; |
|
|
padding: 15px; |
|
|
margin: 10px 0; |
|
|
} |
|
|
|
|
|
.advanced-controls h4 { |
|
|
margin-top: 0; |
|
|
margin-bottom: 10px; |
|
|
color: var(--text-primary); |
|
|
} |
|
|
|
|
|
/* Responsive design */ |
|
|
@media (max-width: 768px) { |
|
|
.header h1 { |
|
|
font-size: 2em; |
|
|
} |
|
|
|
|
|
.settings-panel { |
|
|
padding: 15px; |
|
|
} |
|
|
|
|
|
.gr-button { |
|
|
padding: 8px 16px; |
|
|
font-size: 13px; |
|
|
} |
|
|
} |
|
|
|
|
|
/* Loading animation */ |
|
|
@keyframes thinking { |
|
|
0%, 80%, 100% { opacity: 1; } |
|
|
40% { opacity: 0.3; } |
|
|
} |
|
|
|
|
|
.thinking { |
|
|
animation: thinking 1.5s infinite; |
|
|
} |
|
|
|
|
|
/* Fade in animation */ |
|
|
@keyframes fadeIn { |
|
|
from { opacity: 0; transform: translateY(10px); } |
|
|
to { opacity: 1; transform: translateY(0); } |
|
|
} |
|
|
|
|
|
.fade-in { |
|
|
animation: fadeIn 0.3s ease-out; |
|
|
} |
|
|
""" |
|
|
|
|
|
|
|
|
with gr.Blocks(css=custom_css, title="🤖 ChatGPT-like AI Assistant", theme=gr.themes.Soft()) as demo: |
|
|
|
|
|
with gr.Row(elem_classes="header"): |
|
|
gr.HTML(""" |
|
|
<div> |
|
|
<h1>🤖 ChatGPT-like AI Assistant</h1> |
|
|
<p>Powered by OpenAI's GPT models with advanced conversation features</p> |
|
|
</div> |
|
|
""") |
|
|
|
|
|
with gr.Row(): |
|
|
|
|
|
with gr.Column(scale=1): |
|
|
with gr.Group(elem_classes="settings-panel fade-in"): |
|
|
gr.Markdown("### ⚙️ **Configuration**") |
|
|
|
|
|
|
|
|
api_key_input = gr.Textbox( |
|
|
label="🔑 OpenAI API Key", |
|
|
type="password", |
|
|
value=initial_api_key, |
|
|
placeholder="sk-...", |
|
|
info="Your OpenAI API key for accessing GPT models" |
|
|
) |
|
|
|
|
|
|
|
|
model_dropdown = gr.Dropdown( |
|
|
choices=[ |
|
|
"gpt-4o-mini", |
|
|
"gpt-4o", |
|
|
"gpt-4-turbo", |
|
|
"gpt-4", |
|
|
"gpt-3.5-turbo" |
|
|
], |
|
|
value="gpt-4o-mini", |
|
|
label="🧠 AI Model", |
|
|
info="Choose the GPT model for your conversation" |
|
|
) |
|
|
|
|
|
|
|
|
model_info_display = gr.Markdown( |
|
|
get_model_info("gpt-4o-mini"), |
|
|
elem_classes="model-info" |
|
|
) |
|
|
|
|
|
|
|
|
system_prompt_dropdown = gr.Dropdown( |
|
|
choices=list(SYSTEM_PROMPTS.keys()) + ["Custom"], |
|
|
value="Default Assistant", |
|
|
label="🎭 Assistant Personality", |
|
|
info="Choose how the AI should behave" |
|
|
) |
|
|
|
|
|
|
|
|
custom_system_prompt = gr.Textbox( |
|
|
label="✏️ Custom System Prompt", |
|
|
placeholder="Enter your custom system prompt here...", |
|
|
lines=3, |
|
|
visible=False, |
|
|
info="Define custom behavior for the AI" |
|
|
) |
|
|
|
|
|
|
|
|
with gr.Group(elem_classes="advanced-controls fade-in"): |
|
|
gr.Markdown("### 🎛️ **Advanced Settings**") |
|
|
|
|
|
temperature_slider = gr.Slider( |
|
|
minimum=0.1, |
|
|
maximum=2.0, |
|
|
value=0.7, |
|
|
step=0.1, |
|
|
label="🌡️ Temperature", |
|
|
info="Higher values = more creative, lower = more focused" |
|
|
) |
|
|
|
|
|
max_tokens_slider = gr.Slider( |
|
|
minimum=100, |
|
|
maximum=4000, |
|
|
value=2000, |
|
|
step=100, |
|
|
label="📝 Max Tokens", |
|
|
info="Maximum length of the AI response" |
|
|
) |
|
|
|
|
|
|
|
|
with gr.Group(elem_classes="settings-panel fade-in"): |
|
|
gr.Markdown("### 🎮 **Controls**") |
|
|
|
|
|
with gr.Row(): |
|
|
clear_btn = gr.Button("🗑️ Clear Chat", variant="secondary") |
|
|
export_btn = gr.Button("📥 Export", variant="primary") |
|
|
|
|
|
|
|
|
status_display = gr.Markdown( |
|
|
get_status_info(), |
|
|
elem_classes="status-bar" |
|
|
) |
|
|
|
|
|
|
|
|
with gr.Group(elem_classes="settings-panel fade-in"): |
|
|
gr.Markdown(""" |
|
|
### 💡 **Tips** |
|
|
- **Temperature**: 0.3-0.7 for focused responses, 0.7-1.2 for creative writing |
|
|
- **Max Tokens**: Higher values allow longer responses but cost more |
|
|
- **System Prompts**: Try different personalities for varied conversation styles |
|
|
- **Memory**: The AI remembers your conversation context automatically |
|
|
""") |
|
|
|
|
|
|
|
|
with gr.Column(scale=2): |
|
|
with gr.Group(elem_classes="chat-container fade-in"): |
|
|
|
|
|
chatbot = gr.Chatbot( |
|
|
label="💬 **Conversation**", |
|
|
height=600, |
|
|
show_copy_button=True, |
|
|
show_label=True, |
|
|
container=True, |
|
|
type="messages", |
|
|
avatar_images=("https://cdn-icons-png.flaticon.com/512/847/847969.png", |
|
|
"https://cdn-icons-png.flaticon.com/512/4712/4712109.png") |
|
|
) |
|
|
|
|
|
|
|
|
with gr.Row(): |
|
|
msg = gr.Textbox( |
|
|
label="", |
|
|
placeholder="Type your message here... (Shift+Enter for new line)", |
|
|
lines=2, |
|
|
max_lines=6, |
|
|
scale=4, |
|
|
container=False, |
|
|
autofocus=True |
|
|
) |
|
|
send_btn = gr.Button("📤 Send", variant="primary", scale=1) |
|
|
|
|
|
|
|
|
with gr.Group(elem_classes="settings-panel fade-in"): |
|
|
gr.Markdown(""" |
|
|
## 🚀 **Welcome to Your AI Assistant!** |
|
|
|
|
|
This ChatGPT-like interface offers advanced features for natural conversation: |
|
|
|
|
|
### ✨ **Key Features** |
|
|
- **🧠 Multiple AI Models**: Choose from GPT-3.5, GPT-4, and GPT-4o variants |
|
|
- **🎭 Personality Modes**: Pre-configured system prompts for different use cases |
|
|
- **💾 Conversation Memory**: Maintains context throughout your session |
|
|
- **🎛️ Advanced Controls**: Fine-tune temperature and response length |
|
|
- **📥 Export Conversations**: Save your chats as JSON files |
|
|
- **⚡ Streaming Responses**: Real-time response generation |
|
|
|
|
|
### 🎯 **Getting Started** |
|
|
1. **Add your OpenAI API key** (required for functionality) |
|
|
2. **Choose your preferred model** (GPT-4o-mini recommended for speed) |
|
|
3. **Select an assistant personality** or create your own |
|
|
4. **Start chatting** - the AI will remember your conversation! |
|
|
|
|
|
*Built By ❤️ Mahfujul Karim* |
|
|
""") |
|
|
|
|
|
|
|
|
async def submit_message(message, history, api_key, model, temperature, max_tokens, system_prompt_choice, custom_system_prompt): |
|
|
"""Handle message submission""" |
|
|
if not message.strip(): |
|
|
yield history, "" |
|
|
|
|
|
|
|
|
async for x in chat_response_stream(message, history, api_key, model, temperature, max_tokens, system_prompt_choice, custom_system_prompt): |
|
|
yield x |
|
|
|
|
|
|
|
|
def on_system_prompt_change(choice): |
|
|
if choice == CUSTOM_PROMPT: |
|
|
return gr.update(visible=True) |
|
|
else: |
|
|
return gr.update(visible=False) |
|
|
|
|
|
def on_model_change(model): |
|
|
"""Handle model selection change""" |
|
|
return get_model_info(model), get_status_info() |
|
|
|
|
|
def refresh_status(): |
|
|
"""Refresh status display""" |
|
|
return get_status_info() |
|
|
|
|
|
|
|
|
send_btn.click( |
|
|
submit_message, |
|
|
inputs=[msg, chatbot, api_key_input, model_dropdown, temperature_slider, max_tokens_slider, system_prompt_dropdown, custom_system_prompt], |
|
|
outputs=[chatbot, msg] |
|
|
) |
|
|
|
|
|
msg.submit( |
|
|
submit_message, |
|
|
inputs=[msg, chatbot, api_key_input, model_dropdown, temperature_slider, max_tokens_slider, system_prompt_dropdown, custom_system_prompt], |
|
|
outputs=[chatbot, msg] |
|
|
) |
|
|
|
|
|
clear_btn.click( |
|
|
clear_chat, |
|
|
outputs=[chatbot] |
|
|
) |
|
|
|
|
|
system_prompt_dropdown.change( |
|
|
on_system_prompt_change, |
|
|
inputs=[system_prompt_dropdown], |
|
|
outputs=[custom_system_prompt] |
|
|
) |
|
|
|
|
|
model_dropdown.change( |
|
|
on_model_change, |
|
|
inputs=[model_dropdown], |
|
|
outputs=[model_info_display, status_display] |
|
|
) |
|
|
|
|
|
|
|
|
demo.load(refresh_status, outputs=[status_display]) |
|
|
|
|
|
|
|
|
if __name__ == "__main__": |
|
|
demo.launch( |
|
|
share=False, |
|
|
show_error=True, |
|
|
show_api=False, |
|
|
quiet=False, |
|
|
ssl_verify=False |
|
|
) |