SPARKNET / src /agents /scenario4 /award_identification_agent.py
MHamdan's picture
Enhance SPARKNET for TTO automation with new scenarios and security features
76c3b0a
"""
Award Identification Agent for SPARKNET
AI-powered funding opportunity discovery and award nomination assistance.
Part of Scenario 4: Award Identification.
FEATURES:
---------
1. OPPORTUNITY DISCOVERY:
- Scan funding databases and announcements
- Match opportunities to research capabilities
- Track application deadlines
2. NOMINATION ASSISTANCE:
- Prepare award nomination documents
- Review and validate submissions
- Generate supporting materials
3. APPLICATION SUPPORT:
- Document preparation workflows
- Compliance checking
- Reviewer matching
INTEGRATIONS (Planned):
-----------------------
- Horizon Europe CORDIS database
- National funding agency APIs
- ERC portal integration
- Patent databases for innovation evidence
Author: SPARKNET Team
Project: VISTA/Horizon EU
Status: Placeholder - In Development
"""
from typing import Optional, Dict, Any, List
from dataclasses import dataclass, field
from datetime import datetime, date
from enum import Enum
from loguru import logger
class OpportunityType(str, Enum):
"""Type of funding opportunity."""
GRANT = "grant"
AWARD = "award"
FELLOWSHIP = "fellowship"
PRIZE = "prize"
INVESTMENT = "investment"
PARTNERSHIP = "partnership"
class OpportunityStatus(str, Enum):
"""Opportunity tracking status."""
IDENTIFIED = "identified"
EVALUATING = "evaluating"
PREPARING = "preparing"
SUBMITTED = "submitted"
AWARDED = "awarded"
REJECTED = "rejected"
EXPIRED = "expired"
class EligibilityStatus(str, Enum):
"""Eligibility assessment status."""
ELIGIBLE = "eligible"
INELIGIBLE = "ineligible"
PARTIAL = "partial" # Some criteria met
UNKNOWN = "unknown" # Needs review
@dataclass
class FundingOpportunity:
"""
Funding opportunity data model.
Represents a grant, award, or other funding opportunity
identified by the scanning system.
"""
opportunity_id: str
title: str
description: str
opportunity_type: OpportunityType
funder: str
funder_type: str # government, foundation, corporate, EU, etc.
amount_min: Optional[float] = None
amount_max: Optional[float] = None
currency: str = "EUR"
deadline: Optional[date] = None
url: Optional[str] = None
eligibility_criteria: List[str] = field(default_factory=list)
keywords: List[str] = field(default_factory=list)
status: OpportunityStatus = OpportunityStatus.IDENTIFIED
match_score: Optional[float] = None # How well it matches capabilities
notes: Optional[str] = None
metadata: Dict[str, Any] = field(default_factory=dict)
@dataclass
class OpportunityMatch:
"""
Match between opportunity and research/technology.
Represents alignment between a funding opportunity
and institutional capabilities.
"""
match_id: str
opportunity_id: str
technology_id: Optional[str] = None
research_area: Optional[str] = None
match_score: float = 0.0 # 0.0 to 1.0
match_rationale: str = ""
eligibility_status: EligibilityStatus = EligibilityStatus.UNKNOWN
eligibility_notes: List[str] = field(default_factory=list)
recommended_action: str = ""
confidence_score: float = 0.0
@dataclass
class NominationDocument:
"""
Award nomination document.
Contains structured content for award/grant applications.
"""
document_id: str
opportunity_id: str
document_type: str # proposal, nomination_letter, cv, budget, etc.
title: str
content: str
version: str = "1.0"
status: str = "draft" # draft, review, final
created_at: datetime = field(default_factory=datetime.now)
updated_at: datetime = field(default_factory=datetime.now)
created_by: Optional[str] = None
reviewer_comments: List[Dict[str, Any]] = field(default_factory=list)
critic_validation: Optional[Dict[str, Any]] = None
class AwardIdentificationAgent:
"""
Agent for identifying funding opportunities and assisting nominations.
This agent:
- Scans funding databases for opportunities
- Matches opportunities to research capabilities
- Assists with nomination document preparation
- Tracks application deadlines and status
HUMAN-IN-THE-LOOP WORKFLOW:
---------------------------
Award applications require human judgment. This agent implements:
1. AUTOMATED SCANNING:
- Regular scans of funding databases
- Keyword matching and filtering
- Initial eligibility screening
2. AI-ASSISTED MATCHING:
- Score opportunities against capabilities
- Generate match rationale
- Identify gaps and risks
3. HUMAN DECISION POINTS:
- Approval to pursue opportunities
- Review of application documents
- Final submission authorization
4. QUALITY ASSURANCE:
- CriticAgent validation of documents
- Compliance checking
- Reviewer feedback integration
"""
def __init__(
self,
llm_client: Optional[Any] = None,
critic_agent: Optional[Any] = None,
database_url: Optional[str] = None,
):
"""
Initialize Award Identification Agent.
Args:
llm_client: LangChain LLM client for AI analysis
critic_agent: CriticAgent for document validation
database_url: Database connection URL
"""
self.llm_client = llm_client
self.critic_agent = critic_agent
self.database_url = database_url
self.name = "AwardIdentificationAgent"
self.description = "Funding opportunity discovery and nomination assistance"
logger.info(f"Initialized {self.name} (placeholder)")
async def scan_opportunities(
self,
keywords: Optional[List[str]] = None,
opportunity_types: Optional[List[OpportunityType]] = None,
min_amount: Optional[float] = None,
max_deadline_days: Optional[int] = None,
) -> List[FundingOpportunity]:
"""
Scan for funding opportunities matching criteria.
Args:
keywords: Keywords to search for
opportunity_types: Types of opportunities to find
min_amount: Minimum funding amount
max_deadline_days: Maximum days until deadline
Returns:
List of matching opportunities
TODO: Implement actual opportunity scanning
"""
logger.info(f"Scanning for opportunities with keywords: {keywords}")
# Placeholder - would integrate with funding databases
return []
async def match_opportunity(
self,
opportunity_id: str,
technology_ids: Optional[List[str]] = None,
research_areas: Optional[List[str]] = None,
) -> OpportunityMatch:
"""
Evaluate match between opportunity and capabilities.
Args:
opportunity_id: Opportunity to evaluate
technology_ids: Technologies to consider
research_areas: Research areas to consider
Returns:
Match result with score and rationale
TODO: Implement actual matching logic
"""
logger.info(f"Matching opportunity: {opportunity_id}")
# Placeholder response
return OpportunityMatch(
match_id=f"match_{datetime.now().strftime('%Y%m%d_%H%M%S')}",
opportunity_id=opportunity_id,
match_score=0.0,
match_rationale="Matching not yet implemented",
eligibility_status=EligibilityStatus.UNKNOWN,
recommended_action="Review manually",
confidence_score=0.0,
)
async def check_eligibility(
self,
opportunity_id: str,
applicant_profile: Dict[str, Any],
) -> Dict[str, Any]:
"""
Check eligibility for a funding opportunity.
Args:
opportunity_id: Opportunity to check
applicant_profile: Profile of potential applicant
Returns:
Eligibility assessment with details
TODO: Implement actual eligibility checking
"""
logger.info(f"Checking eligibility for opportunity: {opportunity_id}")
# Placeholder response
return {
"opportunity_id": opportunity_id,
"status": EligibilityStatus.UNKNOWN.value,
"criteria_met": [],
"criteria_not_met": [],
"criteria_unknown": [],
"recommendation": "Manual review required",
"confidence": 0.0,
}
async def prepare_nomination(
self,
opportunity_id: str,
document_type: str,
context: Dict[str, Any],
) -> NominationDocument:
"""
Prepare a nomination/application document.
Args:
opportunity_id: Target opportunity
document_type: Type of document to prepare
context: Context information for document generation
Returns:
Generated nomination document
TODO: Implement actual document preparation with LLM
"""
logger.info(f"Preparing {document_type} for opportunity: {opportunity_id}")
# Placeholder response
return NominationDocument(
document_id=f"doc_{datetime.now().strftime('%Y%m%d_%H%M%S')}",
opportunity_id=opportunity_id,
document_type=document_type,
title=f"{document_type.replace('_', ' ').title()} - Draft",
content="[Document content to be generated]",
status="draft",
)
async def validate_document(
self,
document: NominationDocument,
) -> Dict[str, Any]:
"""
Validate nomination document using CriticAgent.
Args:
document: Document to validate
Returns:
Validation result with suggestions
TODO: Implement CriticAgent integration
"""
logger.info(f"Validating document: {document.document_id}")
# Placeholder response
return {
"document_id": document.document_id,
"valid": False,
"overall_score": 0.0,
"dimension_scores": {},
"issues": ["Validation not yet implemented"],
"suggestions": ["Complete document implementation"],
"human_review_required": True,
}
async def get_upcoming_deadlines(
self,
days_ahead: int = 30,
) -> List[FundingOpportunity]:
"""
Get opportunities with upcoming deadlines.
Args:
days_ahead: Number of days to look ahead
Returns:
List of opportunities with deadlines
TODO: Implement actual deadline tracking
"""
logger.info(f"Getting deadlines for next {days_ahead} days")
# Placeholder response
return []
def get_vista_quality_criteria(self) -> Dict[str, Any]:
"""
Get VISTA quality criteria for award identification.
Returns quality thresholds for opportunity matching and
document preparation aligned with VISTA objectives.
"""
return {
"opportunity_relevance": {
"weight": 0.30,
"threshold": 0.75,
"description": "Opportunities must be relevant to research capabilities",
},
"eligibility_accuracy": {
"weight": 0.25,
"threshold": 0.90,
"description": "Eligibility assessments must be accurate",
},
"document_quality": {
"weight": 0.25,
"threshold": 0.85,
"description": "Nomination documents must meet quality standards",
},
"deadline_tracking": {
"weight": 0.20,
"threshold": 0.95,
"description": "Deadlines must be tracked accurately",
},
}