
    ilP                        d Z ddlmZmZmZmZmZ ddlmZ ddl	m
Z
 ddlmZmZ ddlmZ ddlmZ ddlmZ dd	lZdd	lZdd
lmZmZ ddlmZmZ dd	lZddlmZmZmZm Z m!Z!m"Z"m#Z#m$Z$m%Z%m&Z&m'Z'm(Z(m)Z)m*Z* ddl+m,Z, ddl-m.Z. ddl/m/Z/ ddl0m0Z0 dd	l1Z1e1jd                  jg                  d       ddl4m5Z5 edefd       Z6 eddde6      Z7 ee      Z8e8e7jr                  _8        e7ju                  ee        ejv                  dd      Z<e<j{                  d      D  cg c]  } | j}                          c} Z?de?v rdgZ?e7j                  ee?dgk7  re?ndge?dgk7  rdnddgdg         e5j                         ZB ej                  dYi eBZD ed!      fd"eEd#eFfd$ZG ed	      fd%eEd"eeE   d#eFfd&ZH ed!      fd'eEd#eIfd(ZJe7j                  d)eF*      d+        ZLe7j                  d,e(*      d-        ZMe7j                  d.e*      e8j                  d/       eeG      fd0ed1ed2eFfd3              ZPe7j                  d4e *      e8j                  d/       ed	      fd5ed0ed"eeE   fd6              ZQe7j                  d7e!*       eeG      fd%eEd2eFfd8       ZRe7j                  d9      e8j                  d:       eeG      fd0ed;e"d2eFfd<              ZSe7j                  d=e$*      d>d?d	 eeG       eeJ      fd%eEd@eTdAeTdBeeE   d2eFdCeIfdD       ZUe7j                  dEe%*      dF eeG       eeJ      fd%eEdGeEd2eFdCeIfdH       ZVe7j                  dIeF*       eeJ      fdJe&dCeIfdK       ZWe7j                  e      d0edLefdM       ZYe7j                  eZ      d0edLeZfdN       Z[e\dOk(  rUdd	l]Z] e]j                  dPdQ eT ejv                  dRdS             ejv                  dTdU      j                         dVk(  dWX       y	y	c c} w )Zzh
Genesis Talking Website Widget - FastAPI Server
Production-ready API for voice-enabled website widgets
    )FastAPIHTTPExceptionDependsHeaderRequest)CORSMiddleware)JSONResponse)Limiter_rate_limit_exceeded_handler)get_remote_address)RateLimitExceeded)asynccontextmanagerN)datetime	timedelta)OptionalList)ConversationRequestConversationResponseTextConversationRequestTextConversationResponseWidgetConfigLeadCaptureRequestLeadLeadListResponseAnalyticsResponseBusinessCreateRequestBusinessHealthResponseErrorResponse
LeadStatus)db)memory)voice_handler)tenant_managerz)/mnt/e/genesis-system/data/genesis-memory)RedisConfigappc                  K   t        d       t        d       t        j                          t        d       d}t        j                         st        d       d}nt        d       t	        j                         st        d       nt        d	       t        j                         st        d
       nt        d       |st        d      t        d       d t        d       yw)zStartup and shutdown events.u#   🚀 Genesis Widget API starting...u$   📊 Initializing database schema...u#   🔌 Testing service connections...Tu    ❌ PostgreSQL connection failedFu   ✅ PostgreSQL connectedu/   ⚠️  Qdrant connection failed (non-critical)u   ✅ Qdrant connectedu/   ⚠️  Telnyx connection failed (non-critical)u   ✅ Telnyx connectedzCritical services unavailableu   ✅ Genesis Widget API readyNu(   🛑 Genesis Widget API shutting down...)printr!   init_schematest_connectionr"   r#   RuntimeError)r&   services_oks     0/mnt/e/genesis-system/RECEPTIONISTAI/api/main.pylifespanr.   (   s      

/0 

01NN 

/0K01()!!#?@$%((*?@$%:;;	
()	 

