feat: add privacy mode, thinking levels, PWA support, and i18n
- Add privacy mode to hide IP and user preferences from AI requests - Add model thinking levels (low/medium/high) for context analysis depth - Add PWA support with service worker, manifest, and app icons - Add SettingsPanel for user preferences (theme, background, language) - Add i18n translations for en/zh/ja/ko/de/fr - Add Pinia store for centralized settings management - Update backend to support user preferences and thinking levels - Update config to use absolute API URLs
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
OPENAI_API_KEY=ollama
|
||||
OLLAMA_BASE_URL=http://192.168.0.120:11434/v1/
|
||||
OLLAMA_MODEL=gpt-oss:120b
|
||||
OLLAMA_MODEL=gpt-oss:20b
|
||||
VLM_MODEL=qwen3-vl:30b
|
||||
|
||||
+1
-1
@@ -7,7 +7,7 @@ from dotenv import load_dotenv
|
||||
|
||||
load_dotenv()
|
||||
|
||||
OLLAMA_MODEL = os.getenv('OLLAMA_MODEL', 'gpt-oss:120b')
|
||||
OLLAMA_MODEL = os.getenv('OLLAMA_MODEL', 'gpt-oss:20b')
|
||||
OLLAMA_HOST = os.getenv('OLLAMA_HOST', 'http://192.168.0.120:11434')
|
||||
VLM_MODEL = os.getenv('VLM_MODEL', 'qwen3-vl:30b')
|
||||
|
||||
|
||||
+33
-9
@@ -27,10 +27,20 @@ app.add_middleware(
|
||||
allow_headers=["*"],
|
||||
)
|
||||
|
||||
from typing import Optional
|
||||
|
||||
class UserPreferences(BaseModel):
|
||||
language: str = 'auto'
|
||||
currency: str = 'auto'
|
||||
timezone: str = 'auto'
|
||||
|
||||
class CompletionRequest(BaseModel):
|
||||
prefix: str
|
||||
suffix: str
|
||||
languageId: str = 'markdown'
|
||||
model_thinking: str = 'low'
|
||||
privacy_mode: bool = False
|
||||
user_preferences: Optional[UserPreferences] = None
|
||||
|
||||
class OCRRequest(BaseModel):
|
||||
image: str
|
||||
@@ -50,26 +60,40 @@ def get_client_ip(request: Request) -> str:
|
||||
@app.post("/v1/completions")
|
||||
async def create_completion(request: Request, req: CompletionRequest):
|
||||
request_id = str(uuid.uuid4())[:8]
|
||||
client_ip = get_client_ip(request)
|
||||
# 查询 IP 归属地
|
||||
location = get_ip_location_text(client_ip)
|
||||
if location:
|
||||
logger.info("[%s] client_location=%s", request_id, location)
|
||||
|
||||
client_ip = "hidden"
|
||||
location = ""
|
||||
|
||||
if not req.privacy_mode:
|
||||
client_ip = get_client_ip(request)
|
||||
# 查询 IP 归属地
|
||||
location = get_ip_location_text(client_ip)
|
||||
if location:
|
||||
logger.info("[%s] client_location=%s", request_id, location)
|
||||
|
||||
try:
|
||||
logger.info(
|
||||
"[%s] /v1/completions client_ip=%s prefix_chars=%d suffix_chars=%d lang=%s prefix_tail='%s' suffix_head='%s'",
|
||||
"[%s] /v1/completions client_ip=%s prefix_chars=%d suffix_chars=%d lang=%s thinking=%s privacy=%s",
|
||||
request_id,
|
||||
client_ip,
|
||||
len(req.prefix or ""),
|
||||
len(req.suffix or ""),
|
||||
req.languageId,
|
||||
_preview((req.prefix or "")[-120:]),
|
||||
_preview((req.suffix or "")[:120]),
|
||||
req.model_thinking,
|
||||
req.privacy_mode
|
||||
)
|
||||
llm_prefix, llm_suffix = prepare_prompt_context(req.prefix or "", req.suffix or "")
|
||||
logger.info("[%s] llm_input_prefix=%r", request_id, llm_prefix)
|
||||
logger.info("[%s] llm_input_suffix=%r", request_id, llm_suffix)
|
||||
prompt = build_prompt(req.prefix, req.suffix, req.languageId, location=location)
|
||||
|
||||
prompt = build_prompt(
|
||||
req.prefix,
|
||||
req.suffix,
|
||||
req.languageId,
|
||||
location=location,
|
||||
thinking_level=req.model_thinking,
|
||||
preferences=req.user_preferences
|
||||
)
|
||||
result = await call_ollama(prompt, tag=f"{request_id}-primary", temperature=0.7)
|
||||
|
||||
content = result["content"] or ""
|
||||
|
||||
+29
-3
@@ -29,20 +29,46 @@ def prepare_prompt_context(prefix: str, suffix: str) -> Tuple[str, str]:
|
||||
return _prepare_context(prefix, suffix)
|
||||
|
||||
|
||||
def build_prompt(prefix: str, suffix: str, language_id: str = "markdown", location: str = "") -> str:
|
||||
def build_prompt(
|
||||
prefix: str,
|
||||
suffix: str,
|
||||
language_id: str = "markdown",
|
||||
location: str = "",
|
||||
thinking_level: str = "low",
|
||||
preferences: object = None
|
||||
) -> str:
|
||||
safe_language_id = _sanitize_language_id(language_id)
|
||||
recent_prefix, recent_suffix = _prepare_context(prefix, suffix)
|
||||
current_time = _get_current_datetime()
|
||||
location_info = f"\nUser location: {location}" if location else ""
|
||||
|
||||
thinking_instruction = ""
|
||||
if thinking_level == "medium":
|
||||
thinking_instruction = "\n- Briefly analyze the context before suggesting."
|
||||
elif thinking_level == "high":
|
||||
thinking_instruction = "\n- Deeply analyze the context, structure, and intent before suggesting. Think step-by-step."
|
||||
|
||||
prompt = f"""Current time: {current_time}{location_info}
|
||||
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"User timezone: {preferences.timezone}")
|
||||
|
||||
preferences_instruction = "\n".join(pref_info)
|
||||
if preferences_instruction:
|
||||
preferences_instruction = f"\nUser Preferences:\n{preferences_instruction}"
|
||||
|
||||
prompt = f"""Current time: {current_time}{location_info}{preferences_instruction}
|
||||
|
||||
You are an inline completion engine for a {safe_language_id} editor with ghost-text suggestions.
|
||||
|
||||
Your job:
|
||||
- Return ONLY the text that should be inserted at the cursor between PREFIX and SUFFIX.
|
||||
- Prefer a meaningful, non-empty insertion with moderate length.
|
||||
- Avoid overly short outputs with little information value.
|
||||
- Avoid overly short outputs with little information value.{thinking_instruction}
|
||||
|
||||
Important context:
|
||||
- PREFIX may contain OCR metadata inline after images, e.g.  <OCR:description>.
|
||||
|
||||
Reference in New Issue
Block a user