
    Piv!                        d Z ddlZddlZddlmZ ddlmZ ddlmZm	Z	  ej                  e      ZdefdZ	 	 dded	ed
edz  deeeef      fdZ	 	 dded	ed
edz  deeeef      fdZ	 	 	 	 dded	ededed
edz  deeeef      fdZy)u  
search.py

Supabase knowledge base에 대한 의미 검색, 키워드 검색, 혼합 검색 기능을 제공한다.

의존성:
- embedding_service.get_embedding: 텍스트 임베딩 벡터 생성
- supabase: 벡터 DB 및 전문 검색

구현 전략:
- semantic_search: hybrid_search(semantic_weight=1.0, keyword_weight=0.0)
- keyword_search: hybrid_search(semantic_weight=0.0, keyword_weight=1.0)
- hybrid_search: Supabase RPC("hybrid_search") 직접 호출
    N)Any)get_embedding)Clientcreate_clientreturnc                  8   t         j                  j                  d      xs t         j                  j                  d      } t         j                  j                  d      xs t         j                  j                  d      }| r|st        d      t	        | |      S )u   
    환경변수에서 Supabase 연결 정보를 읽어 클라이언트를 반환한다.

    Raises:
        ValueError: 필요한 환경변수가 설정되지 않은 경우.
    INSURO_SUPABASE_URLINSURO_NEW_SUPABASE_URLINSURO_NEW_SERVICE_ROLE_KEY INSURO_SUPABASE_SERVICE_ROLE_KEYz-Supabase URL and service role key must be set)osenvironget
ValueErrorr   )urlkeys     </home/jay/workspace/.worktrees/task-2117-dev1/libs/search.py_get_supabase_clientr      sn     **...
/
\2::>>B[3\C
**..6
7
m2::>>Jl;mCcHIIc""    querylimitsource_filterc           	          t        | |dd|      }g }|D ](  }|j                  |d   |d   |d   |d   |d   d	       * t        |d
 d      S )u]  
    쿼리 텍스트의 임베딩 벡터를 사용하여 코사인 유사도 기반 의미 검색을 수행한다.

    내부적으로 hybrid_search(semantic_weight=1.0, keyword_weight=0.0)를 호출한다.

    Args:
        query: 검색할 텍스트.
        limit: 반환할 최대 결과 수. 기본값 10.
        source_filter: knowledge_documents.source 기준 필터. None이면 전체 검색.

    Returns:
        검색 결과 딕셔너리 목록. similarity 내림차순 정렬.
        각 항목: {"content": str, "similarity": float, "source": str, "title": str, "document_id": str}
          ?        )r   r   semantic_weightkeyword_weightr   content
similaritysourcetitledocument_idr   r   r    r!   r"   c                     | d   S )Nr    xs    r   <lambda>z!semantic_search.<locals>.<lambda>Y   s
    !L/ r   Tr   reverse)hybrid_searchappendsorted)r   r   r   resultssemantic_resultsrows         r   semantic_searchr1   2   s    & #G .0 	
y>!,/h-W"=1	
	
 "(A4PPr   c                    t         j                  d| dd |       t               }dgdz  }| ||dd|d}|j                  d|      j	                         }|j
                  xs g }g }|D ]  }	|j                  t        |	j                  d	d
            t        |	j                  dd            t        |	j                  dd
            t        |	j                  dd
            t        |	j                  dd
            d        |st         j                  d| dd        	 |j                  d      j                  d      j                  d	d|  d      j                  |      j	                         }
|
j
                  }t        |t              r|ng }|D ]K  }	|j                  t        |	j                  d	d
            dd
