
    zݢip                        d dl Z d dlZd dlZd dlZd dlmZ d dlmZmZm	Z	m
Z
  ej                  e      Zdededz  fdZdedz  fdZdedz  fdZdedz  fd	Zdedz  fd
Zdedz  fdZdedz  fdZ e       Z e       Zer
 eee      ndZdedefdZ e ed       e	ddd       e
e      d      ddedededefd       Z e ed       e	ddd       e
e      d      ddedededefd       ZddedefdZ  e ed       e	ddd       e
e      d      dd ed!ed"ed#ed$ed%ed&ede!fd'       Z" e ed       e	ddd       e
e      d      dded(ede#fd)       Z$d*d+d,d-d.d/d0d1d2d,d-d.d/d0d3d4d,d-d.d/d0d5d6d7d-d.d/d0d8d9d:d-d;d<d0d=d>d?d@d;dAd0dBdCdDd-d.d/d0dEdFd7d-d.d/d0dGdHd7d-d.d/d0dIdJd7d@d.dKd0dLdMd7d@dNdOd0dPZ%dQdRdSdRdTgd;dUdVd@dWdXg dYdZd[d\d]d^g dYdZd_d-d:gd`dadbdcdddeg dfdgdhdid@djdkg dldZdmd-dndog dldZdpdbdqdrdrdsgd;dtdud@dvdwd7dDgdZdxd\dydyd7dDgdZdzd{d|Z&i Z'd}ede!fd~Z(d}ededede!fdZ)d}efdZ*de!fdZ+i Z,dNdiZ-defdZ.ddefdZ/de!fdZ0ddededefdZ1ddededededDef
dZ2ddededededDef
dZ3ddedededededDefdZ4 e ed       e	ddd       e
e      d      dd}ededededef
d       Z5 e ed       e	ddd       e
e      d      dd}ededededef
d       Z6dd}ededefdZ7 e ed       e	ddd       e
e      d      dd}eded(ede#fd       Z8de!fdZ9y)    N)OpenAI)retrystop_after_attemptwait_exponentialretry_if_exceptionkeysreturnc                  \    | D ]'  }t         j                  j                  |      }|s%|c S  y)zATry multiple env var names in priority order, return first found.N)osenvironget)r   keyvals      ./sites/domain-asset-master/app/services/llm.py_resolve_envr      s0     jjnnS!J     c                      t        dd      S )NAI_INTEGRATIONS_OPENAI_API_KEYOPENAI_API_KEYr    r   r   _resolve_openai_keyr      s    8:JKKr   c                      t        d      S )NAI_INTEGRATIONS_OPENAI_BASE_URLr   r   r   r   _resolve_openai_baser          9::r   c                      t        ddd      S )NAI_INTEGRATIONS_GEMINI_API_KEYGEMINI_API_KEYGOOGLE_API_KEYr   r   r   r   _resolve_gemini_keyr!      s    8:JL\]]r   c                      t        d      S )NAI_INTEGRATIONS_GEMINI_BASE_URLr   r   r   r   _resolve_gemini_baser$   !   r   r   c                      t        dd      S )NPERPLEXITY_APIPERPLEXITY_API_KEYr   r   r   r   _resolve_perplexity_keyr(   $   s    (*>??r   c                      t        dd      S )NHUGGINGFACE_API_KEY
HF_API_KEYr   r   r   r   _resolve_huggingface_keyr,   '   s    -|<<r   api_keybase_url	exceptionc                     t        |       }d|v xsK d|v xsE d|j                         v xs1 d|j                         v xs t        | d      xr | j                  dk(  S )N429RATELIMIT_EXCEEDEDquotaz
rate limitstatus_codei  )strlowerhasattrr5   )r0   	error_msgs     r   is_rate_limit_errorr:   4   st    II 	P9,	Pioo''	P 9??,,	P I}-N)2G2G32Nr            <   )
multiplierminmaxT)stopwaitr   reraisepromptsystem_prompt
max_tokensc                    t         syg }|r|j                  d|d       |j                  d| d       t         j                  j                  j	                  d||ddi      }|j
                  d	   j                  j                  xs dS )
