o
    ưiL                     @   s  d Z ddlZddlmZmZmZmZ ddlZddlm	Z	m
Z
mZmZmZmZ ddlmZ ddlZddlmZ ddlT ddlmZmZ dd	lmZmZmZ e	 Z		d"d
edeeeeef  dee deeef fddZ		d"d
edeeef dedeeeeef  dee ddfddZ dedeeeef eeeeef  ee ee f fddZ!ej"de
egedgdej"de
egedgde
efdededefddZ#ej"de
egedgdej"de
egedgde
efdededefd d!Z$dS )#z
RAG Endpoints for LiteLLM Proxy.

Provides:
- /rag/ingest: All-in-one document ingestion pipeline (Upload -> Chunk -> Embed -> Vector Store)
- /rag/query: RAG query pipeline (Search -> Rerank -> LLM Completion)
    N)AnyDictOptionalTuple)	APIRouterDependsHTTPExceptionRequestResponsestatus)ORJSONResponse)verbose_proxy_logger)*)UserAPIKeyAuthuser_api_key_auth)_read_request_body_safe_get_request_headersget_form_dataresponse	file_datafile_urlreturnc           
      C   s   ddl m }m} d}t| dr| d}nt| dr| j}d}d}d}|rC|d }t|dkr5t|d nd}t|dkrA|d nd}|||||j d}	|durX||	d	< |dur`||	d
< |	S )a  
    Build a file metadata entry for storing in vector_store_metadata.
    
    Args:
        response: The response from litellm.aingest containing file_id
        file_data: Optional tuple of (filename, content, content_type)
        file_url: Optional URL if file was ingested from URL
    
    Returns:
        Dictionary with file metadata (file_id, filename, file_url, ingested_at, etc.)
    r   )datetimetimezoneNgetfile_id      )r   filenamer   Zingested_at	file_sizecontent_type)	r   r   hasattrr   r   lennowutc	isoformat)
r   r   r   r   r   r   r   r   r    
file_entry r'   \/home/app/Keep/.python/lib/python3.10/site-packages/litellm/proxy/rag_endpoints/endpoints.py_build_file_metadata_entry   s.   

r)   ingest_optionsuser_api_key_dictc                    s  ddl m} t| dr| d}nt| dr| j}ntdt|   dS |du s0t|t	s7td dS |di }|d	}	|d
i }
|
d}|
d}i }d	dh}|
 D ]\}}||vrl|durl|||< q\t| ||d}z|jjjd|idI dH }|du rtd| d d|gi}|pd|dd  }|pd}|||	pd|||||r|nd|j|jd	I dH  td| d W dS td| d |jpi }t|t	rddl}||}|dg }|| ||d< ddlm} |jjjd|id||idI dH  td|dp|dd  d!| d" W dS  tyA } ztd#| d$|  W Y d}~dS d}~ww )%a  
    Helper function to save a newly created vector store from RAG ingest to the database.
    
    This function:
    - Extracts vector store ID and config from the ingest response
    - Checks if the vector store already exists in the database
    - Creates a new database entry if it doesn't exist
    - Adds the vector store to the registry
    - Tracks team_id and user_id for access control
    
    Args:
        response: The response from litellm.aingest()
        ingest_options: The ingest options containing vector store config
        prisma_client: The Prisma database client
        user_api_key_dict: User API key authentication info
    r   )create_vector_store_in_dbr   vector_store_idz6Unable to extract vector_store_id from response type: Nz?Vector store ID is None or not a string, skipping database savevector_storecustom_llm_providerlitellm_vector_store_paramsvector_store_namevector_store_description)r   r   r   )wherez"Saving newly created vector store z to databaseingested_fileszRAG Vector Store -    zCreated via RAG ingest endpointZopenai)	r-   r/   prisma_clientr1   r2   vector_store_metadataZlitellm_paramsteam_iduser_idzVector store z saved to database successfullyz+ already exists, appending file to metadata)
safe_dumpsr7   )r3   datazAdded file r   r   Unknownz to vector store z	 metadatazFailed to save vector store z to database: )Z9litellm.proxy.vector_store_endpoints.management_endpointsr,   r!   r   r-   r   warningtype
isinstancestritemsr)   dbZ litellm_managedvectorstorestableZfind_uniqueinfor8   r9   r7   jsonloadsappendZlitellm.proxy.utilsr:   update	Exception	exception)r   r*   r6   r+   r   r   r,   r-   Zvector_store_configr/   r0   Zcustom_vector_store_nameZcustom_vector_store_descriptionZprovider_specific_paramsZexcluded_keyskeyvaluer&   Zexisting_vector_storeZinitial_metadatar1   r2   Zexisting_metadatarD   r4   r:   Zdb_errorr'   r'   r(   (_save_vector_store_to_db_from_rag_ingestQ   s   















$
rL   requestc              
      s  t | }|dd}d}d}d}i }d|v rWt| I dH }|d}|dur:t|dr:| I dH }	|j|	|jf}|d}
|
rVt|
}|di }|d	}|d
}n[t	| I dH }|di }|d	}|d
}|d}|rt
|tr|d}|d}|dd}|r|rzt|}	||	|f}W n ty } ztddd| idd}~ww |du r|du r|du rtdddidd|vrtdddid||||fS )z
    Parse RAG ingest request.

    Supports:
    - Form: file + request JSON in form field
    - JSON body for URL-based ingestion

    Returns:
        Tuple of (ingest_options, file_data, file_url, file_id)
    zcontent-type Nzmultipart/form-datafilereadrM   r*   r   r   r   contentr    zapplication/octet-stream  errorzInvalid base64 content: status_codedetailz'Must provide file, file_url, or file_idr.   z8ingest_options must contain 'vector_store' configuration)r   r   r   r!   rP   r   r    orjsonrE   r   r?   dictbase64	b64decoderH   r   )rM   headersr    r   r   r   r*   Z	form_dataZfile_objZfile_contentZrequest_json_strrequest_datar;   r   Zcontent_b64er'   r'   r(   parse_rag_ingest_request   sf   










r^   z/v1/rag/ingestZrag)dependenciesresponse_classtagsz/rag/ingestfastapi_responsec              
      s  ddl m}m}m}m}m}m} zt| I dH \}	}
}}|jt	j
jkr6|	di ds6ttjddidi }||| ||||d	I dH }td
|	  tjd|	|
|||d|I dH }td|du d|du dt|  |dur|durt||	|||
|dI dH  |W S td|du d|du  |W S  ty     ty } ztd|  tddt|idd}~ww )a  
    RAG Ingest endpoint - all-in-one document ingestion pipeline.

    Supports form upload (for files) or JSON body (for URLs).

    ## Form upload (for files):
    ```bash
    curl -X POST "http://localhost:4000/v1/rag/ingest" \
        -H "Authorization: Bearer sk-1234" \
        -F file="@document.pdf" \
        -F 'ingest_options={"vector_store": {"custom_llm_provider": "openai"}}'
    ```

    ## JSON body (for URLs):
    ```bash
    curl -X POST "http://localhost:4000/v1/rag/ingest" \
        -H "Authorization: Bearer sk-1234" \
        -H "Content-Type: application/json" \
        -d '{
            "file_url": "https://example.com/document.pdf",
            "ingest_options": {"vector_store": {"custom_llm_provider": "openai"}}
        }'
    ```

    ## Bedrock:
    ```bash
    curl -X POST "http://localhost:4000/v1/rag/ingest" \
        -H "Authorization: Bearer sk-1234" \
        -F file="@document.pdf" \
        -F 'ingest_options={"vector_store": {"custom_llm_provider": "bedrock"}}'
    ```
    r   )add_litellm_data_to_requestgeneral_settings
llm_routerr6   proxy_configversionNr.   r-   rS   zinternal_user_viewer role can only ingest files to an existing vector store. Provide 'vector_store_id' in ingest_options.vector_store.rT   r;   rM   rd   r+   rg   rf   zRAG Ingest - options: )r*   r   r   r   routerz>RAG Ingest - Checking database save conditions: prisma_client=z, response=z, response_type=)r   r*   r6   r+   r   r   z&Skipping database save: prisma_client=zRAG Ingest failed:   r'   )litellm.proxy.proxy_serverrc   rd   re   r6   rf   rg   r^   Z	user_roleZLitellmUserRolesZINTERNAL_USER_VIEW_ONLYrK   r   r   r   ZHTTP_403_FORBIDDENr   debuglitellmZaingestr>   rL   r=   rH   rI   r@   )rM   rb   r+   rc   rd   re   r6   rf   rg   r*   r   r   r   r\   r   r]   r'   r'   r(   
rag_ingest*  st    1			
 
rn   z/v1/rag/queryz
/rag/queryc              
      s^  ddl m}m}m}m}m} zzt| I dH }|d}	|d}
|d}|d}|dd	}|	s;td
ddid|
sEtd
ddid|sOtd
ddidd|vr[td
ddidi }||| ||||dI dH }t	
d|	 d|  tjd|	|
||||d|I dH }|W S  ty     ty } zt	d|  tddt|idd}~ww )a  
    RAG Query endpoint - search vector store, optionally rerank, and generate LLM response.

    This endpoint:
    1. Extracts the query from the last user message
    2. Searches the vector store for relevant context
    3. Optionally reranks the results
    4. Generates an LLM response with the retrieved context

    ## Example Request:
    ```bash
    curl -X POST "http://localhost:4000/v1/rag/query" \
        -H "Authorization: Bearer sk-1234" \
        -H "Content-Type: application/json" \
        -d '{
            "model": "gpt-4o-mini",
            "messages": [{"role": "user", "content": "What is LiteLLM?"}],
            "retrieval_config": {
                "vector_store_id": "vs_abc123",
                "custom_llm_provider": "openai",
                "top_k": 5
            }
        }'
    ```

    ## With Reranking:
    ```bash
    curl -X POST "http://localhost:4000/v1/rag/query" \
        -H "Authorization: Bearer sk-1234" \
        -H "Content-Type: application/json" \
        -d '{
            "model": "gpt-4o-mini",
            "messages": [{"role": "user", "content": "What is LiteLLM?"}],
            "retrieval_config": {
                "vector_store_id": "vs_abc123",
                "custom_llm_provider": "openai",
                "top_k": 10
            },
            "rerank": {
                "enabled": true,
                "model": "cohere/rerank-english-v3.0",
                "top_n": 3
            }
        }'
    ```
    r   )rc   rd   re   rf   rg   Nmodelmessagesretrieval_configrerankstreamFrR   rS   zmodel is requiredrT   zmessages is requiredzretrieval_config is requiredr-   z/retrieval_config must contain 'vector_store_id'rh   zRAG Query - model: z, retrieval_config: )ro   rp   rq   rr   rs   ri   zRAG Query failed: rj   r'   )rk   rc   rd   re   rf   rg   r   r   r   r   rl   rm   ZaqueryrH   rI   r@   )rM   rb   r+   rc   rd   re   rf   rg   r;   ro   rp   rq   rr   rs   r\   r   r]   r'   r'   r(   	rag_query  s|   ?



	

rt   )NN)%__doc__rY   typingr   r   r   r   rW   Zfastapir   r   r   r	   r
   r   Zfastapi.responsesr   rm   Zlitellm._loggingr   Zlitellm.proxy._typesZ$litellm.proxy.auth.user_api_key_authr   r   Z-litellm.proxy.common_utils.http_parsing_utilsr   r   r   ri   r@   bytesr)   rL   r^   postrn   rt   r'   r'   r'   r(   <module>   s     

9

 ,
O	u	