planner.py 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687
  1. import inspect
  2. import json
  3. import logging
  4. from json import JSONDecodeError
  5. from typing import Dict, List
  6. from openai import AsyncOpenAI, APIResponseValidationError
  7. from .rule_manager import RuleManager
  8. from podslv21_bot.config import Config
  9. class AsyncModerator:
  10. def __init__(self,
  11. config: Config,
  12. rule_manager: RuleManager):
  13. self._logger = logging.getLogger("simpleforward.moderation.executor")
  14. self.config = config
  15. self.rule_manager = rule_manager
  16. self._client = AsyncOpenAI(api_key=self.config.openai.api_key.get_secret_value(),
  17. timeout=self.config.openai.timeout,
  18. max_retries=self.config.openai.max_retries)
  19. self.moderation = self.config.moderation.enabled
  20. self._functions: List[Dict[str]] = []
  21. def add_functions(self, *functions):
  22. for func in functions:
  23. sig = inspect.signature(func)
  24. args = {name: str(param.annotation) if param.annotation != inspect._empty else "str"
  25. for name, param in sig.parameters.items()}
  26. self._functions.append({
  27. "name": func.__name__,
  28. "args": args,
  29. "description": func.__doc__ or ""
  30. })
  31. async def plan(self, message: str):
  32. funcs = self._functions
  33. funcs_prompt = "\n".join(
  34. f"- {func['name']}({', '.join(f'{arg}: {ann}' for arg, ann in (func.get('args') or {}).items())})"
  35. f" - {func.get('description', '')}"
  36. for func in funcs
  37. )
  38. retry = 0
  39. result = None
  40. while retry <= self._client.max_retries:
  41. response = await self._client.responses.create(
  42. model=self.config.moderation.model,
  43. input=[
  44. {
  45. "role": "system",
  46. "content": "Ответь строго JSON-массивом вида:\n"
  47. '`[{"function": ..., "args": [...]}, ...]`\n'
  48. "Выведи только подходящий JSON, выбирай функции в "
  49. "соответствии с запросом пользователя и описанием функции "
  50. "Ты в праве вызывать несколько функций, указывая их по порядку в выводе.\n\n"
  51. "Доступные функции:\n"
  52. f"{funcs_prompt}"
  53. },
  54. *[{"role": "system", "content": rule} for rule in self.rule_manager.get_rules()],
  55. {
  56. "role": "user",
  57. "content": message
  58. }
  59. ]
  60. )
  61. try:
  62. text = response.output_text
  63. start = text.index("[")
  64. end = text.rindex("]") + 1
  65. result = json.loads(text[start:end])
  66. break
  67. except JSONDecodeError:
  68. retry += 1
  69. if result is None:
  70. raise APIResponseValidationError()
  71. return result