N systemrolecontentusergpt-4otypejson_objectmodelmessagesmax_completion_tokensresponse_formatr   clientappendchatcompletionscreatechoicesmessagerM   rE   rF   rG   rT   responses        r   call_llmra   C   s     HmDEOOV78{{&&--(/	 . H A&&..4"4r   c                    t         syg }|r|j                  d|d       |j                  d| d       t         j                  j                  j	                  d||      }|j
                  d   j                  j                  xs dS )NrI   rJ   rK   rN   rO   rS   rT   rU   r   rW   r_   s        r   call_llm_textrd   Z   s     HmDEOOV78{{&&--( . H
 A&&..4"4r   rT   c                 j    t         sy t         j                  j                  j                  d| |d      S )NrO   T)rS   rT   rU   stream)rX   rZ   r[   r\   )rT   rG   s     r   call_llm_streamrg   p   s6    ;;""))(	 *  r         img_b64	mime_typedomainnicheclassificationsfilenamefilename_hintc                 |   t         si S dj                  |      }d}|r	|d| dz  }|r	|d| dz  }ddd	d
dd| d|  ddddd| d| d| d| d	dgd	g}	t        d      D ]  }
t         j                  j                  j                  d|	dddi      }|j                  d   j                  j                  xs d}	 t        j                  |      }|j                  d       r|j                  d       d!k7  r|c S |j                  d       d!k(  r|j                  d"      r|c S |
dk(  st        j                  d#|d d$          j                  d       sd!|d <   |j                  d%      sg |d%<   |j                  d"      sd&|d"<   |j                  d'      sg |d'<   |S # t        j                  $ r i }Y w xY w)(Nz, rI   z
The original filename is: "z".z0
Based on the filename, this image is likely a "zK". Give strong weight to this hint unless the image clearly contradicts it.rJ   u#  You are an image analyst for website design. Classify uploaded images and suggest where they should be used on a website. Pay close attention to both the visual content AND the filename when provided — filenames often contain reliable human-assigned labels. Always respond with valid JSON.rK   rN   	image_urlzdata:z;base64,auto)urldetail)rP   rr   textz(Analyze this image for a website about "z" (domain: z).z,

Classify it into ONE of these categories: a2  

Return JSON:
{
  "classification": "one_of_the_categories_above",
  "tags": ["tag1", "tag2", "tag3"],
  "description": "Brief description of what the image shows",
  "suggested_sections": ["hero", "about", "team", "gallery", "testimonials", "features"],
  "quality_score": 1-10,
  "is_logo": true/false
})rP   rv   r=   rO   i  rP   rQ   rR   r   z{}classificationotherdescriptionzIImage classification returned incomplete response (attempt 1), retrying:    tagsz!Image could not be fully analyzedsuggested_sections)rX   joinrangerZ   r[   r\   r]   r^   rM   jsonloadsJSONDecodeErrorr   loggerwarning)rj   rk   rl   rm   rn   ro   rp   
class_listfilename_contextrT   attemptr`   rM   parseds                 r   call_llm_with_imager   {   s?    	?+J;H:RHHOP]  _j  k  	k   'L  	M ykRYQZ7[gm/no)QRWQXXcdjckkmn~m @++5, 
7& %
 	H( 8 ;;**11"%#]3	 2 
 ""1%--55=	ZZ(F ::&'FJJ7G,HG,SM::&'72vzz-7PMa<NNfgnospsgtfuvw'* ::&'#* ::fv::m$ C}::*+')#$M' ## 	F	s   ?F##F;:F;sizec                     t         st        d      t         j                  j                  d| |      }|j                  d   j
                  xs d}t        j                  |      S )Nz1OpenAI client not configured for image generationdall-e-3rS   rE   r   r   rI   )rX   RuntimeErrorimagesgeneratedatab64_jsonbase64	b64decode)rE   r   r`   image_base64s       r   generate_imager      sa     NOO}}%% & H
 ==#,,2LL))r   zDomain Analysisz/Decompose domain keywords, generate niche ideasr   	legendarygeminigemini-2.0-pro-exp-02-05)namery   modequality_tierdefault_providerdefault_modelzBrand Identityz:Generate brand names, taglines, color palettes, typographyzSite Copy Generationz.Generate 16 website sections with full contentzSales LetterzPersuasive long-form sales copyrv   zHero Image GenerationzAI-generated hero/banner imagesimageopenair   zBrand Kit Classificationz*Classify uploaded images by type and usagevisionpremiumrO   zAI Advisor Chatz,Context-aware business strategy conversationrf   zForce Multiplier Docsz2Generate 33 business document types across 6 tierszContent Refinementz#Refine and improve existing contentzGraphics Pack Promptsz(Generate image prompts for graphics packgemini-2.0-flashzMarket Researchz2Live marketplace intelligence via Perplexity Sonar
