PMFlow/backend/app/services/llm_service/mixin_task_helpers.py

106 lines
4.2 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

from __future__ import annotations
from .shared import *
class LLMTaskHelpersMixin:
async def generate_task_breakdown_async(self, description: str, **kwargs: Any) -> list[dict]:
if not description.strip():
return []
prompt = (
"请将以下需求拆解为 5-8 条执行任务,返回 JSON 数组,"
"每项格式: {\"task_name\":\"\",\"type\":\"backend|frontend|database|ai|devops\","
"\"estimated_hours\":4,\"priority\":\"high|medium|low\"}。\n"
f"需求:{description[:1200]}"
)
text = await self.generate(prompt, function_name="generate_task_breakdown", **kwargs)
rows = self._parse_json_array(text)
if rows:
return rows
return [
{
"task_name": "需求分析",
"type": "ai",
"priority": "medium",
"estimated_hours": 4,
"description": description[:200],
}
]
async def get_embedding_async(self, text: str, **kwargs: Any) -> list[float]:
db = kwargs.get("db") or kwargs.get("db_session")
tenant_id = int(kwargs.get("tenant_id") or 1)
if db is None:
async with AsyncSessionLocal() as session:
return await self.get_embedding_async(text, db=session, tenant_id=tenant_id)
try:
engine = await self._resolve_active_engine(db, tenant_id=tenant_id)
provider = str(engine.get("provider") or "")
if provider == "openai":
endpoint = f"{str(engine.get('base_url') or '').rstrip('/')}/embeddings"
headers = {
"Authorization": f"Bearer {str(engine.get('api_key') or '').strip()}",
"Content-Type": "application/json",
}
payload = {
"model": str(engine.get("model") or "text-embedding-3-small"),
"input": text,
}
async with httpx.AsyncClient(timeout=self.request_timeout, trust_env=False) as client:
resp = await client.post(endpoint, headers=headers, json=payload)
resp.raise_for_status()
data = resp.json()
return list(data.get("data", [{}])[0].get("embedding") or [])
except Exception:
pass
# DeepSeek/GLM 及失败场景:回退伪向量,保持功能可用。
seed = hashlib.md5(text.encode("utf-8")).digest()
return [((seed[i % 16] / 255.0) * 2 - 1) for i in range(64)]
async def extract_context_insights_async(self, text: str, **kwargs: Any) -> dict:
prompt = (
"请提炼以下技术文档上下文的关键结论,返回 JSON:"
"{\"summary\":\"\",\"keywords\":[\"\",\"\"],\"risks\":[\"\",\"\"]}\n"
f"上下文:{text[:2000]}"
)
raw = await self.generate(prompt, function_name="extract_context_insights", **kwargs)
parsed = self._parse_json_object(raw)
if parsed:
return parsed
return {"summary": text[:120], "keywords": []}
async def find_relevant_context(self, **_: Any) -> list[dict]:
return []
async def evaluate_with_voting_async(self, signal_content: str, impact_score: float, **_: Any) -> dict:
is_strategic = impact_score >= 0.8
return {
"is_strategic": is_strategic,
"confidence": min(1.0, max(0.2, impact_score)),
"decision_proposal": {
"summary": signal_content[:120],
"action": "follow_up" if is_strategic else "observe",
},
"tasks": [],
"_voting": {
"enabled": False,
"models_used": [],
"strategic_votes": 0,
"total_votes": 0,
"unanimous": True,
},
}
async def audit_code_change_async(
self,
task_name: str,
commit_message: str,
code_diff: str,
**_: Any,
) -> dict:
passed = bool(task_name and commit_message and code_diff is not None)
return {
"passed": passed,
"reason": "basic_stub_check_passed" if passed else "invalid_input",
}