45s   C$C&"Genesis Talking Website Widget APIz2Voice-enabled AI chat widget for business websites1.0.0)titledescriptionversionr.   )key_funcALLOWED_ORIGINSz+http://localhost:8888,http://127.0.0.1:8888,*TF)allow_originsallow_credentialsallow_methodsallow_headers.	x_api_keyreturnc                 V   K   t        j                  |       }|st        dd      |S w)z2Verify widget API key and return business context.  zInvalid API keystatus_codedetail)r$   get_business_by_api_keyr   )r<   businesss     r-   verify_api_keyrE   v   s,     55i@H4EFFOs   ')business_idc                     |rt        j                  |      }|r|S | rt        j                  |       }|r|S t        dd      )z
    Get business context from API key (header) or business_id (from request).
    For local testing, allows business_id without API key.
    r?   zInvalid API key or business IDr@   )r$   rC   get_business_by_idr   )rF   r<   rD   s      r-   get_business_from_id_or_keyrI   ~   sJ     !99)DO !44[AO
C0P
QQ    authorizationc                 b   K   t        j                  dd      }| d| k7  rt        dd      yw)z4Verify admin JWT token (placeholder for production).ADMIN_TOKENgenesis_admin_secret_change_mezBearer   Unauthorizedr@   T)osgetenvr   )rK   expected_tokens     r-   verify_admin_tokenrT      s9      YY}.NON'.!122NCCs   -//)response_modelc                     K   dddddS w)zAPI root endpoint.r/   r0   operationalz/docs)servicer3   statusdocs r\   rJ   r-   rootr]      s      8	 s   
z
/v1/healthc                    K   i } t        j                         rdnd| d<   	 t        j                          d| d<   t	        j                         rdnd| d<   t        j                         rdnd| d<   t        d | j                         D              rdnd}t        |d	| t        j                         
      S #  d| d<   Y xY ww)zHealth check endpoint.healthy	unhealthy
postgresqlredisdegradedqdranttelnyxc              3   $   K   | ]  }|d v  
 yw))r_   rc   Nr\   ).0ss     r-   	<genexpr>zhealth_check.<locals>.<genexpr>   s      &)*$$&s   r0   )rZ   r3   services	timestamp)r!   r*   redis_clientpingr"   r#   allvaluesr   r   now)rj   overall_statuss     r-   health_checkrr      s      H +-*<*<*>YKH\(%
 '-&<&<&>JHX '4&C&C&E:HX"% &.6oo.?& #Y  ,,.	 ('s   CB5 A;C5B><Cz/v1/conversationz	60/minuterequestconversation_reqrD   c                 N  K   	 d|j                    }t        j                  |      }|rddl}|j	                  |      }n|j
                  xs i }t        j                  |j                         d{   }|d   |j                  dd      |j                  d      |j                  di       j                  d	i       d
}t        j                  |||       d{   \  }	}
t        j                  |	|d          d{   }dt        j                  d       }t        j                         5 }|j                  d||d   |j                   d| d|	 |
f       ddd       t        j                   ||
       d{   }d}|rdt        j                  d       }t        j                         5 }|j                  d||d   |j                   ||j                  d      |j                  d      |j                  d      f       ddd       d}d|
d<   ddl}t        j#                  |d|j%                  |
             t'        |||	|
|      S 7 7 v7 U# 1 sw Y   xY w7 # 1 sw Y   axY w# t(        $ r)}t+        d|        t-        dt/        |            d}~ww xY ww)z
    Process voice conversation from widget.

    Flow:
    1. Receive audio from visitor
    2. Transcribe to text (STT)
    3. Generate AI response based on business context
    4. Convert response to speech (TTS)
    5. Return audio + transcript
    session:r   Nname
agent_nameSarahwebsite_urlconfigknowledge_base)rw   rx   rz   r|   idconv_   z
                INSERT INTO widget_conversations
                (id, business_id, visitor_id, mode, transcript, session_context)
                VALUES (%s, %s, %s, 'voice', %s, %s)
            	Visitor: 