perplexity	sonar-pro)domain_analysisbrand_identity	site_copysales_letter
hero_imagebrand_kit_classifyadvisor_chatforce_multipliercontent_refinegraphics_promptmarket_researchr   r   r   r   z#10B981zGPT-4og      @g      $@)r   rv   rf   r   )r   tier
input_costoutput_costmodeszGPT-4o Minieconomyg333333?g333333?zDALL-E 3)r   r   r   )rO   zgpt-4o-minir   )r   env_keyenv_baseportable_keyssdkcolormodelszGoogle Geminir   r#   )r   r   r    google_genaiz#3B82F6zGemini 2.0 Flashg?g?)r   rv   rf   zGemini 2.0 Prog      ?g      @)r   r   
Perplexityr&   r'   z#22D3EEz	Sonar Prog      @g      .@Sonarg      ?)r   sonar)r   r   r   r   r   r   )r   r   r   stagec                    | t         vrt        d|        t         |    }| t        v rt        |    }|d   }|d   }d}n|d   }|d   }d}t        j	                  |i       }|j	                  di       j	                  |i       }| |d	   |d
   ||j	                  d	|      ||j	                  d	|      |j	                  dd      |d   |d
S )z4Get the current provider+model for a pipeline stage.Unknown pipeline stage: providerrS   Tr   r   Fr   r   r   r   unknownr   )
r   
stage_namer   r   provider_namerS   
model_namer   default_quality_tieris_override)PIPELINE_STAGES
ValueError_routing_overrides	PROVIDERSr   )r   stage_configoverrideprovider_idmodel_idr   provider_info
model_infos           r   	get_router     s    O#3E7;<<"5)L""%e,z*G$"#560MM+r2M""8R044XrBJ "6*V$&**6;? nnVX6"vy9 ,^ <" r   r   rS   c           
         | t         vrt        d|        |t        vrt        d|       |t        |   d   vrt        d| d| d      t         |    d   }t        |   d   |   j                  dg       }||vrt        d	| d
| d|  d|       ||dt        | <   t
        j                  d|  d| d|        t        |       S )z*Override the routing for a pipeline stage.r   Unknown provider: r   zUnknown model 'z' for provider ''r   r   zModel 'z' does not support mode 'z' required by stage 'z'. Supported modes: )r   rS   zRoute override set:  -> /)r   r   r   r   r   r   infor   )r   r   rS   
stage_modemodel_modess        r   	set_router     s   O#3E7;<<y -hZ899Ih'11?5'1A(1MNN '/JH%h/6::7BGK$eW5j\ B""'(<[MK
 	

 .6 Fu
KK&ugT(1UGDEUr   c                     | t         vrt        d|        t        j                  | d      }|rt        j                  d|  d       t        |       S )z8Reset a stage back to its default (quality-first) route.r   NzRoute override cleared: z (back to default))r   r   r   popr   r   r   )r   removeds     r   reset_router     sT    O#3E7;<< $$UD1G.ug5GHIUr   c                  H    t         D  ci c]  } | t        |        c} S c c} w )zDReturn all stages with their current routing (defaults + overrides).)r   r   )r   s    r   get_all_routesr     s     1@AE9U##AAAs   zhttps://api.perplexity.aic                 4   | t         v r	t         |    S | t        vrt        d|        t        |    }|j                  dd      }|j                  d|d   g      }t	        | }|s$dj                  |      }t        |d    d|       |d	k(  r>d
dlm} t	        |j                  dd            }i }|rd|d}|j                  ||      }	nEd}|j                  d      rt	        |d         }|st        j                  |       }t        ||      }	|	t         | <   |	S )z+Lazy-initialize and cache provider clients.r   r   r   r   r   z / r   z% API key not configured. Set one of: r   r   )genair   rI   )api_versionr/   )r.   http_optionsNr-   )_provider_clientsr   r   r   r   r}   r   googler   Client_OPENAI_COMPAT_BASE_URLSr   )
r   prov_cfgsdk_typer   r.   triedr   r/   	http_optscs
             r   _get_clientr     s2   $$ **y -hZ899"H||E8,HLL8I3F2GHMM*G