d
t        |	j                  dd
            d       M |S |S # t         $ r!}t         j#                  d|       Y d}~|S d}~ww xY w)u  
    전문 검색(full-text search)을 사용하여 키워드 기반 검색을 수행한다.

    Supabase RPC("hybrid_search")를 semantic_weight=0.0, keyword_weight=1.0으로 호출한다.
    embedding 생성 없이 더미 벡터를 사용하여 키워드 전용 검색을 수행한다.

    Args:
        query: 검색할 키워드 텍스트.
        limit: 반환할 최대 결과 수. 기본값 10.
        source_filter: knowledge_documents.source 기준 필터. None이면 전체 검색.

    Returns:
        검색 결과 딕셔너리 목록.
        각 항목: {"content": str, "similarity": float, "source": str, "title": str, "document_id": str}
    u)   keyword_search 호출: query=%r, limit=%dN2   r   i   r   
query_textquery_embeddingmatch_countr   r   r   r+   r    r   r    r!   r"   r#   u<   keyword_search: RPC 결과 없음, ILIKE fallback 시도: %rknowledge_chunkszid, document_id, content%g      ?u   ILIKE fallback 실패: %s)loggerdebugr   rpcexecutedatar,   strr   floatfrom_selectiliker   
isinstancelist	Exceptionwarning)r   r   r   supabasedummy_embeddingparamsresponserowsr.   r0   fallback_responsefallback_datafallback_rowsexcs                 r   keyword_searchrR   \   s   ( LL3cr
 $%H %(54<O *&F ||OV4<<>H!)!4"D$&G 	
swwy"56#CGGL#$>?cggh34SWWWb12"377="#=>	
	
 SUZ[^\^U_`	=1223yAeWA,/u  .22MCMm]aCb-hjM$ 	#&swwy"'=#>&)"$!#'*377="+E'F	 N N  	=NN6<<N	=s   CG" "	H+HHr   r   c                 b   t         j                  d| dd |||       t        |       }t               }| |||||d}|j	                  d|      j                         }|j                  xs g }	g }
|	D ]  }|
j                  t        |j                  dd            t        |j                  dd	            t        |j                  d
d	            t        |j                  dd            t        |j                  dd            t        |j                  dd            d        t        |
d d      S )u  
    의미 검색과 키워드 검색을 결합한 혼합 검색을 수행한다.

    Supabase RPC("hybrid_search")를 호출하며, combined_score 내림차순으로 정렬하여 반환한다.

    Args:
        query: 검색할 텍스트.
        limit: 반환할 최대 결과 수. 기본값 10.
        semantic_weight: 의미 검색 가중치. 기본값 0.7.
        keyword_weight: 키워드 검색 가중치. 기본값 0.3.
        source_filter: knowledge_documents.source 기준 필터. None이면 전체 검색.

    Returns:
        검색 결과 딕셔너리 목록. combined_score 내림차순 정렬.
        각 항목: {"content": str, "similarity": float, "combined_score": float,
                  "source": str, "title": str, "document_id": str}
    uS   hybrid_search 호출: query=%r, limit=%d, semantic_weight=%.2f, keyword_weight=%.2fNr3   r4   r+   r   r8   r   r   combined_scorer    r!   r"   )r   r   rT   r    r!   r"   c                     | d   S )NrT   r%   r&   s    r   r(   zhybrid_search.<locals>.<lambda>   s    +;)< r   Tr)   )r;   r<   r   r   r=   r>   r?   r,   r@   r   rA   r-   )r   r   r   r   r   r6   rI   rK   rL   rM   r.   r0   s               r   r+   r+      s'   0 LL]cr
 $E*O#%H **(&F ||OV4<<>H!)!4"D$&G 

swwy"56#CGGL#$>?"'0@#(F"Gcggh34SWWWb12"377="#=>		


 '<dKKr   )
   N)rV   gffffff?g333333?N)__doc__loggingr   typingr   embedding_servicer   rI   r   r   	getLogger__name__r;   r   r@   intrF   dictr1   rR   rA   r+   r%   r   r   <module>r_      s,    	  + *			8	$#f #*  $'Q'Q'Q :'Q 
$sCx.	'QX  $RRR :R 
$sCx.	Rn   $>L>L>L >L 	>L
 :>L 
$sCx.>Lr   