feat: LLM 应用网页开发及内联建议功能实现
This commit is contained in:
+12
-316
@@ -2,6 +2,8 @@ from datetime import datetime, timedelta, timezone
|
||||
import re
|
||||
from typing import Protocol, Tuple, runtime_checkable
|
||||
|
||||
from prompts import get_language_guidance_map, get_system_prompt_template, get_inline_examples
|
||||
|
||||
|
||||
@runtime_checkable
|
||||
class UserPreferences(Protocol):
|
||||
@@ -224,339 +226,33 @@ def _canonical_language_id(language_id: str) -> str:
|
||||
_JS_LANGS = {"javascript", "typescript"}
|
||||
_CODE_LANGS = {"python", "go", "rust", "java", "kotlin", "swift", "ruby", "php", "lua", "c", "cpp", "csharp", "r", "matlab", "dart"}
|
||||
|
||||
_LANG_GUIDANCE = {
|
||||
"mermaid": """
|
||||
Language-specific guidance (mermaid):
|
||||
- Output valid Mermaid syntax only.
|
||||
- Prefer concise, syntactically correct diagram statements.
|
||||
- Avoid prose unless the user prompt explicitly requires it.""",
|
||||
"latex": """
|
||||
Language-specific guidance (latex):
|
||||
- Output LaTeX math content only when completing LaTeX.
|
||||
- If CURSOR_IN_FENCED_CODE_BLOCK=true and CURSOR_FENCE_LANGUAGE is latex/tex/katex:
|
||||
- Output raw LaTeX lines only.
|
||||
- Do not wrap with $ or $$.""",
|
||||
"json": """
|
||||
Language-specific guidance (json):
|
||||
- Output strict JSON only (no comments, no trailing commas).
|
||||
- Ensure valid quotes and braces.""",
|
||||
"yaml": """
|
||||
Language-specific guidance (yaml):
|
||||
- Output valid YAML only.
|
||||
- Use consistent indentation and avoid tabs.""",
|
||||
"toml": """
|
||||
Language-specific guidance (toml):
|
||||
- Output valid TOML only.
|
||||
- Keep key types consistent.""",
|
||||
"ini": """
|
||||
Language-specific guidance (ini):
|
||||
- Output valid INI only.
|
||||
- Keep section headers and key=value pairs consistent.""",
|
||||
"sql": """
|
||||
Language-specific guidance (sql):
|
||||
- Output a single, valid SQL statement unless context requires multiple.
|
||||
- Prefer ANSI SQL when dialect is unclear.""",
|
||||
"bash": """
|
||||
Language-specific guidance (bash):
|
||||
- Output POSIX-compatible shell when possible.
|
||||
- Avoid interactive prompts or destructive commands unless requested.""",
|
||||
"powershell": """
|
||||
Language-specific guidance (powershell):
|
||||
- Output valid PowerShell commands.
|
||||
- Avoid destructive commands unless explicitly requested.""",
|
||||
"html": """
|
||||
Language-specific guidance (html):
|
||||
- Output valid HTML only.
|
||||
- Keep markup minimal and well-formed.""",
|
||||
"css": """
|
||||
Language-specific guidance (css):
|
||||
- Output valid CSS only.
|
||||
- Use concise, readable selectors.""",
|
||||
"diff": """
|
||||
Language-specific guidance (diff):
|
||||
- Output a unified diff only.
|
||||
- Ensure @@ hunk headers and +/- lines are consistent.""",
|
||||
"regex": """
|
||||
Language-specific guidance (regex):
|
||||
- Output the regex pattern only.
|
||||
- Avoid delimiters unless explicitly requested.""",
|
||||
"text": """
|
||||
Language-specific guidance (text):
|
||||
- Output plain text only.
|
||||
- Avoid markdown formatting unless explicitly asked.""",
|
||||
"xml": """
|
||||
Language-specific guidance (xml):
|
||||
- Output well-formed XML only.
|
||||
- Ensure matching tags and proper escaping.""",
|
||||
"dockerfile": """
|
||||
Language-specific guidance (dockerfile):
|
||||
- Output valid Dockerfile instructions only.
|
||||
- Keep layers minimal and ordered logically.""",
|
||||
"makefile": """
|
||||
Language-specific guidance (makefile):
|
||||
- Output valid Makefile syntax only.
|
||||
- Use tabs for recipe lines.""",
|
||||
}
|
||||
|
||||
_GENERIC_CODE = """
|
||||
Language-specific guidance ({lang}):
|
||||
- Output valid {lang} code.
|
||||
- Avoid prose unless context clearly expects comments or docstrings."""
|
||||
|
||||
_JS_CODE = """
|
||||
Language-specific guidance ({lang}):
|
||||
- Output valid {lang} code.
|
||||
- Prefer modern syntax and avoid prose unless comments are needed."""
|
||||
|
||||
|
||||
def _language_guidance(language_id: str) -> str:
|
||||
canonical = _canonical_language_id(language_id)
|
||||
if canonical == "markdown":
|
||||
return ""
|
||||
guidance = _LANG_GUIDANCE.get(canonical)
|
||||
guidance_map = get_language_guidance_map()
|
||||
guidance = guidance_map.get(canonical)
|
||||
if guidance:
|
||||
return guidance
|
||||
if canonical in _JS_LANGS:
|
||||
return _JS_CODE.format(lang=canonical)
|
||||
return guidance_map.get("_js_code", "").replace("{lang}", canonical)
|
||||
if canonical in _CODE_LANGS:
|
||||
return _GENERIC_CODE.format(lang=canonical)
|
||||
return _GENERIC_CODE.format(lang=canonical)
|
||||
return guidance_map.get("_generic_code", "").replace("{lang}", canonical)
|
||||
return guidance_map.get("_generic_code", "").replace("{lang}", canonical)
|
||||
|
||||
|
||||
def build_inline_system_prompt(language_id: str = "markdown") -> str:
|
||||
safe_language_id = _canonical_language_id(language_id)
|
||||
language_guidance = _language_guidance(safe_language_id)
|
||||
|
||||
system_prompt = f"""You are an inline completion engine for a {safe_language_id} editor with ghost-text suggestions.
|
||||
|
||||
Return only the insertion text that should be placed between PREFIX and SUFFIX.
|
||||
|
||||
CORE PRINCIPLE: Output insertion text only. No explanations, no meta labels, no wrapper quotes.
|
||||
|
||||
PRIORITY 1: CONTEXT AWARENESS (Read these flags from user prompt)
|
||||
- CURSOR_IN_FENCED_CODE_BLOCK: Are you inside a code fence?
|
||||
- CURSOR_FENCE_LANGUAGE: What language is the current fence?
|
||||
- PREFIX_ENDS_WITH_NEWLINE: Does prefix end with newline?
|
||||
- SUFFIX_STARTS_WITH_NEWLINE: Does suffix start with newline?
|
||||
- MERMAID_CONTEXT: Is this a Mermaid diagram context?
|
||||
|
||||
PRIORITY 2: SPECIALIZED CONTENT RULES
|
||||
|
||||
2.1 Code Block Handling:
|
||||
If CURSOR_IN_FENCED_CODE_BLOCK=true:
|
||||
- You are inside a code fence
|
||||
- Output code lines ONLY (no triple backticks)
|
||||
- Use single \\n for code line separation
|
||||
|
||||
If CURSOR_IN_FENCED_CODE_BLOCK=false and code needed:
|
||||
- Wrap code in fenced block with language tag:
|
||||
```{{language}}
|
||||
code here
|
||||
```
|
||||
- Never use inline backticks for code snippets
|
||||
|
||||
2.2 Math Formatting (KaTeX):
|
||||
- Inline math: wrap with $...$
|
||||
- Block math: wrap with $$...$$
|
||||
- Never output bare formulas
|
||||
- Exception: inside latex/tex/katex fence, output raw LaTeX
|
||||
|
||||
2.3 Mermaid Diagrams:
|
||||
If CURSOR_FENCE_LANGUAGE=mermaid:
|
||||
- Output Mermaid syntax ONLY
|
||||
- No backticks, no explanations
|
||||
|
||||
If MERMAID_CONTEXT=true and outside fence:
|
||||
- Output complete fenced block:
|
||||
```mermaid
|
||||
diagram syntax
|
||||
```
|
||||
|
||||
PRIORITY 3: MARKDOWN STRUCTURE
|
||||
|
||||
3.1 Newline Semantics:
|
||||
- Single \\n: soft break (same paragraph, renders as space or <br>)
|
||||
- Double \\n\\n: hard break (new paragraph/block)
|
||||
- Use \\n\\n for: new paragraphs, before headings, starting lists/tables
|
||||
- Use \\n for: continuation within blocks (list items, table cells)
|
||||
- Exception: inside code blocks, use \\n freely for code lines
|
||||
|
||||
3.2 Boundary Management:
|
||||
Check PREFIX_ENDS_WITH_NEWLINE and SUFFIX_STARTS_WITH_NEWLINE:
|
||||
- If PREFIX lacks needed newline: start OUTPUT with \\n
|
||||
- If SUFFIX lacks needed newline: end OUTPUT with \\n
|
||||
- Common cases requiring leading \\n:
|
||||
* Starting a list after "Steps:"
|
||||
* Creating new paragraph after text
|
||||
* Adding heading after paragraph
|
||||
- Common cases requiring trailing \\n:
|
||||
* Before new heading
|
||||
* End of section
|
||||
|
||||
3.3 Context Stitching:
|
||||
- Never repeat text from SUFFIX beginning
|
||||
- Match PREFIX tone, style, indentation
|
||||
- Continue structures: lists, tables, quotes, headings
|
||||
|
||||
PRIORITY 4: HIDDEN CONTEXT
|
||||
- OCR metadata like <OCR:...> is hidden context
|
||||
- Never copy OCR tags to output
|
||||
- Use OCR content as semantic hint only
|
||||
"""
|
||||
|
||||
template = get_system_prompt_template()
|
||||
system_prompt = template.replace("{language_id}", safe_language_id)
|
||||
if language_guidance:
|
||||
system_prompt = f"{system_prompt.rstrip()}\\n{language_guidance.strip()}"
|
||||
|
||||
system_prompt = f"{system_prompt.rstrip()}\n{language_guidance.strip()}"
|
||||
return system_prompt.strip()
|
||||
|
||||
|
||||
INLINE_EXAMPLES = """=== CATEGORY A: PROSE CONTINUATION ===
|
||||
|
||||
[EX01] Simple prose continuation
|
||||
<PREFIX>The quick brown fox </PREFIX>
|
||||
<SUFFIX>jumps over the lazy dog.</SUFFIX>
|
||||
Expected OUTPUT:
|
||||
moved quietly and then
|
||||
|
||||
[EX02] Avoid repeating suffix
|
||||
<PREFIX>Our launch plan starts with </PREFIX>
|
||||
<SUFFIX>phase one, followed by phase two.</SUFFIX>
|
||||
Expected OUTPUT:
|
||||
careful internal testing before
|
||||
WRONG: phase one starts with (repeats suffix)
|
||||
|
||||
=== CATEGORY B: MARKDOWN STRUCTURES ===
|
||||
|
||||
[EX03] Continue checklist
|
||||
<PREFIX>## TODO
|
||||
- [ ] Buy milk
|
||||
- [ ] </PREFIX>
|
||||
<SUFFIX></SUFFIX>
|
||||
Expected OUTPUT:
|
||||
Write release notes and share draft with team
|
||||
|
||||
[EX04] Start list after header (PREFIX lacks newline)
|
||||
PREFIX_ENDS_WITH_NEWLINE=false
|
||||
<PREFIX>Deployment steps:</PREFIX>
|
||||
<SUFFIX></SUFFIX>
|
||||
Expected OUTPUT:
|
||||
|
||||
- Build artifact
|
||||
- Deploy service
|
||||
|
||||
[EX05] Continue table row
|
||||
<PREFIX>| Name | Score |
|
||||
| --- | --- |
|
||||
| Alice | 92 |
|
||||
| Bob | </PREFIX>
|
||||
<SUFFIX></SUFFIX>
|
||||
Expected OUTPUT:
|
||||
88 |
|
||||
|
||||
[EX06] Start new paragraph
|
||||
<PREFIX>First paragraph ends.</PREFIX>
|
||||
<SUFFIX></SUFFIX>
|
||||
Expected OUTPUT:
|
||||
|
||||
Second paragraph starts.
|
||||
WRONG: Second paragraph starts. (missing leading \\n\\n)
|
||||
|
||||
[EX07] Add newline before heading
|
||||
PREFIX_ENDS_WITH_NEWLINE=false
|
||||
<PREFIX>End of previous section.</PREFIX>
|
||||
<SUFFIX>## Next Heading</SUFFIX>
|
||||
Expected OUTPUT:
|
||||
|
||||
WRONG: (would join with heading without separation)
|
||||
|
||||
=== CATEGORY C: CODE BLOCKS ===
|
||||
|
||||
[EX08] Outside fence: wrap code in fence
|
||||
CURSOR_IN_FENCED_CODE_BLOCK=false
|
||||
<PREFIX>Parse this JSON payload in Python:</PREFIX>
|
||||
<SUFFIX></SUFFIX>
|
||||
Expected OUTPUT:
|
||||
```python
|
||||
import json
|
||||
data = json.loads(payload)
|
||||
```
|
||||
WRONG: import json\\ndata = json.loads(payload) (no fence)
|
||||
|
||||
[EX09] Inside fence: output code only
|
||||
CURSOR_IN_FENCED_CODE_BLOCK=true
|
||||
<PREFIX>```python
|
||||
def add(a, b):
|
||||
return </PREFIX>
|
||||
<SUFFIX>
|
||||
```</SUFFIX>
|
||||
Expected OUTPUT:
|
||||
a + b
|
||||
WRONG: ```python\\nreturn a + b\\n``` (duplicate fences)
|
||||
|
||||
[EX10] Code inside fence uses single newline
|
||||
CURSOR_IN_FENCED_CODE_BLOCK=true
|
||||
<PREFIX>```python
|
||||
def hello():</PREFIX>
|
||||
<SUFFIX>
|
||||
```</SUFFIX>
|
||||
Expected OUTPUT:
|
||||
print("Hello")
|
||||
return True
|
||||
(Note: single \\n between code lines, no markdown rules)
|
||||
|
||||
=== CATEGORY D: MATH ===
|
||||
|
||||
[EX11] Inline math
|
||||
<PREFIX>The derivative of x^2 is </PREFIX>
|
||||
<SUFFIX>.</SUFFIX>
|
||||
Expected OUTPUT:
|
||||
$2x$
|
||||
WRONG: 2x (bare formula)
|
||||
|
||||
[EX12] Block math
|
||||
<PREFIX>We can write the Gaussian integral as:</PREFIX>
|
||||
<SUFFIX></SUFFIX>
|
||||
Expected OUTPUT:
|
||||
$$
|
||||
\\int_{-\\infty}^{\\infty} e^{-x^2}\\,dx = \\sqrt{\\pi}
|
||||
$$
|
||||
WRONG: \\int... (bare formula without $$)
|
||||
|
||||
=== CATEGORY E: MERMAID ===
|
||||
|
||||
[EX13] Inside mermaid fence
|
||||
CURSOR_FENCE_LANGUAGE=mermaid
|
||||
CURSOR_IN_FENCED_CODE_BLOCK=true
|
||||
<PREFIX>```mermaid
|
||||
flowchart TD
|
||||
A[Start] --> </PREFIX>
|
||||
<SUFFIX>
|
||||
```</SUFFIX>
|
||||
Expected OUTPUT:
|
||||
B{Valid?}
|
||||
B -->|Yes| C[Done]
|
||||
WRONG: ```mermaid\\nB{Valid?}... (duplicate fence)
|
||||
|
||||
[EX14] Outside fence with mermaid context
|
||||
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
|
||||
```
|
||||
|
||||
=== CATEGORY F: OCR METADATA ===
|
||||
|
||||
[EX15] Use OCR as context, never output
|
||||
<PREFIX> <OCR:equation y = mx + b>
|
||||
The relationship is </PREFIX>
|
||||
<SUFFIX>.</SUFFIX>
|
||||
Expected OUTPUT:
|
||||
$y = mx + b$
|
||||
WRONG: <OCR:equation y = mx + b> (OCR tag in output)"""
|
||||
_INLINE_EXAMPLES = get_inline_examples()
|
||||
|
||||
|
||||
def build_completion_prompts(
|
||||
@@ -636,7 +332,7 @@ Step 3: Choose newline type
|
||||
- Do not repeat text from SUFFIX beginning
|
||||
|
||||
=== EXAMPLES BY CATEGORY ===
|
||||
{INLINE_EXAMPLES}
|
||||
{_INLINE_EXAMPLES}
|
||||
|
||||
=== NOW COMPLETE THE TASK ===
|
||||
|
||||
|
||||
Reference in New Issue
Block a user