=)hv.//TUZT[\]]>! Z <=	(*AILLyLA <<
##HZ$89H/33H=H7X6"#hHr   c                 ^    | rt         j                  | d       yt         j                          y)zClear cached provider clients.N)r   r   clear)r   s    r   invalidate_provider_cacher     s!    h-!r   c                  V   i } t         j                         D ]  \  }}|j                  d|d   g      }t        | }t	        |      }d}|r*|D ]%  }t
        j                  j                  |      s#|} n |d   ||d   |||j                  dd      |d   t        |d         d	| |<    | S )
z5Check which providers have valid API keys configured.r   r   Nr   r   r   r   r   )r   
configuredr   
active_keyr   r   r   model_count)r   itemsr   r   boolr   r   len)statuspidpr   resolved_keyr   active_key_nameks           r   get_provider_statusr    s    F//# 
Qo)~>#]3,'
" ::>>!$&'O
 fI$|)*55)wZq{+	
s
( Mr   c                 ^    g }|r|j                  d|d       |j                  d| d       |S )z Build a standard messages array.rJ   rK   rN   )rY   )rE   rF   rT   s      r   _build_messagesr  .  s1    HmDEOOV78Or   	json_modec                    |||d}|rddi|d<   |r+d|d<    | j                   j                  j                  d	i |S  | j                   j                  j                  d	i |}|j                  d   j                  j
                  xs dS )
z-Dispatch a call to any OpenAI-compatible API.rc   rP   rQ   rV   Trf   r   rI   r   )rZ   r[   r\   r]   r^   rM   )client_instancerS   rT   rG   r  rf   kwargsr`   s           r   _call_openai_compatibler  7  s     !+F
 %+]$; !x6##//66@@@6##//66@@HA&&..4"4r   c                    ddl m} g }g }|D ]3  }	|	d   dk(  r|j                  |	d           |j                  |	d          5 |rdj                  |      nd}
d|i}|rd	|d
<   |rdj                  |      nd}|r||d<    |j                  di |}|r| j
                  j                  ||
|      S | j
                  j                  ||
|      }|j                  xs dS )z6Dispatch a call to Gemini via native google.genai SDK.r   typesrL   rJ   rM   z

rI   max_output_tokenszapplication/jsonresponse_mime_typeNsystem_instructionrS   contentsconfigr   )	google.genair  rY   r}   GenerateContentConfigr   generate_content_streamgenerate_contentrv   )r  rS   rT   rG   r  rf   genai_typessystem_parts
user_partsmsgr  config_kwargsr  r  r`   s                  r   _call_gemini_nativer  H  s   1LJ .v;("I/c)n-	. +5v{{:&"H(*5M.@*+6B\2.@*+.[..??F%%==(6 > 
 	
 %%66hv 7 H ==Br   c                     t         j                  | i       j                  dd      }|dk(  rt        ||||||      S t        ||||||      S )zUniversal dispatcher.r   r   r   )r  rf   )r   r   r  r  )r   r  rS   rT   rG   r  rf   r   s           r   _dispatch_callr  k  sU    }}Xr*..uh?H>!"?E8Z[dmstt&x_hqwxxr   c                     t        |       }|d   }|d   }t        ||      }t        j                  d|  d| d| d|d    d	       t	        |      }t        |||||d	
      S )zRoute a JSON-mode LLM call.r   rS   	[ROUTED] r   r    (tier: r   z, mode: json)Tr  r   r  r   r   r   r  	r   rE   rF   rG   router   rS   rT   r  s	            r   call_llm_routedr%  u  s{     eEZ H'NEv}5H
KK)E7$xj%~AV@WWdef!(+O(OUHj\`aar   c                     t        |       }|d   }|d   }t        ||      }t        j                  d|  d| d| d|d    d	       t	        |      }t        |||||d	
      S )zRoute a text-mode LLM call.r   rS   r  r   r   r   r   z, mode: text)Fr!  r"  r#  s	            r   call_llm_text_routedr'    s{     eEZ H'NEv}5H
KK)E7$xj%~AV@WWdef!(+O(OUHj\abbr   c                     t        |       }|d   }|d   }t        j                  d|  d| d| d|d    d	       t        |      }t	        |||||d	
      S )zRoute a streaming LLM call.r   rS   r  r   r   r   r   z, mode: stream)T)rf   )r   r   r   r   r  )r   rT   rG   r$  r   rS   r  s          r   call_llm_stream_routedr)    sm    eEZ H'NE
KK)E7$xj%~AV@WWfgh!(+O(OUHjY]^^r   c                 N   t        |       }|d   }|d   }t        j                  |i       j                  dd      }t        j	                  d|  d| d| d|d	    d
	       t        |      }|dk(  rddlm} |j                  j                  |||j                  ddg            }	|	j                  r|	j                  d   j                  j                  D ]\  }
t        |
d      s|
j                  s|
j                  j                   }t#        |t$              rt'        j(                  |      c S |c S  t+        d      |dk(  rP|j,                  j/                  |||      }	|	j                   d   j0                  xs d}t'        j(                  |      S t+        d| d| d      )zRoute an image generation call.r   rS   r   r   r  r   r   r   r   z, mode: image)r   r   r
  TEXTIMAGE)response_modalitiesr  inline_dataz.Gemini image generation returned no image datar   rI   z-Image generation not supported for provider: z (sdk: ))r   r   r   r   r   r   r  r  r   r  r  
candidatesrM   partsr8   r.  r   
isinstancer6   r   r   r   r   r   r   )r   rE   r   r$  r   rS   r   r  r  r`   partimg_datar   s                r   generate_image_routedr5    s    eEZ H'NE}}Xr*..uh?H
KK)E7$xj%~AV@WWefg!(+O>!5"))::44%+W$5 5  ; 
  ++A.66<< $4/D4D4D#//44H!(C0%//99#O$ IJJ	X	"))22 3 

  }}Q'006B-- H
