Spaces:
Runtime error
Runtime error
| """ | |
| PregoPal - ๆจกๅๅ ่ฝฝๅจ๏ผๅ จๅๅทฅ็ๆฌ๏ผ | |
| ====================================== | |
| ๅฏนๆฅๆฌๅฐ llama-server ๅ จๅๅทฅ APIใ | |
| ๆถๆ๏ผ | |
| core/model_loader.py โHTTPโ api/go_server.py โ llama-server (omni) | |
| โ | |
| ๆฌๅฐๆจ็ + TTS | |
| ็จๆณ๏ผ | |
| from core.model_loader import ModelLoader | |
| loader = ModelLoader() | |
| # ๆๆฌๅฏน่ฏ | |
| resp = loader.chat([{"role": "user", "content": "ไฝ ๅฅฝ"}]) | |
| # ่ฏญ้ณๅฏน่ฏ๏ผๅ จๅๅทฅ๏ผ | |
| result = loader.voice_chat("/path/to/audio.wav") | |
| """ | |
| import os | |
| import io | |
| import json | |
| import base64 | |
| import logging | |
| import numpy as np | |
| import soundfile as sf | |
| import requests as req | |
| from typing import Optional | |
| logger = logging.getLogger(__name__) | |
| # ๅ็ซฏ API ๅฐๅ | |
| LLAMA_SERVER_URL = os.environ.get("LLAMA_SERVER_URL", "http://127.0.0.1:8081") | |
| API_BASE = os.environ.get("MINICPM_API_BASE", LLAMA_SERVER_URL) | |
| class ModelLoader: | |
| """MiniCPM-o 4.5 ๆจกๅๅ ่ฝฝๅจ๏ผๆฏๆๆๆฌ + ๅ จๅๅทฅ่ฏญ้ณ๏ผ""" | |
| def __init__(self, api_base: str = None): | |
| self.api_base = (api_base or API_BASE).rstrip("/") | |
| self._omni_initialized = False | |
| # โโ ๆๆฌๅฏน่ฏ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| def chat(self, messages: list[dict], max_tokens: int = 300, | |
| temperature: float = 0.7, stream: bool = False) -> dict: | |
| """ | |
| ๆๆฌๅฏน่ฏ๏ผ้่ฟ llama-server๏ผ | |
| Args: | |
| messages: [{"role": "system"/"user", "content": "..."}] | |
| max_tokens: ๆๅคง่พๅบ token ๆฐ | |
| temperature: ็ๆๆธฉๅบฆ | |
| stream: ๆฏๅฆๆตๅผ๏ผๆไธๆฏๆ๏ผ | |
| Returns: | |
| dict: {"text": str, ...} | |
| """ | |
| body = { | |
| "messages": messages, | |
| "max_tokens": max_tokens, | |
| "temperature": temperature, | |
| "stream": False, | |
| } | |
| try: | |
| url = f"{self.api_base}/v1/chat/completions" | |
| resp = req.post(url, json=body, timeout=120) | |
| if resp.status_code == 200: | |
| data = resp.json() | |
| return { | |
| "text": data["choices"][0]["message"]["content"], | |
| "success": True, | |
| } | |
| else: | |
| logger.error(f"chat ๅคฑ่ดฅ: {resp.status_code}") | |
| return {"text": "", "success": False, "error": str(resp.status_code)} | |
| except Exception as e: | |
| logger.error(f"chat ๅผๅธธ: {e}") | |
| return {"text": "", "success": False, "error": str(e)} | |
| def ask(self, prompt: str, system_prompt: Optional[str] = None, | |
| max_tokens: int = 300) -> str: | |
| """็ฎๅๆๆฌๅฏน่ฏ""" | |
| messages = [] | |
| if system_prompt: | |
| messages.append({"role": "system", "content": system_prompt}) | |
| messages.append({"role": "user", "content": prompt}) | |
| result = self.chat(messages, max_tokens=max_tokens) | |
| return result.get("text", "") | |
| # โโ ๅ จๅๅทฅ่ฏญ้ณๅฏน่ฏ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| def voice_chat(self, audio_path: str, max_tokens: int = 300) -> dict: | |
| """ | |
| ๅ จๅๅทฅ่ฏญ้ณๅฏน่ฏ | |
| ๆต็จ: WAV้ณ้ข โ llama-server omni prefill โ decode โ TTS้ณ้ข่พๅบ | |
| Args: | |
| audio_path: WAV ๆไปถ่ทฏๅพ๏ผ16kHz ๅๅฃฐ้ float32๏ผ | |
| max_tokens: ๆๅคง่พๅบ token ๆฐ | |
| Returns: | |
| dict: { | |
| "text": str, # AI ๅๅคๆๆฌ | |
| "audio_base64": str, # TTS ้ณ้ข base64 | |
| "success": bool, | |
| "round": int, | |
| } | |
| """ | |
| try: | |
| # 1. ่ฏปๅ้ณ้ข | |
| audio_data, sr = sf.read(audio_path, dtype='float32') | |
| if len(audio_data.shape) > 1: | |
| audio_data = audio_data.mean(axis=1) | |
| if sr != 16000: | |
| try: | |
| import librosa | |
| audio_data = librosa.resample(audio_data, orig_sr=sr, target_sr=16000) | |
| except ImportError: | |
| pass | |
| # 2. ่ฝฌ base64 | |
| buf = io.BytesIO() | |
| sf.write(buf, audio_data, 16000, format='WAV', subtype='PCM_16') | |
| audio_b64 = base64.b64encode(buf.getvalue()).decode('utf-8') | |
| # 3. ่ฐ็จๅ็ซฏ | |
| body = { | |
| "audio_base64": audio_b64, | |
| "sample_rate": 16000, | |
| "max_tokens": max_tokens, | |
| } | |
| url = f"{self.api_base}/v1/omni/voice_chat" | |
| resp = req.post(url, json=body, timeout=180) | |
| if resp.status_code == 200: | |
| data = resp.json() | |
| return { | |
| "success": data.get("success", False), | |
| "text": data.get("text", ""), | |
| "audio_base64": data.get("audio_base64", ""), | |
| "round": data.get("round", 0), | |
| } | |
| else: | |
| return {"success": False, "error": f"HTTP {resp.status_code}"} | |
| except Exception as e: | |
| logger.error(f"voice_chat ๅผๅธธ: {e}") | |
| return {"success": False, "error": str(e)} | |
| # โโ ๅฅๅบทๆฃๆฅ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| def health(self) -> dict: | |
| """ๆฃๆฅๅ็ซฏๆๅก็ถๆ""" | |
| try: | |
| resp = req.get(f"{self.api_base}/health", timeout=5) | |
| if resp.status_code == 200: | |
| return resp.json() | |
| return {"status": "error", "message": f"HTTP {resp.status_code}"} | |
| except Exception as e: | |
| return {"status": "error", "message": str(e)} | |
| def unload(self): | |
| """้ๆพ่ตๆบ""" | |
| self._omni_initialized = False | |