business.py 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105
  1. """业务级准入和停止感知,独立执行与委派共用。"""
  2. import asyncio
  3. from contextlib import contextmanager
  4. from db import business_store
  5. from extensions.registry import load_extensions
  6. from langchain.agents.middleware.types import AgentMiddleware
  7. class BusinessError(ValueError):
  8. pass
  9. class BusinessStopped(BusinessError):
  10. pass
  11. class BusinessBusy(BusinessError):
  12. pass
  13. _counts: dict[str, int] = {}
  14. _active: dict[str, dict[asyncio.Task, int]] = {}
  15. def ensure_enabled(name):
  16. load_extensions().business(name)
  17. state = business_store.state(name)
  18. if not state["local_enabled"]:
  19. raise BusinessStopped(f"业务智能体已停止:{name}")
  20. @contextmanager
  21. def track(name):
  22. task = asyncio.current_task()
  23. group = _active.setdefault(name, {})
  24. group[task] = group.get(task, 0) + 1
  25. try:
  26. yield
  27. finally:
  28. if group.get(task, 0) <= 1:
  29. group.pop(task, None)
  30. else:
  31. group[task] -= 1
  32. class BusinessLease:
  33. def __init__(self, name):
  34. ensure_enabled(name)
  35. limit = load_extensions().business(name).max_concurrency
  36. if _counts.get(name, 0) >= limit:
  37. raise BusinessBusy(f"业务智能体并发已达上限:{name}")
  38. self.name, self.released = name, False
  39. self._tracking = None
  40. _counts[name] = _counts.get(name, 0) + 1
  41. def bind(self):
  42. if self._tracking is None:
  43. self._tracking = track(self.name)
  44. self._tracking.__enter__()
  45. def release(self):
  46. if not self.released:
  47. if self._tracking is not None:
  48. self._tracking.__exit__(None, None, None)
  49. _counts[self.name] = max(0, _counts.get(self.name, 0) - 1)
  50. self.released = True
  51. async def stop(name):
  52. load_extensions().business(name)
  53. await asyncio.to_thread(business_store.set_state, name, local_enabled=False)
  54. current = asyncio.current_task()
  55. targets = [task for task in list(_active.get(name, {})) if task is not current and not task.done()]
  56. for task in targets:
  57. task.cancel()
  58. if targets:
  59. await asyncio.gather(*targets, return_exceptions=True)
  60. async def start(name):
  61. load_extensions().business(name)
  62. await asyncio.to_thread(business_store.set_state, name, local_enabled=True)
  63. class BusinessGateMiddleware(AgentMiddleware):
  64. def __init__(self, agent_name):
  65. self.agent_name = agent_name
  66. @property
  67. def name(self):
  68. return f"BusinessGate_{self.agent_name}"
  69. async def abefore_agent(self, state, runtime):
  70. await asyncio.to_thread(ensure_enabled, self.agent_name)
  71. async def awrap_model_call(self, request, handler):
  72. await asyncio.to_thread(ensure_enabled, self.agent_name)
  73. with track(self.agent_name):
  74. return await handler(request)
  75. async def awrap_tool_call(self, request, handler):
  76. await asyncio.to_thread(ensure_enabled, self.agent_name)
  77. with track(self.agent_name):
  78. return await handler(request)