
    ^iI              
          S r SSKrSSKrSSKrSSKrSSKrSSKrSSKJrJr  SSK	J
r
  SSKr\
" S5      r\S-  S-  r\S-  S	-  r\S
-  S-  r\S-  r\S-  r\R$                  " \R&                  S\R(                  " \R*                  5      /S9  \R,                  " S5      rSSSSSS.rSrSr1 Skr1 Skr1 Skr1 Skr1 Sk1 Sk1 Sk1 Sk1 SkS.rS\ S \ 4S! jr!S \"4S" jr#S#\ S$\$S \ 4S% jr%S&\&S \'\(\&4   4S' jr)S&\&S(\(S)\ S*\&S \&4
S+ jr*S&\&S(\(S)\ S \&4S, jr+S-\
S.\&S S4S/ jr,S0\&S S4S1 jr-S9S&\&S2\.S \&4S3 jjr/S\ S \&4S4 jr0S:S5\(S S4S6 jjr1S7 r2\3S8:X  a  \2" 5         gg);u  
Memory Competitor Intelligence Scout — Webhook Processor
=========================================================
Receives POST requests from n8n when new memory tools/frameworks are discovered.
Classifies, scores, deduplicates, and routes discoveries into the Genesis KG and
RWL task loop.

Webhook endpoint: POST /memory-scout-ingest
Check endpoint:   POST /memory-scout-check

Run as a lightweight Flask service:
    python process_discovery.py --serve --port 8765

Or import and call process_discovery() directly from other scripts.
    N)datetimetimezone)PathzE:/genesis-systemKNOWLEDGE_GRAPHentitiesloopz
