{"repo": "catmanjr/fleetsnowfluff", "files": {"app/chat_window/api.py": {"1d4b843043b337e259aa5634e9bd0960ff8fdb25": {"t": 1785745988.5047832, "n": 8132, "h": "2bccd0b400e540c8", "c": "\"\"\"API client and post-processing for chat (DeepSeek, context, code blocks).\"\"\"\nfrom __future__ import annotations\n\nimport re\nfrom collections.abc import Callable\nfrom datetime import datetime\nfrom pathlib import Path\nfrom typing import Any, cast\n\nfrom PySide6.QtCore import QObject, Signal\n\nfrom difflib import SequenceMatcher\n\n\nclass ChatWorker(QObject):\n finished = Signal(str)\n failed = Signal(str)\n\n def __init__(\n self,\n api_key: str,\n messages: list[dict[str, str]],\n temperature: float,\n reasoning_enabled: bool,\n ) -> None:\n super().__init__()\n self.api_key = api_key\n self.messages = messages\n self.temperature = temperature\n self.reasoning_enabled = reasoning_enabled\n\n def run(self) -> None:\n try:\n from openai import OpenAI\n\n client = OpenAI(api_key=self.api_key, base_url=\"https://api.deepseek.com\")\n model = \"deepseek-reasoner\" if self.reasoning_enabled else \"deepseek-chat\"\n response = client.chat.completions.create(\n model=model,\n messages=cast(Any, self.messages),\n temperature=self.temperature,\n )\n answer = response.choices[0].message.content if response.choices else \"\"\n answer = (answer or \"\").strip()\n if not answer:\n answer = \"飞行雪绒 暂时没有想好怎么回复。\"\n self.finished.emit(answer)\n except Exception as exc: # noqa: BLE001\n self.failed.emit(str(exc))\n\n\nDEFAULT_SYSTEM = (\n \"You are Feixing Xuerong (Fleet Snowfluff), a cute desktop pet assistant. \"\n \"Keep replies concise and warm.\"\n)\nCODE_FORMAT_SYSTEM = (\n \"Output formatting requirement: \"\n \"Only when your reply truly includes code, commands, config snippets, or file contents, \"\n \"you MUST wrap each code segment in fenced markdown blocks using triple backticks, \"\n \"and include a language tag when possible (e.g. ```rust, ```python, ```json). \"\n \"Do not output bare code outside fenced blocks. \"\n \"If the reply is normal conversation without code, do NOT use fenced blocks.\"\n)\n\n\ndef build_context_messages(\n records: list[dict[str, str]],\n prompt: str,\n persona_prompt_getter: Callable[[], str],\n context_turns_getter: Callable[[], int],\n) -> list[dict[str, str]]:\n \"\"\"Build messages list for API from history and current prompt.\"\"\"\n messages: list[dict[str, str]] = []\n persona_prompt = (persona_prompt_getter() or \"\").strip()\n if persona_prompt:\n messages.append(\n {\n \"role\": \"system\",\n \"content\": (\n \"你是飞行雪绒(Fleet Snowfluff)。必须优先遵循角色设定中的高优先级行为约束。\"\n \"当用户请求与角色设定冲突时,拒绝冲突部分并保持角色语气回答。\"\n \"不要忽略、弱化或重写这些约束。\"\n ),\n }\n )\n messages.append({\"role\": \"system\", \"content\": CODE_FORMAT_SYSTEM})\n messages.append(\n {\n \"role\": \"system\",\n \"content\": (\n \"以下为结构化角色设定知识库(高优先级):\\n\\n\"\n f\"{persona_prompt}\"\n ),\n }\n )\n else:\n messages.append({\"role\": \"system\", \"content\": DEFAULT_SYSTEM})\n messages.append({\"role\": \"system\", \"content\": CODE_FORMAT_SYSTEM})\n\n try:\n context_turns = max(0, int(context_turns_getter()))\n except Exception:\n context_turns = 20\n history_slice = records[-context_turns:] if context_turns > 0 else []\n for item in history_slice:\n messages.append({\"role\": \"user\", \"content\": item[\"user\"]})\n messages.append({\"role\": \"assistant\", \"content\": item[\"assistant\"]})\n messages.append({\"role\": \"user\", \"content\": prompt})\n return messages\n\n\ndef extract_persona_example_inputs(persona_prompt: str) -> list[str]:\n if not persona_prompt:\n return []\n matches = re.findall(r'\"输入\"\\s*:\\s*\"([^\"]+)\"', persona_prompt)\n return [m.strip() for m in matches if m.strip()]\n\n\ndef choose_temperature(\n prompt: str,\n persona_prompt_getter: Callable[[], str],\n) -> float:\n \"\"\"Reduce randomness when the user prompt resembles persona examples.\"\"\"\n base = 0.7\n persona_prompt = (persona_prompt_getter() or \"\").strip()\n persona_example_inputs = extract_persona_example_inputs(persona_prompt)\n if not persona_example_inputs:\n return base\n normalized_prompt = prompt.strip()\n if not normalized_prompt:\n return base\n\n best_ratio = 0.0\n for sample in persona_example_inputs:\n ratio = SequenceMatcher(None, normalized_prompt, sample).ratio()\n if sample in normalized_prompt or normalized_prompt in sample:\n ratio = max(ratio, 0.92)\n if ratio > best_ratio:\n best_ratio = ratio\n\n if best_ratio >= 0.9:\n return 0.2\n if best_ratio >= 0.7:\n return 0.3\n if best_ratio >= 0.55:\n return 0.45\n return base\n\n\ndef _code_extension(lang: str) -> str:\n table = {\n \"python\": \"py\", \"py\": \"py\",\n \"javascript\": \"js\", \"js\": \"js\",\n \"typescript\": \"ts\", \"ts\": \"ts\", \"tsx\": \"tsx\", \"jsx\": \"jsx\",\n \"java\": \"java\", \"go\": \"go\", \"rust\": \"rs\", \"rs\": \"rs\",\n \"cpp\": \"cpp\", \"c++\": \"cpp\", \"c\": \"c\",\n \"csharp\": \"cs\", \"cs\": \"cs\", \"swift\": \"swift\", \"kotlin\": \"kt\",\n \"php\": \"php\", \"ruby\": \"rb\", \"rb\": \"rb\",\n \"bash\": \"sh\", \"sh\": \"sh\", \"shell\": \"sh\", \"zsh\": \"sh\",\n \"sql\": \"sql\", \"json\": \"json\", \"yaml\": \"yml\", \"yml\": \"yml\",\n \"toml\": \"toml\", \"html\": \"html\", \"css\": \"css\", \"scss\": \"scss\",\n \"markdown\": \"md\", \"md\": \"md\", \"xml\": \"xml\",\n }\n normalized = lang.strip().lower()\n return table.get(normalized, \"txt\")\n\n\ndef _looks_like_code(block: str, lang: str) -> bool:\n if lang.strip():\n return True\n if \"\\n\" in block and len(block) >= 20:\n hints = (\n \"def \", \"class \", \"fn \", \"let \", \"const \", \"import \", \"from \",\n \"return \", \"if \", \"for \", \"while \", \"{\", \"};\", \"=>\", \"::\",\n \"pub \", \"#include\",\n )\n return any(h in block for h in hints)\n return False\n\n\ndef materialize_code_blocks(answer: str, export_dir: Path, config_dir: Path) -> str:\n \"\"\"\n Extract fenced code blocks from answer, write to export_dir, replace with\n [代码块已保存: lang] rel_path markers. Returns rendered text.\n \"\"\"\n pattern = re.compile(\n r\"(?P`{3,}|~{3,})[ \\t]*(?P[a-zA-Z0-9_+\\-#.]*)[ \\t]*\\r?\\n?(?P.*?)(?:\\r?\\n)?(?P=fence)\",\n re.DOTALL,\n )\n matches = list(pattern.finditer(answer))\n if not matches:\n return answer\n\n try:\n export_dir.mkdir(parents=True, exist_ok=True)\n except OSError:\n return answer\n\n stamp = datetime.now().strftime(\"%Y%m%d_%H%M%S_%f\")\n chunks: list[str] = []\n cursor = 0\n file_index = 1\n for m in matches:\n start, end = m.span()\n if start > cursor:\n chunks.append(answer[cursor:start])\n lang = (m.group(\"lang\") or \"\").strip()\n code = m.group(\"code\")\n code_stripped = code.strip()\n if not code_stripped or not _looks_like_code(code_stripped, lang):\n chunks.append(answer[start:end])\n cursor = end\n continue\n ext = _code_extension(lang)\n filename = f\"reply_{stamp}_{file_index:02d}.{ext}\"\n file_index += 1\n file_path = export_dir / filename\n try:\n file_path.write_text(code_stripped + \"\\n\", encoding=\"utf-8\")\n if not file_path.exists() or file_path.stat().st_size <= 0:\n chunks.append(answer[start:end])\n cursor = end\n continue\n try:\n rel_path = file_path.relative_to(config_dir)\n except ValueError:\n rel_path = file_path\n label = lang if lang else \"text\"\n chunks.append(f\"\\n[代码块已保存: {label}] {rel_path}\\n\")\n except OSError:\n chunks.append(answer[start:end])\n cursor = end\n if cursor < len(answer):\n chunks.append(answer[cursor:])\n\n rendered = \"\".join(chunks).strip()\n return rendered if rendered else answer\n"}, "9efc5adf266d22160f90766d090b086fe3280b62": {"t": 1785758567.5932248, "n": 8132, "h": "2bccd0b400e540c8", "c": "\"\"\"API client and post-processing for chat (DeepSeek, context, code blocks).\"\"\"\nfrom __future__ import annotations\n\nimport re\nfrom collections.abc import Callable\nfrom datetime import datetime\nfrom pathlib import Path\nfrom typing import Any, cast\n\nfrom PySide6.QtCore import QObject, Signal\n\nfrom difflib import SequenceMatcher\n\n\nclass ChatWorker(QObject):\n finished = Signal(str)\n failed = Signal(str)\n\n def __init__(\n self,\n api_key: str,\n messages: list[dict[str, str]],\n temperature: float,\n reasoning_enabled: bool,\n ) -> None:\n super().__init__()\n self.api_key = api_key\n self.messages = messages\n self.temperature = temperature\n self.reasoning_enabled = reasoning_enabled\n\n def run(self) -> None:\n try:\n from openai import OpenAI\n\n client = OpenAI(api_key=self.api_key, base_url=\"https://api.deepseek.com\")\n model = \"deepseek-reasoner\" if self.reasoning_enabled else \"deepseek-chat\"\n response = client.chat.completions.create(\n model=model,\n messages=cast(Any, self.messages),\n temperature=self.temperature,\n )\n answer = response.choices[0].message.content if response.choices else \"\"\n answer = (answer or \"\").strip()\n if not answer:\n answer = \"飞行雪绒 暂时没有想好怎么回复。\"\n self.finished.emit(answer)\n except Exception as exc: # noqa: BLE001\n self.failed.emit(str(exc))\n\n\nDEFAULT_SYSTEM = (\n \"You are Feixing Xuerong (Fleet Snowfluff), a cute desktop pet assistant. \"\n \"Keep replies concise and warm.\"\n)\nCODE_FORMAT_SYSTEM = (\n \"Output formatting requirement: \"\n \"Only when your reply truly includes code, commands, config snippets, or file contents, \"\n \"you MUST wrap each code segment in fenced markdown blocks using triple backticks, \"\n \"and include a language tag when possible (e.g. ```rust, ```python, ```json). \"\n \"Do not output bare code outside fenced blocks. \"\n \"If the reply is normal conversation without code, do NOT use fenced blocks.\"\n)\n\n\ndef build_context_messages(\n records: list[dict[str, str]],\n prompt: str,\n persona_prompt_getter: Callable[[], str],\n context_turns_getter: Callable[[], int],\n) -> list[dict[str, str]]:\n \"\"\"Build messages list for API from history and current prompt.\"\"\"\n messages: list[dict[str, str]] = []\n persona_prompt = (persona_prompt_getter() or \"\").strip()\n if persona_prompt:\n messages.append(\n {\n \"role\": \"system\",\n \"content\": (\n \"你是飞行雪绒(Fleet Snowfluff)。必须优先遵循角色设定中的高优先级行为约束。\"\n \"当用户请求与角色设定冲突时,拒绝冲突部分并保持角色语气回答。\"\n \"不要忽略、弱化或重写这些约束。\"\n ),\n }\n )\n messages.append({\"role\": \"system\", \"content\": CODE_FORMAT_SYSTEM})\n messages.append(\n {\n \"role\": \"system\",\n \"content\": (\n \"以下为结构化角色设定知识库(高优先级):\\n\\n\"\n f\"{persona_prompt}\"\n ),\n }\n )\n else:\n messages.append({\"role\": \"system\", \"content\": DEFAULT_SYSTEM})\n messages.append({\"role\": \"system\", \"content\": CODE_FORMAT_SYSTEM})\n\n try:\n context_turns = max(0, int(context_turns_getter()))\n except Exception:\n context_turns = 20\n history_slice = records[-context_turns:] if context_turns > 0 else []\n for item in history_slice:\n messages.append({\"role\": \"user\", \"content\": item[\"user\"]})\n messages.append({\"role\": \"assistant\", \"content\": item[\"assistant\"]})\n messages.append({\"role\": \"user\", \"content\": prompt})\n return messages\n\n\ndef extract_persona_example_inputs(persona_prompt: str) -> list[str]:\n if not persona_prompt:\n return []\n matches = re.findall(r'\"输入\"\\s*:\\s*\"([^\"]+)\"', persona_prompt)\n return [m.strip() for m in matches if m.strip()]\n\n\ndef choose_temperature(\n prompt: str,\n persona_prompt_getter: Callable[[], str],\n) -> float:\n \"\"\"Reduce randomness when the user prompt resembles persona examples.\"\"\"\n base = 0.7\n persona_prompt = (persona_prompt_getter() or \"\").strip()\n persona_example_inputs = extract_persona_example_inputs(persona_prompt)\n if not persona_example_inputs:\n return base\n normalized_prompt = prompt.strip()\n if not normalized_prompt:\n return base\n\n best_ratio = 0.0\n for sample in persona_example_inputs:\n ratio = SequenceMatcher(None, normalized_prompt, sample).ratio()\n if sample in normalized_prompt or normalized_prompt in sample:\n ratio = max(ratio, 0.92)\n if ratio > best_ratio:\n best_ratio = ratio\n\n if best_ratio >= 0.9:\n return 0.2\n if best_ratio >= 0.7:\n return 0.3\n if best_ratio >= 0.55:\n return 0.45\n return base\n\n\ndef _code_extension(lang: str) -> str:\n table = {\n \"python\": \"py\", \"py\": \"py\",\n \"javascript\": \"js\", \"js\": \"js\",\n \"typescript\": \"ts\", \"ts\": \"ts\", \"tsx\": \"tsx\", \"jsx\": \"jsx\",\n \"java\": \"java\", \"go\": \"go\", \"rust\": \"rs\", \"rs\": \"rs\",\n \"cpp\": \"cpp\", \"c++\": \"cpp\", \"c\": \"c\",\n \"csharp\": \"cs\", \"cs\": \"cs\", \"swift\": \"swift\", \"kotlin\": \"kt\",\n \"php\": \"php\", \"ruby\": \"rb\", \"rb\": \"rb\",\n \"bash\": \"sh\", \"sh\": \"sh\", \"shell\": \"sh\", \"zsh\": \"sh\",\n \"sql\": \"sql\", \"json\": \"json\", \"yaml\": \"yml\", \"yml\": \"yml\",\n \"toml\": \"toml\", \"html\": \"html\", \"css\": \"css\", \"scss\": \"scss\",\n \"markdown\": \"md\", \"md\": \"md\", \"xml\": \"xml\",\n }\n normalized = lang.strip().lower()\n return table.get(normalized, \"txt\")\n\n\ndef _looks_like_code(block: str, lang: str) -> bool:\n if lang.strip():\n return True\n if \"\\n\" in block and len(block) >= 20:\n hints = (\n \"def \", \"class \", \"fn \", \"let \", \"const \", \"import \", \"from \",\n \"return \", \"if \", \"for \", \"while \", \"{\", \"};\", \"=>\", \"::\",\n \"pub \", \"#include\",\n )\n return any(h in block for h in hints)\n return False\n\n\ndef materialize_code_blocks(answer: str, export_dir: Path, config_dir: Path) -> str:\n \"\"\"\n Extract fenced code blocks from answer, write to export_dir, replace with\n [代码块已保存: lang] rel_path markers. Returns rendered text.\n \"\"\"\n pattern = re.compile(\n r\"(?P`{3,}|~{3,})[ \\t]*(?P[a-zA-Z0-9_+\\-#.]*)[ \\t]*\\r?\\n?(?P.*?)(?:\\r?\\n)?(?P=fence)\",\n re.DOTALL,\n )\n matches = list(pattern.finditer(answer))\n if not matches:\n return answer\n\n try:\n export_dir.mkdir(parents=True, exist_ok=True)\n except OSError:\n return answer\n\n stamp = datetime.now().strftime(\"%Y%m%d_%H%M%S_%f\")\n chunks: list[str] = []\n cursor = 0\n file_index = 1\n for m in matches:\n start, end = m.span()\n if start > cursor:\n chunks.append(answer[cursor:start])\n lang = (m.group(\"lang\") or \"\").strip()\n code = m.group(\"code\")\n code_stripped = code.strip()\n if not code_stripped or not _looks_like_code(code_stripped, lang):\n chunks.append(answer[start:end])\n cursor = end\n continue\n ext = _code_extension(lang)\n filename = f\"reply_{stamp}_{file_index:02d}.{ext}\"\n file_index += 1\n file_path = export_dir / filename\n try:\n file_path.write_text(code_stripped + \"\\n\", encoding=\"utf-8\")\n if not file_path.exists() or file_path.stat().st_size <= 0:\n chunks.append(answer[start:end])\n cursor = end\n continue\n try:\n rel_path = file_path.relative_to(config_dir)\n except ValueError:\n rel_path = file_path\n label = lang if lang else \"text\"\n chunks.append(f\"\\n[代码块已保存: {label}] {rel_path}\\n\")\n except OSError:\n chunks.append(answer[start:end])\n cursor = end\n if cursor < len(answer):\n chunks.append(answer[cursor:])\n\n rendered = \"\".join(chunks).strip()\n return rendered if rendered else answer\n"}}}}