Enhance LLM functionality with PRO mode support and improved prompt handling
- Added support for PRO mode in LLM with specific instruction handling and context awareness. - Updated prompt building functions to include prefill options for better context management. - Introduced new inline examples for PRO mode in JSON format. - Enhanced system prompts to reflect PRO mode capabilities and rules. - Modified API endpoints to accommodate new parameters and ensure backward compatibility. - Improved test cases to validate new functionality and ensure comprehensive coverage.
This commit is contained in:
+21
-7
@@ -18,6 +18,9 @@ load_dotenv()
|
||||
LLM_BASE_URL = os.getenv('LLM_BASE_URL', 'http://localhost:11434/v1/')
|
||||
LLM_API_KEY = os.getenv('LLM_API_KEY', 'ollama')
|
||||
|
||||
# Auth headers for upstream LLM service (OpenAI-compatible Bearer token)
|
||||
LLM_HEADERS = {'Authorization': f'Bearer {LLM_API_KEY}'}
|
||||
|
||||
# Model names (backward compat: fall back to OLLAMA_MODEL if LLM_MODEL not set)
|
||||
_raw_model = os.getenv('LLM_MODEL') or os.getenv('OLLAMA_MODEL', 'gpt-oss:20b')
|
||||
LLM_MODEL = _raw_model.strip() if _raw_model else 'gpt-oss:20b'
|
||||
@@ -73,6 +76,7 @@ def _build_chat_payload(
|
||||
thinking: str | None = None,
|
||||
model: str | None = None,
|
||||
use_pro_model: bool = False,
|
||||
prefill: str | None = None,
|
||||
) -> dict:
|
||||
messages = []
|
||||
sys_prompt = _resolve_system_prompt(system_prompt)
|
||||
@@ -80,6 +84,9 @@ def _build_chat_payload(
|
||||
messages.append({'role': 'system', 'content': sys_prompt})
|
||||
messages.append({'role': 'user', 'content': prompt})
|
||||
|
||||
if prefill:
|
||||
messages.append({'role': 'assistant', 'content': prefill})
|
||||
|
||||
payload = {
|
||||
'model': _resolve_model_name(model, use_pro_model=use_pro_model),
|
||||
'messages': messages,
|
||||
@@ -101,6 +108,7 @@ def _build_chat_stream_payload(
|
||||
thinking: str | None = None,
|
||||
model: str | None = None,
|
||||
use_pro_model: bool = False,
|
||||
prefill: str | None = None,
|
||||
) -> dict:
|
||||
messages = []
|
||||
sys_prompt = _resolve_system_prompt(system_prompt)
|
||||
@@ -108,6 +116,9 @@ def _build_chat_stream_payload(
|
||||
messages.append({'role': 'system', 'content': sys_prompt})
|
||||
messages.append({'role': 'user', 'content': prompt})
|
||||
|
||||
if prefill:
|
||||
messages.append({'role': 'assistant', 'content': prefill})
|
||||
|
||||
payload = {
|
||||
'model': _resolve_model_name(model, use_pro_model=use_pro_model),
|
||||
'messages': messages,
|
||||
@@ -145,6 +156,7 @@ async def call_ollama(
|
||||
thinking: str | None = None,
|
||||
model: str | None = None,
|
||||
use_pro_model: bool = False,
|
||||
prefill: str | None = None,
|
||||
) -> dict:
|
||||
"""Call OpenAI-compatible chat completions (non-streaming) and return content/thinking."""
|
||||
start = time.perf_counter()
|
||||
@@ -161,13 +173,13 @@ async def call_ollama(
|
||||
|
||||
payload = _build_chat_payload(
|
||||
prompt=prompt, system_prompt=system_prompt, temperature=temperature,
|
||||
thinking=thinking, model=model, use_pro_model=use_pro_model,
|
||||
thinking=thinking, model=model, use_pro_model=use_pro_model, prefill=prefill,
|
||||
)
|
||||
|
||||
http_timeout = httpx.Timeout(connect=10.0, read=None, write=30.0, pool=30.0)
|
||||
|
||||
try:
|
||||
async with httpx.AsyncClient(base_url=LLM_BASE_URL, timeout=http_timeout) as client:
|
||||
async with httpx.AsyncClient(base_url=LLM_BASE_URL, headers=LLM_HEADERS, timeout=http_timeout) as client:
|
||||
resp = await asyncio.wait_for(
|
||||
client.post('/chat/completions', json=payload), timeout=COMPLETION_TIMEOUT,
|
||||
)
|
||||
@@ -229,6 +241,7 @@ async def stream_ollama(
|
||||
thinking: str | None = None,
|
||||
model: str | None = None,
|
||||
use_pro_model: bool = False,
|
||||
prefill: str | None = None,
|
||||
) -> AsyncIterator[str]:
|
||||
"""Stream text deltas from OpenAI-compatible chat completions."""
|
||||
start = time.perf_counter()
|
||||
@@ -246,13 +259,13 @@ async def stream_ollama(
|
||||
|
||||
payload = _build_chat_stream_payload(
|
||||
prompt=prompt, system_prompt=system_prompt, temperature=temperature,
|
||||
thinking=thinking, model=model, use_pro_model=use_pro_model,
|
||||
thinking=thinking, model=model, use_pro_model=use_pro_model, prefill=prefill,
|
||||
)
|
||||
|
||||
http_timeout = httpx.Timeout(connect=10.0, read=None, write=30.0, pool=30.0)
|
||||
|
||||
try:
|
||||
async with httpx.AsyncClient(base_url=LLM_BASE_URL, timeout=http_timeout) as client:
|
||||
async with httpx.AsyncClient(base_url=LLM_BASE_URL, headers=LLM_HEADERS, timeout=http_timeout) as client:
|
||||
try:
|
||||
async with client.stream('POST', '/chat/completions', json=payload) as response:
|
||||
response.raise_for_status()
|
||||
@@ -352,6 +365,7 @@ async def stream_ollama_events(
|
||||
model: str | None = None,
|
||||
use_pro_model: bool = False,
|
||||
enable_thinking: bool = True,
|
||||
prefill: str | None = None,
|
||||
timeout: float | None = None,
|
||||
) -> AsyncIterator[tuple[Literal['thinking', 'content'], str]]:
|
||||
"""Stream (event_type, payload) tuples from OpenAI-compatible chat completions."""
|
||||
@@ -370,7 +384,7 @@ async def stream_ollama_events(
|
||||
|
||||
payload = _build_chat_stream_payload(
|
||||
prompt=prompt, system_prompt=system_prompt, temperature=temperature,
|
||||
thinking=thinking if enable_thinking else None, model=model, use_pro_model=use_pro_model,
|
||||
thinking=thinking if enable_thinking else None, model=model, use_pro_model=use_pro_model, prefill=prefill,
|
||||
)
|
||||
|
||||
effective_timeout = timeout if timeout is not None else COMPLETION_TIMEOUT
|
||||
@@ -378,7 +392,7 @@ async def stream_ollama_events(
|
||||
sent_thinking = False
|
||||
|
||||
try:
|
||||
async with httpx.AsyncClient(base_url=LLM_BASE_URL, timeout=http_timeout) as client:
|
||||
async with httpx.AsyncClient(base_url=LLM_BASE_URL, headers=LLM_HEADERS, timeout=http_timeout) as client:
|
||||
try:
|
||||
async with client.stream('POST', '/chat/completions', json=payload) as response:
|
||||
response.raise_for_status()
|
||||
@@ -507,7 +521,7 @@ async def call_vlm_ocr(image_bytes: bytes, language: str = 'auto') -> str:
|
||||
http_timeout = httpx.Timeout(connect=10.0, read=None, write=30.0, pool=30.0)
|
||||
|
||||
try:
|
||||
async with httpx.AsyncClient(base_url=LLM_BASE_URL, timeout=http_timeout) as client:
|
||||
async with httpx.AsyncClient(base_url=LLM_BASE_URL, headers=LLM_HEADERS, timeout=http_timeout) as client:
|
||||
resp = await asyncio.wait_for(
|
||||
client.post('/chat/completions', json=payload), timeout=OCR_TIMEOUT,
|
||||
)
|
||||
|
||||
+4
-2
@@ -179,7 +179,7 @@ async def create_completion(request: Request, req: CompletionRequest, api_key: s
|
||||
logger.info("[%s] llm_input_prefix=%r", request_tag, llm_prefix)
|
||||
logger.info("[%s] llm_input_suffix=%r", request_tag, llm_suffix)
|
||||
|
||||
system_prompt, user_prompt = build_completion_prompts(
|
||||
system_prompt, user_prompt, prefill = build_completion_prompts(
|
||||
req.prefix,
|
||||
req.suffix,
|
||||
req.languageId,
|
||||
@@ -196,6 +196,7 @@ async def create_completion(request: Request, req: CompletionRequest, api_key: s
|
||||
temperature=_clamp_temperature(req.temperature, 0.7),
|
||||
thinking=req.model_thinking if req.model_thinking != "none" else None,
|
||||
model=req.model,
|
||||
prefill=prefill or None,
|
||||
)
|
||||
)
|
||||
|
||||
@@ -262,7 +263,7 @@ async def create_pro_completion_stream(request: Request, req: CompletionRequest,
|
||||
logger.info("[%s] pro_llm_input_prefix=%r", request_tag, llm_prefix)
|
||||
logger.info("[%s] pro_llm_input_suffix=%r", request_tag, llm_suffix)
|
||||
|
||||
system_prompt, user_prompt = build_completion_prompts(
|
||||
system_prompt, user_prompt, prefill = build_completion_prompts(
|
||||
req.prefix,
|
||||
req.suffix,
|
||||
req.languageId,
|
||||
@@ -282,6 +283,7 @@ async def create_pro_completion_stream(request: Request, req: CompletionRequest,
|
||||
thinking=req.model_thinking if req.model_thinking != "none" else None,
|
||||
model=req.model,
|
||||
use_pro_model=True,
|
||||
prefill=prefill or None,
|
||||
):
|
||||
chunks.append(delta)
|
||||
await queue.put(("chunk", json.dumps({"delta": delta}, ensure_ascii=False)))
|
||||
|
||||
+12
-35
@@ -16,6 +16,7 @@ from pydantic import BaseModel
|
||||
from geoip import get_ip_location_text
|
||||
from llm import stream_ollama_events
|
||||
from models import UserPreferences
|
||||
from prompt import build_pro_completion_prompts
|
||||
|
||||
logger = logging.getLogger("api.pro")
|
||||
|
||||
@@ -125,44 +126,19 @@ def _build_pro_prompts(
|
||||
suffix: str,
|
||||
language_id: str,
|
||||
instruction: str,
|
||||
pro_thinking: str = "medium",
|
||||
location: str = "",
|
||||
preferences: UserPreferences | None = None,
|
||||
) -> tuple[str, str]:
|
||||
safe_language = (language_id or "markdown").strip() or "markdown"
|
||||
safe_instruction = (instruction or "").strip()
|
||||
preference_lines: list[str] = []
|
||||
if preferences:
|
||||
if preferences.language and preferences.language != "auto":
|
||||
preference_lines.append(f"- Preferred language: {preferences.language}")
|
||||
if preferences.currency and preferences.currency != "auto":
|
||||
preference_lines.append(f"- Preferred currency: {preferences.currency}")
|
||||
if preferences.timezone and preferences.timezone != "auto":
|
||||
preference_lines.append(f"- Timezone: {preferences.timezone}")
|
||||
if location:
|
||||
preference_lines.append(f"- Location hint: {location}")
|
||||
|
||||
system_prompt = f"""You edit Markdown documents.
|
||||
Return only the Markdown text to insert at the cursor.
|
||||
Do not explain, analyze, label the answer, or wrap the whole answer in a code fence.
|
||||
Match the document language, style, and Markdown structure.
|
||||
Language: {safe_language}."""
|
||||
|
||||
preferences_text = "\n".join(preference_lines) if preference_lines else "- none"
|
||||
instruction_text = safe_instruction or "Continue the Markdown naturally."
|
||||
user_prompt = f"""Instruction:
|
||||
{instruction_text}
|
||||
|
||||
User preferences:
|
||||
{preferences_text}
|
||||
|
||||
Markdown before cursor:
|
||||
{prefix}
|
||||
|
||||
Markdown after cursor:
|
||||
{suffix}
|
||||
|
||||
Write only the Markdown that belongs at the cursor."""
|
||||
return system_prompt.strip(), user_prompt.strip()
|
||||
return build_pro_completion_prompts(
|
||||
prefix=prefix,
|
||||
suffix=suffix,
|
||||
instruction=instruction,
|
||||
language_id=language_id,
|
||||
location=location,
|
||||
pro_thinking_level=pro_thinking,
|
||||
preferences=preferences,
|
||||
)
|
||||
|
||||
|
||||
def _get_client_ip(request: Request) -> str:
|
||||
@@ -237,6 +213,7 @@ def register_pro_completion_routes(app: FastAPI, get_api_key):
|
||||
suffix=suffix,
|
||||
language_id=req.languageId,
|
||||
instruction=req.instruction,
|
||||
pro_thinking=req.pro_thinking,
|
||||
location=location,
|
||||
preferences=req.user_preferences,
|
||||
)
|
||||
|
||||
+106
-1
@@ -3,7 +3,13 @@ import re
|
||||
from typing import Tuple
|
||||
|
||||
from models import UserPreferences
|
||||
from prompts import get_language_guidance_map, get_system_prompt_template, get_inline_examples
|
||||
from prompts import (
|
||||
get_inline_examples,
|
||||
get_inline_examples_pro,
|
||||
get_language_guidance_map,
|
||||
get_system_prompt_pro_template,
|
||||
get_system_prompt_template,
|
||||
)
|
||||
|
||||
|
||||
def _get_current_datetime(timezone_pref: str = "auto") -> str:
|
||||
@@ -294,6 +300,17 @@ def build_inline_system_prompt(language_id: str = "markdown") -> str:
|
||||
|
||||
|
||||
_INLINE_EXAMPLES = get_inline_examples()
|
||||
_PRO_INLINE_EXAMPLES = get_inline_examples_pro()
|
||||
|
||||
|
||||
def build_pro_system_prompt(language_id: str = "markdown") -> str:
|
||||
safe_language_id = _canonical_language_id(language_id)
|
||||
language_guidance = _language_guidance(safe_language_id)
|
||||
template = get_system_prompt_pro_template()
|
||||
system_prompt = template.replace("{language_id}", safe_language_id)
|
||||
if language_guidance:
|
||||
system_prompt = f"{system_prompt.rstrip()}\n{language_guidance.strip()}"
|
||||
return system_prompt.strip()
|
||||
|
||||
|
||||
def build_completion_prompts(
|
||||
@@ -392,3 +409,91 @@ def build_prompt(
|
||||
preferences=preferences,
|
||||
)
|
||||
return user_prompt
|
||||
|
||||
|
||||
def build_pro_completion_prompts(
|
||||
prefix: str,
|
||||
suffix: str,
|
||||
instruction: str = "",
|
||||
language_id: str = "markdown",
|
||||
location: str = "",
|
||||
pro_thinking_level: str = "medium",
|
||||
preferences: UserPreferences | None = None,
|
||||
) -> Tuple[str, str]:
|
||||
safe_language_id = _canonical_language_id(language_id)
|
||||
recent_prefix, recent_suffix = _prepare_context(prefix, suffix)
|
||||
recent_prefix = _normalize_newlines(recent_prefix)
|
||||
recent_suffix = _normalize_newlines(recent_suffix)
|
||||
|
||||
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")
|
||||
|
||||
tz_pref = preferences.timezone if preferences else "auto"
|
||||
current_time = _get_current_datetime(tz_pref)
|
||||
location_info = f"\nUser location: {location}" if location else ""
|
||||
|
||||
pref_info = []
|
||||
if preferences:
|
||||
if preferences.language and preferences.language != "auto":
|
||||
pref_info.append(f"Preferred language: {preferences.language}")
|
||||
if preferences.currency and preferences.currency != "auto":
|
||||
pref_info.append(f"Preferred currency: {preferences.currency}")
|
||||
if preferences.timezone and preferences.timezone != "auto":
|
||||
pref_info.append(f"Preferred timezone: {preferences.timezone}")
|
||||
|
||||
preferences_instruction = "\n".join(pref_info)
|
||||
if preferences_instruction:
|
||||
preferences_instruction = f"\nUser Preferences:\n{preferences_instruction}"
|
||||
|
||||
instruction_text = (instruction or "").strip() or "Continue the Markdown naturally."
|
||||
user_prompt = f"""Current time: {current_time}{location_info}{preferences_instruction}
|
||||
PRO_MODE: true
|
||||
PRO_THINKING_LEVEL: {pro_thinking_level}
|
||||
Editor language: {safe_language_id}
|
||||
|
||||
=== 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"}
|
||||
|
||||
=== PRO INSTRUCTION (HIGHEST PRIORITY) ===
|
||||
{instruction_text}
|
||||
|
||||
=== PRO TASK ===
|
||||
Produce the best insertion text between PREFIX and SUFFIX for [PRO] mode.
|
||||
Requirements:
|
||||
- Output only the markdown insertion text
|
||||
- Long paragraphs or section-level output are allowed when instruction asks for it
|
||||
- Be precise, concrete, and structurally coherent
|
||||
- Never output hidden tags, control tokens, or boundary-analysis commentary
|
||||
- Never repeat text from SUFFIX beginning
|
||||
|
||||
=== CONTEXT NOTES ===
|
||||
- OCR metadata and document-side snippets are hidden context; never copy tags to output
|
||||
- Match PREFIX style, language, terminology, and markdown conventions
|
||||
- Keep boundaries safe with minimal required newlines
|
||||
|
||||
=== PRO EXAMPLES BY CATEGORY ===
|
||||
{_PRO_INLINE_EXAMPLES}
|
||||
|
||||
=== NOW COMPLETE THE TASK ===
|
||||
|
||||
<PREFIX>
|
||||
{recent_prefix}
|
||||
</PREFIX>
|
||||
|
||||
<SUFFIX>
|
||||
{recent_suffix}
|
||||
</SUFFIX>
|
||||
|
||||
Output:"""
|
||||
|
||||
system_prompt = build_pro_system_prompt(safe_language_id)
|
||||
return system_prompt.strip(), user_prompt.strip()
|
||||
|
||||
@@ -40,5 +40,13 @@ def get_inline_examples() -> str:
|
||||
return _prompts.get("inline_examples", {}).get("content", "")
|
||||
|
||||
|
||||
def get_system_prompt_pro_template() -> str:
|
||||
return _prompts.get("system_prompt_pro", {}).get("template", "")
|
||||
|
||||
|
||||
def get_inline_examples_pro() -> str:
|
||||
return _prompts.get("inline_examples_pro", {}).get("content", "")
|
||||
|
||||
|
||||
def get_vlm_ocr_prompt() -> str:
|
||||
return _prompts.get("vlm_ocr", {}).get("prompt", "")
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
{
|
||||
"content": "=== CATEGORY A: PROSE CONTINUATION ===\n\n[EX01] Simple prose continuation\n<PREFIX>The quick brown fox </PREFIX>\n<SUFFIX>jumps over the lazy dog.</SUFFIX>\nExpected OUTPUT:\nmoved quietly and then\n\n[EX02] Avoid repeating suffix\n<PREFIX>Our launch plan starts with </PREFIX>\n<SUFFIX>phase one, followed by phase two.</SUFFIX>\nExpected OUTPUT:\ncareful internal testing before\nWRONG: phase one starts with (repeats suffix)\n\n=== CATEGORY B: MARKDOWN STRUCTURES ===\n\n[EX03] Continue checklist\n<PREFIX>## TODO\n- [ ] Buy milk\n- [ ] </PREFIX>\n<SUFFIX></SUFFIX>\nExpected OUTPUT:\nWrite release notes and share draft with team\n\n[EX04] Start list after header (PREFIX lacks newline)\nPREFIX_ENDS_WITH_NEWLINE=false\n<PREFIX>Deployment steps:</PREFIX>\n<SUFFIX></SUFFIX>\nExpected OUTPUT:\n\n- Build artifact\n- Deploy service\n\n[EX05] Continue table row\n<PREFIX>| Name | Score |\n| --- | --- |\n| Alice | 92 |\n| Bob | </PREFIX>\n<SUFFIX></SUFFIX>\nExpected OUTPUT:\n88 |\n\n[EX06] Start new paragraph\n<PREFIX>First paragraph ends.</PREFIX>\n<SUFFIX></SUFFIX>\nExpected OUTPUT:\n\nSecond paragraph starts.\nWRONG: Second paragraph starts. (missing leading \\n\\n)\n\n[EX07] Add newline before heading\nPREFIX_ENDS_WITH_NEWLINE=false\n<PREFIX>End of previous section.</PREFIX>\n<SUFFIX>## Next Heading</SUFFIX>\nExpected OUTPUT:\n\nWRONG: (would join with heading without separation)\n\n=== CATEGORY C: CODE BLOCKS ===\n\n[EX08] Outside fence: wrap code in fence\nCURSOR_IN_FENCED_CODE_BLOCK=false\n<PREFIX>Parse this JSON payload in Python:</PREFIX>\n<SUFFIX></SUFFIX>\nExpected OUTPUT:\n```python\nimport json\ndata = json.loads(payload)\n```\nWRONG: import json\\ndata = json.loads(payload) (no fence)\n\n[EX09] Inside fence: output code only\nCURSOR_IN_FENCED_CODE_BLOCK=true\n<PREFIX>```python\ndef add(a, b):\nreturn </PREFIX>\n<SUFFIX>\n```</SUFFIX>\nExpected OUTPUT:\na + b\nWRONG: ```python\\nreturn a + b\\n``` (duplicate fences)\n\n[EX10] Code inside fence uses single newline\nCURSOR_IN_FENCED_CODE_BLOCK=true\n<PREFIX>```python\ndef hello():</PREFIX>\n<SUFFIX>\n```</SUFFIX>\nExpected OUTPUT:\nprint(\"Hello\")\nreturn True\n(Note: single \\n between code lines, no markdown rules)\n\n=== CATEGORY D: MATH ===\n\n[EX11] Inline math\n<PREFIX>The derivative of x^2 is </PREFIX>\n<SUFFIX>.</SUFFIX>\nExpected OUTPUT:\n$2x$\nWRONG: 2x (bare formula)\n\n[EX12] Block math\n<PREFIX>We can write the Gaussian integral as:</PREFIX>\n<SUFFIX></SUFFIX>\nExpected OUTPUT:\n$$\n\\int_{-\\infty}^{\\infty} e^{-x^2}\\,dx = \\sqrt{\\pi}\n$$\nWRONG: \\int... (bare formula without $$)\n\n=== CATEGORY E: MERMAID ===\n\n[EX13] Inside mermaid fence\nCURSOR_FENCE_LANGUAGE=mermaid\nCURSOR_IN_FENCED_CODE_BLOCK=true\n<PREFIX>```mermaid\nflowchart TD\nA[Start] --> </PREFIX>\n<SUFFIX>\n```</SUFFIX>\nExpected OUTPUT:\nB{Valid?}\nB -->|Yes| C[Done]\nWRONG: ```mermaid\\nB{Valid?}... (duplicate fence)\n\n[EX14] Outside fence with mermaid context\nCURSOR_IN_FENCED_CODE_BLOCK=false\nMERMAID_CONTEXT=true\n<PREFIX>Please provide a simple release pipeline diagram.</PREFIX>\n<SUFFIX></SUFFIX>\nExpected OUTPUT:\n```mermaid\nflowchart LR\nBuild --> Test --> Deploy\n```\n\n=== CATEGORY F: OCR METADATA ===\n\n[EX15] Use OCR as context, never output\n<PREFIX> <OCR:equation y = mx + b>\nThe relationship is </PREFIX>\n<SUFFIX>.</SUFFIX>\nExpected OUTPUT:\n$y = mx + b$\nWRONG: <OCR:equation y = mx + b> (OCR tag in output)"
|
||||
"content": "=== CATEGORY A: PROSE CONTINUATION ===\n\n[EX01] Simple prose continuation\n<PREFIX>The quick brown fox </PREFIX>\n<SUFFIX>jumps over the lazy dog.</SUFFIX>\nExpected OUTPUT:\nmoved quietly and then\n\n[EX02] Avoid repeating suffix\n<PREFIX>Our launch plan starts with </PREFIX>\n<SUFFIX>phase one, followed by phase two.</SUFFIX>\nExpected OUTPUT:\ncareful internal testing before\nWRONG: phase one starts with (repeats suffix)\n\n=== CATEGORY B: MARKDOWN STRUCTURES ===\n\n[EX03] Continue checklist\n<PREFIX>## TODO\n- [ ] Buy milk\n- [ ] </PREFIX>\n<SUFFIX></SUFFIX>\nExpected OUTPUT:\nWrite release notes and share draft with team\n\n[EX04] Start list after header (PREFIX lacks newline)\nPREFIX_ENDS_WITH_NEWLINE=false\n<PREFIX>Deployment steps:</PREFIX>\n<SUFFIX></SUFFIX>\nExpected OUTPUT:\n\n- Build artifact\n- Deploy service\n\n[EX05] Continue table row\n<PREFIX>| Name | Score |\n| --- | --- |\n| Alice | 92 |\n| Bob | </PREFIX>\n<SUFFIX></SUFFIX>\nExpected OUTPUT:\n88 |\n\n[EX06] Start new paragraph\n<PREFIX>First paragraph ends.</PREFIX>\n<SUFFIX></SUFFIX>\nExpected OUTPUT:\n\nSecond paragraph starts.\nWRONG: Second paragraph starts. (missing leading \\n\\n)\n\n[EX07] Add newline before heading\nPREFIX_ENDS_WITH_NEWLINE=false\n<PREFIX>End of previous section.</PREFIX>\n<SUFFIX>## Next Heading</SUFFIX>\nExpected OUTPUT:\n\nWRONG: (would join with heading without separation)\n\n=== CATEGORY C: CODE BLOCKS ===\n\n[EX08] Outside fence: wrap code in fence\nCURSOR_IN_FENCED_CODE_BLOCK=false\n<PREFIX>Parse this JSON payload in Python:</PREFIX>\n<SUFFIX></SUFFIX>\nExpected OUTPUT:\n```python\nimport json\ndata = json.loads(payload)\n```\nWRONG: import json\\ndata = json.loads(payload) (no fence)\n\n[EX09] Inside fence: output code only\nCURSOR_IN_FENCED_CODE_BLOCK=true\n<PREFIX>```python\ndef add(a, b):\n return </PREFIX>\n<SUFFIX>\n```</SUFFIX>\nExpected OUTPUT:\na + b\nWRONG: ```python\\nreturn a + b\\n``` (duplicate fences)\n\n[EX10] Code inside fence uses single newline\nCURSOR_IN_FENCED_CODE_BLOCK=true\n<PREFIX>```python\ndef hello():</PREFIX>\n<SUFFIX>\n```</SUFFIX>\nExpected OUTPUT:\n print(\"Hello\")\n return True\n(Note: single \\n between code lines, no markdown rules)\n\n=== CATEGORY D: MATH ===\n\n[EX11] Inline math\n<PREFIX>The derivative of x^2 is </PREFIX>\n<SUFFIX>.</SUFFIX>\nExpected OUTPUT:\n$2x$\nWRONG: 2x (bare formula)\n\n[EX12] Block math\n<PREFIX>We can write the Gaussian integral as:</PREFIX>\n<SUFFIX></SUFFIX>\nExpected OUTPUT:\n$$\n\\int_{-\\infty}^{\\infty} e^{-x^2}\\,dx = \\sqrt{\\pi}\n$$\nWRONG: \\int... (bare formula without $$)\n\n=== CATEGORY E: MERMAID ===\n\n[EX13] Inside mermaid fence\nCURSOR_FENCE_LANGUAGE=mermaid\nCURSOR_IN_FENCED_CODE_BLOCK=true\n<PREFIX>```mermaid\nflowchart TD\n A[Start] --> </PREFIX>\n<SUFFIX>\n```</SUFFIX>\nExpected OUTPUT:\nB{Valid?}\nB -->|Yes| C[Done]\nWRONG: ```mermaid\\nB{Valid?}... (duplicate fence)\n\n[EX14] Outside fence with mermaid context\nCURSOR_IN_FENCED_CODE_BLOCK=false\nMERMAID_CONTEXT=true\n<PREFIX>Please provide a simple release pipeline diagram.</PREFIX>\n<SUFFIX></SUFFIX>\nExpected OUTPUT:\n```mermaid\nflowchart LR\n Build --> Test --> Deploy\n```\n\n=== CATEGORY F: OCR METADATA ===\n\n[EX15] Use OCR as context, never output\n<PREFIX> <OCR:equation y = mx + b>\nThe relationship is </PREFIX>\n<SUFFIX>.</SUFFIX>\nExpected OUTPUT:\n$y = mx + b$\nWRONG: <OCR:equation y = mx + b> (OCR tag in output)"
|
||||
}
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"content": "=== PRO CATEGORY A: CONTINUATION AND EXPANSION ===\n\n[PRO-EX01] Continue naturally without suffix repetition\n<PREFIX>Project update: This week we completed </PREFIX>\n<SUFFIX>and started preparing the release checklist.</SUFFIX>\nExpected OUTPUT:\nbackend integration tests\nWRONG: and started preparing (repeats suffix start)\n\n[PRO-EX02] Long paragraph expansion allowed\nINSTRUCTION: Expand into a fuller paragraph with concrete details.\n<PREFIX>Our migration reduced incidents.</PREFIX>\n<SUFFIX></SUFFIX>\nExpected OUTPUT:\n\nIt also improved deployment confidence by cutting rollback frequency and clarifying ownership for each service boundary, which made post-release diagnosis significantly faster.\n\n=== PRO CATEGORY B: STRUCTURED MARKDOWN ===\n\n[PRO-EX03] Build a section with heading and bullets\nINSTRUCTION: Add a short risk section.\n<PREFIX>## Launch Plan\nCurrent status is green.</PREFIX>\n<SUFFIX></SUFFIX>\nExpected OUTPUT:\n\n### Risks\n- Third-party API latency may delay webhook retries.\n- Data backfill window could overlap with peak traffic.\n\n[PRO-EX04] Preserve list numbering continuity\n<PREFIX>1. Prepare schema\n2. Run dry-run\n3. </PREFIX>\n<SUFFIX></SUFFIX>\nExpected OUTPUT:\nValidate production metrics and sign off\n\n[PRO-EX05] Keep table shape valid\n<PREFIX>| Metric | Before | After |\n| --- | --- | --- |\n| P95 latency | 420ms | </PREFIX>\n<SUFFIX></SUFFIX>\nExpected OUTPUT:\n260ms |\n\n=== PRO CATEGORY C: CODE AND TECHNICAL CONTEXT ===\n\n[PRO-EX06] Inside code fence: output code only\nCURSOR_IN_FENCED_CODE_BLOCK=true\n<PREFIX>```python\ndef build_payload(user_id):\n return </PREFIX>\n<SUFFIX>\n```</SUFFIX>\nExpected OUTPUT:\n{\"id\": user_id, \"active\": True}\n\n[PRO-EX07] Outside code fence: include fenced block when instruction asks code\nCURSOR_IN_FENCED_CODE_BLOCK=false\nINSTRUCTION: Show a minimal SQL query.\n<PREFIX>Fetch active users:</PREFIX>\n<SUFFIX></SUFFIX>\nExpected OUTPUT:\n\n```sql\nSELECT id, email\nFROM users\nWHERE active = TRUE;\n```\n\n=== PRO CATEGORY D: MATH AND MERMAID ===\n\n[PRO-EX08] Inline math remains inline\n<PREFIX>The expected value is </PREFIX>\n<SUFFIX> under this distribution.</SUFFIX>\nExpected OUTPUT:\n$\\mu$\n\n[PRO-EX09] Mermaid inside mermaid fence\nCURSOR_FENCE_LANGUAGE=mermaid\nCURSOR_IN_FENCED_CODE_BLOCK=true\n<PREFIX>```mermaid\nflowchart TD\nA[Input] --> </PREFIX>\n<SUFFIX>\n```</SUFFIX>\nExpected OUTPUT:\nB{Validated?}\nB -->|Yes| C[Persist]\n\n[PRO-EX10] Mermaid outside fence with context\nMERMAID_CONTEXT=true\nCURSOR_IN_FENCED_CODE_BLOCK=false\n<PREFIX>Show the pipeline as a diagram.</PREFIX>\n<SUFFIX></SUFFIX>\nExpected OUTPUT:\n\n```mermaid\nflowchart LR\nQueue --> Worker --> Storage\n```\n\n=== PRO CATEGORY E: INSTRUCTION-FIRST REWRITE ===\n\n[PRO-EX11] Rewrite style per instruction\nINSTRUCTION: Rewrite as concise executive tone in Chinese.\n<PREFIX>这个方案看起来不错,但是细节很多,可能会拖慢推进。</PREFIX>\n<SUFFIX></SUFFIX>\nExpected OUTPUT:\n该方案方向正确,但需聚焦关键路径并压缩实现范围,以保障交付节奏。\n\n[PRO-EX12] Add constrained output length\nINSTRUCTION: Add one sentence under 25 words.\n<PREFIX>结论:</PREFIX>\n<SUFFIX></SUFFIX>\nExpected OUTPUT:\n\n先完成最小可用版本,再按风险优先级迭代。\n\n=== PRO CATEGORY F: HIDDEN CONTEXT SAFETY ===\n\n[PRO-EX13] Never leak OCR tags\n<PREFIX> <OCR:roadmap Q3 launch>\nTimeline:</PREFIX>\n<SUFFIX></SUFFIX>\nExpected OUTPUT:\n\nQ3 launch with weekly checkpoints and ownership tracking.\nWRONG: <OCR:roadmap Q3 launch>\n\n=== PRO CATEGORY G: BOUNDARY PRECISION ===\n\n[PRO-EX14] Add leading newline when needed\nPREFIX_ENDS_WITH_NEWLINE=false\n<PREFIX>Action items:</PREFIX>\n<SUFFIX></SUFFIX>\nExpected OUTPUT:\n\n- Confirm rollout window\n- Notify on-call rotation\n\n[PRO-EX15] Do not break upcoming heading\nPREFIX_ENDS_WITH_NEWLINE=false\nSUFFIX_STARTS_WITH_NEWLINE=false\n<PREFIX>Summary complete.</PREFIX>\n<SUFFIX>## Next Steps</SUFFIX>\nExpected OUTPUT:\n\n\n=== PRO CATEGORY H: MIXED STRUCTURE ===\n\n[PRO-EX16] Combine short prose + list + code block\nINSTRUCTION: Add a short explanation, then checklist, then shell command.\n<PREFIX>Deployment guide draft:</PREFIX>\n<SUFFIX></SUFFIX>\nExpected OUTPUT:\n\nUse the following sequence to reduce release risk:\n- Verify migrations on staging\n- Freeze non-critical merges\n- Capture rollback snapshot\n\n```bash\n./scripts/deploy.sh --env prod\n```"
|
||||
}
|
||||
@@ -1,3 +1,3 @@
|
||||
{
|
||||
"template": "You are an inline completion engine for a {language_id} editor with ghost-text suggestions.\n\nReturn only the insertion text that should be placed between PREFIX and SUFFIX.\n\nCORE PRINCIPLE: Output insertion text only. No explanations, no meta labels, no wrapper quotes, no analysis.\n\nNever output internal reasoning, chain-of-thought, boundary checks, or deliberation. Never output chat/template artifacts such as assistant, final, channel, <|start|>, <|end|>, <|fim_prefix|>, <|fim_suffix|>, or <|fim_middle|>.\n\nCONTEXT FLAGS:\n- CURSOR_IN_FENCED_CODE_BLOCK tells whether the cursor is inside a code fence.\n- CURSOR_FENCE_LANGUAGE gives the active fence language, or none.\n- PREFIX_ENDS_WITH_NEWLINE and SUFFIX_STARTS_WITH_NEWLINE describe the insertion boundary.\n- MERMAID_CONTEXT tells whether Mermaid syntax is likely expected.\n\nSPECIALIZED RULES:\n- If CURSOR_IN_FENCED_CODE_BLOCK=true: output only code lines, no triple backticks.\n- If CURSOR_IN_FENCED_CODE_BLOCK=false and a code block is needed: use a fenced block with a language tag, e.g. ```{language}.\n- Inline math must use $...$; block math must use $$...$$.\n- Inside latex/tex/katex fences, output raw LaTeX only.\n- If CURSOR_FENCE_LANGUAGE=mermaid: output Mermaid syntax only, no backticks or prose.\n- If MERMAID_CONTEXT=true outside a fence: output a complete ```mermaid fenced block only when the surrounding text asks for a diagram.\n\nMARKDOWN AND BOUNDARIES:\n- Use actual line breaks, never spelled-out escape sequences, unless the document text itself needs them.\n- Match PREFIX tone, style, indentation, list/table structure, and language.\n- Never repeat text from the beginning of SUFFIX.\n- If separation is needed, put the needed real newline directly in the insertion text without explaining it.\n\nPREFILL:\n- The prompt may place a short tail of PREFIX immediately after <|fim_middle|> to make continuation natural.\n- Continue from that PREFILL. Do not describe it or output control markers.\n\nHIDDEN CONTEXT:\n- OCR metadata like <OCR:...> and document context are hidden context.\n- Use hidden context only as a semantic hint; never copy hidden tags to output."
|
||||
"template": "You are an inline completion engine for a {language_id} editor with ghost-text suggestions.\n\nReturn only the insertion text that should be placed between PREFIX and SUFFIX.\n\nCORE PRINCIPLE: Output insertion text only. No explanations, no meta labels, no wrapper quotes.\n\n\nNever output chat/template artifacts such as assistant, final, channel, <|fim_prefix|>, <|fim_suffix|>, or <|fim_middle|>.\n\nPRIORITY 1: CONTEXT AWARENESS (Read these flags from user prompt)\n- CURSOR_IN_FENCED_CODE_BLOCK: Are you inside a code fence?\n- CURSOR_FENCE_LANGUAGE: What language is the current fence?\n- PREFIX_ENDS_WITH_NEWLINE: Does prefix end with newline?\n- SUFFIX_STARTS_WITH_NEWLINE: Does suffix start with newline?\n- MERMAID_CONTEXT: Is this a Mermaid diagram context?\n\nPRIORITY 2: SPECIALIZED CONTENT RULES\n\n2.1 Code Block Handling:\nIf CURSOR_IN_FENCED_CODE_BLOCK=true:\n- You are inside a code fence\n- Output code lines ONLY (no triple backticks)\n- Separate code lines with actual newline characters\n\nIf CURSOR_IN_FENCED_CODE_BLOCK=false and code needed:\n- Wrap code in fenced block with language tag:\n```{language}\ncode here\n```\n- Never use inline backticks for code snippets\n\n2.2 Math Formatting (KaTeX):\n- Inline math: wrap with $...$\n- Block math: wrap with $$...$$\n- Never output bare formulas\n- Exception: inside latex/tex/katex fence, output raw LaTeX\n\n2.3 Mermaid Diagrams:\nIf CURSOR_FENCE_LANGUAGE=mermaid:\n- Output Mermaid syntax ONLY\n- No backticks, no explanations\n\nIf MERMAID_CONTEXT=true and outside fence:\n- Output complete fenced block:\n```mermaid\ndiagram syntax\n```\n\nPRIORITY 3: MARKDOWN STRUCTURE\n\n3.1 Newline Semantics:\n- Use actual line breaks in output, not spelled-out escape sequences, unless the surrounding content explicitly needs that text\n- A single line break usually continues the current block\n- A blank line starts a new paragraph or block\n- Use blank lines for: new paragraphs, before headings, starting lists/tables\n- Use single line breaks for: continuation within blocks (list items, table cells)\n- Exception: inside code blocks, use actual newline characters freely for code lines\n\n3.2 Boundary Management:\nCheck PREFIX_ENDS_WITH_NEWLINE and SUFFIX_STARTS_WITH_NEWLINE:\n- If PREFIX lacks needed newline: start OUTPUT on a new line\n\n- If SUFFIX lacks needed newline: end OUTPUT with a trailing line break\n\n- Common cases requiring a leading line break:\n* Starting a list after \"Steps:\"\n* Creating new paragraph after text\n* Adding heading after paragraph\n- Common cases requiring a trailing line break:\n* Before new heading\n* End of section\n\n3.3 Context Stitching:\n- Never repeat text from SUFFIX beginning\n- Match PREFIX tone, style, indentation\n- Continue structures: lists, tables, quotes, headings\n\nPRIORITY 4: HIDDEN CONTEXT\n- OCR metadata like <OCR:...> is hidden context\n- Never copy OCR tags to output\n- Use OCR content as semantic hint only"
|
||||
}
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"template": "You are the [PRO] model for LLM-IN-TEXT, specializing in high-precision markdown insertion for a {language_id} editor.\n\nReturn only the insertion text that should be placed between PREFIX and SUFFIX.\n\nPRO CORE PRINCIPLE:\n- Output insertion text only. No explanations, no analysis, no labels, no wrapper quotes.\n- Never output chain-of-thought or internal reasoning.\n- Never output control markers like <|fim_prefix|>, <|fim_suffix|>, <|fim_middle|>, assistant, final, channel.\n\nPRO MODE INTENT:\n- This is PRO_MODE=true. You may produce longer, structured markdown when instruction requires it.\n- Prioritize instruction fidelity first, then boundary safety, then style continuity.\n- If instruction is vague, continue naturally with concrete and useful content.\n\nBOUNDARY AND CONTEXT RULES:\n- Respect CURSOR_IN_FENCED_CODE_BLOCK, CURSOR_FENCE_LANGUAGE, MERMAID_CONTEXT, PREFIX_ENDS_WITH_NEWLINE, and SUFFIX_STARTS_WITH_NEWLINE.\n- Never repeat text from the beginning of SUFFIX.\n- Use minimum necessary newlines to avoid boundary collision.\n- Match PREFIX tone, language, and formatting conventions.\n\nSYNTAX PRIORITY:\n- Code block contexts must keep valid syntax and indentation.\n- Math must use $...$ for inline and $$...$$ for blocks unless inside latex fences.\n- Mermaid contexts must output valid mermaid statements; do not duplicate fences when already inside one.\n\nHIDDEN CONTEXT SAFETY:\n- OCR metadata and document-side context are hidden hints only.\n- Never copy hidden tags (e.g., <OCR:...>) into output.\n\nQUALITY BAR FOR PRO:\n- Prefer specific, information-dense output over generic filler.\n- For structured requests, preserve headings/list hierarchy and produce coherent section flow.\n- Keep output directly insertable without post-edit cleanups."
|
||||
}
|
||||
+22
-16
@@ -128,9 +128,9 @@ def test_build_chat_stream_payload_with_thinking():
|
||||
def test_call_ollama_non_streaming(monkeypatch):
|
||||
captured = {}
|
||||
|
||||
async def fake_post(url, json=None):
|
||||
captured["url"] = url
|
||||
captured["json"] = json
|
||||
async def fake_post(*args, **kwargs):
|
||||
captured["url"] = args[1] if len(args) > 1 else kwargs.get("url", "")
|
||||
captured["json"] = kwargs.get("json")
|
||||
|
||||
class FakeResp:
|
||||
def raise_for_status(self): pass
|
||||
@@ -138,7 +138,7 @@ def test_call_ollama_non_streaming(monkeypatch):
|
||||
|
||||
return FakeResp()
|
||||
|
||||
async def fake_client(*args, **kwargs):
|
||||
def fake_client(*args, **kwargs):
|
||||
class Ctx:
|
||||
async def __aenter__(self2): return self2
|
||||
async def __aexit__(*a): pass
|
||||
@@ -168,7 +168,8 @@ def test_stream_ollama_text_deltas(monkeypatch):
|
||||
])
|
||||
|
||||
class LineIterator:
|
||||
async def __anext__(self):
|
||||
def __aiter__(self2): return self2
|
||||
async def __anext__(self2):
|
||||
try:
|
||||
return next(lines_iter)
|
||||
except StopIteration:
|
||||
@@ -177,8 +178,8 @@ def test_stream_ollama_text_deltas(monkeypatch):
|
||||
class Response:
|
||||
def __init__(self2): self2._lines = LineIterator()
|
||||
|
||||
async def raise_for_status(self2): pass
|
||||
async def aiter_lines(self2): return self2._lines
|
||||
def raise_for_status(self2): pass
|
||||
def aiter_lines(self2): return self2._lines
|
||||
|
||||
class StreamCtx:
|
||||
async def __aenter__(self2): return Response()
|
||||
@@ -186,10 +187,12 @@ def test_stream_ollama_text_deltas(monkeypatch):
|
||||
|
||||
class Client:
|
||||
stream = lambda self2, *args, **kw: StreamCtx()
|
||||
async def __aenter__(self2): return self2
|
||||
async def __aexit__(*a): pass
|
||||
|
||||
return Client()
|
||||
|
||||
async def fake_client(*args, **kwargs):
|
||||
def fake_client(*args, **kwargs):
|
||||
captured["called"] = True
|
||||
return make_lines()
|
||||
|
||||
@@ -217,7 +220,8 @@ def test_stream_ollama_events_thinking_and_content(monkeypatch):
|
||||
])
|
||||
|
||||
class LineIterator:
|
||||
async def __anext__(self):
|
||||
def __aiter__(self2): return self2
|
||||
async def __anext__(self2):
|
||||
try:
|
||||
return next(lines_iter)
|
||||
except StopIteration:
|
||||
@@ -226,8 +230,8 @@ def test_stream_ollama_events_thinking_and_content(monkeypatch):
|
||||
class Response:
|
||||
def __init__(self2): self2._lines = LineIterator()
|
||||
|
||||
async def raise_for_status(self2): pass
|
||||
async def aiter_lines(self2): return self2._lines
|
||||
def raise_for_status(self2): pass
|
||||
def aiter_lines(self2): return self2._lines
|
||||
|
||||
class StreamCtx:
|
||||
async def __aenter__(self2): return Response()
|
||||
@@ -235,10 +239,12 @@ def test_stream_ollama_events_thinking_and_content(monkeypatch):
|
||||
|
||||
class Client:
|
||||
stream = lambda self2, *args, **kw: StreamCtx()
|
||||
async def __aenter__(self2): return self2
|
||||
async def __aexit__(*a): pass
|
||||
|
||||
return Client()
|
||||
|
||||
async def fake_client(*args, **kwargs):
|
||||
def fake_client(*args, **kwargs):
|
||||
captured["called"] = True
|
||||
return make_lines()
|
||||
|
||||
@@ -260,9 +266,9 @@ def test_stream_ollama_events_thinking_and_content(monkeypatch):
|
||||
def test_call_vlm_ocr(monkeypatch):
|
||||
captured = {}
|
||||
|
||||
async def fake_post(url, json=None):
|
||||
captured["url"] = url
|
||||
captured["json"] = json
|
||||
async def fake_post(*args, **kwargs):
|
||||
captured["url"] = args[1] if len(args) > 1 else kwargs.get("url", "")
|
||||
captured["json"] = kwargs.get("json")
|
||||
|
||||
class FakeResp:
|
||||
def raise_for_status(self): pass
|
||||
@@ -270,7 +276,7 @@ def test_call_vlm_ocr(monkeypatch):
|
||||
|
||||
return FakeResp()
|
||||
|
||||
async def fake_client(*args, **kwargs):
|
||||
def fake_client(*args, **kwargs):
|
||||
class Ctx:
|
||||
async def __aenter__(self2): return self2
|
||||
async def __aexit__(*a): pass
|
||||
|
||||
@@ -138,7 +138,7 @@ def test_post_completions_privacy_mode(monkeypatch):
|
||||
captured["kwargs"] = kwargs
|
||||
return {"content": "done", "think": ""}
|
||||
monkeypatch.setattr(main, "call_ollama", fake_call)
|
||||
monkeypatch.setattr(main, "build_completion_prompts", lambda *a, **k: ("sys", "user"))
|
||||
monkeypatch.setattr(main, "build_completion_prompts", lambda *a, **k: ("sys", "user", ""))
|
||||
monkeypatch.setattr(main, "prepare_prompt_context", lambda *a, **k: ("p", "s"))
|
||||
|
||||
client = TestClient(main.app)
|
||||
|
||||
@@ -56,19 +56,22 @@ def test_pro_status_missing_returns_404():
|
||||
assert response.status_code == 404
|
||||
|
||||
|
||||
def test_pro_prompt_uses_simple_chat_instruction():
|
||||
def test_pro_prompt_uses_pro_specific_instruction():
|
||||
system_prompt, user_prompt = pro_completions._build_pro_prompts(
|
||||
prefix="欢迎使用 LLM-IN-TEXT\n\n即时可用的 LLM 系统",
|
||||
suffix="",
|
||||
language_id="markdown",
|
||||
instruction="",
|
||||
pro_thinking="high",
|
||||
)
|
||||
combined = f"{system_prompt}\n{user_prompt}".lower()
|
||||
assert "pro block" not in combined
|
||||
assert "replacement" not in combined
|
||||
assert "final answer" not in combined
|
||||
assert "markdown before cursor" in combined
|
||||
assert "markdown after cursor" in combined
|
||||
assert "[pro] model for llm-in-text" in combined
|
||||
assert "pro_mode: true" in combined
|
||||
assert "pro_thinking_level: high" in combined
|
||||
assert "long paragraphs or section-level output are allowed" in combined
|
||||
assert "highest priority" in combined
|
||||
assert "never copy tags to output" in combined
|
||||
assert "write only the markdown that belongs at the cursor" not in combined
|
||||
assert "continue the markdown naturally" in combined
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user