RYZbYccdeffr   c                  @    t         t               t        t               dS )zRReturn everything the admin routing page needs: stages, routes, providers, status.)stagesroutes	providersprovider_status)r   r   r   r  r   r   r   get_routing_dashboard_datar;    s      " ".0	 r   )Ni    )Ni @  )i   )rI   rI   )	1536x1024)N)FF):r   r   r   loggingr   r   tenacityr   r   r   r   	getLogger__name__r   r6   r   r   r   r!   r$   r(   r,   _openai_key_openai_baserX   BaseExceptionr   r:   intra   rd   listrg   dictr   bytesr   r   r   r   r   r   r   r   r   r   r   r   r  r  r  r  r  r%  r'  r)  r5  r;  r   r   r   <module>rH     s4   	     T T			8	$ d
 LS4Z L;cDj ;^S4Z ^;cDj ;@t @=#* = "##%
  

  = T  	A		QA2	6
0
1	5S 5 5 5PS 55" 	A		QA2	6
0
1	5# 5c 5c 5VY 55 d   	A		QA2	6
0
1	< < <c <# <`d <ps <  JM <  W[ <<~ 	A		QA2	6
0
1		*3 	*c 	*E 	*	*$ "H#$3 !S#$3 'G#$3 8#$3 (8#$# +C!$! "E#$3 (K#$3 %<#$3 (A!$+ "K!($cYD 35:<LM !!"#= &!"#= ##!
<  35_ +!"#3! )#"#3)
2 #*,@A $!!# (+  !!" (+
mM	h  S T DS C   ,s B B   - 
"# "J" "T <C  t 5C 54 5UX 5ei 5{ 5"   t  QT  ae  w{  FyS y# y y[^ yko y  BF y 	A		QA2	6
0
1	
b3 
b 
bC 
bTW 
bcf 
b
b 	A		QA2	6
0
1	
c 
cS 
c 
cY\ 
cil 
c
c	_# 	_ 	_3 	_ 	A		QA2	6
0
1	'g 'gc 'g 'gu 'g'g\D r   