feat(api): add completion request cancellation and mermaid rendering

Add support for cancelling in-progress LLM completion requests via new /v1/completions/cancel endpoint with task tracking. Implement mermaid diagram rendering in the Milkdown editor with a new mermaidPlugin. Update copilotPlugin to properly abort requests with descriptive reasons. Refactor settings panel to handle system theme changes reactively. Add camera capture support for image uploads.
This commit is contained in:
2026-02-25 19:00:17 +08:00
parent e28125079c
commit 637456ee34
13 changed files with 2013 additions and 147 deletions
+118
View File
@@ -0,0 +1,118 @@
import asyncio
import importlib
import sys
import threading
from pathlib import Path
import pytest
from fastapi.testclient import TestClient
BACKEND_DIR = Path(__file__).resolve().parents[1]
if str(BACKEND_DIR) not in sys.path:
sys.path.insert(0, str(BACKEND_DIR))
try:
main = importlib.import_module("main")
except ModuleNotFoundError:
pytest.skip("main module dependencies are not available", allow_module_level=True)
API_KEY_HEADERS = {"X-API-Key": "your-secret-key-here"}
def _completion_payload():
return {
"prefix": "hello",
"suffix": "",
"languageId": "markdown",
"model_thinking": "low",
"privacy_mode": True,
}
def test_cancel_endpoint_cancels_running_task(monkeypatch):
main.ACTIVE_COMPLETIONS.clear()
started = threading.Event()
cancelled = threading.Event()
async def fake_call_ollama(*args, **kwargs):
started.set()
try:
while True:
await asyncio.sleep(0.05)
except asyncio.CancelledError:
cancelled.set()
raise
monkeypatch.setattr(main, "call_ollama", fake_call_ollama)
monkeypatch.setattr(main, "build_completion_prompts", lambda *a, **k: ("system", "user"))
monkeypatch.setattr(main, "prepare_prompt_context", lambda *a, **k: ("prefix", "suffix"))
with TestClient(main.app) as client:
request_id = "req-cancel-1"
completion_headers = {**API_KEY_HEADERS, "X-Request-Id": request_id}
response_box = {}
def send_completion():
response_box["response"] = client.post(
"/v1/completions",
headers=completion_headers,
json=_completion_payload(),
)
completion_thread = threading.Thread(target=send_completion, daemon=True)
completion_thread.start()
assert started.wait(timeout=2.0)
cancel_response = client.post(
"/v1/completions/cancel",
headers=API_KEY_HEADERS,
json={"request_id": request_id, "reason": "superseded"},
)
assert cancel_response.status_code == 200
assert cancel_response.json() == {"cancelled": True, "status": "ok"}
completion_thread.join(timeout=5.0)
assert not completion_thread.is_alive()
assert cancelled.wait(timeout=2.0)
completion_response = response_box["response"]
assert completion_response.status_code == 200
assert '"cancelled": true' in completion_response.text
def test_cancel_not_found():
main.ACTIVE_COMPLETIONS.clear()
with TestClient(main.app) as client:
response = client.post(
"/v1/completions/cancel",
headers=API_KEY_HEADERS,
json={"request_id": "missing", "reason": "abort"},
)
assert response.status_code == 200
assert response.json() == {"cancelled": False, "status": "not_found"}
def test_completion_normal_flow(monkeypatch):
main.ACTIVE_COMPLETIONS.clear()
async def fake_call_ollama(*args, **kwargs):
return {"content": "completion text", "think": ""}
monkeypatch.setattr(main, "call_ollama", fake_call_ollama)
monkeypatch.setattr(main, "build_completion_prompts", lambda *a, **k: ("system", "user"))
monkeypatch.setattr(main, "prepare_prompt_context", lambda *a, **k: ("prefix", "suffix"))
with TestClient(main.app) as client:
response = client.post(
"/v1/completions",
headers=API_KEY_HEADERS,
json=_completion_payload(),
)
assert response.status_code == 200
assert '"content": "completion text"' in response.text
assert '"done": true' in response.text
assert main.ACTIVE_COMPLETIONS == {}
+36 -1
View File
@@ -21,7 +21,13 @@ def test_prompt_builds_system_and_user():
assert "$...$" in system_prompt
assert "$$...$$" in system_prompt
assert "```{language}" in system_prompt
assert "Mermaid-specific completion rules" in system_prompt
assert "CURSOR_FENCE_LANGUAGE" in system_prompt
assert "MERMAID_CONTEXT" in system_prompt
assert "Output Mermaid statements only." in system_prompt
assert "CURSOR_IN_FENCED_CODE_BLOCK" in user_prompt
assert "CURSOR_FENCE_LANGUAGE" in user_prompt
assert "MERMAID_CONTEXT" in user_prompt
assert "PREFIX_ENDS_WITH_NEWLINE" in user_prompt
assert "SUFFIX_STARTS_WITH_NEWLINE" in user_prompt
@@ -33,12 +39,22 @@ def test_cursor_in_fence_detection():
assert prompt._cursor_in_fenced_code_block("text ```not-a-fence``` tail") is False
def test_active_fence_language_detection():
assert prompt._active_fence_language("") == "none"
assert prompt._active_fence_language("```mermaid\nflowchart TD\nA-->B\n") == "mermaid"
assert prompt._active_fence_language("```python\nprint('x')\n") == "python"
assert prompt._active_fence_language("```\nline\n") == "unknown"
assert prompt._active_fence_language("```mermaid\nA-->B\n```\n") == "none"
def test_newline_flags():
_, user_prompt_a = prompt.build_completion_prompts(
prefix="Hello",
suffix="World",
)
assert "CURSOR_IN_FENCED_CODE_BLOCK: false" in user_prompt_a
assert "CURSOR_FENCE_LANGUAGE: none" in user_prompt_a
assert "MERMAID_CONTEXT: false" in user_prompt_a
assert "PREFIX_ENDS_WITH_NEWLINE: false" in user_prompt_a
assert "SUFFIX_STARTS_WITH_NEWLINE: false" in user_prompt_a
@@ -46,11 +62,30 @@ def test_newline_flags():
prefix="Hello\n",
suffix="\nWorld",
)
assert "CURSOR_FENCE_LANGUAGE: none" in user_prompt_b
assert "PREFIX_ENDS_WITH_NEWLINE: true" in user_prompt_b
assert "SUFFIX_STARTS_WITH_NEWLINE: true" in user_prompt_b
def test_mermaid_context_flags():
_, prompt_in_mermaid = prompt.build_completion_prompts(
prefix="```mermaid\nflowchart TD\nA --> ",
suffix="\n```",
)
assert "CURSOR_IN_FENCED_CODE_BLOCK: true" in prompt_in_mermaid
assert "CURSOR_FENCE_LANGUAGE: mermaid" in prompt_in_mermaid
assert "MERMAID_CONTEXT: true" in prompt_in_mermaid
_, prompt_mermaid_keyword = prompt.build_completion_prompts(
prefix="Please draw a mermaid flowchart for deploy pipeline.",
suffix="",
)
assert "CURSOR_IN_FENCED_CODE_BLOCK: false" in prompt_mermaid_keyword
assert "CURSOR_FENCE_LANGUAGE: none" in prompt_mermaid_keyword
assert "MERMAID_CONTEXT: true" in prompt_mermaid_keyword
def test_examples_coverage():
_, user_prompt = prompt.build_completion_prompts(prefix="", suffix="")
for ex in range(1, 13):
for ex in range(1, 15):
assert f"[EX{ex:02d}]" in user_prompt