From 92f28031368d301f29147138c7ee7319d4289b53 Mon Sep 17 00:00:00 2001 From: STiFLeR7 Date: Tue, 3 Feb 2026 13:35:07 +0530 Subject: [PATCH] feat(devpulse_ai): add multi-agent signal intelligence reference implementation --- .../multi_agent_apps/devpulse_ai/README.md | 150 +++++++++++ .../__pycache__/arxiv.cpython-312.pyc | Bin 0 -> 3617 bytes .../__pycache__/github.cpython-312.pyc | Bin 0 -> 2795 bytes .../__pycache__/hackernews.cpython-312.pyc | Bin 0 -> 3016 bytes .../__pycache__/huggingface.cpython-312.pyc | Bin 0 -> 3292 bytes .../__pycache__/medium.cpython-312.pyc | Bin 0 -> 2461 bytes .../devpulse_ai/adapters/arxiv.py | 86 ++++++ .../devpulse_ai/adapters/github.py | 65 +++++ .../devpulse_ai/adapters/hackernews.py | 72 +++++ .../devpulse_ai/adapters/huggingface.py | 80 ++++++ .../devpulse_ai/adapters/medium.py | 70 +++++ .../devpulse_ai/agents/__init__.py | 21 ++ .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 825 bytes .../relevance_agent.cpython-312.pyc | Bin 0 -> 5258 bytes .../__pycache__/risk_agent.cpython-312.pyc | Bin 0 -> 5807 bytes .../signal_collector.cpython-312.pyc | Bin 0 -> 4485 bytes .../synthesis_agent.cpython-312.pyc | Bin 0 -> 7145 bytes .../devpulse_ai/agents/relevance_agent.py | 121 +++++++++ .../devpulse_ai/agents/risk_agent.py | 133 +++++++++ .../devpulse_ai/agents/signal_collector.py | 100 +++++++ .../devpulse_ai/agents/synthesis_agent.py | 154 +++++++++++ .../multi_agent_apps/devpulse_ai/main.py | 143 ++++++++++ .../devpulse_ai/requirements.txt | 4 + .../multi_agent_apps/devpulse_ai/verify.py | 207 ++++++++++++++ .../signal-intelligence-pipeline.json | 254 ++++++++++++++++++ 25 files changed, 1660 insertions(+) create mode 100644 advanced_ai_agents/multi_agent_apps/devpulse_ai/README.md create mode 100644 advanced_ai_agents/multi_agent_apps/devpulse_ai/adapters/__pycache__/arxiv.cpython-312.pyc create mode 100644 advanced_ai_agents/multi_agent_apps/devpulse_ai/adapters/__pycache__/github.cpython-312.pyc create mode 100644 advanced_ai_agents/multi_agent_apps/devpulse_ai/adapters/__pycache__/hackernews.cpython-312.pyc create mode 100644 advanced_ai_agents/multi_agent_apps/devpulse_ai/adapters/__pycache__/huggingface.cpython-312.pyc create mode 100644 advanced_ai_agents/multi_agent_apps/devpulse_ai/adapters/__pycache__/medium.cpython-312.pyc create mode 100644 advanced_ai_agents/multi_agent_apps/devpulse_ai/adapters/arxiv.py create mode 100644 advanced_ai_agents/multi_agent_apps/devpulse_ai/adapters/github.py create mode 100644 advanced_ai_agents/multi_agent_apps/devpulse_ai/adapters/hackernews.py create mode 100644 advanced_ai_agents/multi_agent_apps/devpulse_ai/adapters/huggingface.py create mode 100644 advanced_ai_agents/multi_agent_apps/devpulse_ai/adapters/medium.py create mode 100644 advanced_ai_agents/multi_agent_apps/devpulse_ai/agents/__init__.py create mode 100644 advanced_ai_agents/multi_agent_apps/devpulse_ai/agents/__pycache__/__init__.cpython-312.pyc create mode 100644 advanced_ai_agents/multi_agent_apps/devpulse_ai/agents/__pycache__/relevance_agent.cpython-312.pyc create mode 100644 advanced_ai_agents/multi_agent_apps/devpulse_ai/agents/__pycache__/risk_agent.cpython-312.pyc create mode 100644 advanced_ai_agents/multi_agent_apps/devpulse_ai/agents/__pycache__/signal_collector.cpython-312.pyc create mode 100644 advanced_ai_agents/multi_agent_apps/devpulse_ai/agents/__pycache__/synthesis_agent.cpython-312.pyc create mode 100644 advanced_ai_agents/multi_agent_apps/devpulse_ai/agents/relevance_agent.py create mode 100644 advanced_ai_agents/multi_agent_apps/devpulse_ai/agents/risk_agent.py create mode 100644 advanced_ai_agents/multi_agent_apps/devpulse_ai/agents/signal_collector.py create mode 100644 advanced_ai_agents/multi_agent_apps/devpulse_ai/agents/synthesis_agent.py create mode 100644 advanced_ai_agents/multi_agent_apps/devpulse_ai/main.py create mode 100644 advanced_ai_agents/multi_agent_apps/devpulse_ai/requirements.txt create mode 100644 advanced_ai_agents/multi_agent_apps/devpulse_ai/verify.py create mode 100644 advanced_ai_agents/multi_agent_apps/devpulse_ai/workflows/signal-intelligence-pipeline.json diff --git a/advanced_ai_agents/multi_agent_apps/devpulse_ai/README.md b/advanced_ai_agents/multi_agent_apps/devpulse_ai/README.md new file mode 100644 index 00000000..d66d32b6 --- /dev/null +++ b/advanced_ai_agents/multi_agent_apps/devpulse_ai/README.md @@ -0,0 +1,150 @@ +## 🧠 DevPulseAI - Multi-Agent Signal Intelligence Pipeline + +A reference implementation demonstrating a **multi-agent system** for aggregating, analyzing, and synthesizing technical signals from multiple developer-focused sources. + +### Features + +- **Multi-Source Signal Collection** - Aggregates data from GitHub, ArXiv, HackerNews, Medium, and HuggingFace +- **LLM-Powered Analysis** - Four specialized agents working in concert +- **Structured Intelligence Output** - Prioritized digest with actionable recommendations + +### Architecture + +``` +β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” +β”‚ Signal Intelligence Pipeline β”‚ +β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ +β”‚ β”‚ +β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ +β”‚ β”‚ GitHub β”‚ β”‚ ArXiv β”‚ β”‚ HN β”‚ β”‚ Medium β”‚ β”‚ HF β”‚ ← Data β”‚ +β”‚ β””β”€β”€β”€β”¬β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”¬β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”¬β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”¬β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”¬β”€β”€β”€β”€β”˜ β”‚ +β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ +β”‚ β”‚ β”‚ β”‚ β”‚ +β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ +β”‚ β–Ό β”‚ +β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ +β”‚ β”‚ Signal Collectorβ”‚ ← Agent 1: Ingestion β”‚ +β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ +β”‚ β–Ό β”‚ +β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ +β”‚ β”‚ Relevance Agent β”‚ ← Agent 2: Scoring (0-100) β”‚ +β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ +β”‚ β–Ό β”‚ +β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ +β”‚ β”‚ Risk Agent β”‚ ← Agent 3: Security Assessment β”‚ +β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ +β”‚ β–Ό β”‚ +β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ +β”‚ β”‚ Synthesis Agent β”‚ ← Agent 4: Final Digest β”‚ +β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ +β”‚ β–Ό β”‚ +β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ +β”‚ β”‚ Intelligence β”‚ ← Prioritized Output β”‚ +β”‚ β”‚ Digest β”‚ β”‚ +β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ +β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ +``` + +### Agent Responsibilities + +| Agent | Role | Output | +|-------|------|--------| +| **SignalCollectorAgent** | Aggregates & normalizes signals | Unified signal list | +| **RelevanceAgent** | Scores developer relevance (0-100) | Score + reasoning | +| **RiskAgent** | Identifies security/breaking changes | Risk level + concerns | +| **SynthesisAgent** | Produces final intelligence digest | Prioritized recommendations | + +### How to Get Started + +1. Clone the repository + +```bash +git clone https://github.com/Shubhamsaboo/awesome-llm-apps.git +cd advanced_ai_agents/multi_agent_apps/devpulse_ai +``` + +1. Install dependencies + +```bash +pip install -r requirements.txt +``` + +1. Set your OpenAI API key (optional for live mode) + +```bash +export OPENAI_API_KEY=your_api_key +``` + +1. Run the verification script (no API key needed) + +```bash +python verify.py +``` + +1. Run the full pipeline (requires API key for LLM agents) + +```bash +python main.py +``` + +### Verification Script + +The `verify.py` script tests the entire pipeline using **mock data only** - no network calls or API keys required: + +```bash +python verify.py +``` + +Expected output: + +``` +[OK] DevPulseAI reference pipeline executed successfully +``` + +### Optional: n8n Automation + +An n8n workflow is included for those who want to automate the pipeline: + +- **Location**: `workflows/signal-intelligence-pipeline.json` +- **Import**: n8n β†’ Settings β†’ Import from File +- **Requires**: n8n instance + configured credentials + +This is entirely optional - the Python implementation works standalone. + +### Directory Structure + +``` +devpulse_ai/ +β”œβ”€β”€ adapters/ +β”‚ β”œβ”€β”€ github.py # GitHub trending repos +β”‚ β”œβ”€β”€ arxiv.py # AI/ML research papers +β”‚ β”œβ”€β”€ hackernews.py # Tech news stories +β”‚ β”œβ”€β”€ medium.py # Tech blog RSS feeds +β”‚ └── huggingface.py # HuggingFace models +β”œβ”€β”€ agents/ +β”‚ β”œβ”€β”€ __init__.py +β”‚ β”œβ”€β”€ signal_collector.py +β”‚ β”œβ”€β”€ relevance_agent.py +β”‚ β”œβ”€β”€ risk_agent.py +β”‚ └── synthesis_agent.py +β”œβ”€β”€ workflows/ +β”‚ └── signal-intelligence-pipeline.json +β”œβ”€β”€ main.py # Full pipeline demo +β”œβ”€β”€ verify.py # Mock data verification +β”œβ”€β”€ requirements.txt +└── README.md +``` + +### How It Works + +1. **Signal Collection**: Adapters fetch data from GitHub, ArXiv, HackerNews, Medium, and HuggingFace +2. **Normalization**: SignalCollectorAgent unifies signals to a common schema +3. **Relevance Scoring**: RelevanceAgent rates each signal 0-100 for developer relevance +4. **Risk Assessment**: RiskAgent flags security issues and breaking changes +5. **Synthesis**: SynthesisAgent produces a prioritized intelligence digest + +### Built With + +- [Agno](https://github.com/agno-agi/agno) - Multi-agent framework +- [OpenAI GPT-4o-mini](https://openai.com/) - LLM backbone +- [httpx](https://www.python-httpx.org/) - Async HTTP client diff --git a/advanced_ai_agents/multi_agent_apps/devpulse_ai/adapters/__pycache__/arxiv.cpython-312.pyc b/advanced_ai_agents/multi_agent_apps/devpulse_ai/adapters/__pycache__/arxiv.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..47312c5edbcca11a5b85150659d274e1f16f9f4a GIT binary patch literal 3617 zcmbVOO>7iL7Ow7|pPs+=&zSKa4s9C=CdM-m6E-+;h{49d5<`F}!~u=kQ)Rn{>6uA& z54J}=kaU@YA3^)i$~FC50HJ^LLA}Xa!s8VXZXt{&5EihQ7A6GeDH>8z z6p-PXa0`4!N_!0Ngx8QKWWzV%1D%3h#E;#?OJoutzA3RCJn{dO3wwU=o(Pf<_FmbG z5bYHa31ayh-(8{h)(M3uBj|DqLK6)z*NFY30SCA(7IClxeSvW3E2)ka(B}~sniuEA zXhY%8{$YAizp4&nEoTv`_N&K9p3SFCGJF!8O%X~mNuqK~V!&%O zOV4Iv^;piH+ z4HK~CYaO7>&Wc$I^|Ep{jJ8&&%2Y${L;%Y87 z*h;bS72h7-_U%n})3u#;Qwlcfi!HTPLEH7X#a8P11pf-&VF#_&I>HXs>jFovi~udj zb_h4(NC(2L1ps9PJ7g<%quqepz7%o$m!4v?Eu}asywL3Cx+eju=JPHW@*!I~k9v{y z44Hyjda>igQ9ZsZL$lQ$-TS+i4HHb z&g#|-r`rtHDAo%Et0vI4q1AjAdS)yuXAZ{WWG8P6pYo|fN@x$B@2DMng z(B=|A()qMyGLH#wJvz^P{CS3ALZ@pfioF*VnJ1@F%`lnQ(hZW$18Zwm)+luHpdsej z+1c3afD`Q-`}e;YALf&O4jBaIhsbNPrCVuI2s(N*Zx|ZPc%6}s3yLE+XNWmN@Hmq{ zghK-iFhhk_XELp4u6**Iqa50k!&7@MGuI3pPR#ABmi`2||EUy<#UNq{=9%xAyQqhW zI_6)Qr%A%xjuRH(i$wJt&k~dJG|gm#Sa1?rT4CoW&mFXv)e{#koK*>>Svsf|x?h~V z2!}2#l1ZLHT3>{W)@-9q%+1$rj!DzRVvSVOO_G3PH^C3%yvfAT3(U{U*0d<3k9m{X zj0Nk+yi+<QkrC7kjp{tnUBZpyq39h;LBqEvpmL{ z$~QWfB+j=-TPWY;OrF#r6j%q!*`9f8b!;*jsw9`qn1s1#Hfu34V=}jsS>|_EG68o8 z1FEc+)Y{0as6%YrkeJ8QmcSSCBUOX!$#reiYXEx{Abe7vuD@YDif0x>xi^9}f7 zd-~zX;3aLAm|27Lr_)A1EP;7R!&f!9x8Q`PC$wpZ*t}#wxo-^;4hEB}IeyUr4B(wx z6&O0(FE`Jcr}*_MQ46w0DfB4Bz>jk?3OjEBRa;Qw!G#kK+xAzQnk%h4E6v@N9o?G& zkK$Q4UTs7TO$%dHSD(B8hj81i!#58vU0j*EhaZHGmE?-txFL71$=wg+-X|W^*nFdS zy|{F7qpN?dtAD*AR&rI8mU3jE96DGMAMR+sb?N4%<$bH-`i_CeXs55YG+JqY?M}z- zj^)#13s$t=zD?(z$!1vu~}lZ=*B5))`;*+>_TkhZkkAYTZ@o z=!Vbx;jOptjr}qBC-eSWrtOlpN0ruH4?BA+-LG%DrLN}9kUyf7 zj#ncn)N*6|`uNhJzXy9Z55qcDPYEa(UKs!1lLi!Qx^d+Ck^dsW{l*V+sNA@3<%5-D zWo7?{e4s2Js0JP2k52+9sN5L3KE#2)aexO_yH~?y<;@NGP+2}ymA8Oi6x_i<{SIi) zim+^!mA(ymUs>MwFwnd({$te(vro{p#_^ynQ>QhvpV3Xk|b7Rbe$ zNZ@QTk)SG!*^W6Q9U~ScBsN;Rx?cbpb}v`4z&0K2<#>-Zp99jN{B0EO+K6(uOycAr z0o6Fg&!fs{21z8Cw|;;Kl*R!0HF^v^qQlTVmESS4IG-l(((^FnH*@oM&{ZWt5H>}( z;CieeA@onwQ${_|uc4kz&rv}LFD0vpKUa3ISgXhGg;!5}{n^^=^v)l(vJ1xtnd zQv1IYb@|f=N_1hY5^CW&h?azZ$e~4X@xbCoHwTxJOLXx_xw&_Fpseiqo4j{Z0!Y=1 KTpjcr#PNUm%fd7O literal 0 HcmV?d00001 diff --git a/advanced_ai_agents/multi_agent_apps/devpulse_ai/adapters/__pycache__/github.cpython-312.pyc b/advanced_ai_agents/multi_agent_apps/devpulse_ai/adapters/__pycache__/github.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..624048ed94e1397a5cd9c07dc8c3f48e84d182d2 GIT binary patch literal 2795 zcma)8OKcm*8J>M|Ns-jk)+3R~iUu zp(PPys{%d9Z2{Zp!7^ea3Q!;n?1S5rZ!J*Ya*-kjRu=~1K2X3w4^*5(;avL9l8l|W zEjl1){{NrLMNX)hHjfdWoh8iVloYU2$eWZDedchVIv~wa-!mIFW3kxj zS%;bMvrXoBMc2tW1n)H&w>fbM19{VH&Zj1yCFUeKf2!m%GVvOC%@K%@C8n?>9w~s8 z;}^zaQ@&}JK)b-5Y$yWVb0vooYzjX!HgmFOu{7htFmdWN)6Qlg)>!PJIGL%G92gkl zbH^h=RP>m*+)gQh`hAD-K%I25JWwVIi-G1kp2Gv35?-Q(Z1ZG=7ZiuT0{=(9l*5~q zsuKTlehy?`Z#S9(uC->cd{b$ic^PtO4zlO#4XZ7Us*JUY40oqrS1JfcuE|v;kE&`E z@o39xF3iLh*BC2OMZw0+=nb)2XjQGE%_SQ2(|v1w5`y{mr)L^;Yrd*i^trZY=W#5L zZZ!7|d6$Ps5#HI-t3+^{$h6jOa2zBFA*n?Eqqk&Bb3tMXoc-u9-YE;y@?$%15j1{Bh3o61cXb$h|_N}gFLc{2bC ze(tLvzFbA~@F{nq;)FGG@&(&9F_a#ZxE&(Nyu)V&R1~mHu~UYXGQb_r zZul(kndQ95GUMZR(HYBwZfRyL>wDvj*fcvk{`6KKipoq$fr{-#RyKB>N$>K~yKp($ zJc7)ja+}F~$} zh#;CPxvo{P0bbfLVOi=F#iq(hz^$kFz?kKpYn7-QbcnEdyG$svvVN(+1H-io`4R*V zbnX|OECYDb!o^XMDO6NIN~c{g1UiEo%aTCj4tELNCS;0mYj}hMz;e4h_~T{*G*SF* z^Tg@Xr%XbrPc!CJx$l|zqm8Llf(qOS^b*e&{CTPh#$cT0Lip6g9V`S&p75Z9+72TY zAg~1s{50KiFjE-``A z*0~a)i&Q{T5NYHI3k*tF(JwF}?v&esDy{)eB21$|B1|BmNV`UR{G-XtS$m!^-y><) z_0q5$cGkw{ZFpa>Wjhueb%C?9UdiQ6#tL;XIbReH66k<__yn@E&1@?!2HSGtF|-;2 ztMww%7a_X9SoRiV-V&uB}Ad_T=)hdLK&kT%5Wv zwQ}H-`0&HQgmxkp&{QfC9@3 zB9OC?UM!F(g-jTPmEW$6ro&? LAa#Hqg2ntB^3mxG literal 0 HcmV?d00001 diff --git a/advanced_ai_agents/multi_agent_apps/devpulse_ai/adapters/__pycache__/hackernews.cpython-312.pyc b/advanced_ai_agents/multi_agent_apps/devpulse_ai/adapters/__pycache__/hackernews.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6b77c1bdfc9030f84fb99b725a7b3f3a7b8c62b2 GIT binary patch literal 3016 zcma)8U2Ggj9iP43z1#b=9oxCs8b?^bzN|1Vz+24v zOmSQ^rE$rW$7KQGMl2COmWfCt5+L%VuMPP4e^SE!AIswk3F5%@7Z9QeA0i4?-VZ($ zYC6V4Bs7GswIehhhP?<5kuVN(-nS8s03GEtj%}_qyd&2+1@zWl3C;Lse2J#wpFSl;KL~e}bvkn=&s8TXXD6vvR<($MM7H}E1)falz z;o1Q`m`&SRLsu`1s)J(}RW4+LxZFyI=rom4Q+iIHFtUaV*7b#+@Y@+ph)fzW9G6K$ zM#^R4pf$tftYI21^HbvHsg>HiMh-_2_>1s=x(lA*HlYko!(s_x3Cs7TvM-IwVi37o zJX1c-_Mf99lziC#e&8XO1fwjKq$#;ZKmUBVIS~--2cF-l(e?eZT#~0YWj&9=N*X=f z)DHphSJ31y`M(gk6)euK9T1^ z@3}AGw)_5aup~}}>-V|+E^S!`Y50kRf~X{Z7j+?b3o^x{@xMklqHX1n+fZM|?WGXE z^ST!V^g6;#<Ti0&1ug5hR(C9<$iu~Fo9LRmBQn#^Kv#&vT}vcErL_35=b)0eW% zem!UO-#F4=D=^x`jE3Qi#cn8A-?^-gj9lWcOL{6}SVSFxDr6Z}ny;N7yTW8%C})aF z&NeL9IW>@wnEZo0p)*W!^|Zsn8N+qP2p!YYgvH?8gc_+~BMTMHN!R?<{c^&`{5hTK zro#fRVG=tJrHnH&F;BBB#k#jn=SV?j0Vn}MTD7?uw9c&9R4 zVxBheH(4m-m3$6jvTL|m!jxAnkHDiwFH||u?wL<{onaBMQUhVUJUh&<=iQ7=Ss-s+ zx9ozIkmw$cQtAi_JqaXaEDdZ;_?geZ%mq^ObOexgktuSr~J6RuwD zcw@V_)cesj^}>}aV=AH4rb)H9_X{ECYP*Rblld)7GciqE)<|{3AsTcIjW?vc!=x$4 zwwUO+6i$-M5eu^*7w_>iIY+^5EO>4@MLhcwVe0oRoX|lI9EisFG~jsA<}ig4Cuds@ zVG_5WiQpv`2FDNz(Wn`i?&ElW4bKGP@S?yoU~@bhA4*=;3&gQaayXkc4}($8RUO~Z zp)F!fH#7(oxc94OKI?8yG!KKxjU4ZzfC0SMxt*)quIwFNbvn!WJ2>3F3vUWxCmw0Ca=<(PkVxEezZO|$2#(gFGC$IW~0p1gB% z{(Fm)%Xq!{?3_|j8XqYgt4hbZ(p71Q-zwiM|MZ7*(&Og%-4l0C%nvV|S!?e36txDs z=FUED-hKD@o#XQZzc{tn_3NJZdX{?E51p$m2Ht9}&kth67jwC#NmzZYMKFCJQJ8>pN)_lKU}_WZGT{mg6g&V$0e z!a`~3^4ftD%Pq@Ys|QZ4T+=G?_Pd2Uh57u?-~2G%zqtGH{ti!1W=UAvf3%`@{VMQo zU@^8Vtf|S$)?RRJKg{?A?Ax^wjqHwr1??!>a%=SF==`ydBApvwg9BAhph)xV=)XP- zqe#=O(>G6liUj%i$4Ydi@yJrgQu9h|;E{4{MLAYgJmBY_1yH1wgN}Nj-o;lI&#uH? ze54#%QI0$g@0uO`yc&SrXHGXb=4V5PhLY%a$>w3d_(3#wu0#BwBf{y!v0*{{y&wb4 z6iqXA!_u@T{6irjc(Gda9ag+pD{yky!q zpCza0%P{31PR>u@Q56M2*zn1M^fZQq=qIRi6?Ja-2ZZK@vsDDIC28r(a@Vr6{K9X( zzxv|PQ@&Qs2}p{}6+aZ){uVp9_{w^$fA)MO+QRShtS~41MTy?_-9B>rwL8h%z4H?b lO$*6)_bpDZcAQvIPcFw-VuK$kXEsC*t_G0QPEUg+{|0r-9`FDF literal 0 HcmV?d00001 diff --git a/advanced_ai_agents/multi_agent_apps/devpulse_ai/adapters/__pycache__/huggingface.cpython-312.pyc b/advanced_ai_agents/multi_agent_apps/devpulse_ai/adapters/__pycache__/huggingface.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..99fd54e1e16213ba4659c40329696339c9127e2b GIT binary patch literal 3292 zcma)8UvLx08Q;^LPABOk%YVRsz;HH(SPj_NX26&Pa1pj?5|=jalsHiJ(5~e(I!WxE zuu-MfooQ#xlo{X&7|5XXrKV&W@f1JuL^IPDxpHgeCNm||C;BAUdFYUretWWvXmF-C z(%!ebzwg^`zy0?2-Cuk@4}$0MAAha=9Hf7;PW6#Cg!SKpa2sJXi7aEBHy?B8 z{3NdnlY;J?baDs>us}rYBs>v_i#Vqot)R#LQvw!$4ly6oExxb*4#l5(h0U5%2N* z#9$5pI+?l{(;}}dJAC$x(j8AIY6<{xQ;XO-VC-^AqXa8#p^K^kT52Q$9t3+l&spHc zgS;iYs~M&xgtUlh@x$?}mNTa5nrVrYm?;{MY($vBkstm%{MY{kEu1Kqh4aLG24Ueo zpLIl0mUkm_Q)9-#lwN%@T*iT&pNNl`9eA=rMwoF{&JC7-f!E;NN{KB+fdcU|JZx|x(!?S-iVv3zT<88gjYO! zy`}A2SaN)aB^PeSEg2W{6t}*1PoC;-a9ahp#VR%aZh@%rBjdJXwWAGff!+D?rO7?t$N5o-d#T~AUDeJ@1s4)+&rQgc~jgJx)5#6%4S`40PoJoub$Wk zz=2u$mol?fk!CqFP{n^sU-^IZXVD!00h;B49qFP+fyd^uO(yG2CX%U`O23=Yn4YaX zOryq-EwLe+D29~x)HzL0=}J7MUj({LOjV7U37{ZGB0ZZbw(w(nu*MLhAf{(%(YP8@ zFmMrYo=P>KyjjhhX51OaD#cnF*vtSf*42v5bjP%5CXJ!q-suW+PXTlHL=wGfQtPd- zG$XW2#7H|1^f9_K64bQZ7=ZxXf-VkWU5-Nsa zaG~0vjDlWp`_ry>nWHf0t=UZ4Gd7~U$LbB(;+2e&Zr)gwl5%R;wv;LFCA z+edF5y>o7HY6-72o?4JfQeai;Se80gq&=m&<{Q~-+28$YL3q~GvW|AS_bh};%{y+- z-I~j}7Y}_p_;4^ES_&553>BMCFN~E%)YNi&@Ydj+k=*x-jc>dJ`}2-s^T5JbN$D)e z?O)Y3mjWHDf$rr%_u^1~IzL$mo?HnGe^J-?a{O?<>$BkF;8N&G!;_Asv10$}Lf=Rs zIJy!TdnUK9%3aHHSMC@2L!S*k9$b3o^ZMezJD(31`o{})88;mXBK&-^^bwb{s9WcRVuitP<+KHql#!bo{1@-^KUzczm7@ZaUmwYMRVZ}Ei`4G_)q*zI=RL(aM^`~HhLU9`7e<)SXKZ4 literal 0 HcmV?d00001 diff --git a/advanced_ai_agents/multi_agent_apps/devpulse_ai/adapters/__pycache__/medium.cpython-312.pyc b/advanced_ai_agents/multi_agent_apps/devpulse_ai/adapters/__pycache__/medium.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8947ec16e1763efa2a9843325094542690c08210 GIT binary patch literal 2461 zcmaJ?OKclO7@mFk72D0jEsfJu+l0_MG>K3tk5CFBPRp}Glmc~9tc_=EZ@RlSv*RYQ z926uDJrpTDwGo9zoKlLw#~e6Rs)P_+QUYoiDgmOGa&npqp>pA$b*xZcBYS4&f6qVv z_s`dnNC?6B^y7ET&jSekA~uZydvC0N2+KKyQ3_!R%a)XqU{x&HR#J+src_%?X%fmI ztbQb=e0U4ih#&iiLR1nUT2^iceeut#*#EAU3X%{GoZN#D?UoS<;@~Ht%aZ2_rJ6`n z5}k-4lnMhcf}2PfhlMVdaU_o3M>zU{(vX(WnQbbXmZ#-JbNQ>#O9Y!GTOYzkkrAr* z>4yl*%Rf@9F)(D#5{(W zpa{M|XhseqCS<%Zp*FrY*NWUIh8d-zO!ziAjV~-!P+Zu%0QOWCY|twzl$3IL!}^ru zMkI6(SKZ{!Xcdjdv4$l}oAFhA6C(D%4eY~8MQhjtfcWDG^wfzhe-nf)4gkIRpY+Re zorZ(JZ~Z5~BJBN(-aT=LHsijB-h{)A=qdoF3kr^2@Xh!u(!|z(@#Jsh6+rf(iq3qg zqKb4BbtAT|A$>oh9WwzY(CH1lr6L1Bn=66$(Ff|L{&8pf=FHr73=A3j2lTJdRQBbC$sJJTPPxxq9YkW%*-pc(O+2j5k7-fudnG&wJ zrE#tRg{zk7oTP1DJVvod9L$3wQyJnN8;n4MV2L0VFoooJ8FhI;_(?;kv_;T0;bWQ# zpwbS{QeBWs#1%wwRo=M!T5@pAm?Un&hVEtAec;g@Gw>+`dLB+2X4-)E$J{ZyWU-A+ z+LOWLR8jP7kO6t`xd5qpLoH78RxcsxIw9>n92A}_Byi%k!)?u?S`2MznLT_n)_xo9 z&<;tb12wrLcu2a@)_J|H`&wJ~!m}%FeW#N@wRc|JF~4IWyHHwg@4Jn*Mt09>H{-h& zdKTNO&#c5B{U$#4`J-3%f7!Que01fBu{r;m55+ouh;^>+)Gub|vkR$eYxVV&olh>| zC3kt}@Z8~=4|V7l6Z46M*3KCs%^b#ZilbhW#871)FGgExEjE*`7C zaHVynCwZeQvDj1HzV!G?*T|~AXU(tf+O`&Hinq@BYTHrE_H)s*(etOiYkpwuL5Q#x z6i2EZK;gF8BfsDAqh>gzBu*D@S<ieWUvP()g8@E6Q?f@_KmWT6pA-nj-jj-R+Q~ z_lA|>n0z%B9DYo@+SxL^PrJHLgKr*4r)|S@(&<|Q013&fgR3y*0J~my1GNqztQ}Gy3Ax1?*;tEt26e6SEO$k?yVUu*4`zdkZ z{#`Cnx(g&Pp>>!M-3`;-zyZ5}OBOjuAAv30EOOt02~HzPYqBP(>ro`N{)oD+p|0Bj zEhx`wYt6k<+xbil!LoS&BC8HBwN; List[Dict[str, Any]]: + """ + Fetch recent AI/ML papers from ArXiv. + + Args: + limit: Maximum number of papers to return. + + Returns: + List of signal dictionaries with standardized schema. + """ + base_url = "https://export.arxiv.org/api/query" + params = { + "search_query": "cat:cs.AI OR cat:cs.LG", + "start": 0, + "max_results": limit, + "sortBy": "submittedDate", + "sortOrder": "descending" + } + + signals = [] + + try: + response = httpx.get(base_url, params=params, timeout=15.0) + response.raise_for_status() + + # Parse Atom XML response + root = ET.fromstring(response.content) + ns = {"atom": "http://www.w3.org/2005/Atom"} + + for entry in root.findall("atom:entry", ns): + title_elem = entry.find("atom:title", ns) + summary_elem = entry.find("atom:summary", ns) + id_elem = entry.find("atom:id", ns) + published_elem = entry.find("atom:published", ns) + + title = title_elem.text.strip() if title_elem is not None else "Untitled" + summary = summary_elem.text.strip() if summary_elem is not None else "" + arxiv_id = id_elem.text.strip() if id_elem is not None else "" + published = published_elem.text if published_elem is not None else "" + + # Get PDF link + pdf_link = arxiv_id + link_elem = entry.find("atom:link[@title='pdf']", ns) + if link_elem is not None: + pdf_link = link_elem.attrib.get("href", arxiv_id) + + signal = { + "id": arxiv_id, + "source": "arxiv", + "title": title, + "description": summary[:500] + "..." if len(summary) > 500 else summary, + "url": arxiv_id, + "metadata": { + "pdf": pdf_link, + "published": published + } + } + signals.append(signal) + + except httpx.HTTPError as e: + print(f"[ArXiv Adapter] HTTP error: {e}") + except ET.ParseError as e: + print(f"[ArXiv Adapter] XML parse error: {e}") + except Exception as e: + print(f"[ArXiv Adapter] Error: {e}") + + return signals + + +if __name__ == "__main__": + # Quick test + results = fetch_arxiv_papers(limit=3) + for r in results: + print(f"- {r['title'][:60]}...") diff --git a/advanced_ai_agents/multi_agent_apps/devpulse_ai/adapters/github.py b/advanced_ai_agents/multi_agent_apps/devpulse_ai/adapters/github.py new file mode 100644 index 00000000..682548cc --- /dev/null +++ b/advanced_ai_agents/multi_agent_apps/devpulse_ai/adapters/github.py @@ -0,0 +1,65 @@ +""" +GitHub Adapter - Fetches trending repositories from GitHub. + +This is a simplified, stateless adapter for the DevPulseAI reference implementation. +No authentication required for basic public API access. +""" + +import httpx +from datetime import datetime, timedelta +from typing import List, Dict, Any + + +def fetch_github_trending(limit: int = 5) -> List[Dict[str, Any]]: + """ + Fetch trending GitHub repositories created in the last 24 hours. + + Args: + limit: Maximum number of repositories to return. + + Returns: + List of signal dictionaries with standardized schema. + """ + base_url = "https://api.github.com/search/repositories" + date_query = (datetime.utcnow() - timedelta(days=1)).strftime("%Y-%m-%d") + + params = { + "q": f"created:>{date_query} sort:stars", + "per_page": limit + } + + signals = [] + + try: + response = httpx.get(base_url, params=params, timeout=10.0) + response.raise_for_status() + data = response.json() + + for item in data.get("items", []): + signal = { + "id": str(item["id"]), + "source": "github", + "title": item["full_name"], + "description": item.get("description") or "No description", + "url": item["html_url"], + "metadata": { + "stars": item["stargazers_count"], + "language": item.get("language"), + "topics": item.get("topics", []) + } + } + signals.append(signal) + + except httpx.HTTPError as e: + print(f"[GitHub Adapter] HTTP error: {e}") + except Exception as e: + print(f"[GitHub Adapter] Error: {e}") + + return signals + + +if __name__ == "__main__": + # Quick test + results = fetch_github_trending(limit=3) + for r in results: + print(f"- {r['title']}: {r['metadata']['stars']} stars") diff --git a/advanced_ai_agents/multi_agent_apps/devpulse_ai/adapters/hackernews.py b/advanced_ai_agents/multi_agent_apps/devpulse_ai/adapters/hackernews.py new file mode 100644 index 00000000..aad4b499 --- /dev/null +++ b/advanced_ai_agents/multi_agent_apps/devpulse_ai/adapters/hackernews.py @@ -0,0 +1,72 @@ +""" +HackerNews Adapter - Fetches top AI/ML stories from HackerNews. + +This is a simplified, stateless adapter for the DevPulseAI reference implementation. +Uses the Algolia HN API for better search capabilities. +""" + +import httpx +from typing import List, Dict, Any + + +def fetch_hackernews_stories(limit: int = 5) -> List[Dict[str, Any]]: + """ + Fetch recent AI/ML related stories from HackerNews. + + Args: + limit: Maximum number of stories to return. + + Returns: + List of signal dictionaries with standardized schema. + """ + base_url = "https://hn.algolia.com/api/v1/search_by_date" + params = { + "query": "AI OR LLM OR Machine Learning OR GPT", + "tags": "story", + "hitsPerPage": limit, + "numericFilters": "points>5" + } + + signals = [] + + try: + response = httpx.get(base_url, params=params, timeout=10.0) + response.raise_for_status() + data = response.json() + + for hit in data.get("hits", []): + # Skip stories without URLs (Ask HN, etc.) + if not hit.get("url") and not hit.get("story_text"): + continue + + external_id = str(hit.get("objectID", "")) + hn_url = f"https://news.ycombinator.com/item?id={external_id}" + + signal = { + "id": external_id, + "source": "hackernews", + "title": hit.get("title", "Untitled"), + "description": hit.get("story_text", "")[:300] if hit.get("story_text") else "", + "url": hit.get("url") or hn_url, + "metadata": { + "points": hit.get("points", 0), + "comments": hit.get("num_comments", 0), + "author": hit.get("author", "unknown"), + "hn_url": hn_url + } + } + signals.append(signal) + + except httpx.HTTPError as e: + print(f"[HackerNews Adapter] HTTP error: {e}") + except Exception as e: + print(f"[HackerNews Adapter] Error: {e}") + + return signals + + +if __name__ == "__main__": + # Quick test + results = fetch_hackernews_stories(limit=3) + for r in results: + print(f"- {r['title']}: {r['metadata']['points']} points") diff --git a/advanced_ai_agents/multi_agent_apps/devpulse_ai/adapters/huggingface.py b/advanced_ai_agents/multi_agent_apps/devpulse_ai/adapters/huggingface.py new file mode 100644 index 00000000..fb7ab8ad --- /dev/null +++ b/advanced_ai_agents/multi_agent_apps/devpulse_ai/adapters/huggingface.py @@ -0,0 +1,80 @@ +""" +HuggingFace Adapter - Fetches trending models from HuggingFace Hub. + +This is a simplified, stateless adapter for the DevPulseAI reference implementation. +Uses the public HuggingFace API (no authentication required for basic access). +""" + +import httpx +from typing import List, Dict, Any + + +def fetch_huggingface_models(limit: int = 5) -> List[Dict[str, Any]]: + """ + Fetch trending/popular models from HuggingFace Hub. + + Args: + limit: Maximum number of models to return. + + Returns: + List of signal dictionaries with standardized schema. + """ + base_url = "https://huggingface.co/api/models" + params = { + "sort": "likes", + "direction": "-1", + "limit": limit + } + + signals = [] + + try: + response = httpx.get(base_url, params=params, timeout=10.0) + response.raise_for_status() + data = response.json() + + for item in data: + model_id = item.get("modelId", item.get("id", "unknown")) + + # Build description from model metadata + tags = item.get("tags", []) + pipeline = item.get("pipeline_tag", "") + description_parts = [] + + if pipeline: + description_parts.append(f"Pipeline: {pipeline}") + if tags: + description_parts.append(f"Tags: {', '.join(tags[:5])}") + + description_parts.append(f"Downloads: {item.get('downloads', 0):,}") + description_parts.append(f"Likes: {item.get('likes', 0):,}") + + signal = { + "id": model_id, + "source": "huggingface", + "title": f"HF Model: {model_id}", + "description": " | ".join(description_parts), + "url": f"https://huggingface.co/{model_id}", + "metadata": { + "downloads": item.get("downloads", 0), + "likes": item.get("likes", 0), + "pipeline_tag": pipeline, + "tags": tags[:10], + "author": item.get("author", "") + } + } + signals.append(signal) + + except httpx.HTTPError as e: + print(f"[HuggingFace Adapter] HTTP error: {e}") + except Exception as e: + print(f"[HuggingFace Adapter] Error: {e}") + + return signals + + +if __name__ == "__main__": + # Quick test + results = fetch_huggingface_models(limit=3) + for r in results: + print(f"- {r['title']}: {r['metadata']['likes']} likes") diff --git a/advanced_ai_agents/multi_agent_apps/devpulse_ai/adapters/medium.py b/advanced_ai_agents/multi_agent_apps/devpulse_ai/adapters/medium.py new file mode 100644 index 00000000..f5fe8ff8 --- /dev/null +++ b/advanced_ai_agents/multi_agent_apps/devpulse_ai/adapters/medium.py @@ -0,0 +1,70 @@ +""" +Medium Adapter - Fetches tech blogs from Medium and other RSS feeds. + +This is a simplified, stateless adapter for the DevPulseAI reference implementation. +Uses feedparser to fetch from RSS/Atom feeds. +""" + +import feedparser +from typing import List, Dict, Any + + +# Tech blog feeds to monitor +FEEDS = [ + "https://medium.com/feed/tag/artificial-intelligence", + "https://medium.com/feed/tag/machine-learning", + "https://medium.com/feed/@netflixtechblog", + "https://engineering.fb.com/feed/", +] + + +def fetch_medium_blogs(limit: int = 5) -> List[Dict[str, Any]]: + """ + Fetch recent tech blogs from Medium and engineering blogs. + + Args: + limit: Maximum number of entries per feed. + + Returns: + List of signal dictionaries with standardized schema. + """ + signals = [] + + for feed_url in FEEDS: + try: + feed = feedparser.parse(feed_url) + + for entry in feed.entries[:limit]: + # Get summary or description + summary = getattr(entry, "summary", "") or getattr(entry, "description", "") + + # Clean HTML tags from summary (simple approach) + if summary: + import re + summary = re.sub(r'<[^>]+>', '', summary)[:500] + + signal = { + "id": entry.get("id", entry.link), + "source": "medium", + "title": entry.title, + "description": summary, + "url": entry.link, + "metadata": { + "published": getattr(entry, "published", ""), + "author": getattr(entry, "author", "Unknown"), + "feed": feed_url + } + } + signals.append(signal) + + except Exception as e: + print(f"[Medium Adapter] Error fetching {feed_url}: {e}") + + return signals + + +if __name__ == "__main__": + # Quick test + results = fetch_medium_blogs(limit=2) + for r in results: + print(f"- {r['title'][:60]}...") diff --git a/advanced_ai_agents/multi_agent_apps/devpulse_ai/agents/__init__.py b/advanced_ai_agents/multi_agent_apps/devpulse_ai/agents/__init__.py new file mode 100644 index 00000000..e49cf077 --- /dev/null +++ b/advanced_ai_agents/multi_agent_apps/devpulse_ai/agents/__init__.py @@ -0,0 +1,21 @@ +""" +DevPulseAI Agents Package + +This package contains four specialized agents for the signal intelligence pipeline: +- SignalCollectorAgent: Aggregates signals from multiple sources +- RelevanceAgent: Scores signals based on developer relevance +- RiskAgent: Assesses security risks and breaking changes +- SynthesisAgent: Produces final intelligence digest +""" + +from .signal_collector import SignalCollectorAgent +from .relevance_agent import RelevanceAgent +from .risk_agent import RiskAgent +from .synthesis_agent import SynthesisAgent + +__all__ = [ + "SignalCollectorAgent", + "RelevanceAgent", + "RiskAgent", + "SynthesisAgent" +] diff --git a/advanced_ai_agents/multi_agent_apps/devpulse_ai/agents/__pycache__/__init__.cpython-312.pyc b/advanced_ai_agents/multi_agent_apps/devpulse_ai/agents/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..df229b4b6c3fcd3076612cf76f4dc232d32f30e4 GIT binary patch literal 825 zcmZuvJC74F5Vkj)$1S=?aXqGtP-y5Pgg9{x4T=b&SVc0v^<-nkYp?80Q0VS2(9qM} zZ{Z)TbQKjHaH6@2v6GxQffY?Wp839S9{ct1Fe7-ft4sAMA>>Xbo>sYe@=2dBgX38|6 zwZd2dtX4H>Wni3**fhppTCJgItc#*!)Gi&Aq5;1nKtg9LR;_fSYK^ogTYyJ`H=toH zkah>17S{a*yAU3WvW7`mgSIs|=Jp820$%y$Ue$XDKjxuWIn`{K!=PuvNOs{sER`u) zu@I(=%F~U(4!!a__{3Sc!lKw*{nN6<)_s$G#~G6$ZBPH#HohO-?*?BE?&=HS`;x=d zb&$`c!E}S^p?N*b+CJyf7M!;y+xhciKj4UHkMCx{Mc7xQXtU7n?jg{%$9~`9?ltfS za-lWnSL6m=a{KOlJQE+l+X_ZnS0ho^ekNpGisV9Vv3?epw{>{T!IjkEW5b+TEW^(@ zS4K6QKdCpjgJ%^ERKs(3f-+%p{vnzl9ZKoXWI)s3r-VMeCXcVlBecKL?t|pA?~X9% E4_4O)BLDyZ literal 0 HcmV?d00001 diff --git a/advanced_ai_agents/multi_agent_apps/devpulse_ai/agents/__pycache__/relevance_agent.cpython-312.pyc b/advanced_ai_agents/multi_agent_apps/devpulse_ai/agents/__pycache__/relevance_agent.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a1dedd9523118bd636f9cfe3f3cec38a2f80b105 GIT binary patch literal 5258 zcmaJ_U2Gdw7QW+|@z2=#Y22pNP?#p&Hg4n4QYgWcRG|S1v}x585K&n(@x4hV9nY9M zJ@?Ld zoQ9Nx|GkSA$4HWf)n%U>$zh(RuCW}c}1 zW5jgSj5?IJD6v&rA2T(>R!20OVAV2JOfC__DiW&FI!}+HoFCV1RpYEB8`w^rIt}xh zZJD|`raG2tGjS@>^5ecpb(C5K^-$*Up+ia_TzBk-SjN`>^5dsZHP(bROso=fOg9Ok zc8_w4og*!}(tw_KT(RGra-(O8j&8xG(}FAW(?CNxQzT~p%O}P)C+{8IE2DmA{ZG)C zMFb5CaH}CK42!rKhln&9!r~QqSjG|w6NyAdL%V=!E?TCokLZT(=)}(QQD5zW*139qRrfyIS!!L#?tXimd8cpf|wO0)C>hOJbe@PSTK8Zfz{Yy^w+dhjd(i%J)f! zRy8efcxS3x)eA*XT6ds1 zW;rm-Jd!IAeA2di%8#F=fs*U!;Y)TJF5U3q^8UKqkPt8#-*zF-WteW z&?bp(6-dS~3K^|fv@d9wLn6*;dXBT&7YZfA@jE$A0~cN_88!i$3qDP7jUH1^5ilv2 z)4`Tw@ZEv&);|nD zR}Tw+1}Av{mD~s(KoxlaRTRhSvao{VDFn2MPa@Ny7^axq7LF4W-3CRZwPfFhlf-)y%*8nOiLiCX#=s}tO!m^eY7IgA)_uQ zA9@pEWm>u{4f*w?Fp1t3-$au_x~2Tj05m*b(}FMuE*@A!!^s2kUqXhb%d9%YL$8V< zvaxtZr~4=J~zZ^N5L|bbDHA;Ua)V~ z8U+M|5by1cx1wRj6I2rP~1h*63))v5I;= z4TMJyXO05pX~2HUcG1{Dmkz-JG7^U+6eA@EaLG|ilzTKV9zC4t1;&&5*!X=+g`!0r z4Kf-AwE;TX>?$vs)+AfA_b^LBvg!#InHZ{WShP^GoeU#vz@9p9c{=e(LBkj z;Dfy7MoK1QFv|+ep4T0NP09{yxY5^4-oWKnm>eK>p6WbX0g^awbX_#4-vw2aUsoVC zW04xGTzh8*Pra#X>MKKM232chg5;g7^1BYM(~hiq%q$f~2<_`uI|AjxaK|yrB&rQ? zLmzGzvgoqLb)FfIV9d*Sq;?OoQeOII<({0HWH42CZm(5ZH;zsyx+T*o0lxX`fdo_D)yc%1LA6Qln){xMxtSPM@9=Lko+8c8dt4ew~ zb^t~Xwyw3d&nnlIx##9PR$DXkc-~%aJ+j>LWDQAA3ALRqZPHA*)*;7w*LFTUTfSbN zy?p(0b#&piYnNAdK09;rhvd$i$^9$I{qxbAUA-$^y^H%+ll{wy{-3{VdFT!jfG?m3 ze<3dP|2@2#eEQqO)Bmf9FnP~@6gK{q(A%FyUu9#@cZpw%spmVzuXhWq-6=!qMm%S- z??Y>r;&Ze%pfQWa&~&IG44}8*HaG%qf!r0bj#bl=jw<4(0${wP3!e(pawRkoBsU<*QBS4ZN4iY@r6%8;Q?-9_ziGc7y=w*Yq>$byi!-tFj#vs{j z2x6uX2-_0B?iXWwZomx$9s?K?+z{r*0?$o{=s`HF8-^fFOq>pThRJLS44<72`&J6p z2{GN>=4~^_yv(y`ngP}y-?&gDwO#XH<$lHg9UeZ4T7OSm=(5C{0*RD-!!}sE5ap2p8&coLNBBO zzM)}>y?3?>ym&(@@atM458GbU?)e} zv$OfwwBuS*UZP6d3iO}Gyl#?>hR6f zGb^cQ7WXZ6tfpSLnR<04_3CQs^tA}U(ros6c5ZAnxoV=Q-^ zBqa)Pr{{gSUF`pllBrz+i5fg{v+6w|UB@IqDgTcV7}>)X6AQYzF?P~tVUzQd5nm-q43k|&8#ApQ{;K4!ShtN2@W-#+1OLI9n$BOg-kDj*Y23rT=rQk( u`{-GiV5n#Bg#z*uLAV`~1?i_|B7`@Tnye2>yUg$+Sa?jTljDgOsFykSQG literal 0 HcmV?d00001 diff --git a/advanced_ai_agents/multi_agent_apps/devpulse_ai/agents/__pycache__/risk_agent.cpython-312.pyc b/advanced_ai_agents/multi_agent_apps/devpulse_ai/agents/__pycache__/risk_agent.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7d3ddecff32f40a28ff7e3b1f2904e2b5479d531 GIT binary patch literal 5807 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 literal 0 HcmV?d00001 diff --git a/advanced_ai_agents/multi_agent_apps/devpulse_ai/agents/__pycache__/signal_collector.cpython-312.pyc b/advanced_ai_agents/multi_agent_apps/devpulse_ai/agents/__pycache__/signal_collector.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..66a72cead9acb09966bdcb82610a38d4259c9e1a GIT binary patch literal 4485 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 literal 0 HcmV?d00001 diff --git a/advanced_ai_agents/multi_agent_apps/devpulse_ai/agents/relevance_agent.py b/advanced_ai_agents/multi_agent_apps/devpulse_ai/agents/relevance_agent.py new file mode 100644 index 00000000..e4c7f71d --- /dev/null +++ b/advanced_ai_agents/multi_agent_apps/devpulse_ai/agents/relevance_agent.py @@ -0,0 +1,121 @@ +""" +Relevance Agent - Scores signals based on developer relevance. + +This agent uses LLM reasoning to score each signal from 0-100 +based on its relevance to AI/ML developers and engineers. +""" + +from typing import Dict, Any, Optional +from agno.agent import Agent +from agno.models.openai import OpenAIChat + + +class RelevanceAgent: + """ + Agent that scores signals based on relevance to developers. + + Responsibilities: + - Score signals 0-100 based on developer relevance + - Provide reasoning for each score + - Prioritize actionable, timely content + """ + + def __init__(self, model_id: str = "gpt-4o-mini"): + """ + Initialize the Relevance Agent. + + Args: + model_id: OpenAI model to use for scoring. + """ + self.model_id = model_id + self.agent = Agent( + name="Relevance Scorer", + model=OpenAIChat(id=model_id), + role="Scores technical signals based on developer relevance", + instructions=[ + "Score each signal from 0-100 based on relevance.", + "Consider: novelty, impact, actionability, and timeliness.", + "Prioritize signals relevant to AI/ML engineers.", + "Provide brief reasoning for each score." + ], + markdown=True + ) + + def score(self, signal: Dict[str, Any]) -> Dict[str, Any]: + """ + Score a signal for relevance. + + Args: + signal: Signal dictionary to score. + + Returns: + Dictionary with score and reasoning. + """ + prompt = f""" +Rate the relevance of this signal for AI/ML developers. +Score from 0-100 where: +- 0-30: Low relevance (noise, off-topic) +- 31-60: Moderate relevance (interesting but not urgent) +- 61-80: High relevance (important for developers to know) +- 81-100: Critical relevance (must-know, actionable) + +Signal: +- Source: {signal.get('source', 'unknown')} +- Title: {signal.get('title', 'Untitled')} +- Description: {signal.get('description', '')[:500]} + +Respond with ONLY a JSON object: +{{"score": , "reasoning": ""}} +""" + + try: + response = self.agent.run(prompt, stream=False) + # Parse response - in real use, would parse JSON + return self._parse_response(response.content, signal) + except Exception as e: + return self._fallback_score(signal, str(e)) + + def score_batch(self, signals: list) -> list: + """ + Score multiple signals. + + Args: + signals: List of signal dictionaries. + + Returns: + List of signals with scores added. + """ + scored = [] + for signal in signals: + result = self.score(signal) + signal_with_score = {**signal, "relevance": result} + scored.append(signal_with_score) + return scored + + def _parse_response(self, content: str, signal: Dict) -> Dict[str, Any]: + """Parse LLM response into structured output.""" + import json + try: + # Try to extract JSON from response + content = content.strip() + if "```" in content: + content = content.split("```")[1].replace("json", "").strip() + return json.loads(content) + except: + return self._fallback_score(signal, "Parse error") + + def _fallback_score(self, signal: Dict, error: str) -> Dict[str, Any]: + """Provide fallback score when LLM call fails.""" + # Simple heuristic based on metadata + score = 50 # Default moderate score + metadata = signal.get("metadata", {}) + + if metadata.get("stars", 0) > 100: + score += 20 + if metadata.get("points", 0) > 50: + score += 15 + + return { + "score": min(score, 100), + "reasoning": f"Heuristic score (LLM unavailable: {error})" + } diff --git a/advanced_ai_agents/multi_agent_apps/devpulse_ai/agents/risk_agent.py b/advanced_ai_agents/multi_agent_apps/devpulse_ai/agents/risk_agent.py new file mode 100644 index 00000000..135207af --- /dev/null +++ b/advanced_ai_agents/multi_agent_apps/devpulse_ai/agents/risk_agent.py @@ -0,0 +1,133 @@ +""" +Risk Agent - Assesses security risks and breaking changes. + +This agent analyzes signals for potential risks including: +- Security vulnerabilities +- Breaking changes in dependencies +- Deprecation notices +""" + +from typing import Dict, Any, List +from agno.agent import Agent +from agno.models.openai import OpenAIChat + + +class RiskAgent: + """ + Agent that assesses risk levels in technical signals. + + Responsibilities: + - Identify security vulnerabilities + - Flag breaking changes + - Detect deprecation notices + - Rate overall risk level + """ + + RISK_LEVELS = ["LOW", "MEDIUM", "HIGH", "CRITICAL"] + + def __init__(self, model_id: str = "gpt-4o-mini"): + """ + Initialize the Risk Agent. + + Args: + model_id: OpenAI model to use for risk assessment. + """ + self.model_id = model_id + self.agent = Agent( + name="Risk Assessor", + model=OpenAIChat(id=model_id), + role="Assesses security and breaking change risks in technical signals", + instructions=[ + "Analyze signals for security vulnerabilities.", + "Identify breaking changes that may affect developers.", + "Flag deprecation notices and migration requirements.", + "Rate risk level: LOW, MEDIUM, HIGH, or CRITICAL." + ], + markdown=True + ) + + def assess(self, signal: Dict[str, Any]) -> Dict[str, Any]: + """ + Assess risk level of a signal. + + Args: + signal: Signal dictionary to assess. + + Returns: + Dictionary with risk assessment. + """ + prompt = f""" +Analyze this technical signal for risks: + +Signal: +- Source: {signal.get('source', 'unknown')} +- Title: {signal.get('title', 'Untitled')} +- Description: {signal.get('description', '')[:500]} + +Assess for: +1. Security vulnerabilities +2. Breaking changes +3. Deprecations + +Respond with ONLY a JSON object: +{{"risk_level": "LOW|MEDIUM|HIGH|CRITICAL", "concerns": [""], "breaking_changes": true|false}} +""" + + try: + response = self.agent.run(prompt, stream=False) + return self._parse_response(response.content, signal) + except Exception as e: + return self._fallback_assessment(signal, str(e)) + + def assess_batch(self, signals: list) -> list: + """ + Assess multiple signals for risk. + + Args: + signals: List of signal dictionaries. + + Returns: + List of signals with risk assessments added. + """ + assessed = [] + for signal in signals: + result = self.assess(signal) + signal_with_risk = {**signal, "risk": result} + assessed.append(signal_with_risk) + return assessed + + def _parse_response(self, content: str, signal: Dict) -> Dict[str, Any]: + """Parse LLM response into structured output.""" + import json + try: + content = content.strip() + if "```" in content: + content = content.split("```")[1].replace("json", "").strip() + return json.loads(content) + except: + return self._fallback_assessment(signal, "Parse error") + + def _fallback_assessment(self, signal: Dict, error: str) -> Dict[str, Any]: + """Provide fallback assessment when LLM call fails.""" + title = signal.get("title", "").lower() + + # Simple keyword-based heuristics + risk_level = "LOW" + concerns = [] + + risk_keywords = { + "HIGH": ["vulnerability", "exploit", "CVE", "critical", "breach"], + "MEDIUM": ["breaking", "deprecated", "removed", "migration"], + } + + for level, keywords in risk_keywords.items(): + if any(kw.lower() in title for kw in keywords): + risk_level = level + concerns.append(f"Keyword match: {level}") + break + + return { + "risk_level": risk_level, + "concerns": concerns if concerns else [f"Heuristic (LLM unavailable: {error})"], + "breaking_changes": "breaking" in title.lower() + } diff --git a/advanced_ai_agents/multi_agent_apps/devpulse_ai/agents/signal_collector.py b/advanced_ai_agents/multi_agent_apps/devpulse_ai/agents/signal_collector.py new file mode 100644 index 00000000..92cd8d9d --- /dev/null +++ b/advanced_ai_agents/multi_agent_apps/devpulse_ai/agents/signal_collector.py @@ -0,0 +1,100 @@ +""" +Signal Collector Agent - Aggregates signals from multiple data sources. + +This agent is responsible for the ingestion phase of the pipeline. +It collects signals from GitHub, ArXiv, and HackerNews, then normalizes +them into a unified schema for downstream processing. +""" + +from typing import List, Dict, Any +from agno.agent import Agent +from agno.models.openai import OpenAIChat + + +class SignalCollectorAgent: + """ + Agent that collects and normalizes signals from multiple sources. + + Responsibilities: + - Fetch data from configured adapters + - Normalize signals to unified schema + - Deduplicate and filter low-quality signals + """ + + def __init__(self, model_id: str = "gpt-4o-mini"): + """ + Initialize the Signal Collector Agent. + + Args: + model_id: OpenAI model to use for signal processing. + """ + self.model_id = model_id + self.agent = Agent( + name="Signal Collector", + model=OpenAIChat(id=model_id), + role="Collects and normalizes technical signals from multiple sources", + instructions=[ + "You aggregate signals from GitHub, ArXiv, and HackerNews.", + "Normalize all signals to a consistent format.", + "Filter out low-quality or duplicate signals.", + "Prioritize signals relevant to AI/ML developers." + ], + markdown=True + ) + + def collect(self, signals: List[Dict[str, Any]]) -> List[Dict[str, Any]]: + """ + Process and normalize collected signals. + + Args: + signals: Raw signals from adapters. + + Returns: + List of normalized signal dictionaries. + """ + normalized = [] + seen_ids = set() + + for signal in signals: + # Deduplicate + signal_id = f"{signal.get('source', 'unknown')}:{signal.get('id', '')}" + if signal_id in seen_ids: + continue + seen_ids.add(signal_id) + + # Ensure required fields + normalized_signal = { + "id": signal.get("id", ""), + "source": signal.get("source", "unknown"), + "title": signal.get("title", "Untitled"), + "description": signal.get("description", ""), + "url": signal.get("url", ""), + "metadata": signal.get("metadata", {}), + "collected_at": self._get_timestamp() + } + normalized.append(normalized_signal) + + return normalized + + def _get_timestamp(self) -> str: + """Get current UTC timestamp.""" + from datetime import datetime + return datetime.utcnow().isoformat() + "Z" + + def summarize_collection(self, signals: List[Dict[str, Any]]) -> str: + """ + Generate a summary of collected signals. + + Args: + signals: List of collected signals. + + Returns: + Summary string. + """ + sources = {} + for s in signals: + src = s.get("source", "unknown") + sources[src] = sources.get(src, 0) + 1 + + summary_parts = [f"{count} from {src}" for src, count in sources.items()] + return f"Collected {len(signals)} signals: {', '.join(summary_parts)}" diff --git a/advanced_ai_agents/multi_agent_apps/devpulse_ai/agents/synthesis_agent.py b/advanced_ai_agents/multi_agent_apps/devpulse_ai/agents/synthesis_agent.py new file mode 100644 index 00000000..4055e0e6 --- /dev/null +++ b/advanced_ai_agents/multi_agent_apps/devpulse_ai/agents/synthesis_agent.py @@ -0,0 +1,154 @@ +""" +Synthesis Agent - Produces final intelligence digest. + +This agent combines outputs from all previous agents to create +a comprehensive, actionable intelligence summary for developers. +""" + +from typing import Dict, Any, List +from agno.agent import Agent +from agno.models.openai import OpenAIChat + + +class SynthesisAgent: + """ + Agent that synthesizes all signal intelligence into a final digest. + + Responsibilities: + - Combine relevance and risk assessments + - Prioritize signals by importance + - Generate executive summary + - Produce actionable recommendations + """ + + def __init__(self, model_id: str = "gpt-4o-mini"): + """ + Initialize the Synthesis Agent. + + Args: + model_id: OpenAI model to use for synthesis. + """ + self.model_id = model_id + self.agent = Agent( + name="Intelligence Synthesizer", + model=OpenAIChat(id=model_id), + role="Synthesizes technical signals into actionable intelligence digests", + instructions=[ + "Combine relevance scores and risk assessments.", + "Prioritize by: high relevance + critical risks first.", + "Generate an executive summary.", + "Provide actionable recommendations for developers." + ], + markdown=True + ) + + def synthesize(self, signals: List[Dict[str, Any]]) -> Dict[str, Any]: + """ + Synthesize signals into a final intelligence digest. + + Args: + signals: List of signals with relevance and risk data. + + Returns: + Complete intelligence digest. + """ + # Sort by priority (high relevance + high risk first) + prioritized = self._prioritize_signals(signals) + + # Group by category + grouped = self._group_by_source(prioritized) + + # Generate summary + summary = self._generate_summary(prioritized) + + return { + "generated_at": self._get_timestamp(), + "total_signals": len(signals), + "executive_summary": summary, + "priority_signals": prioritized[:5], # Top 5 + "signals_by_source": grouped, + "recommendations": self._generate_recommendations(prioritized) + } + + def _prioritize_signals(self, signals: List[Dict]) -> List[Dict]: + """Sort signals by priority score.""" + def priority_score(signal): + relevance = signal.get("relevance", {}).get("score", 50) + risk = signal.get("risk", {}) + risk_multiplier = { + "CRITICAL": 2.0, + "HIGH": 1.5, + "MEDIUM": 1.0, + "LOW": 0.8 + }.get(risk.get("risk_level", "LOW"), 1.0) + return relevance * risk_multiplier + + return sorted(signals, key=priority_score, reverse=True) + + def _group_by_source(self, signals: List[Dict]) -> Dict[str, List]: + """Group signals by their source.""" + grouped = {} + for signal in signals: + source = signal.get("source", "unknown") + if source not in grouped: + grouped[source] = [] + grouped[source].append(signal) + return grouped + + def _generate_summary(self, signals: List[Dict]) -> str: + """Generate executive summary.""" + if not signals: + return "No signals to summarize." + + high_priority = [s for s in signals + if s.get("relevance", {}).get("score", 0) >= 70] + critical_risks = [s for s in signals + if s.get("risk", {}).get("risk_level") in ["HIGH", "CRITICAL"]] + + parts = [f"Analyzed {len(signals)} signals."] + + if high_priority: + parts.append(f"{len(high_priority)} high-relevance items detected.") + + if critical_risks: + parts.append(f"⚠️ {len(critical_risks)} signals with elevated risk.") + + if signals: + top = signals[0] + parts.append(f"Top signal: {top.get('title', 'Unknown')}") + + return " ".join(parts) + + def _generate_recommendations(self, signals: List[Dict]) -> List[str]: + """Generate actionable recommendations.""" + recommendations = [] + + critical_risks = [s for s in signals + if s.get("risk", {}).get("risk_level") == "CRITICAL"] + if critical_risks: + recommendations.append( + f"🚨 Review {len(critical_risks)} critical-risk signals immediately" + ) + + high_relevance = [s for s in signals + if s.get("relevance", {}).get("score", 0) >= 80] + if high_relevance: + recommendations.append( + f"πŸ“Œ Prioritize {len(high_relevance)} high-relevance items" + ) + + github_signals = [s for s in signals if s.get("source") == "github"] + if github_signals: + recommendations.append( + f"⭐ Explore {len(github_signals)} trending repositories" + ) + + if not recommendations: + recommendations.append("βœ… No urgent actions required") + + return recommendations + + def _get_timestamp(self) -> str: + """Get current UTC timestamp.""" + from datetime import datetime + return datetime.utcnow().isoformat() + "Z" diff --git a/advanced_ai_agents/multi_agent_apps/devpulse_ai/main.py b/advanced_ai_agents/multi_agent_apps/devpulse_ai/main.py new file mode 100644 index 00000000..bc148158 --- /dev/null +++ b/advanced_ai_agents/multi_agent_apps/devpulse_ai/main.py @@ -0,0 +1,143 @@ +""" +DevPulseAI - Multi-Agent Signal Intelligence Pipeline + +This script demonstrates a complete multi-agent workflow for +aggregating and analyzing technical signals from multiple sources. + +Usage: + python main.py + +Requirements: + - OpenAI API key set as OPENAI_API_KEY environment variable + - Internet connection for fetching live data +""" + +import os +from typing import List, Dict, Any + +# Import adapters +from adapters.github import fetch_github_trending +from adapters.arxiv import fetch_arxiv_papers +from adapters.hackernews import fetch_hackernews_stories +from adapters.medium import fetch_medium_blogs +from adapters.huggingface import fetch_huggingface_models + +# Import agents +from agents import ( + SignalCollectorAgent, + RelevanceAgent, + RiskAgent, + SynthesisAgent +) + + +def collect_signals() -> List[Dict[str, Any]]: + """ + Collect signals from all configured sources. + + Returns: + Combined list of signals from all adapters. + """ + print("\nπŸ“‘ [1/4] Collecting Signals...") + + signals = [] + + # Fetch from each source + print(" β†’ Fetching GitHub trending repos...") + signals.extend(fetch_github_trending(limit=5)) + + print(" β†’ Fetching ArXiv papers...") + signals.extend(fetch_arxiv_papers(limit=5)) + + print(" β†’ Fetching HackerNews stories...") + signals.extend(fetch_hackernews_stories(limit=5)) + + print(" β†’ Fetching Medium blogs...") + signals.extend(fetch_medium_blogs(limit=3)) + + print(" β†’ Fetching HuggingFace models...") + signals.extend(fetch_huggingface_models(limit=5)) + + print(f" βœ“ Collected {len(signals)} raw signals") + return signals + + +def run_pipeline(): + """ + Execute the full signal intelligence pipeline. + + Pipeline stages: + 1. Signal Collection - Aggregate from multiple sources + 2. Relevance Scoring - Rate signals 0-100 + 3. Risk Assessment - Identify security/breaking changes + 4. Synthesis - Produce final intelligence digest + """ + print("=" * 60) + print("🧠 DevPulseAI - Signal Intelligence Pipeline") + print("=" * 60) + + # Check for API key + if not os.environ.get("OPENAI_API_KEY"): + print("\n⚠️ Warning: OPENAI_API_KEY not set.") + print(" LLM-based agents will use fallback heuristics.\n") + + # Stage 1: Collection + raw_signals = collect_signals() + + # Initialize agents + collector = SignalCollectorAgent() + relevance = RelevanceAgent() + risk = RiskAgent() + synthesis = SynthesisAgent() + + # Stage 2: Normalize + print("\nπŸ”„ [2/4] Normalizing Signals...") + normalized = collector.collect(raw_signals) + print(f" βœ“ {collector.summarize_collection(normalized)}") + + # Stage 3: Score for relevance + print("\nπŸ“Š [3/4] Scoring Relevance...") + scored = relevance.score_batch(normalized) + high_relevance = sum(1 for s in scored + if s.get("relevance", {}).get("score", 0) >= 70) + print(f" βœ“ {high_relevance}/{len(scored)} signals rated high-relevance") + + # Stage 4: Assess risks + print("\n⚠️ [4/4] Assessing Risks...") + assessed = risk.assess_batch(scored) + critical = sum(1 for s in assessed + if s.get("risk", {}).get("risk_level") in ["HIGH", "CRITICAL"]) + print(f" βœ“ {critical}/{len(assessed)} signals with elevated risk") + + # Stage 5: Synthesize + print("\nπŸ“‹ Generating Intelligence Digest...") + digest = synthesis.synthesize(assessed) + + # Output results + print("\n" + "=" * 60) + print("πŸ“„ INTELLIGENCE DIGEST") + print("=" * 60) + print(f"\nπŸ• Generated: {digest['generated_at']}") + print(f"πŸ“¦ Total Signals: {digest['total_signals']}") + print(f"\nπŸ“ Summary: {digest['executive_summary']}") + + print("\n🎯 Top Priority Signals:") + for i, signal in enumerate(digest.get("priority_signals", [])[:3], 1): + score = signal.get("relevance", {}).get("score", "?") + risk_level = signal.get("risk", {}).get("risk_level", "?") + print(f" {i}. [{signal['source']}] {signal['title'][:50]}...") + print(f" Relevance: {score} | Risk: {risk_level}") + + print("\nπŸ’‘ Recommendations:") + for rec in digest.get("recommendations", []): + print(f" β€’ {rec}") + + print("\n" + "=" * 60) + print("βœ… Pipeline completed successfully!") + print("=" * 60) + + return digest + + +if __name__ == "__main__": + run_pipeline() diff --git a/advanced_ai_agents/multi_agent_apps/devpulse_ai/requirements.txt b/advanced_ai_agents/multi_agent_apps/devpulse_ai/requirements.txt new file mode 100644 index 00000000..2b939190 --- /dev/null +++ b/advanced_ai_agents/multi_agent_apps/devpulse_ai/requirements.txt @@ -0,0 +1,4 @@ +agno +httpx +openai +feedparser diff --git a/advanced_ai_agents/multi_agent_apps/devpulse_ai/verify.py b/advanced_ai_agents/multi_agent_apps/devpulse_ai/verify.py new file mode 100644 index 00000000..ba3eb132 --- /dev/null +++ b/advanced_ai_agents/multi_agent_apps/devpulse_ai/verify.py @@ -0,0 +1,207 @@ +""" +DevPulseAI Verification Script + +This script verifies the pipeline works correctly using MOCK DATA ONLY. +No network calls or API keys are required. + +Usage: + python verify.py + +Expected output: + [OK] DevPulseAI reference pipeline executed successfully +""" + +from typing import List, Dict, Any + +# Mock signal data for verification +MOCK_SIGNALS = [ + { + "id": "mock-gh-001", + "source": "github", + "title": "awesome-llm-apps", + "description": "A curated collection of awesome LLM apps built with RAG and AI agents.", + "url": "https://github.com/Shubhamsaboo/awesome-llm-apps", + "metadata": {"stars": 5000, "language": "Python", "topics": ["llm", "ai"]} + }, + { + "id": "mock-arxiv-001", + "source": "arxiv", + "title": "Attention Is All You Need: Revisited", + "description": "A comprehensive analysis of transformer architectures and their evolution over the past years.", + "url": "https://arxiv.org/abs/2401.00001", + "metadata": {"pdf": "https://arxiv.org/pdf/2401.00001", "published": "2024-01-15"} + }, + { + "id": "mock-hn-001", + "source": "hackernews", + "title": "GPT-5 Breaking Changes in API", + "description": "OpenAI announces breaking changes to the Chat Completions API.", + "url": "https://news.ycombinator.com/item?id=12345", + "metadata": {"points": 500, "comments": 200, "author": "techwriter"} + }, + { + "id": "mock-medium-001", + "source": "medium", + "title": "Building Production RAG Systems", + "description": "A deep dive into building scalable retrieval-augmented generation pipelines.", + "url": "https://medium.com/@techblog/building-rag", + "metadata": {"author": "TechBlog", "published": "2024-01-20"} + }, + { + "id": "mock-hf-001", + "source": "huggingface", + "title": "HF Model: meta-llama/Llama-3-8B", + "description": "Pipeline: text-generation | Downloads: 1,000,000 | Likes: 5,000", + "url": "https://huggingface.co/meta-llama/Llama-3-8B", + "metadata": {"downloads": 1000000, "likes": 5000, "pipeline_tag": "text-generation"} + } +] + + +def verify_imports(): + """Verify all modules can be imported.""" + print("[1/5] Verifying imports...") + + try: + from agents import ( + SignalCollectorAgent, + RelevanceAgent, + RiskAgent, + SynthesisAgent + ) + from adapters.github import fetch_github_trending + from adapters.arxiv import fetch_arxiv_papers + from adapters.hackernews import fetch_hackernews_stories + from adapters.medium import fetch_medium_blogs + from adapters.huggingface import fetch_huggingface_models + print(" βœ“ All modules imported successfully") + return True + except ImportError as e: + print(f" βœ— Import error: {e}") + return False + + +def verify_signal_collector(): + """Verify SignalCollectorAgent works with mock data.""" + print("[2/5] Verifying Signal Collector...") + + from agents import SignalCollectorAgent + + collector = SignalCollectorAgent() + normalized = collector.collect(MOCK_SIGNALS) + + assert len(normalized) == len(MOCK_SIGNALS), "Signal count mismatch" + assert all("collected_at" in s for s in normalized), "Missing timestamp" + + summary = collector.summarize_collection(normalized) + print(f" βœ“ {summary}") + return normalized + + +def verify_relevance_agent(signals: List[Dict]): + """Verify RelevanceAgent works with mock data.""" + print("[3/5] Verifying Relevance Agent...") + + from agents import RelevanceAgent + + # Use fallback mode (no API key needed) + agent = RelevanceAgent() + + scored = [] + for signal in signals: + # Use fallback scoring directly + result = agent._fallback_score(signal, "Mock mode") + signal_with_score = {**signal, "relevance": result} + scored.append(signal_with_score) + + assert all("relevance" in s for s in scored), "Missing relevance scores" + print(f" βœ“ Scored {len(scored)} signals") + return scored + + +def verify_risk_agent(signals: List[Dict]): + """Verify RiskAgent works with mock data.""" + print("[4/5] Verifying Risk Agent...") + + from agents import RiskAgent + + agent = RiskAgent() + + assessed = [] + for signal in signals: + # Use fallback assessment directly + result = agent._fallback_assessment(signal, "Mock mode") + signal_with_risk = {**signal, "risk": result} + assessed.append(signal_with_risk) + + assert all("risk" in s for s in assessed), "Missing risk assessments" + + # Check that breaking change detection works + breaking = [s for s in assessed if s.get("risk", {}).get("breaking_changes")] + print(f" βœ“ Assessed {len(assessed)} signals ({len(breaking)} with breaking changes)") + return assessed + + +def verify_synthesis_agent(signals: List[Dict]): + """Verify SynthesisAgent produces valid digest.""" + print("[5/5] Verifying Synthesis Agent...") + + from agents import SynthesisAgent + + agent = SynthesisAgent() + digest = agent.synthesize(signals) + + assert "generated_at" in digest, "Missing timestamp" + assert "executive_summary" in digest, "Missing summary" + assert "recommendations" in digest, "Missing recommendations" + assert digest["total_signals"] == len(signals), "Signal count mismatch" + + print(f" βœ“ Generated digest with {len(digest['recommendations'])} recommendations") + return digest + + +def run_verification(): + """Run complete verification suite.""" + print("=" * 60) + print("πŸ” DevPulseAI Verification Suite") + print("=" * 60) + print("\nUsing MOCK DATA - No network calls or API keys required.\n") + + try: + # Step 1: Import verification + if not verify_imports(): + raise AssertionError("Import verification failed") + + # Step 2: Signal collection + normalized = verify_signal_collector() + + # Step 3: Relevance scoring + scored = verify_relevance_agent(normalized) + + # Step 4: Risk assessment + assessed = verify_risk_agent(scored) + + # Step 5: Synthesis + digest = verify_synthesis_agent(assessed) + + # Final summary + print("\n" + "=" * 60) + print("πŸ“Š Verification Summary") + print("=" * 60) + print(f" β€’ Signals processed: {digest['total_signals']}") + print(f" β€’ Summary: {digest['executive_summary']}") + print(f" β€’ Recommendations: {len(digest['recommendations'])}") + + print("\n" + "=" * 60) + print("[OK] DevPulseAI reference pipeline executed successfully") + print("=" * 60) + return True + + except Exception as e: + print(f"\n[FAIL] Verification failed: {e}") + return False + + +if __name__ == "__main__": + success = run_verification() + exit(0 if success else 1) diff --git a/advanced_ai_agents/multi_agent_apps/devpulse_ai/workflows/signal-intelligence-pipeline.json b/advanced_ai_agents/multi_agent_apps/devpulse_ai/workflows/signal-intelligence-pipeline.json new file mode 100644 index 00000000..f5fef4ec --- /dev/null +++ b/advanced_ai_agents/multi_agent_apps/devpulse_ai/workflows/signal-intelligence-pipeline.json @@ -0,0 +1,254 @@ +{ + "name": "Signal Intelligence Ingestion Pipeline", + "description": "Optional n8n workflow for automating the DevPulseAI signal intelligence pipeline. Import into n8n to schedule daily digest generation.", + "nodes": [ + { + "parameters": {}, + "id": "trigger-cron", + "name": "Daily Trigger", + "type": "n8n-nodes-base.scheduleTrigger", + "typeVersion": 1.1, + "position": [ + 0, + 300 + ], + "notes": "Triggers the pipeline daily at configured time" + }, + { + "parameters": { + "httpMethod": "POST", + "path": "trigger-pulse", + "responseMode": "responseNode" + }, + "id": "webhook-trigger", + "name": "Webhook Trigger", + "type": "n8n-nodes-base.webhook", + "typeVersion": 2, + "position": [ + 0, + 500 + ], + "webhookId": "devpulse-trigger", + "notes": "Manual trigger via POST request" + }, + { + "parameters": { + "url": "https://api.github.com/search/repositories?q=stars:>1000&sort=stars&order=desc&per_page=10", + "options": { + "response": { + "response": { + "responseFormat": "json" + } + } + } + }, + "id": "github-adapter", + "name": "GitHub Trending Repos", + "type": "n8n-nodes-base.httpRequest", + "typeVersion": 4.2, + "position": [ + 400, + 100 + ], + "notes": "Fetches trending GitHub repositories" + }, + { + "parameters": { + "url": "http://export.arxiv.org/api/query?search_query=cat:cs.AI+OR+cat:cs.LG&sortBy=submittedDate&sortOrder=descending&max_results=10" + }, + "id": "arxiv-adapter", + "name": "ArXiv Papers", + "type": "n8n-nodes-base.httpRequest", + "typeVersion": 4.2, + "position": [ + 400, + 400 + ], + "notes": "Fetches latest AI/ML research papers" + }, + { + "parameters": { + "url": "https://hn.algolia.com/api/v1/search_by_date?query=AI&tags=story&hitsPerPage=10" + }, + "id": "hackernews-adapter", + "name": "HackerNews Top Stories", + "type": "n8n-nodes-base.httpRequest", + "typeVersion": 4.2, + "position": [ + 400, + 550 + ], + "notes": "Fetches top HackerNews stories" + }, + { + "parameters": { + "mode": "multiplex" + }, + "id": "merge-signals", + "name": "Aggregate Signals", + "type": "n8n-nodes-base.merge", + "typeVersion": 3, + "position": [ + 650, + 400 + ], + "notes": "Combines all signal sources into unified stream" + }, + { + "parameters": { + "resource": "chat", + "model": "gpt-4o-mini", + "prompt": { + "messages": [ + { + "role": "system", + "content": "You are a Relevance Scoring Agent. Score the following content from 0-100 based on its relevance to AI/ML developers. Return ONLY a JSON object: {\"score\": , \"reason\": \"<1 sentence>\"}" + }, + { + "role": "user", + "content": "Title: {{ $json.title }}\nDescription: {{ $json.description }}" + } + ] + }, + "options": { + "temperature": 0.1, + "maxOutputTokens": 100 + } + }, + "id": "relevance-agent", + "name": "Relevance Agent", + "type": "@n8n/n8n-nodes-langchain.lmChatOpenAi", + "typeVersion": 1, + "position": [ + 1100, + 400 + ], + "notes": "Scores content 0-100 based on developer relevance" + }, + { + "parameters": { + "resource": "chat", + "model": "gpt-4o-mini", + "prompt": { + "messages": [ + { + "role": "system", + "content": "You are a Risk Assessment Agent. Analyze for: breaking changes, security vulnerabilities, or deprecations. Return ONLY a JSON object: {\"risk_level\": \"HIGH|MEDIUM|LOW\", \"concerns\": [\"\"]}" + }, + { + "role": "user", + "content": "Title: {{ $json.title }}\nDescription: {{ $json.description }}" + } + ] + }, + "options": { + "temperature": 0.1, + "maxOutputTokens": 150 + } + }, + "id": "risk-agent", + "name": "Risk Agent", + "type": "@n8n/n8n-nodes-langchain.lmChatOpenAi", + "typeVersion": 1, + "position": [ + 1100, + 600 + ], + "notes": "Flags breaking changes and security vulnerabilities" + } + ], + "connections": { + "Daily Trigger": { + "main": [ + [ + { + "node": "GitHub Trending Repos", + "type": "main", + "index": 0 + } + ] + ] + }, + "Webhook Trigger": { + "main": [ + [ + { + "node": "GitHub Trending Repos", + "type": "main", + "index": 0 + } + ] + ] + }, + "GitHub Trending Repos": { + "main": [ + [ + { + "node": "Aggregate Signals", + "type": "main", + "index": 0 + } + ] + ] + }, + "ArXiv Papers": { + "main": [ + [ + { + "node": "Aggregate Signals", + "type": "main", + "index": 1 + } + ] + ] + }, + "HackerNews Top Stories": { + "main": [ + [ + { + "node": "Aggregate Signals", + "type": "main", + "index": 2 + } + ] + ] + }, + "Aggregate Signals": { + "main": [ + [ + { + "node": "Relevance Agent", + "type": "main", + "index": 0 + }, + { + "node": "Risk Agent", + "type": "main", + "index": 0 + } + ] + ] + } + }, + "settings": { + "executionOrder": "v1" + }, + "tags": [ + { + "name": "signal-intelligence" + }, + { + "name": "automation" + }, + { + "name": "developer-tools" + }, + { + "name": "ai-agents" + } + ], + "meta": { + "templateCredsSetupCompleted": false, + "instanceId": "devpulse-reference" + } +} \ No newline at end of file