{"repo": "chuifengsky-lang/pdf-translate", "files": {"llm.py": {"b8c5b10d937c28fe73fb7787365d7c2c69807509": {"t": 1785746635.4015348, "n": 5031, "h": "e6f1d95f9b777afc", "c": "\"\"\"LLM client wrapper. DeepSeek is the initial provider.\n\nDeepSeek exposes an OpenAI-compatible API, so we use the `openai` SDK and point\nbase_url at DeepSeek. Both translate and define_word are STREAMING generators so\nthe UI can render output token-by-token.\n\"\"\"\n\nfrom openai import OpenAI\n\nPROVIDERS = {\n \"deepseek\": {\n \"base_url\": \"https://api.deepseek.com\", # OpenAI-compatible\n \"models\": [\"deepseek-v4-flash\", \"deepseek-v4-pro\"],\n \"default_model\": \"deepseek-v4-flash\",\n },\n \"openai\": {\n \"base_url\": \"https://api.openai.com/v1\",\n \"models\": [\"gpt-4o-mini\", \"gpt-4o\"],\n \"default_model\": \"gpt-4o-mini\",\n },\n}\n\n\ndef models_for(provider):\n return PROVIDERS.get(provider, {}).get(\"models\", [])\n\n\nclass LLMClient:\n def __init__(self, provider=\"deepseek\", api_key=\"\", model=None):\n if provider not in PROVIDERS:\n raise ValueError(\"Unknown provider: %s\" % provider)\n cfg = PROVIDERS[provider]\n self.provider = provider\n self.model = model or cfg[\"default_model\"]\n self.client = OpenAI(api_key=api_key, base_url=cfg[\"base_url\"])\n\n def translate_stream(self, text):\n \"\"\"Yield Chinese translation token-by-token for one English paragraph.\"\"\"\n prompt = (\n \"Translate the following academic English text into Simplified Chinese.\\n\"\n \"Rules:\\n\"\n \"- Keep technical terms accurate.\\n\"\n \"- Preserve all special symbols, mathematical notation, formulas, \"\n \"inline equations, citation markers (e.g. [12], (Smith et al., 2020)), \"\n \"URLs, and numbers exactly as they appear — do NOT translate or alter them.\\n\"\n \"- Preserve the original spacing and line structure where meaningful.\\n\"\n \"- Return ONLY the translation, with no notes or explanations.\\n\\n\"\n + text\n )\n stream = self.client.chat.completions.create(\n model=self.model,\n messages=[{\"role\": \"user\", \"content\": prompt}],\n temperature=0.2,\n stream=True,\n )\n for chunk in stream:\n if not chunk.choices:\n continue\n delta = chunk.choices[0].delta.content\n if delta:\n yield delta\n\n def study_summary_stream(self, papers):\n \"\"\"papers = [(name, text), ...]. Yield a Chinese study summary + advice:\n per-paper summary, links between them, and a step-by-step learning plan.\"\"\"\n parts = []\n for i, (name, text) in enumerate(papers, 1):\n parts.append(\"【论文%d:%s】\\n%s\" % (i, name, text))\n body = \"\\n\\n\".join(parts)\n prompt = (\n \"你是一位学术导师。下面是我挑选要学习的论文内容。请用简体中文:\\n\"\n \"1) 对每篇论文给出简明摘要(研究问题、方法、主要结论);\\n\"\n \"2) 指出它们的共同主题与相互联系;\\n\"\n \"3) 给出循序渐进的学习建议:建议的阅读顺序、需要补充的背景知识、\"\n \"每篇的重点章节、以及可延伸的研究方向。\\n\"\n \"条理清晰,使用小标题(用 Markdown)。\\n\\n\" + body\n )\n stream = self.client.chat.completions.create(\n model=self.model,\n messages=[{\"role\": \"user\", \"content\": prompt}],\n temperature=0.4,\n stream=True,\n )\n for chunk in stream:\n if not chunk.choices:\n continue\n delta = chunk.choices[0].delta.content\n if delta:\n yield delta\n\n def explain_stream(self, text):\n \"\"\"Yield a Chinese explanation of a selected passage: what it means plus\n relevant concepts/background. This powers the selection popup (it is a\n look-up / explanation, NOT a translation).\"\"\"\n prompt = (\n \"用简体中文解释下面这段选中的学术内容:\\n\"\n \"1) 先简要说明它在说什么(含义/要点);\\n\"\n \"2) 再补充相关的概念、背景或知识点,帮助理解。\\n\"\n \"只输出解释,不要逐字翻译原文。\\n\\n选中内容:\\n\" + text\n )\n stream = self.client.chat.completions.create(\n model=self.model,\n messages=[{\"role\": \"user\", \"content\": prompt}],\n temperature=0.3,\n stream=True,\n )\n for chunk in stream:\n if not chunk.choices:\n continue\n delta = chunk.choices[0].delta.content\n if delta:\n yield delta\n\n def define_word_stream(self, word, context):\n \"\"\"Yield a Chinese definition + concept note token-by-token.\"\"\"\n prompt = (\n 'Explain the term \"%s\" as used in this sentence:\\n\"%s\"\\n\\n'\n \"Reply in Simplified Chinese with two short parts:\\n\"\n \"1) 释义: a concise definition.\\n\"\n \"2) 相关概念: 2-3 sentences of relevant background/conceptual knowledge.\"\n % (word, context)\n )\n stream = self.client.chat.completions.create(\n model=self.model,\n messages=[{\"role\": \"user\", \"content\": prompt}],\n temperature=0.3,\n stream=True,\n )\n for chunk in stream:\n if not chunk.choices:\n continue\n delta = chunk.choices[0].delta.content\n if delta:\n yield delta\n"}, "fcd7e545776bfe0a195719cb4b5d42b5c486c64d": {"t": 1785766434.3356311, "n": 5031, "h": "e6f1d95f9b777afc", "c": "\"\"\"LLM client wrapper. DeepSeek is the initial provider.\n\nDeepSeek exposes an OpenAI-compatible API, so we use the `openai` SDK and point\nbase_url at DeepSeek. Both translate and define_word are STREAMING generators so\nthe UI can render output token-by-token.\n\"\"\"\n\nfrom openai import OpenAI\n\nPROVIDERS = {\n \"deepseek\": {\n \"base_url\": \"https://api.deepseek.com\", # OpenAI-compatible\n \"models\": [\"deepseek-v4-flash\", \"deepseek-v4-pro\"],\n \"default_model\": \"deepseek-v4-flash\",\n },\n \"openai\": {\n \"base_url\": \"https://api.openai.com/v1\",\n \"models\": [\"gpt-4o-mini\", \"gpt-4o\"],\n \"default_model\": \"gpt-4o-mini\",\n },\n}\n\n\ndef models_for(provider):\n return PROVIDERS.get(provider, {}).get(\"models\", [])\n\n\nclass LLMClient:\n def __init__(self, provider=\"deepseek\", api_key=\"\", model=None):\n if provider not in PROVIDERS:\n raise ValueError(\"Unknown provider: %s\" % provider)\n cfg = PROVIDERS[provider]\n self.provider = provider\n self.model = model or cfg[\"default_model\"]\n self.client = OpenAI(api_key=api_key, base_url=cfg[\"base_url\"])\n\n def translate_stream(self, text):\n \"\"\"Yield Chinese translation token-by-token for one English paragraph.\"\"\"\n prompt = (\n \"Translate the following academic English text into Simplified Chinese.\\n\"\n \"Rules:\\n\"\n \"- Keep technical terms accurate.\\n\"\n \"- Preserve all special symbols, mathematical notation, formulas, \"\n \"inline equations, citation markers (e.g. [12], (Smith et al., 2020)), \"\n \"URLs, and numbers exactly as they appear — do NOT translate or alter them.\\n\"\n \"- Preserve the original spacing and line structure where meaningful.\\n\"\n \"- Return ONLY the translation, with no notes or explanations.\\n\\n\"\n + text\n )\n stream = self.client.chat.completions.create(\n model=self.model,\n messages=[{\"role\": \"user\", \"content\": prompt}],\n temperature=0.2,\n stream=True,\n )\n for chunk in stream:\n if not chunk.choices:\n continue\n delta = chunk.choices[0].delta.content\n if delta:\n yield delta\n\n def study_summary_stream(self, papers):\n \"\"\"papers = [(name, text), ...]. Yield a Chinese study summary + advice:\n per-paper summary, links between them, and a step-by-step learning plan.\"\"\"\n parts = []\n for i, (name, text) in enumerate(papers, 1):\n parts.append(\"【论文%d:%s】\\n%s\" % (i, name, text))\n body = \"\\n\\n\".join(parts)\n prompt = (\n \"你是一位学术导师。下面是我挑选要学习的论文内容。请用简体中文:\\n\"\n \"1) 对每篇论文给出简明摘要(研究问题、方法、主要结论);\\n\"\n \"2) 指出它们的共同主题与相互联系;\\n\"\n \"3) 给出循序渐进的学习建议:建议的阅读顺序、需要补充的背景知识、\"\n \"每篇的重点章节、以及可延伸的研究方向。\\n\"\n \"条理清晰,使用小标题(用 Markdown)。\\n\\n\" + body\n )\n stream = self.client.chat.completions.create(\n model=self.model,\n messages=[{\"role\": \"user\", \"content\": prompt}],\n temperature=0.4,\n stream=True,\n )\n for chunk in stream:\n if not chunk.choices:\n continue\n delta = chunk.choices[0].delta.content\n if delta:\n yield delta\n\n def explain_stream(self, text):\n \"\"\"Yield a Chinese explanation of a selected passage: what it means plus\n relevant concepts/background. This powers the selection popup (it is a\n look-up / explanation, NOT a translation).\"\"\"\n prompt = (\n \"用简体中文解释下面这段选中的学术内容:\\n\"\n \"1) 先简要说明它在说什么(含义/要点);\\n\"\n \"2) 再补充相关的概念、背景或知识点,帮助理解。\\n\"\n \"只输出解释,不要逐字翻译原文。\\n\\n选中内容:\\n\" + text\n )\n stream = self.client.chat.completions.create(\n model=self.model,\n messages=[{\"role\": \"user\", \"content\": prompt}],\n temperature=0.3,\n stream=True,\n )\n for chunk in stream:\n if not chunk.choices:\n continue\n delta = chunk.choices[0].delta.content\n if delta:\n yield delta\n\n def define_word_stream(self, word, context):\n \"\"\"Yield a Chinese definition + concept note token-by-token.\"\"\"\n prompt = (\n 'Explain the term \"%s\" as used in this sentence:\\n\"%s\"\\n\\n'\n \"Reply in Simplified Chinese with two short parts:\\n\"\n \"1) 释义: a concise definition.\\n\"\n \"2) 相关概念: 2-3 sentences of relevant background/conceptual knowledge.\"\n % (word, context)\n )\n stream = self.client.chat.completions.create(\n model=self.model,\n messages=[{\"role\": \"user\", \"content\": prompt}],\n temperature=0.3,\n stream=True,\n )\n for chunk in stream:\n if not chunk.choices:\n continue\n delta = chunk.choices[0].delta.content\n if delta:\n yield delta\n"}, "e8201b62bbdf75331df05cc59f2aa465bd6f6e5d": {"t": 1785766435.7315009, "n": 3967, "h": "6002b6346abd1dac", "c": "\"\"\"LLM client wrapper. DeepSeek is the initial provider.\n\nDeepSeek exposes an OpenAI-compatible API, so we use the `openai` SDK and point\nbase_url at DeepSeek. Both translate and define_word are STREAMING generators so\nthe UI can render output token-by-token.\n\"\"\"\n\nfrom openai import OpenAI\n\nPROVIDERS = {\n \"deepseek\": {\n \"base_url\": \"https://api.deepseek.com\", # OpenAI-compatible\n \"models\": [\"deepseek-v4-flash\", \"deepseek-v4-pro\"],\n \"default_model\": \"deepseek-v4-flash\",\n },\n \"openai\": {\n \"base_url\": \"https://api.openai.com/v1\",\n \"models\": [\"gpt-4o-mini\", \"gpt-4o\"],\n \"default_model\": \"gpt-4o-mini\",\n },\n}\n\n\ndef models_for(provider):\n return PROVIDERS.get(provider, {}).get(\"models\", [])\n\n\nclass LLMClient:\n def __init__(self, provider=\"deepseek\", api_key=\"\", model=None):\n if provider not in PROVIDERS:\n raise ValueError(\"Unknown provider: %s\" % provider)\n cfg = PROVIDERS[provider]\n self.provider = provider\n self.model = model or cfg[\"default_model\"]\n self.client = OpenAI(api_key=api_key, base_url=cfg[\"base_url\"])\n\n def translate_stream(self, text):\n \"\"\"Yield Chinese translation token-by-token for one English paragraph.\"\"\"\n prompt = (\n \"Translate the following academic English text into Simplified Chinese.\\n\"\n \"Rules:\\n\"\n \"- Keep technical terms accurate.\\n\"\n \"- Preserve all special symbols, mathematical notation, formulas, \"\n \"inline equations, citation markers (e.g. [12], (Smith et al., 2020)), \"\n \"URLs, and numbers exactly as they appear — do NOT translate or alter them.\\n\"\n \"- Preserve the original spacing and line structure where meaningful.\\n\"\n \"- Return ONLY the translation, with no notes or explanations.\\n\\n\"\n + text\n )\n stream = self.client.chat.completions.create(\n model=self.model,\n messages=[{\"role\": \"user\", \"content\": prompt}],\n temperature=0.2,\n stream=True,\n )\n for chunk in stream:\n if not chunk.choices:\n continue\n delta = chunk.choices[0].delta.content\n if delta:\n yield delta\n\n def explain_stream(self, text):\n \"\"\"Yield a Chinese explanation of a selected passage: what it means plus\n relevant concepts/background. This powers the selection popup (it is a\n look-up / explanation, NOT a translation).\"\"\"\n prompt = (\n \"用简体中文解释下面这段选中的学术内容:\\n\"\n \"1) 先简要说明它在说什么(含义/要点);\\n\"\n \"2) 再补充相关的概念、背景或知识点,帮助理解。\\n\"\n \"只输出解释,不要逐字翻译原文。\\n\\n选中内容:\\n\" + text\n )\n stream = self.client.chat.completions.create(\n model=self.model,\n messages=[{\"role\": \"user\", \"content\": prompt}],\n temperature=0.3,\n stream=True,\n )\n for chunk in stream:\n if not chunk.choices:\n continue\n delta = chunk.choices[0].delta.content\n if delta:\n yield delta\n\n def define_word_stream(self, word, context):\n \"\"\"Yield a Chinese definition + concept note token-by-token.\"\"\"\n prompt = (\n 'Explain the term \"%s\" as used in this sentence:\\n\"%s\"\\n\\n'\n \"Reply in Simplified Chinese with two short parts:\\n\"\n \"1) 释义: a concise definition.\\n\"\n \"2) 相关概念: 2-3 sentences of relevant background/conceptual knowledge.\"\n % (word, context)\n )\n stream = self.client.chat.completions.create(\n model=self.model,\n messages=[{\"role\": \"user\", \"content\": prompt}],\n temperature=0.3,\n stream=True,\n )\n for chunk in stream:\n if not chunk.choices:\n continue\n delta = chunk.choices[0].delta.content\n if delta:\n yield delta\n"}, "4d9d8d08f7a620eed6f00677fcab8e17c2a89641": {"t": 1785766438.1235468, "n": 3127, "h": "602d369938964401", "c": "\"\"\"LLM client wrapper. DeepSeek is the initial provider.\n\nDeepSeek exposes an OpenAI-compatible API, so we use the `openai` SDK and point\nbase_url at DeepSeek. Both translate and define_word are STREAMING generators so\nthe UI can render output token-by-token.\n\"\"\"\n\nfrom openai import OpenAI\n\nPROVIDERS = {\n \"deepseek\": {\n \"base_url\": \"https://api.deepseek.com\", # OpenAI-compatible\n \"models\": [\"deepseek-v4-flash\", \"deepseek-v4-pro\"],\n \"default_model\": \"deepseek-v4-flash\",\n },\n \"openai\": {\n \"base_url\": \"https://api.openai.com/v1\",\n \"models\": [\"gpt-4o-mini\", \"gpt-4o\"],\n \"default_model\": \"gpt-4o-mini\",\n },\n}\n\n\ndef models_for(provider):\n return PROVIDERS.get(provider, {}).get(\"models\", [])\n\n\nclass LLMClient:\n def __init__(self, provider=\"deepseek\", api_key=\"\", model=None):\n if provider not in PROVIDERS:\n raise ValueError(\"Unknown provider: %s\" % provider)\n cfg = PROVIDERS[provider]\n self.provider = provider\n self.model = model or cfg[\"default_model\"]\n self.client = OpenAI(api_key=api_key, base_url=cfg[\"base_url\"])\n\n def translate_stream(self, text):\n \"\"\"Yield Chinese translation token-by-token for one English paragraph.\"\"\"\n prompt = (\n \"Translate the following academic English text into Simplified Chinese.\\n\"\n \"Rules:\\n\"\n \"- Keep technical terms accurate.\\n\"\n \"- Preserve all special symbols, mathematical notation, formulas, \"\n \"inline equations, citation markers (e.g. [12], (Smith et al., 2020)), \"\n \"URLs, and numbers exactly as they appear — do NOT translate or alter them.\\n\"\n \"- Preserve the original spacing and line structure where meaningful.\\n\"\n \"- Return ONLY the translation, with no notes or explanations.\\n\\n\"\n + text\n )\n stream = self.client.chat.completions.create(\n model=self.model,\n messages=[{\"role\": \"user\", \"content\": prompt}],\n temperature=0.2,\n stream=True,\n )\n for chunk in stream:\n if not chunk.choices:\n continue\n delta = chunk.choices[0].delta.content\n if delta:\n yield delta\n\n def define_word_stream(self, word, context):\n \"\"\"Yield a Chinese definition + concept note token-by-token.\"\"\"\n prompt = (\n 'Explain the term \"%s\" as used in this sentence:\\n\"%s\"\\n\\n'\n \"Reply in Simplified Chinese with two short parts:\\n\"\n \"1) 释义: a concise definition.\\n\"\n \"2) 相关概念: 2-3 sentences of relevant background/conceptual knowledge.\"\n % (word, context)\n )\n stream = self.client.chat.completions.create(\n model=self.model,\n messages=[{\"role\": \"user\", \"content\": prompt}],\n temperature=0.3,\n stream=True,\n )\n for chunk in stream:\n if not chunk.choices:\n continue\n delta = chunk.choices[0].delta.content\n if delta:\n yield delta\n"}, "5b072f36f666f9b35e0c2a33dcb4be825c43f720": {"t": 1785766439.826134, "n": 2124, "h": "7aee9f549842c4c8", "c": "\"\"\"LLM client wrapper. DeepSeek is the initial provider.\n\nDeepSeek exposes an OpenAI-compatible API, so we use the `openai` SDK and just\npoint base_url at DeepSeek. Adding another provider later = one more dict entry.\n\"\"\"\n\nfrom openai import OpenAI\n\nPROVIDERS = {\n \"deepseek\": {\n \"base_url\": \"https://api.deepseek.com/v1\",\n \"default_model\": \"deepseek-chat\",\n },\n \"openai\": {\n \"base_url\": \"https://api.openai.com/v1\",\n \"default_model\": \"gpt-4o-mini\",\n },\n}\n\n\nclass LLMClient:\n def __init__(self, provider=\"deepseek\", api_key=\"\", model=None):\n if provider not in PROVIDERS:\n raise ValueError(\"Unknown provider: %s\" % provider)\n cfg = PROVIDERS[provider]\n self.provider = provider\n self.model = model or cfg[\"default_model\"]\n self.client = OpenAI(api_key=api_key, base_url=cfg[\"base_url\"])\n\n def translate(self, text):\n \"\"\"Translate an English paragraph into Chinese, layout-neutral.\"\"\"\n prompt = (\n \"Translate the following academic English text into Simplified Chinese. \"\n \"Keep technical terms accurate. Return ONLY the translation, no notes.\\n\\n\"\n + text\n )\n resp = self.client.chat.completions.create(\n model=self.model,\n messages=[{\"role\": \"user\", \"content\": prompt}],\n temperature=0.2,\n )\n return resp.choices[0].message.content.strip()\n\n def define_word(self, word, context):\n \"\"\"Return a Chinese definition + concept note for a word in context.\"\"\"\n prompt = (\n 'Explain the term \"%s\" as used in this sentence:\\n\"%s\"\\n\\n'\n \"Reply in Simplified Chinese with two short parts:\\n\"\n \"1) 释义: a concise definition.\\n\"\n \"2) 相关概念: 2-3 sentences of relevant background/conceptual knowledge.\"\n % (word, context)\n )\n resp = self.client.chat.completions.create(\n model=self.model,\n messages=[{\"role\": \"user\", \"content\": prompt}],\n temperature=0.3,\n )\n return resp.choices[0].message.content.strip()\n"}}}}