Spaces:
Runtime error
Runtime error
| import os | |
| import json | |
| import re | |
| from typing import Dict, Any | |
| GROQ_VISION_MODEL = "meta-llama/llama-4-scout-17b-16e-instruct" | |
| OVERLAY_SLM_MODEL = "llama-3.1-8b-instant" | |
| def parse_json_robust(text: str) -> Dict[str, Any]: | |
| text = text.strip() | |
| # Remove markdown code block wrappers if present | |
| if text.startswith("```"): | |
| text = re.sub(r"^```(?:json)?\n", "", text) | |
| text = re.sub(r"\n```$", "", text) | |
| text = text.strip() | |
| try: | |
| return json.loads(text) | |
| except Exception: | |
| # Fallback regex extraction | |
| match = re.search(r"(\{.*\})", text, re.DOTALL) | |
| if match: | |
| try: | |
| return json.loads(match.group(1)) | |
| except Exception: | |
| pass | |
| raise ValueError("Failed to parse JSON response from LLM") | |
| def get_local_fallback_context(active_app: str, window_title: str, accessibility_text: str) -> dict: | |
| try: | |
| from overlay_engine import classify_workflow | |
| workflow = classify_workflow(active_app, window_title, accessibility_text) | |
| except ImportError: | |
| workflow = "general" | |
| app_display = (active_app or "Desktop").split(".")[0].capitalize() | |
| detected = [f"App: {app_display}"] | |
| if window_title: | |
| title_clean = window_title.strip() | |
| if len(title_clean) > 25: | |
| title_clean = title_clean[:22] + "..." | |
| detected.append(title_clean) | |
| suggestions = [] | |
| if workflow == "vscode": | |
| detected.append("VS Code") | |
| suggestions = [ | |
| {"label": "Explain Code", "prompt": "Analyze the active code block on screen and explain it step-by-step."}, | |
| {"label": "Find Syntax Errors", "prompt": "Check the visible code for syntax errors or anti-patterns."}, | |
| {"label": "Refactor Function", "prompt": "Suggest clean optimization and refactoring for the active function."} | |
| ] | |
| elif workflow == "photoshop": | |
| detected.append("Photoshop") | |
| suggestions = [ | |
| {"label": "Blending Guide", "prompt": "How do I use layer styles and masks for seamless blending?"}, | |
| {"label": "Selection Tools", "prompt": "Explain which selection tools are best for complex cutouts."}, | |
| {"label": "Color Correction", "prompt": "Suggest step-by-step curves adjustments for a moody photo."} | |
| ] | |
| elif workflow == "excel": | |
| detected.append("Excel") | |
| suggestions = [ | |
| {"label": "Lookup Formulas", "prompt": "Explain VLOOKUP and XLOOKUP parameters with examples."}, | |
| {"label": "Create Pivot Table", "prompt": "How do I build a dynamic pivot table from raw data?"}, | |
| {"label": "Write Macro", "prompt": "Write a basic VBA script to format new rows automatically."} | |
| ] | |
| elif workflow == "browser": | |
| detected.append("Browser Page") | |
| suggestions = [ | |
| {"label": "Summarize Article", "prompt": "Provide a high-level summary of the active webpage/article."}, | |
| {"label": "Explain Page", "prompt": "Explain the key concepts of the visible web page in simple terms."}, | |
| {"label": "Extract Actions", "prompt": "Extract the key action items or guides from this page."} | |
| ] | |
| else: | |
| detected.append("AI Copilot") | |
| suggestions = [ | |
| {"label": "Analyze Screen", "prompt": "Explain what's currently visible on this screen and suggest next steps."}, | |
| {"label": "Summarize Context", "prompt": "Draft a concise summary of the active task on my screen."}, | |
| {"label": "Optimize Layout", "prompt": "How can I improve my workspace structure to boost focus?"} | |
| ] | |
| return { | |
| "detected_items": detected, | |
| "suggestions": suggestions | |
| } | |
| async def analyze_screen_context( | |
| active_app: str, | |
| window_title: str, | |
| accessibility_text: str, | |
| screenshot_b64: str = None | |
| ) -> dict: | |
| import asyncio | |
| import tempfile | |
| import base64 | |
| import sys | |
| # 1. Attempt to delegate to Hermes Agent CLI | |
| current_dir = os.path.dirname(os.path.abspath(__file__)) | |
| hermes_dir = os.path.abspath(os.path.join(current_dir, "..", "..", "hermes-agent")) | |
| venv_python = os.path.join(hermes_dir, ".venv", "Scripts", "python.exe") | |
| cli_script = os.path.join(hermes_dir, "cli.py") | |
| if not os.path.exists(venv_python): | |
| venv_python = sys.executable | |
| temp_img_path = None | |
| if screenshot_b64: | |
| try: | |
| clean_b64 = screenshot_b64 | |
| if "," in clean_b64: | |
| clean_b64 = clean_b64.split(",")[1] | |
| img_data = base64.b64decode(clean_b64) | |
| with tempfile.NamedTemporaryFile(suffix=".jpg", delete=False) as temp_file: | |
| temp_file.write(img_data) | |
| temp_img_path = temp_file.name | |
| except Exception as e: | |
| print(f"AURA Hermes Context Bridge: Failed to decode/save screenshot: {e}") | |
| prompt_query = ( | |
| f"Analyze active screen context:\n" | |
| f"Active App: {active_app or 'Unknown'}\n" | |
| f"Window Title: {window_title or 'Unknown'}\n" | |
| f"Accessibility Text: {accessibility_text or ''}\n\n" | |
| f"You MUST return a JSON object with this exact schema:\n" | |
| f"{{\n" | |
| f" \"detected_items\": [\"tag1\", \"tag2\", \"tag3\"],\n" | |
| f" \"suggestions\": [\n" | |
| f" {{\"label\": \"Short Action Button Label\", \"prompt\": \"The detailed query that is executed when user clicks this button\"}}\n" | |
| f" ]\n" | |
| f"}}\n" | |
| f"Do not write any markdown wrappers, preambles, or postambles. Output ONLY valid JSON." | |
| ) | |
| args = [ | |
| cli_script, | |
| "--skills", "aura_overlay", | |
| "--model", "nousresearch/hermes-3-llama-3.1-70b", | |
| "--toolsets", "none", | |
| "--quiet", | |
| "-q", prompt_query | |
| ] | |
| if temp_img_path: | |
| args.extend(["--image", temp_img_path]) | |
| env = os.environ.copy() | |
| env["HERMES_HOME"] = hermes_dir | |
| try: | |
| print(f"AURA Hermes Context Bridge: Invoking Hermes Agent context scan...") | |
| process = await asyncio.create_subprocess_exec( | |
| venv_python, | |
| *args, | |
| stdout=asyncio.subprocess.PIPE, | |
| stderr=asyncio.subprocess.PIPE, | |
| env=env | |
| ) | |
| stdout_bytes, stderr_bytes = await process.communicate() | |
| stdout_text = stdout_bytes.decode("utf-8", errors="ignore") | |
| stderr_text = stderr_bytes.decode("utf-8", errors="ignore") | |
| if process.returncode == 0: | |
| res = parse_json_robust(stdout_text) | |
| if "detected_items" in res and "suggestions" in res: | |
| res["detected_items"] = [str(x)[:25] for x in res["detected_items"]][:4] | |
| clean_sug = [] | |
| for item in res["suggestions"]: | |
| if isinstance(item, dict) and "label" in item and "prompt" in item: | |
| item["label"] = str(item["label"])[:25] | |
| clean_sug.append(item) | |
| elif isinstance(item, str): | |
| clean_sug.append({"label": item[:25], "prompt": item}) | |
| res["suggestions"] = clean_sug[:4] | |
| print("AURA Hermes Context Bridge: Successfully analyzed screen context via Hermes Agent.") | |
| return res | |
| else: | |
| print("AURA Hermes Context Bridge: Invalid structure returned by Hermes Agent. Falling back.") | |
| else: | |
| print(f"AURA Hermes Context Bridge: CLI run failed (exit {process.returncode}). Stderr: {stderr_text}. Falling back.") | |
| except Exception as e: | |
| print(f"AURA Hermes Context Bridge Error: {e}. Falling back.") | |
| finally: | |
| if temp_img_path and os.path.exists(temp_img_path): | |
| try: | |
| os.remove(temp_img_path) | |
| except Exception: | |
| pass | |
| # 2. Fallback to Groq/OpenRouter API Client | |
| try: | |
| from main import async_groq_client, map_model_for_backend | |
| client = async_groq_client | |
| except ImportError: | |
| client = None | |
| map_model_for_backend = lambda x: x | |
| if not client: | |
| print("AURA Context Engine: API client unavailable. Using local rule-based fallback.") | |
| return get_local_fallback_context(active_app, window_title, accessibility_text) | |
| system_prompt = """You are AURA Context Engine, a system-level real-time screen analysis service. | |
| Your job is to analyze the user's active screen/window context and return a structured JSON response. | |
| Detect: | |
| 1. The active application and sub-elements (e.g., active page title, current form fields, code errors, edit tools, etc.). | |
| 2. The user's likely intent or current problem based on the visual screen content, accessibility text, or window title. | |
| Generate 3-4 highly action-oriented, contextual suggestions (short chips) that the user can tap to execute a task. | |
| Be exceptionally direct, helpful, and premium. Avoid generic options. | |
| Suggestions must be tailored to what is visible. For example: | |
| - In VS Code with a syntax error: suggestion "Explain error in auth_service.ts" | |
| - In Chrome reading an article: suggestion "Summarize this article" | |
| - In Photoshop editing: suggestion "Blend layers smoothly" | |
| - In a Login form: suggestion "Auto-generate secure password" or "Resolve password error" | |
| Output MUST be a JSON object with this exact schema: | |
| { | |
| "detected_items": ["tag1", "tag2", "tag3"], | |
| "suggestions": [ | |
| {"label": "Short Action Button Label", "prompt": "The detailed query that is executed when user clicks this button"} | |
| ] | |
| } | |
| Do not write any markdown wrappers, preambles, or postambles. Output ONLY valid JSON.""" | |
| try: | |
| has_img = bool(screenshot_b64) | |
| model = GROQ_VISION_MODEL if has_img else OVERLAY_SLM_MODEL | |
| user_content = f"Active App: {active_app or 'Unknown'}\nWindow Title: {window_title or 'Unknown'}\nAccessibility Text: {accessibility_text or ''}" | |
| if has_img: | |
| clean_b64 = screenshot_b64.split(",")[1] if "," in screenshot_b64 else screenshot_b64 | |
| messages = [ | |
| {"role": "system", "content": system_prompt}, | |
| { | |
| "role": "user", | |
| "content": [ | |
| {"type": "text", "text": user_content}, | |
| { | |
| "type": "image_url", | |
| "image_url": { | |
| "url": f"data:image/jpeg;base64,{clean_b64}" | |
| } | |
| } | |
| ] | |
| } | |
| ] | |
| else: | |
| messages = [ | |
| {"role": "system", "content": system_prompt}, | |
| {"role": "user", "content": user_content} | |
| ] | |
| # Call API client | |
| response = await client.chat.completions.create( | |
| model=map_model_for_backend(model), | |
| messages=messages, | |
| temperature=0.2, | |
| max_tokens=600, | |
| response_format={"type": "json_object"} if not has_img else None | |
| ) | |
| raw_text = response.choices[0].message.content | |
| res = parse_json_robust(raw_text) | |
| if "detected_items" in res and "suggestions" in res: | |
| res["detected_items"] = [str(x)[:25] for x in res["detected_items"]][:4] | |
| clean_sug = [] | |
| for item in res["suggestions"]: | |
| if isinstance(item, dict) and "label" in item and "prompt" in item: | |
| item["label"] = str(item["label"])[:25] | |
| clean_sug.append(item) | |
| elif isinstance(item, str): | |
| clean_sug.append({"label": item[:25], "prompt": item}) | |
| res["suggestions"] = clean_sug[:4] | |
| return res | |
| except Exception as e: | |
| print(f"AURA Context Engine fallback error: {e}. Using local rule-based fallback.") | |
| return get_local_fallback_context(active_app, window_title, accessibility_text) | |