AI: Flead_
                    INSERT INTO widget_leads
                    (id, business_id, visitor_id, conversation_id, name, phone, email, status)
                    VALUES (%s, %s, %s, %s, %s, %s, %s, 'new')
                phoneemailTlead_captured  )conversation_idaudio_response
transcriptsession_contextr   zConversation error:   r@   )
visitor_idrl   getjsonloadsr   r#   process_voice_input
audio_datagenerate_ai_responsegenerate_voice_responsesecrets	token_hexr!   
get_cursorexecutedetect_lead_infosetexdumpsr   	Exceptionr(   r   str)rs   rt   rD   session_keysession_datar   r   visitor_messagebusiness_contextai_response_textupdated_sessionr   r   cursor	lead_infor   lead_ides                     r-   process_voice_conversationr      s    "[< !1!<!< =>#''4"jj6O.>>D"O !. A A''!
 
 V$",,|W=#<<6&ll8R8<<=MrR	
 3@2T2T3
 -
)/  -DDTN 
 
 "'"3"3A"6!78 ]]_ 	NN 
   ++O,F3C2DE	
	 (88/ZZ	g//234G F  
 TN$//#MM&)MM'*MM'*	 !M/3OO, 	;djj.IJ#+)'+'
 	
K
-

	 	 [
 :  <$QC()CF;;<s   J%A.I0 2I3A'I0 I#I0 >I?0I0 /+I!I0 ;I"<4I0 0AI$AI0 J%I0 I0 I0 II0 $I-)I0 0	J"9$JJ""J%z/v1/conversation/texttext_reqc                   K   	 t        | j                  |      }d| j                   }t        j	                  |      }|rddl}|j                  |      }n| j                  xs i }|d   |j	                  dd      |j	                  di       j	                  di       d	}t        j                  | j                  ||       d{   \  }	}
d
t        j                  d       }ddl}t        j                         5 }|j                  d||d   | j                  d| j                   d|	 |j!                  |
      f       ddd       t        j"                  | j                  |
       d{   }d}|rdt        j                  d       }t        j                         5 }|j                  d||d   | j                  ||j	                  d      |j	                  d      |j	                  d      f       ddd       d}ddl}t        j%                  |d|j!                  |
             t'        ||	|