tasks.jsonn8nmemory_competitor_scoutzdiscovery_log.jsonlzknown_memory_systems.jsonlz'%(asctime)s [%(levelname)s] %(message)s)levelformathandlersmemory_scout   )open_sourcepython_supportgraph_supporttemporal_supportactive_maintenance      >   pypippypipythonpytorch	langchain
llamaindex>   rdfgraphneo4jcypherfalkordbgraphitiontologyknowledge-graph>   timehistoryepisodictemporaltimeline	versionedchronological>   bsdgplmitapache
opensourceopen-source>   rag	retrievaldocument-qaretrieval-augmented>   kgr   r    r"   r#   r$   graph-dbr%   >   lettamemgptrecallr(   session-memoryconversation-memory>   faisschromaqdrantvectorlancedbpineconeweaviate	embedding>   r)   
time-awaretimeline-memoryversioned-memory)RAGKnowledgeGraphEpisodicMemoryVectorStoreTemporalMemoryurlreturnc                     [         R                  " U R                  5       R                  5       R	                  5       5      R                  5       SS $ )u4   Stable short ID from URL — used for deduplication.N   )hashlibmd5lowerstripencode	hexdigest)rN   s    BE:/genesis-system/n8n/memory_competitor_scout/process_discovery.py	url_to_idrY   W   s9    ;;syy{((*1134>>@"EE    c                  d   [        5       n [        [        4 H  nUR                  5       (       d  M  UR	                  SSS9 nU H  nUR                  5       nU(       d  M   [        R                  " U5      nUR                  SS5      nU(       a  U R                  [        U5      5        UR                  SS5      R                  5       R                  5       nU(       a  U R                  U5        M  M     SSS5        M     U $ ! [        R                   a     M  f = f! , (       d  f       GM  = f)z}Load all known system URLs (as md5 IDs) from known_memory_systems.jsonl
and discovery_log.jsonl to enable fast deduplication.rutf-8encodingrN    nameN)setKNOWN_SYSTEMS_FILEDISCOVERY_LOGexistsopenrU   jsonloadsgetaddrY   rT   JSONDecodeError)knownfpathflineentryrN   ra   s          rX   load_known_idsrq   \   s     EE$m4<<>>C'2aD::<D 
 $

4 0#iir2!IIin5$yy4::<BBD!IIdO    32 5$ L  //  32s1   D!BD0DD	DD	D
D/	descriptiontopicsc                 B   U S-   SR                  U=(       d    / 5      -   R                  5       n[         Vs0 s H  o3S_M     nn[        R                  5        H!  u  p5U H  nXb;   d  M
  XC==   S-  ss'   M     M#     [	        XDR
                  S9nXG   S:X  a  gU$ s  snf )z4Classify discovery into one of the known tool types. r      )keyOther)joinrT   TYPE_KEYWORDSitemsmaxri   )rr   rs   text	tool_typescoreskeywordskwbests           rX   classify_tool_typer   u   s    #2 66==?D,9:MylMF:,224	Bz!Q&!   5 v::&D|qK ;s   Bitemc                 H  ^^ U R                  S5      =(       d    SS-   SR                  U R                  S5      =(       d    / 5      -   S-   U R                  S5      =(       d    S-   R                  5       mU R                  S5      =(       d    SR                  5       mU R                  S5      =(       d    U R                  S5      =(       d    Sn0 nU R                  S	5      S
L =(       d4    [        U4S j[         5       5      =(       d    U R                  S5      S;  nU(       a	  [
        S	   OSUS	'   [        U4S j[         5       5      nU(       a	  [
        S   OSUS'   [        U4S j[         5       5      nU(       a	  [
        S   OSUS'   [        U4S j[         5       5      nU(       a	  [
        S   OSUS'   SnU(       a   UR                  S5      (       a  USS S-   n[        R                  " U5      nUR                  c  UR                  [        R                  S9n[        R                   " [        R                  5      U-
  R"                  n	U	S:*  nU(       a	  [
        S   OSUS'   [)        UR+                  5       5      n
X4$ ! [$        [&        4 a     NCf = f)z[
Score a discovery 0-10 for Genesis relevance.

Returns:
    (score: int, breakdown: dict)
rr   r`   ru   rs   ra   license	pushed_at
updated_atr   Tc              3   ,   >#    U  H	  oT;   v   M     g 7fN ).0klicense_texts     rX   	<genexpr>"score_relevance.<locals>.<genexpr>   s     A*@QL *@   )Nr`   NOASSERTIONrx   r   c              3   ,   >#    U  H	  oT;   v   M     g 7fr   r   r   r   r}   s     rX   r   r      s     81$Yr   r   c              3   ,   >#    U  H	  oT;   v   M     g 7fr   r   r   s     rX   r   r      s     6~!I~r   r   c              3   ,   >#    U  H	  oT;   v   M     g 7fr   r   r   s     rX   r   r      s     <*;QDy*;r   r   FZNz+00:00)tzinfo   r   )ri   ry   rT   anyOPEN_SOURCE_INDICATORSSCORING_WEIGHTSPYTHON_KEYWORDSGRAPH_KEYWORDSTEMPORAL_KEYWORDSendswithr   fromisoformatr   replacer   utcnowdays
ValueError	TypeErrorsumvalues)r   r   	breakdownis_open
has_python	has_graphhas_temporal	is_active	pushed_dtdays_agototalr   r}   s              @@rX   score_relevancer      s\    
-	 	&B
	
((488H%+
,	- 	 88F!r		#
 eg 	 HHY'-2446L%E,)?E2II 	4' 	IA*@AA	I88I&HH 
 BI}=aIm 888JGQ/2B"CWXI 6~66IEN!ATUIo <*;<<LKWO4F$G]^I ! I		!!#&&%crNX5	 ..y9I'%--X\\-B	 X\\2Y>DDH BI PYo6J&K^_I"#	  "#E I& 		s   	BJ J! J!scorer~   r   c           	      *   0 SS[        U R                  SS5      5       3_SU R                  SS5      _SU R                  SS5      _SU_SUR                  SS	5      S	:  _S
S_S[        R                  " [        R
                  5      R                  5       _SU_SU_SU R                  SS5      _SU R                  SS	5      _SU R                  S5      =(       d    SSS _SU R                  S5      =(       d    / _SU R                  SS5      _SS_SS_$ )z(Build a KG entity dict from a discovery.idmemory_competitor_rN   r`   ra   Unknowntyper   r   genesis_integratedFdiscovered_daterelevance_scorescore_breakdownsourceunknownstarsrr   Ni  rs   notes	evaluatedevaluation_task_id)rY   ri   r   r   r   r   	isoformat)r   r   r~   r   s       rX   build_kg_entityr      sa   "9TXXeR-@#A"BC+ 	txxr" 			
 	y}}]A6: 	e 	8<<5??A 	5 	9 	$((8Y/ 	'1% 	/52t< 	$((8$* 	'2& 	U  	d! rZ   c                    U R                  SS5      nU R                  SS5      nS[        U5       S[        R                  " [        R
                  5      R                  S5       3nUSS	U S
3UUUUSUS:  a  SOS[        R                  " [        R
                  5      R                  5       SS	U SU S3U R                  S5      =(       d    SSS U R                  SS5      U R                  SS5      S.$ )z:Build an RWL evaluation task for a high-scoring discovery.ra   zUnknown ToolrN   r`   eval__z%Y%m%dmemory_tool_evaluationz	Evaluate z for Genesis integrationpending	   highmediumr
   z (u5  ) for potential Genesis memory stack integration.
Steps:
1. Clone/install the repo and review architecture (README, src structure)
2. Identify unique capabilities vs Genesis's current 5-tier stack
   (PostgreSQL + Qdrant + Redis + FalkorDB/Graphiti)
3. Test Python API with a simple 10-message conversation memory test
4. Assess: integration effort (hours), unique value-add, overlap with existing
5. Recommend: Integrate / Monitor / Skip — with justification
6. Update known_memory_systems.jsonl with evaluation results
7. If Integrate: create implementation PRDrr   Ni,  r   r   r   r   )r   r   title	tool_nametool_urlr~   r   statuspriority
created_at
created_byinstructionsrr   r   r   )ri   rY   r   r   r   r   strftimer   )r   r   r~   r   r   task_ids         rX   build_evaluation_taskr      s    0Ixxr"Hi)*!HLL,F,O,OPX,Y+Z[G (YK'?@ #qjFhll8<<0::</	{"XJ 	/9 	9 /52t<'1%((8Y/5 rZ   rm   recordc                 l   U R                   R                  SSS9  U R                  SSS9 nUR                  [        R
                  " USS9S-   5        S	S	S	5        [        R                  S
U R                  UR                  S5      =(       d    UR                  SS5      5        g	! , (       d  f       NX= f)z@Append a JSON record to a .jsonl file (creates file if missing).Tparentsexist_okar]   r^   F)ensure_ascii
NzWrote to %s: %sr   ra   r`   )
parentmkdirrf   writerg   dumpsloginfora   ri   )rm   r   rn   s      rX   append_to_jsonlr      s    	LLtd3	C'	*a	

66=> 
+HH

FJJt,<,V

6SU@VW 
+	*s   (B%%
B3taskc                    [         R                  R                  SSS9  / n[         R                  5       (       a4   [         R	                  SSS9 n[
        R                  " U5      nSSS5        U R                  SS5      n[        U5       VVs0 s H  u  pEUR                  SS5      U_M     nnnX6;   a&  [        R                  S	U R                  S
5      5        gUR                  U 5        [         R	                  SSS9 n[
        R                  " XSSS9  SSS5        [        R                  SU S   5        g! , (       d  f       N= f! [
        R                  [        4 a    / n Nf = fs  snnf ! , (       d  f       Na= f)z(Add or update a task in loop/tasks.json.Tr   r\   r]   r^   Nr   r`   u5   Task for %s already exists in tasks.json — skippingr   wr   F)indentr   z'Added evaluation task to tasks.json: %sr   )
TASKS_FILEr   r   re   rf   rg   loadrk   IOErrorri   	enumerater   r   appenddump)r   tasksrn   task_urlitexisting_idss          rX   upsert_tasks_jsonr      s4   D48E	w71		! 8 xx
B'H9B59IJ9IAEE*b)1,9ILJH$((S^J_`	LL	w	/1		%159 
0HH6T
C 87$$g. 	E	
 K 
0	/sA   E
 D9&E
 E, E29
EE
 E
 
E)(E)2
F dry_runc                 f   U R                  SS5      nU R                  SS5      n[        R                  SX#5        [        5       n[	        U5      nUR                  5       R                  5       nXT;   d  Xd;   a  [        R                  SU5        SX#S.$ [        U R                  S	S5      U R                  S
5      =(       d    / 5      n[        U 5      u  p[        R                  SX(Xy5        UUUUU	SSSS.n
[        R                  " [        R                  5      R                  5       UUUUU R                  SS5      U R                  S	5      =(       d    SSS U R                  SS5      SS.	nU[        :  aS  [        XXy5      nUS   U
S'   SUS'   SU
S'   U(       d  [         S-  n[#        X5        O[        R                  SUS   5        U	R                  SS5      S:  nU[$        :  ax  U(       aq  ['        XU5      nUS   U
S'   SUS'   SU
S'   U(       d3  [)        U5        U
S   (       a  [        R                  S US   U
S   5        O[        R                  S!US   5        U(       d  U
S   US'   [#        [*        U5        O[        R                  S"U5        [        R                  S#X*S   X5        U
$ )$aV  
Process a single discovery item from n8n or the cron scout.

Args:
    item: Dict with keys: name, url, description, topics, stars,
          license, pushed_at, source, open_source (optional)
    dry_run: If True, print actions without writing any files

Returns:
    Result dict with: score, tool_type, action_taken, kg_entity_id, task_id
ra   r   rN   r`   u   Processing: %s — %szSKIP (already known): %sskipped_duplicate)action_takenra   rN   rr   rs   z)Scored %s: %d/10 | type=%s | breakdown=%slogged_onlyN)ra   rN   r   r~   r   r   kg_entity_idr   r   r      r   r   r   )		timestampra   rN   r   r~   r   rr   r   actionr   r   kg_entity_writtenr  r   z#memory_competitors_discovered.jsonlz#[DRY RUN] Would write KG entity: %sr   r   evaluation_task_createdz)Evaluation task %s linked to KG entity %sz*[DRY RUN] Would create evaluation task: %sz[DRY RUN] Would log: %sz)Result for %s: action=%s score=%d type=%s)ri   r   r   rq   rY   rT   rU   r   r   r   r   r   r   r   SCORE_KG_THRESHOLDr   KG_ENTITIES_DIRr   SCORE_EVAL_THRESHOLDr   r   rd   )r   r   ra   rN   	known_idsitem_iditem_name_lowerr~   r   r   result	log_entry	kg_entitykg_output_fileis_open_sourcer   s                   rX   process_discoveryr    s    88FI&D
((5"
CHH$d0  InGjjl((*O;+T2 3TNN ## bI 't,EHH3Y %	F \\(,,/99;((8Y//52t<'1%
I ""#DF	!*4~1	(!4~,/TTNN6HH:IdOL ]]=!4q8N$$$T)< Jy7	(!:~d#n%Dd4jRXYgRhiHHA4:N$^4	(y1*I6HH3^$e MrZ   c                     [        5       n[        U 5      nX!;   nU U[        R                  " [        R
                  5      R                  5       S.$ )zc
Check if a URL is already in the Genesis KG.
Returns JSON-serialisable dict for webhook response.
)rN   rl   
checked_at)rq   rY   r   r   r   r   r   )rN   r  r  is_knowns       rX   check_if_knownr    sF    
  InG#Hll8<<0::< rZ   portc                   ^^  SSK JnJmJm  W" S5      nUR                  SS/S9UU4S	 j5       nUR                  S
S/S9UU4S j5       nUR                  SS/S9U4S j5       n[
        R                  SU 5        UR                  SU SS9  g! [         a.    [
        R                  S5        [        R                  " S5         Nf = f)z9Start a minimal Flask webhook server for n8n integration.r   )Flaskjsonifyrequestz4Flask not installed. Install with: pip install flaskrv   r   z/memory-scout-checkPOST)methodsc                     > TR                  SS9=(       d    0 n U R                  SS5      nU(       d  T" SS05      S4$ T" [        U5      5      $ )NTforcerN   r`   errorurl required  )get_jsonri   r  )datarN   r  r  s     rX   check_endpoint"run_server.<locals>.check_endpoint  sS    d+1rhhub!G^45s::~c*++rZ   z/memory-scout-ingestc                     > TR                  SS9=(       d    0 n U R                  S5      (       d  T" SS05      S4$ [        U 5      nT" U5      $ )NTr  rN   r  r  r   )r!  ri   r  )r"  r
  r  r  s     rX   ingest_endpoint#run_server.<locals>.ingest_endpoint  sQ    d+1rxxG^45s::"4(vrZ   z/healthGETc                     > T " SSS.5      $ )Nokr
   )r   servicer   )r  s   rX   healthrun_server.<locals>.health  s    $3LMNNrZ   z/Starting memory scout webhook server on port %dz0.0.0.0F)hostr  debugN)flaskr  r  r  ImportErrorr   r  sysexitrouter   run)r  r  appr#  r&  r,  r  r  s         @@rX   
run_serverr7    s    11
 
CYY$vhY7, 8, 	YY%xY8 9 	YYy5'Y*O +O HH>EGGUG37  		HIs   
B 5CCc            
         [         R                  " SS9n U R                  SS9nUR                  SSS9nUR	                  S[
        S	S
9  UR                  SSS9nUR	                  SSSS9  UR	                  SSSS9  UR	                  SSSS9  UR	                  S[
        SS
9  UR	                  SSS9  UR                  SSS9nUR	                  SSS9  U R                  5       nUR                  S:X  a  [        UR                  5        g UR                  S:X  a  UR                  UR                  UR                  UR                  S/ S [        R                  " [         R"                  5      R%                  5       S!.n['        XeR(                  S"9n[+        [,        R.                  " US#S$95        g UR                  S:X  a4  [1        UR                  5      n[+        [,        R.                  " US#S$95        g U R3                  5         g )%Nu:   Memory Competitor Intelligence Scout — webhook processor)rr   command)destservezStart webhook server)helpz--port="  )r   defaulttestzTest process a single discoveryz--urlTzRepository/article URL)requiredr<  z--namez	Tool namez--descriptionr`   zDescription text)r>  r<  z--starsr   z	--dry-run
store_true)r  checkzCheck if a URL is already known)r@  manual_testMIT)ra   rN   rr   r   r   rs   r   r   )r   r   )r   )argparseArgumentParseradd_subparsers
add_parseradd_argumentint
parse_argsr9  r7  r  ra   rN   rr   r   r   r   r   r   r   r  r   printrg   r   r  
print_help)parser
subparsersserve_parsertest_parsercheck_parserargsr   r
  s           rX   mainrT    s   $$PF &&I&6J ((7M(NLhS$? ''5V'WKWt:RSX;G_b?QRYS!<[> ((7X(YLg5D||w499		II88++ZZ#!hll3==?	
 #4>djj*+		 )djj*+rZ   __main__)F)r=  )4__doc__rE  rR   rg   loggingosr2  r   r   pathlibr   requestsBASE_DIRr  r   	SCOUT_DIRrd   rc   basicConfigINFOStreamHandlerstdout	getLoggerr   r   r  r  r   r   r   r   rz   strrY   rb   rq   listr   dicttuplerJ  r   r   r   r   r   boolr  r  r7  rT  __name__r   rZ   rX   <module>rh     sM        	 
 '   #$..;-
u88	11!==    
,,4cjj) '     Xki U  FohjWF3 F3 F
 2C  # 8$ 85d#3 8v$ s s t PT ,! !S !S !T !HX4 X X$ XDD DT D8eD e4 eD eX  &4S 4D 4L+\ zF rZ   