fix: use Prompture's strip_think_tags and clean_json_text instead of hand-rolled regexes

chat() and chat_json() now delegate think-tag stripping and JSON
cleanup to Prompture's built-in utilities (strip_think_tags,
clean_json_text).  Manual regexes are kept only in the OpenAI
fallback path.  Adds LM Studio integration test script.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Juan Denis 2026-04-04 01:34:38 -04:00
parent 79edc61563
commit 25909ccdca
2 changed files with 79 additions and 10 deletions

View File

@ -16,6 +16,7 @@ from ..config import Config
try:
from prompture.agents import Conversation
from prompture.infra.provider_env import ProviderEnvironment
from prompture.extraction.tools import strip_think_tags, clean_json_text
_HAS_PROMPTURE = True
except ImportError:
_HAS_PROMPTURE = False
@ -121,12 +122,11 @@ class LLMClient:
"""
if _HAS_PROMPTURE:
content = self._chat_prompture(messages, temperature, max_tokens)
return strip_think_tags(content)
else:
content = self._chat_openai(messages, temperature, max_tokens, response_format)
# 部分模型如MiniMax M2.5会在content中包含<think>思考内容,需要移除
content = re.sub(r'<think>[\s\S]*?</think>', '', content).strip()
return content
# Fallback: strip think tags with regex when Prompture is not available
return re.sub(r'<think>[\s\S]*?</think>', '', content).strip()
def chat_json(
self,
@ -147,17 +147,18 @@ class LLMClient:
"""
if _HAS_PROMPTURE:
response = self._chat_prompture(messages, temperature, max_tokens)
# Prompture's clean_json_text strips think tags + markdown fences
cleaned = clean_json_text(response)
else:
response = self._chat_openai(
messages, temperature, max_tokens,
response_format={"type": "json_object"},
)
# 清理markdown代码块标记
cleaned = response.strip()
cleaned = re.sub(r'^```(?:json)?\s*\n?', '', cleaned, flags=re.IGNORECASE)
cleaned = re.sub(r'\n?```\s*$', '', cleaned)
cleaned = cleaned.strip()
# Fallback cleaning when Prompture is not available
cleaned = re.sub(r'<think>[\s\S]*?</think>', '', response).strip()
cleaned = re.sub(r'^```(?:json)?\s*\n?', '', cleaned, flags=re.IGNORECASE)
cleaned = re.sub(r'\n?```\s*$', '', cleaned)
cleaned = cleaned.strip()
try:
return json.loads(cleaned)

View File

@ -0,0 +1,68 @@
"""
Quick test: MiroFish LLMClient LM Studio via Prompture
"""
import sys, os
# Add backend to path so we can import app modules
sys.path.insert(0, os.path.join(os.path.dirname(__file__), ".."))
# Override env vars for LM Studio before Config loads
os.environ["LLM_MODEL_NAME"] = "lmstudio/deepseek/deepseek-r1-0528-qwen3-8b"
os.environ["LLM_BASE_URL"] = "http://localhost:1234/v1"
os.environ["LLM_API_KEY"] = "lm-studio"
# Provide a dummy ZEP key so Config.validate() won't complain
os.environ.setdefault("ZEP_API_KEY", "dummy")
from app.utils.llm_client import LLMClient
def test_basic_chat():
print("=== Test 1: Basic chat ===")
client = LLMClient()
from app.utils.llm_client import _HAS_PROMPTURE
print(f" Backend: Prompture={_HAS_PROMPTURE}")
print(f" Model: {client.model}")
response = client.chat([
{"role": "system", "content": "You are a helpful assistant. Reply in one sentence."},
{"role": "user", "content": "What is social media simulation?"},
], temperature=0.5, max_tokens=256)
print(f" Response: {response[:300]}")
print()
def test_json_chat():
print("=== Test 2: JSON response ===")
client = LLMClient()
result = client.chat_json([
{"role": "system", "content": "You are a JSON-only assistant. Always respond with valid JSON."},
{"role": "user", "content": 'Return a JSON object with keys "platform" and "agents" (an integer). Example: {"platform":"twitter","agents":5}'},
], temperature=0.2, max_tokens=256)
print(f" Parsed JSON: {result}")
print(f" Type: {type(result)}")
print()
def test_multi_turn():
print("=== Test 3: Multi-turn conversation ===")
client = LLMClient()
r1 = client.chat([
{"role": "user", "content": "My name is MiroFish. Remember it."},
], max_tokens=128)
print(f" Turn 1: {r1[:200]}")
r2 = client.chat([
{"role": "user", "content": "My name is MiroFish. Remember it."},
{"role": "assistant", "content": r1},
{"role": "user", "content": "What is my name?"},
], max_tokens=128)
print(f" Turn 2: {r2[:200]}")
print()
if __name__ == "__main__":
print(f"Prompture installed: True")
print(f"LM Studio endpoint: http://localhost:1234/v1\n")
try:
test_basic_chat()
test_json_chat()
test_multi_turn()
print("All tests passed!")
except Exception as e:
print(f"ERROR: {e}")
import traceback; traceback.print_exc()