| 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; |
| } |
| |
| .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π§‘ , LlamaIndex π¦, Mistral AIπ¦Ύ & Sambanova π§π½βπ»</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) |