修复ai
This commit is contained in:
+66
-20
@@ -1,26 +1,58 @@
|
|||||||
"""大模型调用:OpenAI 兼容接口(默认)或本机 Ollama 二选一。"""
|
"""大模型调用:OpenAI 兼容接口(默认)或本机 Ollama 二选一。
|
||||||
|
|
||||||
|
配置从 os.environ 惰性读取:各实例 app.py 在 import 本模块后才 load_env_file(.env),
|
||||||
|
若在 import 时缓存变量会导致 OPENAI_API_KEY 始终为空。
|
||||||
|
"""
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import base64
|
import base64
|
||||||
import os
|
import os
|
||||||
from typing import List, Optional, Sequence
|
from typing import List, Optional, Sequence
|
||||||
from urllib.parse import urljoin
|
|
||||||
|
|
||||||
import requests
|
import requests
|
||||||
|
|
||||||
AI_TIMEOUT_SECONDS = int(os.getenv("AI_TIMEOUT_SECONDS", "120"))
|
|
||||||
AI_PROVIDER = (os.getenv("AI_PROVIDER", "openai") or "openai").strip().lower()
|
|
||||||
|
|
||||||
OPENAI_API_BASE = (os.getenv("OPENAI_API_BASE", "https://op.bz121.com/v1") or "").strip().rstrip("/")
|
def _env_str(name: str, default: str = "") -> str:
|
||||||
OPENAI_API_KEY = (os.getenv("OPENAI_API_KEY") or os.getenv("OPENAI_API_KEY") or "").strip()
|
v = os.getenv(name)
|
||||||
OPENAI_MODEL = (os.getenv("OPENAI_MODEL", "gemma4:e4b") or "gemma4:e4b").strip()
|
if v is None:
|
||||||
|
return default
|
||||||
|
return str(v).strip()
|
||||||
|
|
||||||
OLLAMA_API = os.getenv("OLLAMA_API", "http://127.0.0.1:11434/api/generate")
|
|
||||||
AI_MODEL = os.getenv("AI_MODEL", "huihui_ai/deepseek-r1-abliterated:latest")
|
def _ai_timeout_seconds() -> int:
|
||||||
|
try:
|
||||||
|
return max(10, int(_env_str("AI_TIMEOUT_SECONDS", "120") or "120"))
|
||||||
|
except ValueError:
|
||||||
|
return 120
|
||||||
|
|
||||||
|
|
||||||
|
def _ai_provider() -> str:
|
||||||
|
return (_env_str("AI_PROVIDER", "openai") or "openai").lower()
|
||||||
|
|
||||||
|
|
||||||
|
def _openai_api_base() -> str:
|
||||||
|
base = _env_str("OPENAI_API_BASE", "https://op.bz121.com/v1") or "https://op.bz121.com/v1"
|
||||||
|
return base.rstrip("/")
|
||||||
|
|
||||||
|
|
||||||
|
def _openai_api_key() -> str:
|
||||||
|
return _env_str("OPENAI_API_KEY") or _env_str("AI_API_KEY")
|
||||||
|
|
||||||
|
|
||||||
|
def _openai_model() -> str:
|
||||||
|
return _env_str("OPENAI_MODEL", "gemma4:e4b") or "gemma4:e4b"
|
||||||
|
|
||||||
|
|
||||||
|
def _ollama_api() -> str:
|
||||||
|
return _env_str("OLLAMA_API", "http://127.0.0.1:11434/api/generate") or "http://127.0.0.1:11434/api/generate"
|
||||||
|
|
||||||
|
|
||||||
|
def _ollama_model() -> str:
|
||||||
|
return _env_str("AI_MODEL", "huihui_ai/deepseek-r1-abliterated:latest") or "huihui_ai/deepseek-r1-abliterated:latest"
|
||||||
|
|
||||||
|
|
||||||
def _use_openai() -> bool:
|
def _use_openai() -> bool:
|
||||||
return AI_PROVIDER in ("openai", "openai_compatible", "gateway")
|
return _ai_provider() in ("openai", "openai_compatible", "gateway")
|
||||||
|
|
||||||
|
|
||||||
def _read_image_base64(image_path: str) -> Optional[str]:
|
def _read_image_base64(image_path: str) -> Optional[str]:
|
||||||
@@ -47,17 +79,18 @@ def _collect_images(
|
|||||||
|
|
||||||
|
|
||||||
def _openai_chat_url() -> str:
|
def _openai_chat_url() -> str:
|
||||||
base = OPENAI_API_BASE or "https://op.bz121.com/v1"
|
base = _openai_api_base()
|
||||||
if base.endswith("/chat/completions"):
|
if base.endswith("/chat/completions"):
|
||||||
return base
|
return base
|
||||||
return f"{base}/chat/completions"
|
return f"{base}/chat/completions"
|
||||||
|
|
||||||
|
|
||||||
def _generate_openai(prompt: str, images: List[str], temperature: float) -> str:
|
def _generate_openai(prompt: str, images: List[str], temperature: float) -> str:
|
||||||
if not OPENAI_API_KEY:
|
api_key = _openai_api_key()
|
||||||
return "AI 调用失败:未配置 OPENAI_API_KEY"
|
if not api_key:
|
||||||
|
return "AI 调用失败:未配置 OPENAI_API_KEY(请在当前实例目录 .env 中设置,修改后需重启服务)"
|
||||||
headers = {
|
headers = {
|
||||||
"Authorization": f"Bearer {OPENAI_API_KEY}",
|
"Authorization": f"Bearer {api_key}",
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
}
|
}
|
||||||
if images:
|
if images:
|
||||||
@@ -73,7 +106,7 @@ def _generate_openai(prompt: str, images: List[str], temperature: float) -> str:
|
|||||||
else:
|
else:
|
||||||
messages = [{"role": "user", "content": prompt}]
|
messages = [{"role": "user", "content": prompt}]
|
||||||
body = {
|
body = {
|
||||||
"model": OPENAI_MODEL,
|
"model": _openai_model(),
|
||||||
"messages": messages,
|
"messages": messages,
|
||||||
"temperature": temperature,
|
"temperature": temperature,
|
||||||
"stream": False,
|
"stream": False,
|
||||||
@@ -82,7 +115,7 @@ def _generate_openai(prompt: str, images: List[str], temperature: float) -> str:
|
|||||||
_openai_chat_url(),
|
_openai_chat_url(),
|
||||||
headers=headers,
|
headers=headers,
|
||||||
json=body,
|
json=body,
|
||||||
timeout=AI_TIMEOUT_SECONDS,
|
timeout=_ai_timeout_seconds(),
|
||||||
)
|
)
|
||||||
r.raise_for_status()
|
r.raise_for_status()
|
||||||
data = r.json()
|
data = r.json()
|
||||||
@@ -95,14 +128,14 @@ def _generate_openai(prompt: str, images: List[str], temperature: float) -> str:
|
|||||||
|
|
||||||
def _generate_ollama(prompt: str, images: List[str], temperature: float) -> str:
|
def _generate_ollama(prompt: str, images: List[str], temperature: float) -> str:
|
||||||
payload = {
|
payload = {
|
||||||
"model": AI_MODEL,
|
"model": _ollama_model(),
|
||||||
"prompt": prompt,
|
"prompt": prompt,
|
||||||
"stream": False,
|
"stream": False,
|
||||||
"options": {"temperature": temperature},
|
"options": {"temperature": temperature},
|
||||||
}
|
}
|
||||||
if images:
|
if images:
|
||||||
payload["images"] = images
|
payload["images"] = images
|
||||||
r = requests.post(OLLAMA_API, json=payload, timeout=AI_TIMEOUT_SECONDS)
|
r = requests.post(_ollama_api(), json=payload, timeout=_ai_timeout_seconds())
|
||||||
r.raise_for_status()
|
r.raise_for_status()
|
||||||
return (r.json().get("response") or "").strip() or "AI 生成失败"
|
return (r.json().get("response") or "").strip() or "AI 生成失败"
|
||||||
|
|
||||||
@@ -173,5 +206,18 @@ def ai_short_advice(prompt_text: str) -> str:
|
|||||||
|
|
||||||
def ai_provider_label() -> str:
|
def ai_provider_label() -> str:
|
||||||
if _use_openai():
|
if _use_openai():
|
||||||
return f"OpenAI 兼容 · {OPENAI_MODEL} @ {OPENAI_API_BASE}"
|
return f"OpenAI 兼容 · {_openai_model()} @ {_openai_api_base()}"
|
||||||
return f"Ollama · {AI_MODEL}"
|
return f"Ollama · {_ollama_model()}"
|
||||||
|
|
||||||
|
|
||||||
|
def ai_config_status() -> dict:
|
||||||
|
"""调试用:当前进程内读到的 AI 配置(不含密钥明文)。"""
|
||||||
|
key = _openai_api_key()
|
||||||
|
return {
|
||||||
|
"provider": _ai_provider(),
|
||||||
|
"openai_base": _openai_api_base(),
|
||||||
|
"openai_model": _openai_model(),
|
||||||
|
"openai_key_configured": bool(key),
|
||||||
|
"ollama_api": _ollama_api(),
|
||||||
|
"ollama_model": _ollama_model(),
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user