feat: add Nailong Fund advisor capabilities
This commit is contained in:
+46
-4
@@ -12,6 +12,7 @@ from app.core.config import Settings, get_settings
|
||||
from app.core.contracts import AgentRequest, AgentRequestMetadata, AgentResult, RequestContext
|
||||
from app.core.errors import AgentError, RecoverableAgentError, RunLeaseLostError
|
||||
from app.infrastructure.db import SessionFactory
|
||||
from app.infrastructure.neo4j_graph_driver import Neo4jGraphDriver
|
||||
from app.model.audit import InteractionAudit
|
||||
from app.model.conversation import ConversationMessage
|
||||
from app.model.platform import AgentRun, DomainEventOutbox, RequestIdempotency
|
||||
@@ -22,7 +23,14 @@ from app.service.agent.factory import AgentFactory
|
||||
from app.service.agent_persistence_service import AgentPersistenceService
|
||||
from app.service.identity_service import IdentityService
|
||||
from app.service.memory_service import MemoryService
|
||||
from app.service.portfolio_projection_scheduler import PortfolioProjectionScheduler
|
||||
from app.service.relationship_service import RelationshipService
|
||||
from app.worker.outbox_worker import OutboxWorker
|
||||
from app.worker.portfolio_graph_projection_worker import PortfolioGraphProjectionWorker
|
||||
from app.worker.product_governance_monitor_worker import ProductGovernanceMonitorWorker
|
||||
from app.worker.product_history_sync_worker import ProductHistorySyncWorker
|
||||
from app.worker.product_market_quote_sync_worker import ProductMarketQuoteSyncWorker
|
||||
from app.worker.product_metric_refresh_worker import ProductMetricRefreshWorker
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -35,6 +43,14 @@ class WorkerRuntime:
|
||||
self.factory = factory if factory is not None else get_agent_factory()
|
||||
self.settings = settings or get_settings()
|
||||
self.resolve_identity = resolve_identity or IdentityService().resolve
|
||||
self.portfolio_projection_scheduler = PortfolioProjectionScheduler()
|
||||
self.portfolio_projection_worker = PortfolioGraphProjectionWorker(
|
||||
RelationshipService(Neo4jGraphDriver(self.settings))
|
||||
)
|
||||
self.product_history_sync_worker = ProductHistorySyncWorker()
|
||||
self.product_governance_monitor_worker = ProductGovernanceMonitorWorker()
|
||||
self.product_market_quote_sync_worker = ProductMarketQuoteSyncWorker()
|
||||
self.product_metric_refresh_worker = ProductMetricRefreshWorker()
|
||||
|
||||
async def dispatch_one(self, *, run_id: str | None = None) -> bool:
|
||||
# Outbox acknowledges a durable SQL queue entry, not an in-memory task.
|
||||
@@ -44,10 +60,23 @@ class WorkerRuntime:
|
||||
if run is None:
|
||||
raise ValueError("run not found")
|
||||
|
||||
return await OutboxWorker(session, {"agent.run_requested": dispatch}).publish_one(
|
||||
aggregate_id=run_id)
|
||||
async def project_portfolio(payload: dict[str, Any]) -> None:
|
||||
customer_id = payload.get("customer_id")
|
||||
if type(customer_id) is not int or customer_id <= 0:
|
||||
raise ValueError("invalid portfolio projection customer")
|
||||
await self.portfolio_projection_worker.project_customer(customer_id)
|
||||
|
||||
return await OutboxWorker(session, {
|
||||
"agent.run_requested": dispatch,
|
||||
"portfolio.projection_requested": project_portfolio,
|
||||
}).publish_one(aggregate_id=run_id)
|
||||
|
||||
async def run_once(self) -> bool:
|
||||
governance_changes = await self.product_governance_monitor_worker.refresh_once()
|
||||
quotes_refreshed = await self.product_market_quote_sync_worker.refresh_once()
|
||||
history_refreshed = await self.product_history_sync_worker.refresh_once()
|
||||
metrics_refreshed = await self.product_metric_refresh_worker.refresh_once()
|
||||
projection_enqueued = await self.portfolio_projection_scheduler.enqueue_changes()
|
||||
dispatched = await self.dispatch_one()
|
||||
now = datetime.now(UTC).replace(tzinfo=None)
|
||||
async with SessionFactory() as session:
|
||||
@@ -56,8 +85,21 @@ class WorkerRuntime:
|
||||
(AgentRun.locked_until.is_(None) | (AgentRun.locked_until < now)),
|
||||
).order_by(AgentRun.created_at).limit(1))
|
||||
if run_id is None:
|
||||
return dispatched
|
||||
return await self.execute(run_id) or dispatched
|
||||
return any((
|
||||
dispatched,
|
||||
bool(projection_enqueued),
|
||||
bool(metrics_refreshed),
|
||||
bool(history_refreshed),
|
||||
bool(quotes_refreshed),
|
||||
bool(governance_changes),
|
||||
))
|
||||
return (
|
||||
await self.execute(run_id)
|
||||
or dispatched
|
||||
or bool(projection_enqueued)
|
||||
or bool(metrics_refreshed)
|
||||
or bool(history_refreshed)
|
||||
)
|
||||
|
||||
async def execute(self, run_id: str) -> bool:
|
||||
# A new fencing token for each claim also fences restarts of the same process.
|
||||
|
||||
Reference in New Issue
Block a user