Compare commits

..
Author SHA1 Message Date
Typosaurus f674f2ed6c ticket #102 attempt 1 2026-07-19 21:13:37 +00:00
8 changed files with 51 additions and 10 deletions
+2 -2
View File
@@ -258,13 +258,13 @@ class NotificationDefaultForm(BaseModel):
class AiCorrectionForm(BaseModel):
enabled: bool = False
sync: bool = False
prompt: str = Field(default=DEFAULT_CORRECTION_PROMPT)
prompt: str = Field(default=DEFAULT_CORRECTION_PROMPT, max_length=20000)
class AiModifierForm(BaseModel):
enabled: bool = False
sync: bool = False
prompt: str = Field(default=DEFAULT_MODIFIER_PROMPT)
prompt: str = Field(default=DEFAULT_MODIFIER_PROMPT, max_length=20000)
class InteractionsForm(BaseModel):
@@ -61,7 +61,7 @@ AI_CORRECTION_ACTIONS: tuple[Action, ...] = (
),
arg(
"prompt",
"The correction instruction. Omit to keep the current one.",
"The correction instruction (max 20000 chars). Omit to keep the current one.",
),
),
),
@@ -64,7 +64,7 @@ AI_MODIFIER_ACTIONS: tuple[Action, ...] = (
),
arg(
"prompt",
"The modifier instruction. Omit to keep the current one.",
"The modifier instruction (max 20000 chars). Omit to keep the current one.",
),
),
),
@@ -70,7 +70,7 @@ class AiCorrectionController:
if prompt_raw is None:
prompt = user.get("ai_correction_prompt") or DEFAULT_CORRECTION_PROMPT
else:
prompt = str(prompt_raw).strip() or DEFAULT_CORRECTION_PROMPT
prompt = str(prompt_raw).strip()[:20000] or DEFAULT_CORRECTION_PROMPT
get_table("users").update(
{
"uid": self._owner_id,
@@ -70,7 +70,7 @@ class AiModifierController:
if prompt_raw is None:
prompt = user.get("ai_modifier_prompt") or DEFAULT_MODIFIER_PROMPT
else:
prompt = str(prompt_raw).strip() or DEFAULT_MODIFIER_PROMPT
prompt = str(prompt_raw).strip()[:20000] or DEFAULT_MODIFIER_PROMPT
get_table("users").update(
{
"uid": self._owner_id,
+39 -2
View File
@@ -2,8 +2,10 @@
from __future__ import annotations
import socket
import subprocess
import sys
import time
from devplacepy.config import XMLRPC_BIND, XMLRPC_PORT
from devplacepy.services.base import BaseService
@@ -25,20 +27,48 @@ class XmlrpcService(BaseService):
def __init__(self) -> None:
super().__init__("xmlrpc", interval_seconds=XMLRPC_INTERVAL_SECONDS)
self._process: subprocess.Popen | None = None
self._last_stderr: str | None = None
def _alive(self) -> bool:
return self._process is not None and self._process.poll() is None
def _spawn(self) -> None:
self._last_stderr = None
self._process = subprocess.Popen(
[sys.executable, "-m", SERVER_MODULE],
stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL,
stderr=subprocess.PIPE,
)
self.log(
f"Forking XML-RPC server started (pid {self._process.pid}) on "
f"Forking XML-RPC server spawning (pid {self._process.pid}) on "
f"{XMLRPC_BIND}:{XMLRPC_PORT}"
)
time.sleep(0.5)
if self._process.poll() is not None:
stderr_data = self._process.communicate()[1]
if stderr_data:
self._last_stderr = stderr_data.decode("utf-8", errors="replace")
self.log(
f"Forking XML-RPC server died after spawn (pid {self._process.pid}, "
f"exit code {self._process.returncode})"
)
if self._last_stderr:
for line in self._last_stderr.strip().split("\n"):
self.log(f" stderr: {line}")
return
try:
sock = socket.create_connection((XMLRPC_BIND, XMLRPC_PORT), timeout=1)
sock.close()
except (OSError, socket.timeout):
self.log(
f"Forking XML-RPC server not yet ready (pid {self._process.pid}) "
f"on {XMLRPC_BIND}:{XMLRPC_PORT}"
)
else:
self.log(
f"Forking XML-RPC server started (pid {self._process.pid}) on "
f"{XMLRPC_BIND}:{XMLRPC_PORT}"
)
def _terminate(self) -> None:
if not self._alive():
@@ -65,6 +95,13 @@ class XmlrpcService(BaseService):
self.log(f"XML-RPC server healthy (pid {self._process.pid})")
return
self.log("XML-RPC server not running, starting it")
if self._process is not None:
self.log(
f"Previous process exited with code {self._process.returncode}"
)
if self._last_stderr:
for line in self._last_stderr.strip().split("\n"):
self.log(f" last stderr: {line}")
self._spawn()
def collect_metrics(self) -> dict:
+4
View File
@@ -3,6 +3,7 @@
from __future__ import annotations
import logging
import sys
from socketserver import ForkingMixIn
from xmlrpc.server import SimpleXMLRPCDispatcher, SimpleXMLRPCRequestHandler, SimpleXMLRPCServer
@@ -116,6 +117,9 @@ def main() -> None:
server.serve_forever()
except KeyboardInterrupt:
logger.info("XML-RPC server interrupted")
except Exception:
logging.exception("XML-RPC server crashed with unhandled exception")
sys.exit(1)
finally:
server.server_close()
+2 -2
View File
@@ -226,7 +226,7 @@
<option value="sync" {% if ai_correction_sync %}selected{% endif %}>Synchronously (wait while saving)</option>
</select>
</div>
<textarea data-ai-correction-prompt class="input-sm ai-correction-prompt" rows="3" aria-label="AI content correction prompt">{{ ai_correction_prompt }}</textarea>
<textarea data-ai-correction-prompt class="input-sm ai-correction-prompt" maxlength="20000" rows="3" aria-label="AI content correction prompt">{{ ai_correction_prompt }}</textarea>
<div class="customization-row">
<button type="button" class="btn btn-sm btn-primary" data-ai-correction-save>Save</button>
<span class="ai-correction-status" data-ai-correction-status role="status" aria-live="polite"></span>
@@ -251,7 +251,7 @@
<option value="sync" {% if ai_modifier_sync %}selected{% endif %}>Synchronously (wait while saving)</option>
</select>
</div>
<textarea data-ai-modifier-prompt class="input-sm ai-correction-prompt" rows="3" aria-label="AI modifier prompt">{{ ai_modifier_prompt }}</textarea>
<textarea data-ai-modifier-prompt class="input-sm ai-correction-prompt" maxlength="20000" rows="3" aria-label="AI modifier prompt">{{ ai_modifier_prompt }}</textarea>
<div class="customization-row">
<button type="button" class="btn btn-sm btn-primary" data-ai-modifier-save>Save</button>
<span class="ai-correction-status" data-ai-modifier-status role="status" aria-live="polite"></span>