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:
+94
-7
@@ -63,6 +63,14 @@ def _prepare_context(prefix: str, suffix: str) -> Tuple[str, str]:
|
||||
|
||||
|
||||
FENCE_LINE_RE = re.compile(r"^[ \t]*```.*$")
|
||||
FENCE_INFO_RE = re.compile(r"^[ \t]*```[ \t]*(.*)$")
|
||||
MERMAID_CONTEXT_RE = re.compile(
|
||||
r"```[ \t]*mermaid\b|"
|
||||
r"\b(flowchart|sequencediagram|classdiagram|statediagram(?:-v2)?|"
|
||||
r"erdiagram|journey|gantt|pie|mindmap|timeline|gitgraph|quadrantchart|xychart-beta)\b|"
|
||||
r"\bgraph[ \t]+(TD|TB|BT|RL|LR)\b",
|
||||
re.IGNORECASE,
|
||||
)
|
||||
|
||||
|
||||
def _cursor_in_fenced_code_block(prefix: str) -> bool:
|
||||
@@ -71,12 +79,48 @@ def _cursor_in_fenced_code_block(prefix: str) -> bool:
|
||||
The state is computed by toggling on each markdown fence line that matches:
|
||||
^[ \t]*```.*$
|
||||
"""
|
||||
return _active_fence_language(prefix) != "none"
|
||||
|
||||
|
||||
def _active_fence_language(prefix: str) -> str:
|
||||
"""
|
||||
Return active fence language at cursor based on prefix.
|
||||
- "none": cursor is outside fenced code block
|
||||
- "unknown": cursor is inside a fence without language tag
|
||||
- "<language>": cursor is inside a fenced block with language tag
|
||||
"""
|
||||
normalized = _normalize_newlines(prefix)
|
||||
in_fence = False
|
||||
active_language = "none"
|
||||
for line in normalized.split("\n"):
|
||||
if FENCE_LINE_RE.match(line):
|
||||
in_fence = not in_fence
|
||||
return in_fence
|
||||
if in_fence:
|
||||
in_fence = False
|
||||
active_language = "none"
|
||||
else:
|
||||
info_match = FENCE_INFO_RE.match(line)
|
||||
info = info_match.group(1).strip() if info_match else ""
|
||||
if not info:
|
||||
active_language = "unknown"
|
||||
else:
|
||||
first_token = info.split()[0]
|
||||
lang_chars = []
|
||||
for ch in first_token.strip():
|
||||
if ch.isalnum() or ch in "-_+.":
|
||||
lang_chars.append(ch)
|
||||
active_language = "".join(lang_chars)[:32].lower() or "unknown"
|
||||
in_fence = True
|
||||
return active_language if in_fence else "none"
|
||||
|
||||
|
||||
def _is_mermaid_context(prefix: str, suffix: str, cursor_fence_language: str) -> bool:
|
||||
if cursor_fence_language == "mermaid":
|
||||
return True
|
||||
|
||||
prefix_tail = (prefix or "")[-1200:]
|
||||
suffix_head = (suffix or "")[:400]
|
||||
combined = f"{prefix_tail}\n{suffix_head}"
|
||||
return MERMAID_CONTEXT_RE.search(combined) is not None
|
||||
|
||||
|
||||
def prepare_prompt_context(prefix: str, suffix: str) -> Tuple[str, str]:
|
||||
@@ -113,22 +157,36 @@ Hard constraints you must follow:
|
||||
- Do not output code snippets as inline backticks.
|
||||
- Choose the language tag from context (no default fallback tag instruction).
|
||||
|
||||
4) Boundary newline repair:
|
||||
4) Mermaid-specific completion rules:
|
||||
- Read CURSOR_FENCE_LANGUAGE and MERMAID_CONTEXT from the user prompt.
|
||||
- If CURSOR_FENCE_LANGUAGE=mermaid:
|
||||
- Output Mermaid statements only.
|
||||
- Never output triple backticks.
|
||||
- Never output prose explanations.
|
||||
- If CURSOR_IN_FENCED_CODE_BLOCK=false and MERMAID_CONTEXT=true:
|
||||
- Output a complete Mermaid fenced block:
|
||||
```mermaid
|
||||
...
|
||||
```
|
||||
- Keep Mermaid syntax valid and concise.
|
||||
- Never mix Mermaid code and explanatory narration in one output.
|
||||
|
||||
5) Boundary newline repair:
|
||||
- Read PREFIX_ENDS_WITH_NEWLINE and SUFFIX_STARTS_WITH_NEWLINE from the user prompt.
|
||||
- Carefully reason about whether OUTPUT should start or end with a newline.
|
||||
- If PREFIX lacks a required boundary newline, add it at OUTPUT start.
|
||||
- If SUFFIX lacks a required boundary newline, add it at OUTPUT end.
|
||||
- Ensure PREFIX + OUTPUT + SUFFIX is structurally natural.
|
||||
|
||||
5) Context stitching:
|
||||
6) Context stitching:
|
||||
- Do not repeat text that already appears at the start of SUFFIX.
|
||||
- Preserve nearby language, tone, punctuation, indentation, and markdown structure.
|
||||
- Continue existing structures naturally (lists, tables, block quotes, headings).
|
||||
|
||||
6) OCR safety:
|
||||
7) OCR safety:
|
||||
- PREFIX may include hidden OCR metadata tags like <OCR:...>.
|
||||
- Never output any OCR tag.
|
||||
- Never output strings containing <OCR: or > as OCR artifacts."""
|
||||
- Never output OCR tag fragments such as <OCR:...>."""
|
||||
return system_prompt.strip()
|
||||
|
||||
|
||||
@@ -227,6 +285,29 @@ The area is $A = \\pi r^2$.
|
||||
|
||||
```javascript
|
||||
const area = (r) => Math.PI * r * r;
|
||||
```
|
||||
|
||||
[EX13] Cursor inside mermaid fence: no backticks, mermaid lines only
|
||||
CURSOR_IN_FENCED_CODE_BLOCK=true
|
||||
CURSOR_FENCE_LANGUAGE=mermaid
|
||||
<PREFIX>```mermaid
|
||||
flowchart TD
|
||||
A[Start] --> </PREFIX>
|
||||
<SUFFIX>
|
||||
```</SUFFIX>
|
||||
Expected OUTPUT:
|
||||
B{Valid?}
|
||||
B -->|Yes| C[Done]
|
||||
|
||||
[EX14] Mermaid context outside fence: return full mermaid block
|
||||
CURSOR_IN_FENCED_CODE_BLOCK=false
|
||||
MERMAID_CONTEXT=true
|
||||
<PREFIX>Please provide a simple release pipeline diagram.</PREFIX>
|
||||
<SUFFIX></SUFFIX>
|
||||
Expected OUTPUT:
|
||||
```mermaid
|
||||
flowchart LR
|
||||
Build --> Test --> Deploy
|
||||
```"""
|
||||
|
||||
|
||||
@@ -243,7 +324,11 @@ def build_completion_prompts(
|
||||
recent_prefix = _normalize_newlines(recent_prefix)
|
||||
recent_suffix = _normalize_newlines(recent_suffix)
|
||||
|
||||
cursor_in_fenced_code_block = _cursor_in_fenced_code_block(recent_prefix)
|
||||
cursor_fence_language = _active_fence_language(recent_prefix)
|
||||
cursor_in_fenced_code_block = cursor_fence_language != "none"
|
||||
mermaid_context = _is_mermaid_context(
|
||||
recent_prefix, recent_suffix, cursor_fence_language
|
||||
)
|
||||
prefix_ends_with_newline = recent_prefix.endswith("\n")
|
||||
suffix_starts_with_newline = recent_suffix.startswith("\n")
|
||||
|
||||
@@ -268,6 +353,8 @@ Editor language id: {safe_language_id}
|
||||
|
||||
Completion state flags:
|
||||
- CURSOR_IN_FENCED_CODE_BLOCK: {"true" if cursor_in_fenced_code_block else "false"}
|
||||
- CURSOR_FENCE_LANGUAGE: {cursor_fence_language}
|
||||
- MERMAID_CONTEXT: {"true" if mermaid_context else "false"}
|
||||
- PREFIX_ENDS_WITH_NEWLINE: {"true" if prefix_ends_with_newline else "false"}
|
||||
- SUFFIX_STARTS_WITH_NEWLINE: {"true" if suffix_starts_with_newline else "false"}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user