|      S 7 s# 1 sw Y   xY w7 # 1 sw Y   UxY w# t(        $ r)}t+        d|        t-        dt/        |            d}~ww xY ww)zI
    Process text-only conversation (fallback for no-mic scenarios).
    rv   r   Nrw   rx   ry   r{   r|   )rw   rx   r|   r~   r   z
                INSERT INTO widget_conversations
                (id, business_id, visitor_id, mode, transcript, session_context)
                VALUES (%s, %s, %s, 'text', %s, %s)
            r}   r   r   Fr   r   r   r   Tr   )r   messager   r   zText conversation error: r   r@   )rI   rF   r   rl   r   r   r   r   r#   r   r   r   r   r!   r   r   r   r   r   r   r   r(   r   r   )r   rs   r<   rD   r   r   r   r   r   ai_responser   r   json_moduler   r   r   r   r   s                     r-   process_text_conversationr   <  s    Q<.x/C/CYO !!4!4 56#''4"jj6O&66<"O V$",,|W=&ll8R8<<=MrR
 .;-O-O.
 (
$_ "'"3"3A"6!78 	#]]_ 	NN 
  ##H,,-VK=A!!/2	
	 (889I9I?[[	g//234G F  
 TN''#MM&)MM'*MM'*	 !M 	;djj.IJ'++'	
 	
e(
	 	 \
 6  <)!-.CF;;<sz   JB?I H67I ;AH9?+I *I+4I AI3AI 5J6I 9I>I II 	J$J  JJz/v1/widget/config/{business_id}c                   K   |d   | k7  rt        dd      t        |d   |d   |j                  dd      |j                  dd	      |j                  d
d      |j                  dd      |j                  d            S w)z'Get widget configuration for embedding.r}   rO   Business ID mismatchr@   rw   rx   ry   greeting_messagezHi! How can I help?primary_colorz#6366F1positionzbottom-right
avatar_url)rF   business_namerx   r   r   r   r   )r   r   r   )rF   rD   s     r-   get_widget_configr     s      ~$4JKKTNv&<<g6!&8:OPll?I>j.9<<- s   A=A?z	/v1/leadsz	30/minutelead_reqc                   K   	 dt        j                  d       }t        j                         5 }|j	                  d||d   |j
                  |j                  |j                  |j                  |j                  |j                  f       t        |j                               }ddd       d   ddS # 1 sw Y   xY w# t        $ r)}t        d|        t        d	t!        |      
      d}~ww xY ww)z"Manually capture lead information.r   r   z
                INSERT INTO widget_leads
                (id, business_id, visitor_id, conversation_id, name, phone, email, notes, status)
                VALUES (%s, %s, %s, %s, %s, %s, %s, %s, 'new')
                RETURNING *
            r}   Ncaptured)r   rZ   zLead capture error: r   r@   )r   r   r!   r   r   r   r   rw   r   r   notesdictfetchoner   r(   r   r   )rs   r   rD   r   r   leadr   s          r-   capture_leadr     s     <'++A./0]]_ 	+NN  ##((	  )*D#	+&  :<<'	+ 	+*  <$QC()CF;;<s@   C4,B? A3B3#B? 2C43B<8B? ?	C1$C,,C11C4z/v1/leads/{business_id}   2   page	page_sizerZ   _adminc                   K   |d   | k7  rt        dd      |dz
  |z  }t        j                         5 }|r|j                  d| |f       n|j                  d| f       |j	                         d   }|r|j                  d	| |||f       n|j                  d
| ||f       |j                         D 	cg c]  }	t        di t        |	       }
}	ddd       t        
||      S c c}	w # 1 sw Y   xY ww)z'List leads for a business (admin only).r}   rO   r   r@   r   z
                SELECT COUNT(*) as count FROM widget_leads
                WHERE business_id = %s AND status = %s
            zo
                SELECT COUNT(*) as count FROM widget_leads
                WHERE business_id = %s
            countz
                SELECT * FROM widget_leads
                WHERE business_id = %s AND status = %s
                ORDER BY created_at DESC
                LIMIT %s OFFSET %s
            z
                SELECT * FROM widget_leads
                WHERE business_id = %s
                ORDER BY created_at DESC
                LIMIT %s OFFSET %s
            N)leadstotalr   r   r\   )	r   r!   r   r   r   fetchallr   r   r   )rF   r   r   rZ   rD   r   offsetr   r   rowr   s              r-   
list_leadsr     s+     ~$4JKKQh)#F	 AFNN  v&(
 NN  !
 !'* NN 
 vy&9; NN 
 y&13 /5oo.?@s"S	"@@?AB 	  A?A As/   2C7A>C+2C&C+C7&C++C40C7z/v1/analytics/{business_id}last_30_daysperiodc                   K   |d   | k7  rt        dd      |dk(  r"t        j                         t        d      z
  }nH|dk(  r"t        j                         t        d	      z
  }n!t        j                         t        d	      z
  }t	        j
                         5 }|j                  d
| |f       t        |j                               }|j                  d| |f       |j                         d   }g }ddd       d   dkD  r|d   z  dz  nd}	t        | ||d   xs dt        |	d      |d   xs d|d   xs d|d   xs d	      S # 1 sw Y   XxY ww)z*Get conversation analytics for a business.r}   rO   r   r@   last_7_days   )daysr      a[  
            SELECT
                COUNT(*) as total,
                COUNT(CASE WHEN mode = 'voice' THEN 1 END) as voice,
                COUNT(CASE WHEN mode = 'text' THEN 1 END) as text,
                AVG(duration_seconds) as avg_duration
            FROM widget_conversations
            WHERE business_id = %s AND created_at >= %s
        zx
            SELECT COUNT(*) as count FROM widget_leads
            WHERE business_id = %s AND created_at >= %s
        r   Nr   r   d      avg_durationvoicetext)	rF   r   total_conversationstotal_leadsconversion_rateavg_conversation_durationvoice_conversationstext_conversations	top_pages)r   r   rp   r   r!   r   r   r   r   r   round)
