feat(multimodal): 实现本地模型管线与请求用量配置
This commit is contained in:
@@ -0,0 +1,19 @@
|
||||
param(
|
||||
[ValidateSet('cpu', 'cuda')][string]$Device = 'cpu'
|
||||
)
|
||||
$ErrorActionPreference = 'Stop'
|
||||
$backendRoot = Split-Path $PSScriptRoot -Parent
|
||||
$runtimeRoot = Join-Path $backendRoot '.venv-models'
|
||||
$runtimePython = Join-Path $runtimeRoot 'Scripts/python.exe'
|
||||
if (!(Test-Path -LiteralPath $runtimePython)) {
|
||||
& uv venv --python 3.12 $runtimeRoot
|
||||
if ($LASTEXITCODE -ne 0) { throw '无法创建模型运行环境' }
|
||||
}
|
||||
# CPU is the default. CUDA wheels include the runtime, not the NVIDIA driver.
|
||||
$torchIndex = if ($Device -eq 'cuda') { 'https://download.pytorch.org/whl/cu128' } else { 'https://download.pytorch.org/whl/cpu' }
|
||||
& uv pip install --python $runtimePython --index-url $torchIndex 'torch==2.9.1' 'torchaudio==2.9.1'
|
||||
if ($LASTEXITCODE -ne 0) { throw 'PyTorch 安装失败' }
|
||||
& uv pip install --python $runtimePython -r (Join-Path $PSScriptRoot 'model-requirements.lock') -c (Join-Path $PSScriptRoot 'model-requirements.txt')
|
||||
if ($LASTEXITCODE -ne 0) { throw '模型依赖安装失败' }
|
||||
& $runtimePython -c 'import torch; print({"torch":torch.__version__,"cuda_available":torch.cuda.is_available()})'
|
||||
if ($LASTEXITCODE -ne 0) { throw '模型运行环境检查失败' }
|
||||
@@ -0,0 +1,40 @@
|
||||
"""Explicit real-model smoke: run with the backend Python, never part of unit tests."""
|
||||
import argparse
|
||||
import asyncio
|
||||
import json
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
|
||||
from app.local_models.manager import _download, read_state
|
||||
from app.local_models.runtime import runtime
|
||||
|
||||
|
||||
async def main():
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("model", choices=["bekko", "granite", "qwen3-asr", "eres2netv2"])
|
||||
parser.add_argument("--download", action="store_true")
|
||||
parser.add_argument("--audio")
|
||||
parser.add_argument("--reference")
|
||||
args = parser.parse_args()
|
||||
if args.download:
|
||||
await _download(args.model)
|
||||
state = read_state(args.model)
|
||||
print(json.dumps(state), flush=True)
|
||||
if state["status"] != "installed":
|
||||
raise SystemExit(1)
|
||||
if args.model in {"bekko", "granite"}:
|
||||
result = await runtime.infer(args.model, "embedding", {"texts": ["今天上课学习线性代数", "矩阵与向量是线性代数的基础", "晚餐吃番茄炒蛋"]})
|
||||
print(json.dumps({"count": len(result), "dimensions": len(result[0]),
|
||||
"related_similarity": sum(a * b for a, b in zip(result[0], result[1])),
|
||||
"unrelated_similarity": sum(a * b for a, b in zip(result[0], result[2]))}))
|
||||
elif args.audio:
|
||||
operation = "transcription" if args.model == "qwen3-asr" else "speaker_matching"
|
||||
result = await runtime.infer(args.model, operation, {"source": str(Path(args.audio).resolve()),
|
||||
"language": "zh", "reference": str(Path(args.reference or args.audio).resolve())})
|
||||
print(json.dumps(result, ensure_ascii=False))
|
||||
print(json.dumps(runtime.diagnostics), flush=True)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
@@ -0,0 +1,99 @@
|
||||
accelerate==1.12.0
|
||||
addict==2.4.0
|
||||
annotated-doc==0.0.5
|
||||
annotated-types==0.8.0
|
||||
anyio==4.15.0
|
||||
av==16.1.0
|
||||
blinker==1.9.0
|
||||
brotli==1.2.0
|
||||
certifi==2026.7.22
|
||||
cffi==2.1.1
|
||||
charset-normalizer==3.5.1
|
||||
click==8.5.0
|
||||
cloudpickle==3.1.2
|
||||
colorama==0.4.6
|
||||
cryptography==50.0.1
|
||||
cython==3.3.0
|
||||
decorator==5.3.1
|
||||
dynet38==2.2
|
||||
fastapi==0.141.1
|
||||
filelock==3.32.3
|
||||
flask==3.1.3
|
||||
fsspec==2026.7.0
|
||||
gradio==6.17.3
|
||||
gradio-client==2.5.0
|
||||
groovy==0.1.2
|
||||
h11==0.16.0
|
||||
hf-gradio==0.4.1
|
||||
httpcore==1.0.9
|
||||
httpx==0.28.1
|
||||
huggingface-hub==0.36.2
|
||||
idna==3.19
|
||||
itsdangerous==2.2.0
|
||||
jinja2==3.1.6
|
||||
joblib==1.6.0
|
||||
lazy-loader==0.5
|
||||
librosa==1.0.0
|
||||
llvmlite==0.49.0
|
||||
markdown-it-py==4.2.0
|
||||
markupsafe==3.0.3
|
||||
mdurl==0.1.2
|
||||
modelscope==1.39.1
|
||||
modelscope-hub==0.4.0
|
||||
mpmath==1.3.0
|
||||
msgpack==1.2.2
|
||||
nagisa==0.2.11
|
||||
narwhals==2.25.0
|
||||
networkx==3.6.1
|
||||
numba==0.67.0
|
||||
numpy==2.5.2
|
||||
orjson==3.12.0
|
||||
packaging==26.3
|
||||
pandas==3.0.5
|
||||
pillow==12.3.0
|
||||
platformdirs==4.11.7
|
||||
pooch==1.9.0
|
||||
psutil==7.2.2
|
||||
pycparser==3.0
|
||||
pydantic==2.13.5
|
||||
pydantic-core==2.46.5
|
||||
pydub==0.25.1
|
||||
pygments==2.21.0
|
||||
python-dateutil==2.9.0.post0
|
||||
python-multipart==0.0.32
|
||||
pytz==2026.3.post1
|
||||
pyyaml==6.0.3
|
||||
qwen-asr==0.0.6
|
||||
qwen-omni-utils==0.0.9
|
||||
regex==2026.9.3
|
||||
requests==2.34.2
|
||||
rich==15.0.0
|
||||
safehttpx==0.1.7
|
||||
safetensors==0.8.0
|
||||
scikit-learn==1.9.0
|
||||
scipy==1.18.1
|
||||
semantic-version==2.10.0
|
||||
sentence-transformers==5.2.0
|
||||
setuptools==78.1.0
|
||||
shellingham==1.5.4
|
||||
simplejson==3.20.2
|
||||
six==1.17.0
|
||||
sortedcontainers==2.4.0
|
||||
soundfile==0.14.0
|
||||
sox==1.5.0
|
||||
soxr==1.1.0
|
||||
soynlp==0.0.493
|
||||
starlette==1.6.0
|
||||
sympy==1.14.0
|
||||
threadpoolctl==3.6.0
|
||||
tokenizers==0.22.2
|
||||
tomlkit==0.14.0
|
||||
tqdm==4.70.0
|
||||
transformers==4.57.6
|
||||
typer==0.27.2
|
||||
typing-extensions==4.16.0
|
||||
typing-inspection==0.4.4
|
||||
tzdata==2026.3
|
||||
urllib3==2.7.0
|
||||
uvicorn==0.52.4
|
||||
werkzeug==3.1.8
|
||||
@@ -0,0 +1,12 @@
|
||||
# Separate from the API environment; no vLLM or FlashAttention required.
|
||||
torch==2.9.1
|
||||
torchaudio==2.9.1
|
||||
qwen-asr==0.0.6
|
||||
transformers==4.57.6
|
||||
sentence-transformers==5.2.0
|
||||
modelscope==1.39.1
|
||||
addict==2.4.0
|
||||
simplejson==3.20.2
|
||||
sortedcontainers==2.4.0
|
||||
av==16.1.0
|
||||
psutil==7.2.2
|
||||
Reference in New Issue
Block a user