"""Microsoft Research — RSS feed (blog posts and publication announcements) → ANNOUNCEMENT events. Items categorised as publications (category "Publication" or a /research/publication/ URL) additionally become `paper` entities (title, authors, date, abstract, URL). Blog posts stay events only; release-like posts are queued for LLM extraction. """ from __future__ import annotations import re from aiatlas.registry import org_ref from aiatlas.sdk.connector import BaseConnector, Parsed, RunContext from aiatlas.sdk.extract.feeds import FeedItem from aiatlas.sdk.facts import EntityRef, Facts, Target from aiatlas.sdk.fetch import FetchResult from ._common import announcement_events FEED = "https://www.microsoft.com/en-us/research/feed/" class MicrosoftResearchConnector(BaseConnector): name = "microsoft_research" label = "Microsoft Research — blog & publications feed" description = "Microsoft Research RSS feed: research blog posts (events) and publications (paper entities)." source_key = "microsoft.com/research" version = "1" parser_version = "1" interval_seconds = 7200 min_interval_seconds = 3600 rate_per_min = 10 tier = 1 priority = 1 expected_min_records = 1 concurrency = 1 async def discover(self, ctx: RunContext) -> list[Target]: return [Target(url=FEED, doc_type="feed", key="feed", min_bytes=1000)] async def extract(self, ctx: RunContext, target: Target, res: FetchResult, parsed: Parsed) -> Facts: facts = Facts() org = org_ref("microsoft") facts.entities.append(org) if parsed.kind != "feed": return facts announcement_events(facts, org, parsed.feed_items, source_name="microsoft.com/research", max_follow=10) for it in parsed.feed_items: if is_publication(it): paper_entity(facts, org, it) facts.document_title, facts.document_entity = "Microsoft Research feed", org return facts def is_publication(it: FeedItem) -> bool: cats = {c.lower() for c in it.categories} return "publication" in cats or "publications" in cats or "/research/publication/" in it.url def paper_entity(facts: Facts, org: EntityRef, it: FeedItem) -> EntityRef: paper = facts.entity("paper", it.title, identifiers={"url": it.url}, organization=org) facts.claim(paper, "published_at", it.published_at.date().isoformat() if it.published_at else None) facts.claim(paper, "abstract", (it.summary or "")[:2000] or None) authors = [a.strip() for chunk in it.authors for a in re.split(r",|\band\b", chunk) if a.strip()] facts.claim(paper, "authors", authors or None) facts.claim(paper, "paper_url", it.url) facts.relate(paper, "published_by", org) return paper CONNECTORS = [MicrosoftResearchConnector]