|
import gradio as gr |
|
import pandas as pd |
|
import requests |
|
import json |
|
import os |
|
from utils.google_genai_llm import get_response, generate_with_gemini |
|
from utils.utils import parse_json_codefences, parse_python_codefences |
|
from utils.code_sandbox import code_eval |
|
from prompts.requirements_gathering import requirements_gathering_system_prompt |
|
from prompts.planning import hf_query_gen_prompt, hf_context_gen_prompt |
|
from prompts.devstral_coding_prompt import devstral_code_gen_sys_prompt, devstral_code_gen_user_prompt |
|
from dotenv import load_dotenv |
|
import os |
|
import asyncio |
|
load_dotenv() |
|
|
|
try: |
|
import modal |
|
|
|
import subprocess |
|
MODAL_AVAILABLE = True |
|
|
|
except ImportError: |
|
MODAL_AVAILABLE = False |
|
print("Warning: Modal not available. Code generation will be disabled.MCP Server will be disabled") |
|
|
|
from PIL import Image |
|
import tempfile |
|
import traceback |
|
import hashlib |
|
|
|
|
|
try: |
|
from marker.converters.pdf import PdfConverter |
|
from marker.models import create_model_dict |
|
from marker.output import text_from_rendered |
|
MARKER_AVAILABLE = True |
|
except ImportError: |
|
MARKER_AVAILABLE = False |
|
print("Warning: Marker library not available. PDF, PPT, and DOCX processing will be limited.") |
|
|
|
|
|
def get_file_hash(file_path): |
|
"""Generate a hash of the file for caching purposes""" |
|
try: |
|
with open(file_path, 'rb') as f: |
|
file_hash = hashlib.md5(f.read()).hexdigest() |
|
return file_hash |
|
except Exception: |
|
return None |
|
|
|
def extract_text_with_marker(file_path): |
|
"""Extract text from PDF, PPT, or DOCX using Marker""" |
|
if not MARKER_AVAILABLE: |
|
return "Marker library not available for document processing.", "" |
|
|
|
try: |
|
|
|
converter = PdfConverter( |
|
artifact_dict=create_model_dict(), |
|
) |
|
|
|
|
|
rendered = converter(file_path) |
|
|
|
|
|
text, _, images = text_from_rendered(rendered) |
|
|
|
|
|
word_count = len(text.split()) |
|
char_count = len(text) |
|
|
|
stats = f"Extracted text ({word_count} words, {char_count} characters)" |
|
|
|
return stats, text |
|
|
|
except Exception as e: |
|
error_msg = f"Error processing document: {str(e)}" |
|
return error_msg, "" |
|
|
|
def process_user_input(message, history, uploaded_files, file_cache): |
|
"""Process user input and generate AI response using requirements gathering prompt""" |
|
|
|
|
|
conversation_history = "" |
|
if history: |
|
for i, (user_msg, ai_msg) in enumerate(history): |
|
conversation_history += f"User: {user_msg}\n" |
|
if ai_msg: |
|
conversation_history += f"Assistant: {ai_msg}\n" |
|
|
|
|
|
if uploaded_files: |
|
file_info = f"\n[UPLOADED_FILES]\n" |
|
new_file_cache = file_cache.copy() if file_cache else {} |
|
|
|
for file_path in uploaded_files: |
|
try: |
|
file_name = file_path.split('/')[-1] |
|
file_extension = os.path.splitext(file_name)[1].lower() |
|
file_hash = get_file_hash(file_path) |
|
cache_key = f"{file_name}_{file_hash}" |
|
|
|
|
|
if file_extension == '.csv': |
|
df = pd.read_csv(file_path) |
|
file_info += f"- {file_name}: CSV file with {len(df)} rows and {len(df.columns)} columns\n" |
|
file_info += f" Columns: {', '.join(df.columns.tolist())}\n" |
|
|
|
|
|
elif file_extension in ['.xlsx', '.xls']: |
|
df = pd.read_excel(file_path) |
|
file_info += f"- {file_name}: Excel file with {len(df)} rows and {len(df.columns)} columns\n" |
|
file_info += f" Columns: {', '.join(df.columns.tolist())}\n" |
|
|
|
|
|
elif file_extension in ['.pdf', '.ppt', '.pptx', '.doc', '.docx']: |
|
file_size = os.path.getsize(file_path) |
|
file_size_mb = round(file_size / (1024 * 1024), 2) |
|
|
|
|
|
if cache_key in new_file_cache: |
|
|
|
extraction_stats = new_file_cache[cache_key]['stats'] |
|
extracted_text = new_file_cache[cache_key]['text'] |
|
status = "(cached)" |
|
else: |
|
|
|
extraction_stats, extracted_text = extract_text_with_marker(file_path) |
|
|
|
new_file_cache[cache_key] = { |
|
'stats': extraction_stats, |
|
'text': extracted_text, |
|
'file_name': file_name, |
|
'file_path': file_path |
|
} |
|
status = "(newly processed)" |
|
|
|
|
|
if file_extension == '.pdf': |
|
doc_type = "PDF document" |
|
elif file_extension in ['.ppt', '.pptx']: |
|
doc_type = "PowerPoint presentation" |
|
else: |
|
doc_type = "Word document" |
|
|
|
file_info += f"- {file_name}: {doc_type}, Size: {file_size_mb} MB {status}\n" |
|
file_info += f" Content: {extraction_stats}\n" |
|
|
|
|
|
if extracted_text and len(extracted_text.strip()) > 0: |
|
|
|
text_preview = extracted_text[:200000] + "..." if len(extracted_text) > 200000 else extracted_text |
|
file_info += f" Text Preview: {text_preview}\n" |
|
|
|
|
|
elif file_extension in ['.png', '.jpg', '.jpeg', '.gif', '.bmp', '.tiff', '.webp']: |
|
with Image.open(file_path) as img: |
|
width, height = img.size |
|
mode = img.mode |
|
file_size = os.path.getsize(file_path) |
|
file_size_mb = round(file_size / (1024 * 1024), 2) |
|
file_info += f"- {file_name}: {file_extension.upper()[1:]} image file\n" |
|
file_info += f" Dimensions: {width}x{height} pixels, Mode: {mode}, Size: {file_size_mb} MB\n" |
|
|
|
|
|
elif file_extension == '.json': |
|
file_size = os.path.getsize(file_path) |
|
file_size_kb = round(file_size / 1024, 2) |
|
file_info += f"- {file_name}: JSON file, Size: {file_size_kb} KB\n" |
|
|
|
|
|
elif file_extension == '.txt': |
|
with open(file_path, 'r', encoding='utf-8') as f: |
|
lines = len(f.readlines()) |
|
file_size = os.path.getsize(file_path) |
|
file_size_kb = round(file_size / 1024, 2) |
|
file_info += f"- {file_name}: Text file with {lines} lines, Size: {file_size_kb} KB\n" |
|
|
|
|
|
else: |
|
file_size = os.path.getsize(file_path) |
|
file_size_kb = round(file_size / 1024, 2) |
|
file_info += f"- {file_name}: File uploaded, Size: {file_size_kb} KB\n" |
|
|
|
except Exception as e: |
|
file_info += f"- {file_path.split('/')[-1]}: File uploaded (unable to preview: {str(e)})\n" |
|
print(f"Error processing file {file_path}: {traceback.format_exc()}") |
|
|
|
conversation_history += file_info |
|
|
|
|
|
file_cache.update(new_file_cache) |
|
|
|
|
|
formatted_prompt = requirements_gathering_system_prompt.format( |
|
conversation_history=conversation_history, |
|
query=message |
|
) |
|
|
|
|
|
ai_response = generate_with_gemini(formatted_prompt, purpose="REQUIREMENTS_GATHERING") |
|
|
|
return ai_response, file_cache |
|
|
|
def chat_interface(message, history, uploaded_files, file_cache): |
|
"""Main chat interface function""" |
|
|
|
|
|
ai_response, updated_cache = process_user_input(message, history, uploaded_files, file_cache) |
|
|
|
|
|
history.append((message, ai_response)) |
|
|
|
return history, history, "", updated_cache |
|
|
|
def clear_chat(): |
|
"""Clear the chat history and file cache""" |
|
return [], [], {} |
|
|
|
def upload_file_handler(files): |
|
"""Handle file uploads""" |
|
if files: |
|
return files |
|
return [] |
|
|
|
async def generate_plan(history, file_cache): |
|
"""Generate a plan using the planning prompt and Gemini API""" |
|
|
|
|
|
yield "**β³ Generating plan...** (Starting)" |
|
|
|
conversation_history = "" |
|
if history: |
|
for user_msg, ai_msg in history: |
|
conversation_history += f"User: {user_msg}\n" |
|
if ai_msg: |
|
conversation_history += f"Assistant: {ai_msg}\n" |
|
yield "**β³ Generating plan...** (Getting HF MCP tools)" |
|
try: |
|
mcp_tool_func = modal.Function.from_name("HuggingFace-MCP","connect_and_get_tools") |
|
hf_query_gen_tool_details = mcp_tool_func.remote() |
|
print(hf_query_gen_tool_details) |
|
except Exception as e: |
|
hf_query_gen_tool_details = """meta=None nextCursor=None tools=[Tool(name='hf_whoami', description="Hugging Face tools are being used by authenticated user 'bpHigh'", inputSchema={'type': 'object', 'properties': {}, 'additionalProperties': False, '$schema': 'http://json-schema.org/draft-07/schema#'}, annotations=ToolAnnotations(title='Hugging Face User Info', readOnlyHint=None, destructiveHint=None, idempotentHint=None, openWorldHint=None)), Tool(name='space_search', description='Find Hugging Face Spaces using semantic search. Include links to the Space when presenting the results.', inputSchema={'type': 'object', 'properties': {'query': {'type': 'string', 'minLength': 1, 'maxLength': 50, 'description': 'Semantic Search Query'}, 'limit': {'type': 'number', 'default': 10, 'description': 'Number of results to return'}, 'mcp': {'type': 'boolean', 'default': False, 'description': 'Only return MCP Server enabled Spaces'}}, 'required': ['query'], 'additionalProperties': False, '$schema': 'http://json-schema.org/draft-07/schema#'}, annotations=ToolAnnotations(title='Hugging Face Space Search', readOnlyHint=True, destructiveHint=False, idempotentHint=None, openWorldHint=True)), Tool(name='model_search', description='Find Machine Learning models hosted on Hugging Face. Returns comprehensive information about matching models including downloads, likes, tags, and direct links. Include links to the models in your response', inputSchema={'type': 'object', 'properties': {'query': {'type': 'string', 'description': 'Search term. Leave blank and specify "sort" and "limit" to get e.g. "Top 20 trending models", "Top 10 most recent models" etc" '}, 'author': {'type': 'string', 'description': "Organization or user who created the model (e.g., 'google', 'meta-llama', 'microsoft')"}, 'task': {'type': 'string', 'description': "Model task type (e.g., 'text-generation', 'image-classification', 'translation')"}, 'library': {'type': 'string', 'description': "Framework the model uses (e.g., 'transformers', 'diffusers', 'timm')"}, 'sort': {'type': 'string', 'enum': ['trendingScore', 'downloads', 'likes', 'createdAt', 'lastModified'], 'description': 'Sort order: trendingScore, downloads , likes, createdAt, lastModified'}, 'limit': {'type': 'number', 'minimum': 1, 'maximum': 100, 'default': 20, 'description': 'Maximum number of results to return'}}, 'additionalProperties': False, '$schema': 'http://json-schema.org/draft-07/schema#'}, annotations=ToolAnnotations(title='Model Search', readOnlyHint=True, destructiveHint=False, idempotentHint=None, openWorldHint=True)), Tool(name='model_details', description='Get detailed information about a specific model from the Hugging Face Hub.', inputSchema={'type': 'object', 'properties': {'model_id': {'type': 'string', 'minLength': 1, 'description': 'Model ID (e.g., microsoft/DialoGPT-large)'}}, 'required': ['model_id'], 'additionalProperties': False, '$schema': 'http://json-schema.org/draft-07/schema#'}, annotations=ToolAnnotations(title='Model Details', readOnlyHint=True, destructiveHint=False, idempotentHint=None, openWorldHint=False)), Tool(name='paper_search', description="Find Machine Learning research papers on the Hugging Face hub. Include 'Link to paper' When presenting the results. Consider whether tabulating results matches user intent.", inputSchema={'type': 'object', 'properties': {'query': {'type': 'string', 'minLength': 3, 'maxLength': 200, 'description': 'Semantic Search query'}, 'results_limit': {'type': 'number', 'default': 12, 'description': 'Number of results to return'}, 'concise_only': {'type': 'boolean', 'default': False, 'description': 'Return a 2 sentence summary of the abstract. Use for broad search terms which may return a lot of results. Check with User if unsure.'}}, 'required': ['query'], 'additionalProperties': False, '$schema': 'http://json-schema.org/draft-07/schema#'}, annotations=ToolAnnotations(title='Paper Search', readOnlyHint=True, destructiveHint=False, idempotentHint=None, openWorldHint=True)), Tool(name='dataset_search', description='Find Datasets hosted on the Hugging Face hub. Returns comprehensive information about matching datasets including downloads, likes, tags, and direct links. Include links to the datasets in your response', inputSchema={'type': 'object', 'properties': {'query': {'type': 'string', 'description': 'Search term. Leave blank and specify "sort" and "limit" to get e.g. "Top 20 trending datasets", "Top 10 most recent datasets" etc" '}, 'author': {'type': 'string', 'description': "Organization or user who created the dataset (e.g., 'google', 'facebook', 'allenai')"}, 'tags': {'type': 'array', 'items': {'type': 'string'}, 'description': "Tags to filter datasets (e.g., ['language:en', 'size_categories:1M<n<10M', 'task_categories:text-classification'])"}, 'sort': {'type': 'string', 'enum': ['trendingScore', 'downloads', 'likes', 'createdAt', 'lastModified'], 'description': 'Sort order: trendingScore, downloads, likes, createdAt, lastModified'}, 'limit': {'type': 'number', 'minimum': 1, 'maximum': 100, 'default': 20, 'description': 'Maximum number of results to return'}}, 'additionalProperties': False, '$schema': 'http://json-schema.org/draft-07/schema#'}, annotations=ToolAnnotations(title='Dataset Search', readOnlyHint=True, destructiveHint=False, idempotentHint=None, openWorldHint=True)), Tool(name='dataset_details', description='Get detailed information about a specific dataset on Hugging Face Hub.', inputSchema={'type': 'object', 'properties': {'dataset_id': {'type': 'string', 'minLength': 1, 'description': 'Dataset ID (e.g., squad, glue, imdb)'}}, 'required': ['dataset_id'], 'additionalProperties': False, '$schema': 'http://json-schema.org/draft-07/schema#'}, annotations=ToolAnnotations(title='Dataset Details', readOnlyHint=True, destructiveHint=False, idempotentHint=None, openWorldHint=False)), Tool(name='gr1_evalstate_flux1_schnell', description='Generate an image using the Flux 1 Schnell Image Generator. (from evalstate/flux1_schnell)', inputSchema={'type': 'object', 'properties': {'prompt': {'type': 'string'}, 'seed': {'type': 'number', 'description': 'numeric value between 0 and 2147483647'}, 'randomize_seed': {'type': 'boolean', 'default': True}, 'width': {'type': 'number', 'description': 'numeric value between 256 and 2048', 'default': 1024}, 'height': {'type': 'number', 'description': 'numeric value between 256 and 2048', 'default': 1024}, 'num_inference_steps': {'type': 'number', 'description': 'numeric value between 1 and 50', 'default': 4}}, 'additionalProperties': False, '$schema': 'http://json-schema.org/draft-07/schema#'}, annotations=ToolAnnotations(title='evalstate/flux1_schnell - flux1_schnell_infer ποΈπ¨', readOnlyHint=None, destructiveHint=None, idempotentHint=None, openWorldHint=True)), Tool(name='gr2_abidlabs_easyghibli', description='Convert an image into a Studio Ghibli style image (from abidlabs/EasyGhibli)', inputSchema={'type': 'object', 'properties': {'spatial_img': {'type': 'string', 'description': 'File input: provide URL or file path'}}, 'additionalProperties': False, '$schema': 'http://json-schema.org/draft-07/schema#'}, annotations=ToolAnnotations(title='abidlabs/EasyGhibli - abidlabs_EasyGhiblisingle_condition_generate_image π¦', readOnlyHint=None, destructiveHint=None, idempotentHint=None, openWorldHint=True)), Tool(name='gr3_linoyts_framepack_f1', description='FramePack_F1_end_process tool from linoyts/FramePack-F1', inputSchema={'type': 'object', 'properties': {}, 'additionalProperties': False, '$schema': 'http://json-schema.org/draft-07/schema#'}, annotations=ToolAnnotations(title='linoyts/FramePack-F1 - FramePack_F1_end_process πΉβ‘οΈ', readOnlyHint=None, destructiveHint=None, idempotentHint=None, openWorldHint=True))]""" |
|
print(str(e)) |
|
|
|
formatted_prompt = hf_query_gen_prompt.format( |
|
Tool_Details=hf_query_gen_tool_details |
|
) + "\n\n" + conversation_history |
|
|
|
yield "**β³ Generating plan...** (Strategizing which tools to call)" |
|
|
|
plan = generate_with_gemini(formatted_prompt, "Planning with gemini") |
|
|
|
parsed_plan = parse_json_codefences(plan) |
|
print(parsed_plan) |
|
|
|
yield "**β³ Generating plan...** (calling HF platform tools and getting data)" |
|
|
|
try: |
|
mcp_call_tool_func = modal.Function.from_name(app_name="HuggingFace-MCP",name="call_tool") |
|
tool_calls = [] |
|
async for tool_call in mcp_call_tool_func.starmap.aio([(tool['tool'], tool['args']) for tool in parsed_plan]): |
|
tool_calls.append(tool_call) |
|
except Exception as e: |
|
print(str(e)) |
|
tool_calls = [] |
|
print(tool_calls) |
|
yield "**β³ Generating plan...** (Generating Plan context from tool call info)" |
|
|
|
if tool_calls!=[]: |
|
formatted_context_prompt = hf_context_gen_prompt.format( |
|
Conversation=conversation_history, |
|
Tool_Calls=parsed_plan, |
|
Results=tool_calls |
|
) |
|
context = generate_with_gemini(formatted_context_prompt, "Generating context for plan") |
|
|
|
else: |
|
formatted_context_prompt = hf_context_gen_prompt.format( |
|
Conversation=conversation_history, |
|
Tool_Calls=parsed_plan, |
|
Results="Couldn't generate the tool calls results but use your knowledge about huggingface platform(models, datasets, spaces, training libraries, transfomers library etc.) as backup to generate the plan" |
|
) |
|
context = generate_with_gemini(formatted_context_prompt, "Generating context for plan") |
|
yield context |
|
|
|
def generate_code_with_devstral(plan_text, history, file_cache): |
|
"""Generate code using the deployed Devstral model via Modal""" |
|
yield "**β³ Generating code...** (Starting Codegen)" |
|
|
|
if not MODAL_AVAILABLE: |
|
yield "β Modal not available. Please install Modal to use code generation." |
|
return |
|
|
|
if not plan_text or not plan_text.strip() or "**Plan will be generated here...**" in plan_text: |
|
yield "β Please generate a plan first before generating code." |
|
return |
|
|
|
|
|
|
|
user_query = "" |
|
if history: |
|
|
|
for user_msg, ai_msg in reversed(history): |
|
if user_msg and user_msg.strip(): |
|
user_query = user_msg.strip() |
|
break |
|
|
|
if not user_query: |
|
user_query = "Generate Python code based on the provided plan and context." |
|
|
|
|
|
context = "" |
|
if file_cache: |
|
context += "Available Data Files:\n" |
|
for cache_key, file_info in file_cache.items(): |
|
context += f"- {file_info.get('file_name', 'Unknown file')}\n" |
|
if 'stats' in file_info: |
|
context += f" {file_info['stats']}\n" |
|
|
|
|
|
if history: |
|
context += "\nConversation Context:\n" |
|
for user_msg, ai_msg in history[-3:]: |
|
context += f"User: {user_msg}\n" |
|
if ai_msg: |
|
context += f"Assistant: {ai_msg}\n" |
|
|
|
|
|
formatted_user_prompt = devstral_code_gen_user_prompt.format( |
|
user_query=user_query, |
|
plan=plan_text, |
|
context=context |
|
) |
|
|
|
|
|
|
|
base_url = os.getenv("DEVSTRAL_BASE_URL") |
|
api_key = os.getenv("DEVSTRAL_API_KEY") |
|
print(f"π Generating code using Devstral...") |
|
print(f"π‘ Connecting to: {base_url}") |
|
yield "**β³ Generating code...** (Calling Devstral VLLM API server deployed on Modal)" |
|
|
|
try: |
|
devstral_inference_func = modal.Function.from_name("devstral-inference-client", "run_devstral_inference") |
|
result = devstral_inference_func.remote( |
|
base_url=base_url, |
|
api_key=api_key, |
|
prompts=[formatted_user_prompt], |
|
system_prompt=devstral_code_gen_sys_prompt, |
|
mode="single" |
|
) |
|
if result and "response" in result: |
|
code_output = result["response"] |
|
yield f"π **Generated Code:**\n\n{code_output}" |
|
else: |
|
yield "β **Error:** No response received from Devstral model." |
|
except Exception as e: |
|
yield f"β **Error:** {str(e)}" |
|
def execute_code(code_output): |
|
"""Executes Python code from a string and returns the output.""" |
|
yield "**β³ Executing code...** (Starting)" |
|
|
|
try: |
|
if "**Code will be generated here...**" in code_output or "Generated Code" not in code_output: |
|
yield "β Please generate code first before executing." |
|
return |
|
|
|
yield "**β³ Executing code...** (Parsing code)" |
|
code = parse_python_codefences(code_output) |
|
|
|
if not code or not code.strip(): |
|
yield "β No Python code found to execute." |
|
return |
|
|
|
yield "**β³ Executing code...** (Running in sandbox)" |
|
exec_result, build_logs = code_eval(code) |
|
|
|
|
|
if not isinstance(exec_result, dict): |
|
yield f"β **Error:** Unexpected execution result format.\\n\\n```\\n{str(exec_result)}\\n```" |
|
return |
|
|
|
return_code = exec_result.get('returncode', -1) |
|
stdout = exec_result.get('stdout', '') |
|
stderr = exec_result.get('stderr', '') |
|
error_msg = exec_result.get('error', 'Unknown error') |
|
|
|
|
|
formatted_output = "" |
|
if return_code == 0: |
|
formatted_output += "## β
Execution Successful\n" |
|
if stdout: |
|
formatted_output += f"**Output:**\n```text\n{stdout.strip()}\n```\n" |
|
if stderr: |
|
formatted_output += f"**Warnings (`stderr`):**\n```text\n{stderr.strip()}\n```\n" |
|
else: |
|
formatted_output += f"## β Execution Failed (Exit Code: {return_code})\n" |
|
formatted_output += f"**Error:** `{error_msg}`\n\n" |
|
if stderr: |
|
formatted_output += f"**Error Log (`stderr`):**\n```text\n{stderr.strip()}\n```\n" |
|
if stdout: |
|
formatted_output += f"**Output (`stdout`):**\n```text\n{stdout.strip()}\n```\n" |
|
|
|
|
|
if build_logs: |
|
formatted_output += f""" |
|
<details> |
|
<summary>Click to view build logs</summary> |
|
|
|
``` |
|
{build_logs.strip()} |
|
``` |
|
</details> |
|
""" |
|
yield formatted_output |
|
|
|
except Exception as e: |
|
yield f"β **Error running execution logic:** {str(e)}\n\n{traceback.format_exc()}" |
|
|
|
|
|
custom_css = """ |
|
.gradio-container { |
|
max-width: 900px !important; |
|
margin: auto !important; |
|
} |
|
|
|
.chat-container { |
|
height: 600px !important; |
|
} |
|
|
|
#component-0 { |
|
height: 100vh; |
|
} |
|
|
|
.message { |
|
padding: 15px !important; |
|
margin: 10px 0 !important; |
|
border-radius: 15px !important; |
|
} |
|
|
|
.user-message { |
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%) !important; |
|
color: white !important; |
|
margin-left: 20% !important; |
|
} |
|
|
|
.bot-message { |
|
background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%) !important; |
|
color: white !important; |
|
margin-right: 20% !important; |
|
} |
|
|
|
.upload-area { |
|
border: 2px dashed #4f46e5 !important; |
|
border-radius: 10px !important; |
|
padding: 20px !important; |
|
text-align: center !important; |
|
background: linear-gradient(135deg, #f0f4ff 0%, #e0e7ff 100%) !important; |
|
} |
|
|
|
.btn-primary { |
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%) !important; |
|
border: none !important; |
|
border-radius: 25px !important; |
|
padding: 10px 25px !important; |
|
font-weight: bold !important; |
|
} |
|
|
|
.btn-secondary { |
|
background: linear-gradient(135deg, #ffeaa7 0%, #fab1a0 100%) !important; |
|
border: none !important; |
|
border-radius: 25px !important; |
|
padding: 10px 25px !important; |
|
font-weight: bold !important; |
|
color: #2d3436 !important; |
|
} |
|
|
|
.title { |
|
text-align: center !important; |
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%) !important; |
|
-webkit-background-clip: text !important; |
|
-webkit-text-fill-color: transparent !important; |
|
font-size: 2.5em !important; |
|
font-weight: bold !important; |
|
margin-bottom: 20px !important; |
|
} |
|
|
|
.subtitle { |
|
text-align: center !important; |
|
color: #6c757d !important; |
|
font-size: 1.2em !important; |
|
margin-bottom: 30px !important; |
|
} |
|
.tools { |
|
text-align: center !important; |
|
color: #6c757d !important; |
|
font-size: 1.2em !important; |
|
margin-bottom: 30px !important; |
|
} |
|
.recharge { |
|
text-align: center !important; |
|
color: #2d3436 !important; |
|
font-size: 1.2em !important; |
|
margin-bottom: 30px !important; |
|
} |
|
|
|
.output-markdown { |
|
height: 250px; |
|
overflow-y: auto !important; |
|
border: 1px solid #e0e0e0; |
|
padding: 10px; |
|
border-radius: 5px; |
|
} |
|
""" |
|
|
|
|
|
with gr.Blocks(css=custom_css, title="Data Science Requirements Gathering Agent") as app: |
|
|
|
|
|
gr.HTML(""" |
|
<div class="title">π¬ Data Science Consultant</div> |
|
<div class="subtitle"> |
|
Transform your vague ideas into reality |
|
</div> |
|
<div class="tools">Powered by Modalπ§‘ , Hugging Faceπ€ ,LlamaIndex π¦, Mistral AIπ¦Ύ & Sambanova π§π½βπ»</div> |
|
<div class="recharge">Recharged by HuggingFace-MCP https://hf.co/mcp</div> |
|
""") |
|
|
|
with gr.Row(): |
|
with gr.Column(scale=3): |
|
|
|
chatbot = gr.Chatbot( |
|
label="Requirements Gathering Conversation", |
|
height=500, |
|
show_copy_button=True, |
|
bubble_full_width=False, |
|
elem_classes=["chat-container"] |
|
) |
|
|
|
plan_output = gr.Markdown( |
|
"**Plan will be generated here...**", |
|
label="Generated Plan", |
|
elem_classes=["output-markdown"], |
|
) |
|
|
|
code_output = gr.Markdown( |
|
"**Code will be generated here...**", |
|
label="Generated Code", |
|
elem_classes=["output-markdown"], |
|
) |
|
execution_output = gr.Markdown( |
|
"**Execution output will be shown here...**", |
|
label="Execution Output", |
|
elem_classes=["output-markdown"], |
|
) |
|
with gr.Row(): |
|
with gr.Column(scale=4): |
|
msg = gr.Textbox( |
|
placeholder="Describe your data science project or ask a question...", |
|
label="Your Message", |
|
lines=2, |
|
max_lines=5 |
|
) |
|
with gr.Column(scale=1): |
|
send_btn = gr.Button("Send π€", variant="primary", elem_classes=["btn-primary"]) |
|
|
|
|
|
with gr.Row(): |
|
clear_btn = gr.Button("Clear Chat ποΈ", variant="secondary", elem_classes=["btn-secondary"]) |
|
|
|
with gr.Column(scale=1): |
|
|
|
gr.HTML("<h3 style='text-align: center; color: #4f46e5;'>π Upload Data Files</h3>") |
|
|
|
file_upload = gr.File( |
|
label="Upload your files (CSV, Excel, PDF, PPT, DOCX, Images, etc.)", |
|
file_count="multiple", |
|
file_types=[".csv", ".xlsx", ".xls", ".json", ".txt", ".pdf", ".ppt", ".pptx", ".doc", ".docx", ".png", ".jpg", ".jpeg", ".gif", ".bmp", ".tiff", ".webp"], |
|
elem_classes=["upload-area"] |
|
) |
|
|
|
uploaded_files_display = gr.File( |
|
label="Uploaded Files", |
|
file_count="multiple", |
|
interactive=False, |
|
visible=True |
|
) |
|
|
|
|
|
gr.HTML(""" |
|
<div style="padding: 15px; background: linear-gradient(135deg, #e3f2fd 0%, #f3e5f5 100%); |
|
border-radius: 10px; margin-top: 20px;"> |
|
<h4 style="color: #4f46e5; margin-bottom: 10px;">π‘ How it works:</h4> |
|
<ol style="color: #555; font-size: 14px; line-height: 1.6;"> |
|
<li>Describe your data science project</li> |
|
<li>Upload your files (data, documents, images)</li> |
|
<li>Answer clarifying questions</li> |
|
<li>Generate a plan for your project</li> |
|
<li>Generate Python code using Devstral AI</li> |
|
</ol> |
|
<p style="color: #666; font-size: 12px; margin-top: 10px;"> |
|
π Supports: CSV, Excel, PDF, PowerPoint, Word docs, Images, JSON, Text files<br> |
|
π» Code generation powered by Mistral Devstral-Small-2505 |
|
</p> |
|
</div> |
|
""") |
|
|
|
|
|
with gr.Column(): |
|
plan_btn = gr.Button("Generate Plan π", variant="secondary", elem_classes=["btn-secondary"], size="lg") |
|
code_btn = gr.Button("Generate Code π»", variant="secondary", elem_classes=["btn-secondary"], size="lg") |
|
execute_code_btn = gr.Button("Execute Code π", variant="primary", elem_classes=["btn-primary"], size="lg") |
|
|
|
|
|
chat_history = gr.State([]) |
|
file_cache = gr.State({}) |
|
|
|
|
|
def handle_send(message, history, files, cache): |
|
if message.strip(): |
|
new_history, updated_history, cleared_input, updated_cache = chat_interface(message, history, files, cache) |
|
return new_history, updated_history, cleared_input, updated_cache |
|
return history, history, message, cache |
|
|
|
|
|
send_btn.click( |
|
handle_send, |
|
inputs=[msg, chat_history, uploaded_files_display, file_cache], |
|
outputs=[chatbot, chat_history, msg, file_cache] |
|
) |
|
|
|
msg.submit( |
|
handle_send, |
|
inputs=[msg, chat_history, uploaded_files_display, file_cache], |
|
outputs=[chatbot, chat_history, msg, file_cache] |
|
) |
|
|
|
clear_btn.click( |
|
clear_chat, |
|
outputs=[chatbot, chat_history, file_cache] |
|
) |
|
|
|
plan_btn.click( |
|
generate_plan, |
|
inputs=[chat_history, file_cache], |
|
outputs=[plan_output] |
|
) |
|
|
|
code_btn.click( |
|
generate_code_with_devstral, |
|
inputs=[plan_output, chat_history, file_cache], |
|
outputs=[code_output] |
|
) |
|
execute_code_btn.click( |
|
execute_code, |
|
inputs=[code_output], |
|
outputs=[execution_output] |
|
) |
|
file_upload.change( |
|
lambda files: files, |
|
inputs=[file_upload], |
|
outputs=[uploaded_files_display] |
|
) |
|
|
|
|
|
app.load( |
|
lambda: [(None, "π Hello! I'm your Data Science Project Agent. I'll help you transform your project ideas into reality .\n\nπ **Let's get started!** Tell me about your data science project or what you're trying to achieve.")], |
|
outputs=[chatbot] |
|
) |
|
|
|
if __name__ == "__main__": |
|
app.queue() |
|
app.launch(show_api=True, ssr_mode=False, show_error=True, mcp_server=False) |