import json import os import re import logging import mimetypes import base64 from datetime import datetime from contextlib import asynccontextmanager from fastapi import HTTPException from fastapi.responses import Response from backend.auth import get_auth_token from jsonschema import validate as schema_validate, ValidationError as SchemaValidationError from backend.config.Apps import SubApp from backend.apps.outputs.models import ( Output, OutputCreate, OutputUpdate, OutputExecute, OutputExecuteResult, AutoRunRequest, AutoRunConfig, AutoRunAgentRequest, WorkspaceSeedRequest, ) from backend.apps.outputs.executor import execute_backend_code from backend.apps.outputs.view_builder_templates import VIEW_BUILDER_SKILL, VIEW_TEMPLATE_FILES from backend.apps.settings.settings import load_settings logger = logging.getLogger(__name__) def _get_anthropic_client(api_model: str | None = None): """Create an AsyncAnthropic client using the API key from app settings. When `api_model` is provided and carries a 9Router prefix (cc/, cx/, gc/), the client is pointed at 9Router so non-Anthropic aux calls don't 400 on api.anthropic.com. Without an api_model we fall back to the default connection-mode-driven client. """ from backend.apps.settings.credentials import ( get_anthropic_client, get_anthropic_client_for_model, ) settings = load_settings() if api_model: return get_anthropic_client_for_model(settings, api_model) return get_anthropic_client(settings) def _validate_against_schema(data: dict, schema: dict) -> str | None: """Validate *data* against *schema*. Return an error string or None.""" try: schema_validate(instance=data, schema=schema) return None except SchemaValidationError as exc: path = " -> ".join(str(p) for p in exc.absolute_path) if exc.absolute_path else "(root)" return f"Schema validation failed at {path}: {exc.message}" from backend.config.paths import OUTPUTS_DIR as DATA_DIR, OUTPUTS_WORKSPACE_DIR as WORKSPACE_DIR def _build_data_injection(input_json: str, result_json: str) -> str: """Build a " ) def _inject_data_into_html(html: str, input_json: str = "{}", result_json: str = "null") -> str: injection = _build_data_injection(input_json, result_json) if "" in html: return html.replace("", f"{injection}\n", 1) if " str: """Append `?token=` to every relative href/src in the served HTML. Browsers strip the parent iframe URL's query string before resolving relative `` / `