rF   r   rD   r   
start_dater   statsleads_countr   r   s
             r-   get_analyticsr     so     ~$4JKK \\^iQ&77
	>	!\\^iR&88
\\^iR&88
	 F  :&	( V__&' 	  :&	(
 oo'0 	14 ?DGnq>P{U7^3c9VWO!'N/aoq1"'"7"<1!'N/a =-A
 
9 s    BEAE	2AE	EEz/v1/businessesbusiness_reqc                 <  K   	 t        j                  | j                  | j                  | j                  | j
                  | j                  | j                        }|d   |d   ddS # t        $ r)}t        d|        t        dt        |            d	}~ww xY ww)
z+Create a new business account (admin only).)rw   rz   r   rx   r   r|   r}   api_keyzBusiness created successfully)rF   r   r   zBusiness creation error: r   r@   N)r$   create_businessrw   rz   r   rx   r   r|   r   r(   r   r   )r   r   rD   r   s       r-   r   r   G  s     <!11""$00)::#..&44'66
 $D>	*6
 	
  <)!-.CF;;<s)   BA"A' &B'	B0$BBBexcc           	         K   t        |j                  t        |j                  | j                  j                  d            j                               S w)zCustom HTTP exception handler.X-Request-ID)error
request_idrA   content)r	   rA   r   rB   headersr   r   rs   r   s     r-   http_exception_handlerr   d  sF      OO****>:
 $& s   AAc           
         K   t        dt        dt        j                  d      rt	        |      nd| j
                  j                  d            j                               S w)zCatch-all exception handler.r   zInternal server errorDEBUGNr   )r   rB   r   r   )r	   r   rQ   rR   r   r   r   r   r   s     r-   general_exception_handlerr   p  sR      )!yy13s8t**>:
 $& s   A!A#__main__zmain:appz0.0.0.0PORTi@  r   falsetrueinfo)hostportreload	log_levelr\   )`__doc__fastapir   r   r   r   r   fastapi.middleware.corsr   fastapi.responsesr	   slowapir
   r   slowapi.utilr   slowapi.errorsr   
contextlibr   rQ   r   r   r   typingr   r   rb   modelsr   r   r   r   r   r   r   r   r   r   r   r   r   r    databaser!   memory_handlerr"   r#   r$   syspathappendelestio_configr%   r.   r&   limiterstateadd_exception_handlerrR   allowed_origins_envsplitstripallowed_originsadd_middlewareget_connection_paramsredis_configRedisrl   r   r   rE   rI   boolrT   r   r]   rr   postlimitr   r   r   r   intr   r   r   exception_handlerr   r   r   __name__uvicornrunlower)origins   0r-   <module>r#     sL  
 E D 2 * 9 + , * 	  ( !      ! ' )  ; < &
 %6 %6 %6T 
.D	 -
.		    +-I J  bii 13`a 0C0I0I#0NOf6<<>O /eO   %4%=/C5-#6dE%%   1{002u{{*\*
 +1+ C $   &d|RR}R 
R. 39+ C $  T" # n5 6@ 
-AB{ ^,j<j<)j< j<  Cj<Z 
!2JK{  &d|Y<%Y<Y< }Y<  LY<x 	*<H ^, I& +{ ^, < <  <  <   <F 	"3CD  ^,-.44
4 4 SM	4
 4 4 E4n 	&7HI !^,-.	888 8 	8 J8v 
40 -.<'<< 1<8 }%'   & y!	W 	9 	 "	 zGKK64()ryy'*002f< } Ps   O.