feat(editor): add Markdown rendering for ghost text and optimize prompt system

- Implement Markdown parsing for ghost text using Milkdown parser
- Add support for both block and inline content in ghost text
- Refactor prompt system with comprehensive rules and examples
- Adjust LLM parameters: increase temperature to 0.7, add repeat_penalty
- Add CSS styles for formatted ghost text (bold, italic, code, links)
- Add documentation for Copilot prompt system and ghost text rendering
This commit is contained in:
“ydy0615”
2026-02-13 21:17:45 +08:00
parent 65d4a57d33
commit 7cddfaba30
8 changed files with 966 additions and 30 deletions
Binary file not shown.
+4 -4
View File
@@ -6,7 +6,6 @@ from typing import AsyncGenerator
OLLAMA_MODEL = os.getenv('OLLAMA_MODEL', 'gpt-oss:20b')
OLLAMA_HOST = os.getenv('OLLAMA_HOST', 'http://192.168.0.120:11434')
# 移除 /v1/ 后缀(如果有的话),因为 Ollama Python 包使用原生 API
if OLLAMA_HOST.endswith('/v1/'):
OLLAMA_HOST = OLLAMA_HOST[:-4]
elif OLLAMA_HOST.endswith('/v1'):
@@ -29,9 +28,10 @@ async def stream_openai(prompt: str) -> AsyncGenerator[str, None]:
messages=[{'role': 'user', 'content': prompt}],
stream=True,
options={
'num_predict': 8192,
'temperature': 0.2,
}
'temperature': 0.7,
'repeat_penalty': 1.1,
},
think='high'
)
print(f"[LLM] Got stream object, starting iteration...")
-1
View File
@@ -89,7 +89,6 @@ async def create_completion(request: CompletionRequest):
messages=[{'role': 'user', 'content': prompt}],
stream=False,
options={
'num_predict': 8192,
'temperature': 0.2,
}
)
+192 -22
View File
@@ -3,39 +3,209 @@ from typing import Tuple
def build_prompt(prefix: str, suffix: str) -> str:
"""
改进后的提示词构建函数。
使用明确的指令来引导模型生成高质量的续写内容。
优化后的提示词构建函数。
使用明确的分隔符区分指令部分和实际的 prefix/suffix 内容。
"""
MAX_CONTEXT_LINES = 30
# 修正:把suffix的第一个字符移到prefix末尾(解决光标位置偏差)
if suffix:
first_char = suffix[0]
prefix = prefix + first_char
suffix = suffix[1:]
prefix_lines = prefix.split('\n')
suffix_lines = suffix.split('\n') if suffix else []
recent_prefix = '\n'.join(prefix_lines[-MAX_CONTEXT_LINES:])
recent_suffix = '\n'.join(suffix_lines[:5])
recent_prefix = prefix
recent_suffix = suffix
prompt = f"""You are an expert writing assistant. Continue the text naturally.
prompt = f"""You are an expert writing assistant integrated into a text editor. Your task is to complete the text at the cursor position.
CONTEXT:
⟨CURSOR⟩ marks where to continue.
- Before ⟨CURSOR⟩: existing text
- After ⟨CURSOR⟩: following context (if any)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
RULES
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
RULES:
- Match existing style, tone, and terminology
- Maintain logical flow
- Write only the continuation, nothing else
- IMPORTANT: Start continuation directly from ⟨CURSOR⟩ position
RULE #1: SEAMLESS CONNECTION (MOST CRITICAL)
TEXT:
{recent_prefix}⟨CURSOR⟩{recent_suffix}
Your continuation MUST seamlessly bridge the prefix and suffix. This is the MOST IMPORTANT rule.
CONTINUATION:"""
The "复读机" (Parrot) Error is when you repeat content that already exists in the suffix. This is the WORST mistake you can make.
Requirements:
- Your output must connect prefix to suffix smoothly
- NEVER repeat content that already exists in the suffix
- If prefix already flows naturally into suffix, output NOTHING (empty string)
- The result should read as one coherent text, as if you never interrupted it
RULE #2: WHITESPACE & PUNCTUATION
You must carefully check the LAST character of prefix and FIRST character of suffix to ensure perfect docking.
Requirements:
- If prefix ends with space, do NOT start your output with space (prevents double spaces)
- If prefix does NOT end with space and suffix starts with a letter, you may need to add a space
- If suffix starts with punctuation, do NOT end your output with the same punctuation
- Check for existing spaces around operators before adding more
RULE #3: INDENTATION ALIGNMENT
You MUST match the indentation level of the current context.
Requirements:
- Look at the line where cursor is positioned
- Count the leading spaces/tabs on that line
- Match that indentation for new lines
- Use the SAME type of indentation (spaces OR tabs) as the existing code
- For nested blocks, increase indentation appropriately
- For closing braces, match the opening brace's indentation
RULE #4: LIST MAINTENANCE
When the prefix ends with a list marker, you MUST recognize the pattern and continue it appropriately.
Requirements:
- "- [ ] " indicates an unchecked task → continue with task description
- "- [x] " indicates a checked task → continue with completed task description
- "1. ", "2. ", etc. indicates ordered list → increment the number
- "* " or "- " indicates bullet list → continue with same marker style
- "> " indicates blockquote → continue quoted text
- Maintain the same list format and indentation level
RULE #5: SYNTAX CLOSURE
Before generating content, CHECK if there are unclosed syntax elements. If so, you MUST close them FIRST.
Requirements:
- Scan prefix for opening markers: **, *, `, [, ![, ```
- Check if each opening has a corresponding closing
- If unclosed, add the closing marker FIRST before continuing with content
- Markdown syntax pairs to check:
- Bold: ** must have closing **
- Italic: * must have closing *
- Bold + Italic: *** must have closing ***
- Inline code: ` must have closing `
- Code block: ``` must have closing ```
- Link: [text must have closing ](url)
- Image: ![alt must have closing ](url)
RULE #6: OUTPUT FORMAT
Your output will be directly inserted into the document. Output ONLY the continuation text.
Requirements:
- Output ONLY the text that should appear at the cursor position
- NO explanations, NO comments, NO meta-text
- NO code blocks wrapping your output
- NO phrases like "Here's the continuation:" or "I'll complete this for you:"
- Your output is inserted DIRECTLY into the user's document
RULE #7: ALWAYS OUTPUT SOMETHING (MANDATORY)
You MUST always output some content. Empty output is NOT allowed.
Requirements:
- Even if the prefix seems complete, you should suggest a natural continuation
- If the prefix ends mid-sentence, complete the sentence
- If the prefix ends at a natural break point, suggest the next logical content
- Examples of valid continuations:
- Add the next word or phrase
- Complete an incomplete thought
- Add a relevant follow-up sentence
- Continue a list with the next item
- Add closing punctuation if missing
- NEVER output an empty string - always provide some useful continuation
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
EXAMPLES
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
EXAMPLE 1 - Seamless Connection:
<PREFIX>The quick brown fox </PREFIX>
<SUFFIX>jumps over the lazy dog.</SUFFIX>
Output: "" (empty - nothing needed, prefix already connects to suffix)
Result: "The quick brown fox jumps over the lazy dog."
EXAMPLE 2 - Seamless Connection with Space:
<PREFIX>Hello</PREFIX>
<SUFFIX>world!</SUFFIX>
Output: " "
Result: "Hello world!"
EXAMPLE 3 - Whitespace Docking:
<PREFIX>const a = </PREFIX>
<SUFFIX>1;</SUFFIX>
Output: "1;"
Result: "const a = 1;"
EXAMPLE 4 - Indentation Alignment:
<PREFIX>function test() {{\\n if (true) {{\\n console.log('hi');\\n </PREFIX>
<SUFFIX>\\n}}</SUFFIX>
Output: "}}\\n}}"
Result: " }}\\n}}" (correctly closes if with 4 spaces, then function)
EXAMPLE 5 - Task List:
<PREFIX>## TODO\\n- [ ] Buy groceries\\n- [ ] </PREFIX>
<SUFFIX></SUFFIX>
Output: "Call mom"
Result: "## TODO\\n- [ ] Buy groceries\\n- [ ] Call mom"
EXAMPLE 6 - Ordered List:
<PREFIX>1. First item\\n2. Second item\\n</PREFIX>
<SUFFIX></SUFFIX>
Output: "3. Third item"
Result: "1. First item\\n2. Second item\\n3. Third item"
EXAMPLE 7 - Bullet List:
<PREFIX>* Apple\\n* Banana\\n* </PREFIX>
<SUFFIX></SUFFIX>
Output: "Cherry"
Result: "* Apple\\n* Banana\\n* Cherry"
EXAMPLE 8 - Unclosed Bold:
<PREFIX>This is **important</PREFIX>
<SUFFIX> text continues here.</SUFFIX>
Output: "** "
Result: "This is **important** text continues here."
EXAMPLE 9 - Unclosed Link:
<PREFIX>Click [here for more</PREFIX>
<SUFFIX> information.</SUFFIX>
Output: "](https://example.com)"
Result: "Click [here for more](https://example.com) information."
EXAMPLE 10 - Unclosed Code Block:
<PREFIX>```python\\ndef hello():</PREFIX>
<SUFFIX>\\nprint('done')</SUFFIX>
Output: "\\n print('hello')\\n```"
Result: Code block properly closed with ```
EXAMPLE 11 - Clean Output:
For any completion, output ONLY the continuation text:
Output: "Hello world!"
NOT: "Here's what comes next: Hello world!"
NOT: "```Hello world```"
NOT: "I'll complete this for you: Hello world!"
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
FINAL CHECKLIST
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Before outputting, verify:
□ Does my output connect prefix and suffix WITHOUT repeating suffix content?
□ Are there no double spaces or missing spaces between prefix and suffix?
□ Does my indentation match the context?
□ If there's a list marker, did I continue the list pattern?
□ Did I close any unclosed Markdown syntax?
□ Is my output ONLY the continuation text, nothing else?
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
NOW COMPLETE THE FOLLOWING TEXT
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
<PREFIX>
{recent_prefix}
</PREFIX>
<SUFFIX>
{recent_suffix}
</SUFFIX>
Output:"""
return prompt.strip()