项目文件夹

文件
Gelei Deng 6a767aae02 Model support (#274)
* feat: 🎸 add new models

* minor readme update

* style: format code with Black

This commit fixes the style issues introduced in dafa18b according to the output
from Black.

Details: https://github.com/GreyDGL/PentestGPT/pull/274

---------

Co-authored-by: deepsource-autofix[bot] <62050782+deepsource-autofix[bot]@users.noreply.github.com>
2025-05-01 18:02:42 +08:00

196 行
7.2 KiB
Python

import asyncio
import json
import os
import re
import time
from datetime import UTC, datetime
from typing import Dict
import tiktoken
import yaml
from loguru import logger
from llm_generation.conversation_manager import ConversationManager
from llm_generation.models import get_model
from llm_generation.models.base import BaseModel
class TaskProcessor:
def __init__(
self, prompt_template_config_path: str, model_name: str, streaming_callback=None
):
self.conversation_manager: ConversationManager = ConversationManager()
self.model_name = model_name
# Load the task config -> the whole config file
self.task_config: Dict = self._load_prompt_template_config(
prompt_template_config_path, model_name
)
self._validate_config()
# Load model task config -> the specific model config
self.model_task_config: Dict = self.task_config["model"][model_name]
# Load system prompt
self.system_prompt = self.model_task_config.get("system_prompt", None)
# Load the model
self.model: BaseModel = get_model(model_name)
# Callback function for streaming
self.streaming_callback = streaming_callback
if self.streaming_callback:
self.model.set_streaming_callback(self.streaming_callback)
def init_system_prompt(self, **kwargs):
if self.system_prompt:
self.system_prompt = self.system_prompt.format(**kwargs)
else:
logger.warning("System prompt not found in the model task config")
def _validate_config(self):
if "model" not in self.task_config:
raise ValueError(
f"`model` property not found in the prompt template config"
)
if self.model_name not in self.task_config["model"]:
raise ValueError(
f"Model {self.model_name} not found in the prompt template config"
)
if "rounds" not in self.task_config["model"][self.model_name]:
raise ValueError(
f"`rounds` property not found in the prompt template config"
)
if not isinstance(self.task_config["model"][self.model_name]["rounds"], dict):
raise ValueError(
f"`rounds` property should be a dict in the prompt template config"
)
for round_count in self.task_config["model"][self.model_name]["rounds"]:
if (
"prompt"
not in self.task_config["model"][self.model_name]["rounds"][round_count]
):
raise ValueError(
f"`prompt` property not found in the prompt template config for conversation round {round}"
)
def _load_prompt_template_config(
self, prompt_template_config_path: str, model_name: str
):
base_folder = os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))
template_config_path = os.path.join(base_folder, prompt_template_config_path)
logger.info(f"Loading prompt template config from {template_config_path}")
with open(template_config_path, "r") as f:
prompt_template_config = yaml.safe_load(f)
return prompt_template_config
async def _call_model(self, user_prompt: str, conversation_round: int):
conversation = []
# Check if the system message is needed
if self.system_prompt and len(self.conversation_manager.get_history()) == 0:
system_message = self.system_prompt
self.conversation_manager.add_system_message(system_message)
# Add existing conversation
conversation.extend(self.conversation_manager.get_history())
# Generation parameters
generation_parameters = self.model_task_config["rounds"][
conversation_round
].get("generation_parameters", {})
# Generate response
response = await self.model.generate_response(
user_prompt, conversation, **generation_parameters
)
# Add assistant message to the conversation
self.conversation_manager.add_user_message(user_prompt)
self.conversation_manager.add_assistant_message(response)
return response
def _format_prompt(self, conversation_round: int, **kwargs):
if conversation_round not in self.model_task_config["rounds"]:
raise ValueError(
f"Conversation round {conversation_round} not found in the model task config"
)
if "prompt" not in self.model_task_config["rounds"][conversation_round]:
raise ValueError(
f"`prompt` property not found in the model task config for conversation round {conversation_round}"
)
prompt_template = self.model_task_config["rounds"][conversation_round]["prompt"]
return prompt_template.format(**kwargs)
def _extract_json(self, raw_text):
# Regular expression to match JSON content inside triple backticks
match = re.search(r"```(?:json)?\n?(.*?)\n?```", raw_text, re.DOTALL)
if match:
json_str = match.group(1)
try:
return json.loads(json_str)
except json.JSONDecodeError:
logger.exception(
f"Error: Extracted content is not valid JSON. {json_str}"
)
else:
return json.loads(raw_text)
def _remove_thinking_text(self, raw_text):
# Remove the thinking text from the generated response by deepseek-r1
if "</think>" in raw_text:
return raw_text[raw_text.index("</think>") + len("</think>") :].strip()
return raw_text
async def run(
self,
conversation_round=1,
is_json=False,
should_remove_thinking=False,
**kwargs,
):
user_prompt = self._format_prompt(
conversation_round=conversation_round, **kwargs
)
generation_start_time = time.time()
response = await self._call_model(
user_prompt=user_prompt, conversation_round=conversation_round
)
# Calculate token per second
tokenizer = tiktoken.encoding_for_model("gpt-4o")
response_token_count = len(tokenizer.encode(response))
logger.debug(
f"Response token count {(response_token_count)}, with {(response_token_count)/(time.time()-generation_start_time)} tps"
)
# Extract JSON from the response
if is_json:
return self._extract_json(response)
# Remove the thinking text from the generated response by reasoning models
if should_remove_thinking:
return self._remove_thinking_text(response)
return response
async def main():
def callback_func(content, delta):
print(f"Content: {content}")
print(delta)
task_processor = TaskProcessor(
prompt_template_config_path="prompt_template/public_terminal/reasoning_based_general_response.yml",
model_name="sonar-reasoning-pro",
streaming_callback=callback_func,
)
response = await task_processor.run(
current_date=f'{datetime.now(UTC).strftime("%YYYY-%mm-%dd")}',
user_question="what happened to bybit?",
)
print(response)
if __name__ == "__main__":
asyncio.run(main())