| | import os |
| | import sys |
| | import time |
| | import subprocess |
| | import urllib.request |
| | import socket |
| | import gradio as gr |
| | import http.client |
| | import urllib.parse |
| | import xml.etree.ElementTree as ET |
| | import tarfile |
| | import shutil |
| |
|
| | |
| | |
| | |
| | REPO_URL = "https://github.com/Automattic/atd-server-next.git" |
| | SERVER_DIR = "atd-server-next" |
| | MODELS_DIR = os.path.join(SERVER_DIR, "models") |
| | JAVA_DIR = "jdk8" |
| | HOST = "127.0.0.1" |
| | PORT = 1049 |
| |
|
| | |
| | JAVA_URL = "https://github.com/adoptium/temurin8-binaries/releases/download/jdk8u392-b08/OpenJDK8U-jre_x64_linux_hotspot_8u392b08.tar.gz" |
| | JAVA_BIN = os.path.join(os.getcwd(), JAVA_DIR, "bin", "java") |
| |
|
| | MODEL_BASE_URL = "https://openatd.svn.wordpress.org/atd-server/models/" |
| | MODEL_FILES = [ |
| | "cnetwork.bin", "cnetwork2.bin", "dictionary.txt", "edits.bin", |
| | "endings.bin", "hnetwork.bin", "hnetwork2.bin", "hnetwork4.bin", |
| | "lexicon.bin", "model.bin", "model.zip", "network3f.bin", |
| | "network3p.bin", "not_misspelled.txt", "stringpool.bin", "trigrams.bin" |
| | ] |
| |
|
| | |
| | |
| | |
| | def install_java8(): |
| | """Downloads and installs portable Java 8.""" |
| | print("--- [PHASE -1] CHECKING JAVA RUNTIME ---") |
| | |
| | |
| | if os.path.exists(JAVA_BIN): |
| | print(f"Portable Java 8 found at: {JAVA_BIN}") |
| | return |
| |
|
| | print("Java 8 not found. Downloading portable runtime (approx 40MB)...") |
| | tar_path = "java8.tar.gz" |
| | |
| | try: |
| | |
| | urllib.request.urlretrieve(JAVA_URL, tar_path) |
| | print("Download complete. Extracting...") |
| | |
| | |
| | with tarfile.open(tar_path, "r:gz") as tar: |
| | |
| | root_name = tar.getnames()[0].split('/')[0] |
| | tar.extractall() |
| | |
| | |
| | if os.path.exists(JAVA_DIR): |
| | shutil.rmtree(JAVA_DIR) |
| | os.rename(root_name, JAVA_DIR) |
| | |
| | |
| | os.remove(tar_path) |
| | |
| | |
| | subprocess.run(["chmod", "+x", JAVA_BIN], check=True) |
| | |
| | print(f"Java 8 successfully installed to {JAVA_DIR}") |
| | |
| | except Exception as e: |
| | print(f"FATAL: Failed to install Java 8. Error: {e}") |
| | sys.exit(1) |
| |
|
| | |
| | |
| | |
| | def setup_server(): |
| | |
| | install_java8() |
| |
|
| | print("\n--- [PHASE 0] CHECKING REPOSITORY ---") |
| | if not os.path.exists(SERVER_DIR): |
| | print(f"Cloning {REPO_URL}...") |
| | subprocess.run(["git", "clone", "--depth", "1", REPO_URL, SERVER_DIR], check=True) |
| |
|
| | print("\n--- [PHASE 1] CHECKING MODELS ---") |
| | if not os.path.exists(MODELS_DIR): |
| | os.makedirs(MODELS_DIR, exist_ok=True) |
| |
|
| | for filename in MODEL_FILES: |
| | filepath = os.path.join(MODELS_DIR, filename) |
| | if not os.path.exists(filepath): |
| | print(f"Downloading {filename}...") |
| | try: |
| | urllib.request.urlretrieve(MODEL_BASE_URL + filename, filepath) |
| | except Exception as e: |
| | print(f" -> FAILED: {e}") |
| |
|
| | print("\n--- [PHASE 2] COMPILING RULES ---") |
| | |
| | if not os.path.exists(os.path.join(SERVER_DIR, "models", "grammar.bin")): |
| | print("Compiling rules using Java 8...") |
| | |
| | cp = "lib/sleep.jar:lib/moconti.jar:lib/spellutils.jar" |
| | |
| | try: |
| | subprocess.run( |
| | [ |
| | JAVA_BIN, |
| | "-Datd.lowmem=true", |
| | "-Xmx1024M", |
| | "-classpath", cp, |
| | "sleep.console.TextConsole", |
| | "utils/rules/rules.sl" |
| | ], |
| | cwd=SERVER_DIR, |
| | check=True |
| | ) |
| | print("Rules compiled successfully.") |
| | except subprocess.CalledProcessError as e: |
| | print(f"Rule compilation warning: {e}") |
| |
|
| | |
| | |
| | |
| | def start_backend(): |
| | print("\n--- [PHASE 3] STARTING SERVER ---") |
| | |
| | classpath = "lib/sleep.jar:lib/moconti.jar:lib/spellutils.jar" |
| | sleep_cp = "lib:service/code" |
| |
|
| | cmd = [ |
| | JAVA_BIN, |
| | "-Dfile.encoding=UTF-8", |
| | "-XX:+AggressiveHeap", |
| | "-XX:+UseParallelGC", |
| | "-Datd.lowmem=true", |
| | "-Dbind.interface=127.0.0.1", |
| | f"-Dserver.port={PORT}", |
| | f"-Dsleep.classpath={sleep_cp}", |
| | "-Dsleep.debug=24", |
| | "-classpath", classpath, |
| | "httpd.Moconti", |
| | "atdconfig.sl" |
| | ] |
| | |
| | print(f"Launching with: {JAVA_BIN}") |
| | return subprocess.Popen(cmd, cwd=SERVER_DIR) |
| |
|
| | def wait_for_port(timeout=60): |
| | print(f"Waiting for port {PORT}...") |
| | start = time.time() |
| | while time.time() - start < timeout: |
| | try: |
| | with socket.create_connection((HOST, PORT), timeout=1): |
| | print("Server is Online!") |
| | return True |
| | except (ConnectionRefusedError, OSError): |
| | time.sleep(1) |
| | return False |
| |
|
| | |
| | |
| | |
| | class AtDClient: |
| | def check_document(self, text): |
| | try: |
| | conn = http.client.HTTPConnection(HOST, PORT, timeout=5) |
| | params = urllib.parse.urlencode({'key': 'gradio', 'data': text}) |
| | headers = {"Content-Type": "application/x-www-form-urlencoded"} |
| | conn.request("POST", "/checkDocument", params, headers) |
| | resp = conn.getresponse() |
| | if resp.status != 200: return [] |
| | |
| | xml_text = resp.read().decode('utf-8', errors='ignore') |
| | if not xml_text.strip().startswith("<"): return [] |
| |
|
| | root = ET.fromstring(xml_text) |
| | errors = [] |
| | for e in root.findall('error'): |
| | err = { |
| | 'string': e.find('string').text, |
| | 'description': e.find('description').text, |
| | 'type': e.find('type').text, |
| | 'precontext': e.find('precontext').text or "", |
| | 'suggestions': [] |
| | } |
| | sug = e.find('suggestions') |
| | if sug is not None: |
| | err['suggestions'] = [o.text for o in sug.findall('option') if o.text] |
| | errors.append(err) |
| | return errors |
| | except Exception as e: |
| | print(f"Client Error: {e}") |
| | return [] |
| |
|
| | client = AtDClient() |
| |
|
| | def analyze_text(text): |
| | if not text.strip(): return [] |
| | errors = client.check_document(text) |
| | output = [] |
| | last_pos = 0 |
| | |
| | for err in errors: |
| | word = err['string'] |
| | search_start = last_pos |
| | if err['precontext']: |
| | context_idx = text.find(err['precontext'], last_pos) |
| | if context_idx != -1: |
| | search_start = context_idx + len(err['precontext']) |
| |
|
| | idx = text.find(word, search_start) |
| | if idx != -1: |
| | if idx > last_pos: |
| | output.append((text[last_pos:idx], None)) |
| | |
| | label = f"{err['type']}: {err['description']}" |
| | if err['suggestions']: |
| | label += f" -> {', '.join(err['suggestions'][:3])}" |
| | |
| | output.append((text[idx:idx+len(word)], label)) |
| | last_pos = idx + len(word) |
| | |
| | if last_pos < len(text): |
| | output.append((text[last_pos:], None)) |
| | return output |
| |
|
| | if __name__ == "__main__": |
| | setup_server() |
| | server_proc = start_backend() |
| | |
| | time.sleep(5) |
| | if server_proc.poll() is not None: |
| | print("FATAL: Java server exited immediately.") |
| | sys.exit(1) |
| |
|
| | if wait_for_port(timeout=120): |
| | with gr.Blocks(title="AtD Self-Hosted") as demo: |
| | gr.Markdown("# 🛡️ After The Deadline") |
| | gr.Markdown("Running on Portable Java 8") |
| | |
| | with gr.Row(): |
| | inp = gr.Textbox(label="Input", placeholder="Type here... e.g., I has a error.", lines=6) |
| | out = gr.HighlightedText(label="Analysis", combine_adjacent=True) |
| | |
| | btn = gr.Button("Check Text", variant="primary") |
| | btn.click(analyze_text, inputs=inp, outputs=out) |
| | |
| | demo.launch(server_name="0.0.0.0", server_port=7860) |
| | else: |
| | print("FATAL: Server did not start (Timeout).") |
| | server_proc.kill() |