Use direct LLM config for qqzot
This commit is contained in:
parent
2d15c23975
commit
f14378c0b4
20
README.md
20
README.md
|
|
@ -55,3 +55,23 @@ QQzot 不负责:
|
|||
- 需要调用 LLM 生成笔记或表格时,使用 AwesomeGPT 或 DeepSeek 相关环境配置。
|
||||
|
||||
不要把 API key 写入 skill 文件、Obsidian 笔记、Git 提交、终端输出或聊天记录。
|
||||
## LLM 直接配置
|
||||
|
||||
`qqzot` 默认使用脚本直接读取环境变量或 `.env`,不再默认读取 Zotero 的 AwesomeGPT/ZoteroGPT preferences。
|
||||
|
||||
推荐配置:
|
||||
|
||||
```powershell
|
||||
$env:DEEPSEEK_API_KEY="..."
|
||||
$env:DEEPSEEK_MODEL="deepseek-chat"
|
||||
```
|
||||
|
||||
也可以使用中性命名:
|
||||
|
||||
```powershell
|
||||
$env:QQZOT_LLM_API_KEY="..."
|
||||
$env:QQZOT_LLM_BASE_URL="https://api.deepseek.com"
|
||||
$env:QQZOT_LLM_MODEL="deepseek-chat"
|
||||
```
|
||||
|
||||
只有显式加 `--use-awesomegpt-prefs` 时,脚本才会读取 AwesomeGPT/ZoteroGPT 的旧配置。
|
||||
|
|
|
|||
18
SKILL.md
18
SKILL.md
|
|
@ -27,8 +27,12 @@ Boundary:
|
|||
- Default Obsidian vault, only when a script needs templates or an output path:
|
||||
`C:\Users\qyh15\Documents\Obsidian Vault`.
|
||||
- `ZOTERO_API_KEY` is required only for Zotero Web API writes.
|
||||
- LLM settings come from `AWESOMEGPT_API_KEY`, `AWESOMEGPT_BASE_URL`,
|
||||
`AWESOMEGPT_MODEL`, or AwesomeGPT preferences in the default Zotero profile.
|
||||
- LLM settings come from direct script configuration, not Zotero AwesomeGPT by
|
||||
default. Prefer `DEEPSEEK_API_KEY`, optional `DEEPSEEK_BASE_URL`, and optional
|
||||
`DEEPSEEK_MODEL`; or use `QQZOT_LLM_API_KEY`, `QQZOT_LLM_BASE_URL`, and
|
||||
`QQZOT_LLM_MODEL` for a provider-neutral config.
|
||||
- AwesomeGPT/ZoteroGPT preferences are legacy fallback only. Use
|
||||
`--use-awesomegpt-prefs` explicitly if the user asks to reuse those settings.
|
||||
|
||||
Never store API keys in skill files, vault helpers, Git commits, zip files,
|
||||
terminal output, or chat. If keys appear in logs or chat, tell the user to
|
||||
|
|
@ -53,6 +57,16 @@ Use the smallest Zotero operation that answers the request:
|
|||
State clearly before live writes that Zotero child notes will be created.
|
||||
Use `--dry-run` for first-time validation or template changes.
|
||||
|
||||
Default LLM config should be direct:
|
||||
|
||||
```powershell
|
||||
$env:DEEPSEEK_API_KEY="..."
|
||||
$env:DEEPSEEK_MODEL="deepseek-chat"
|
||||
```
|
||||
|
||||
Do not rely on AwesomeGPT preferences unless the command includes
|
||||
`--use-awesomegpt-prefs`.
|
||||
|
||||
Single item:
|
||||
|
||||
```powershell
|
||||
|
|
|
|||
|
|
@ -3,11 +3,14 @@
|
|||
Generate an AI literature note from Zotero metadata and save it as a Zotero child note.
|
||||
|
||||
Required environment variables:
|
||||
AWESOMEGPT_API_KEY DeepSeek/OpenAI-compatible API key
|
||||
AWESOMEGPT_BASE_URL Example: https://api.deepseek.com
|
||||
AWESOMEGPT_MODEL Example: deepseek-v4-pro
|
||||
DEEPSEEK_API_KEY or QQZOT_LLM_API_KEY
|
||||
ZOTERO_API_KEY Zotero Web API key with library write permission
|
||||
|
||||
Optional LLM environment variables:
|
||||
DEEPSEEK_BASE_URL or QQZOT_LLM_BASE_URL, default: https://api.deepseek.com
|
||||
DEEPSEEK_MODEL or QQZOT_LLM_MODEL, default: deepseek-chat
|
||||
OPENAI_API_KEY / OPENAI_BASE_URL / OPENAI_MODEL for other OpenAI-compatible APIs
|
||||
|
||||
Optional:
|
||||
ZOTERO_USER_ID If omitted, resolved from /keys/current
|
||||
"""
|
||||
|
|
@ -57,6 +60,14 @@ def load_dotenv(path: Path) -> None:
|
|||
os.environ.setdefault(key, value)
|
||||
|
||||
|
||||
def first_env(*names: str) -> str:
|
||||
for name in names:
|
||||
value = os.environ.get(name)
|
||||
if value:
|
||||
return value
|
||||
return ""
|
||||
|
||||
|
||||
def zotero_profile_prefs() -> Path | None:
|
||||
profiles_ini = Path.home() / "AppData/Roaming/Zotero/Zotero/profiles.ini"
|
||||
profiles_root = profiles_ini.parent
|
||||
|
|
@ -444,11 +455,20 @@ def build_prompt(item: dict[str, Any], bibtex: str, fulltext: str, vault: Path)
|
|||
|
||||
|
||||
def call_llm(prompt: str) -> str:
|
||||
api_key = os.environ.get("AWESOMEGPT_API_KEY")
|
||||
base_url = (os.environ.get("AWESOMEGPT_BASE_URL") or "").rstrip("/")
|
||||
model = os.environ.get("AWESOMEGPT_MODEL")
|
||||
api_key = first_env("QQZOT_LLM_API_KEY", "DEEPSEEK_API_KEY", "OPENAI_API_KEY", "AWESOMEGPT_API_KEY")
|
||||
base_url = first_env("QQZOT_LLM_BASE_URL", "DEEPSEEK_BASE_URL", "OPENAI_BASE_URL", "AWESOMEGPT_BASE_URL")
|
||||
model = first_env("QQZOT_LLM_MODEL", "DEEPSEEK_MODEL", "OPENAI_MODEL", "AWESOMEGPT_MODEL")
|
||||
if api_key and not base_url and os.environ.get("DEEPSEEK_API_KEY"):
|
||||
base_url = "https://api.deepseek.com"
|
||||
if api_key and not model and os.environ.get("DEEPSEEK_API_KEY"):
|
||||
model = "deepseek-chat"
|
||||
base_url = base_url.rstrip("/")
|
||||
if not api_key or not base_url or not model:
|
||||
fail("AWESOMEGPT_API_KEY, AWESOMEGPT_BASE_URL, and AWESOMEGPT_MODEL are required")
|
||||
fail(
|
||||
"LLM config is required. Set DEEPSEEK_API_KEY plus optional "
|
||||
"DEEPSEEK_BASE_URL/DEEPSEEK_MODEL, or set QQZOT_LLM_API_KEY, "
|
||||
"QQZOT_LLM_BASE_URL, and QQZOT_LLM_MODEL."
|
||||
)
|
||||
if not base_url.endswith("/v1"):
|
||||
base_url = base_url + "/v1"
|
||||
payload = {
|
||||
|
|
@ -527,11 +547,17 @@ def main() -> None:
|
|||
parser.add_argument("--dry-run", action="store_true", help="generate but do not write Zotero note")
|
||||
parser.add_argument("--vault", default=str(DEFAULT_VAULT), help="Obsidian vault containing 00 Templater")
|
||||
parser.add_argument("--env-file", help="Optional .env path; defaults to <vault>/.env")
|
||||
parser.add_argument(
|
||||
"--use-awesomegpt-prefs",
|
||||
action="store_true",
|
||||
help="Opt in to reading Zotero AwesomeGPT/ZoteroGPT preferences as a legacy config source.",
|
||||
)
|
||||
args = parser.parse_args()
|
||||
|
||||
vault = Path(args.vault).expanduser().resolve()
|
||||
load_dotenv(Path(args.env_file).expanduser().resolve() if args.env_file else vault / ".env")
|
||||
load_awesomegpt_prefs()
|
||||
if args.use_awesomegpt_prefs:
|
||||
load_awesomegpt_prefs()
|
||||
|
||||
keys = list(args.item_key)
|
||||
if args.item_keys:
|
||||
|
|
|
|||
|
|
@ -3,12 +3,15 @@
|
|||
Summarize Zotero items into a Markdown table using an OpenAI-compatible LLM.
|
||||
|
||||
Required environment variables:
|
||||
AWESOMEGPT_API_KEY DeepSeek/OpenAI-compatible API key
|
||||
AWESOMEGPT_BASE_URL Example: https://api.deepseek.com
|
||||
AWESOMEGPT_MODEL Example: deepseek-v4-pro
|
||||
DEEPSEEK_API_KEY or QQZOT_LLM_API_KEY
|
||||
|
||||
Optional LLM environment variables:
|
||||
DEEPSEEK_BASE_URL or QQZOT_LLM_BASE_URL, default: https://api.deepseek.com
|
||||
DEEPSEEK_MODEL or QQZOT_LLM_MODEL, default: deepseek-chat
|
||||
OPENAI_API_KEY / OPENAI_BASE_URL / OPENAI_MODEL for other OpenAI-compatible APIs
|
||||
|
||||
Optional:
|
||||
Load from <vault>/.env or AwesomeGPT preferences in the default Zotero profile.
|
||||
Load from <vault>/.env.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
|
@ -63,6 +66,14 @@ def load_dotenv(path: Path) -> None:
|
|||
os.environ.setdefault(key.strip(), value.strip().strip('"').strip("'"))
|
||||
|
||||
|
||||
def first_env(*names: str) -> str:
|
||||
for name in names:
|
||||
value = os.environ.get(name)
|
||||
if value:
|
||||
return value
|
||||
return ""
|
||||
|
||||
|
||||
def zotero_profile_prefs() -> Path | None:
|
||||
profiles_ini = Path.home() / "AppData/Roaming/Zotero/Zotero/profiles.ini"
|
||||
profiles_root = profiles_ini.parent
|
||||
|
|
@ -388,11 +399,20 @@ def build_prompt(records: list[dict[str, Any]], columns: list[str]) -> str:
|
|||
|
||||
|
||||
def call_llm(prompt: str) -> str:
|
||||
api_key = os.environ.get("AWESOMEGPT_API_KEY")
|
||||
base_url = (os.environ.get("AWESOMEGPT_BASE_URL") or "").rstrip("/")
|
||||
model = os.environ.get("AWESOMEGPT_MODEL")
|
||||
api_key = first_env("QQZOT_LLM_API_KEY", "DEEPSEEK_API_KEY", "OPENAI_API_KEY", "AWESOMEGPT_API_KEY")
|
||||
base_url = first_env("QQZOT_LLM_BASE_URL", "DEEPSEEK_BASE_URL", "OPENAI_BASE_URL", "AWESOMEGPT_BASE_URL")
|
||||
model = first_env("QQZOT_LLM_MODEL", "DEEPSEEK_MODEL", "OPENAI_MODEL", "AWESOMEGPT_MODEL")
|
||||
if api_key and not base_url and os.environ.get("DEEPSEEK_API_KEY"):
|
||||
base_url = "https://api.deepseek.com"
|
||||
if api_key and not model and os.environ.get("DEEPSEEK_API_KEY"):
|
||||
model = "deepseek-chat"
|
||||
base_url = base_url.rstrip("/")
|
||||
if not api_key or not base_url or not model:
|
||||
fail("AWESOMEGPT_API_KEY, AWESOMEGPT_BASE_URL, and AWESOMEGPT_MODEL are required")
|
||||
fail(
|
||||
"LLM config is required. Set DEEPSEEK_API_KEY plus optional "
|
||||
"DEEPSEEK_BASE_URL/DEEPSEEK_MODEL, or set QQZOT_LLM_API_KEY, "
|
||||
"QQZOT_LLM_BASE_URL, and QQZOT_LLM_MODEL."
|
||||
)
|
||||
if not base_url.endswith("/v1"):
|
||||
base_url = base_url + "/v1"
|
||||
payload = {
|
||||
|
|
@ -458,11 +478,17 @@ def main() -> None:
|
|||
parser.add_argument("--vault", default=str(DEFAULT_VAULT), help="Obsidian vault containing optional .env")
|
||||
parser.add_argument("--env-file", help="Optional .env path; defaults to <vault>/.env")
|
||||
parser.add_argument("--out", help="Optional output Markdown file path")
|
||||
parser.add_argument(
|
||||
"--use-awesomegpt-prefs",
|
||||
action="store_true",
|
||||
help="Opt in to reading Zotero AwesomeGPT/ZoteroGPT preferences as a legacy config source.",
|
||||
)
|
||||
args = parser.parse_args()
|
||||
|
||||
vault = Path(args.vault).expanduser().resolve()
|
||||
load_dotenv(Path(args.env_file).expanduser().resolve() if args.env_file else vault / ".env")
|
||||
load_awesomegpt_prefs()
|
||||
if args.use_awesomegpt_prefs:
|
||||
load_awesomegpt_prefs()
|
||||
|
||||
keys = list(args.item_key)
|
||||
if args.item_keys:
|
||||
|
|
|
|||
Loading…
Reference in New Issue