import os import time import uuid import re import gc import glob import subprocess import nest_asyncio from dotenv import load_dotenv from llama_index.core import Settings from llama_index.llms.openrouter import OpenRouter from llama_index.core import PromptTemplate from llama_index.core import SimpleDirectoryReader from llama_index.core import VectorStoreIndex from llama_index.core.storage.storage_context import StorageContext from llama_index.core.node_parser import CodeSplitter, MarkdownNodeParser from llama_index.core.indices.vector_store.base import VectorStoreIndex from llama_index.embeddings.huggingface import HuggingFaceEmbedding from llama_index.vector_stores.milvus import MilvusVectorStore from cleanlab_codex.project import Project from cleanlab_codex.client import Client import streamlit as st from validation import codex_validated_query # Setting up the llm @st.cache_resource def load_llm(model_name, api_key): llm = OpenRouter(api_key=api_key, model=model_name, max_tokens=1024) return llm # Initialize Codex project @st.cache_resource def initialize_codex_project(codex_api_key): os.environ["CODEX_API_KEY"] = codex_api_key codex_client = Client() project = codex_client.create_project( name="Chat-with-Code", description="Code RAG project with added validation of Codex", ) access_key = project.create_access_key("test-access-key") project = Project.from_access_key(access_key) return project ##################### # Utility functions ##################### def parse_github_url(url): """Parse the GitHub URL to extract owner and repository name.""" pattern = r"https://github\.com/([^/]+)/([^/]+)" match = re.match(pattern, url) return match.groups() if match else (None, None) def clone_repo(repo_url): """Clone the GitHub repository.""" return subprocess.run( ["git", "clone", repo_url], check=True, text=True, capture_output=True ) def validate_owner_repo(owner, repo): """Validate the owner and repository name.""" return bool(owner) and bool(repo) def parse_docs_by_file_types(ext, language, input_dir_path): """Parse documents by file types in the specified directory.""" files = glob.glob(f"{input_dir_path}/**/*{ext}", recursive=True) if len(files) > 0: loader = SimpleDirectoryReader( input_dir=input_dir_path, required_exts=[ext], recursive=True ) docs = loader.load_data() parser = ( MarkdownNodeParser() if ext == ".md" else CodeSplitter.from_defaults(language=language) ) return parser.get_nodes_from_documents(docs) else: return [] def create_index(nodes): """Create a Milvus collection and return a vectorstore index.""" unique_collection_id = uuid.uuid4().hex collection_name = f"chat_with_docs_{unique_collection_id}" vector_store = MilvusVectorStore( uri="http://localhost:19530", dim=768, overwrite=True, collection_name=collection_name, ) storage_context = StorageContext.from_defaults(vector_store=vector_store) index = VectorStoreIndex( nodes, storage_context=storage_context, ) return index if "id" not in st.session_state: st.session_state.id = uuid.uuid4() st.session_state.file_cache = {} session_id = st.session_state.id client = None def reset_chat(): """Reset the chat state.""" st.session_state.messages = [] st.session_state.context = None gc.collect() with st.sidebar: st.header("API Configuration 🔑") # API Key inputs for OpenRouter and Codex codex_logo_html = """
""" st.markdown(codex_logo_html, unsafe_allow_html=True) st.markdown( "[Get your API key](https://codex.cleanlab.ai/account)", unsafe_allow_html=True ) codex_api_key = st.text_input( "Codex API Key", type="password", help="Get your API key from Cleanlab Codex", ) openrouter_logo_html = """
""" st.markdown(openrouter_logo_html, unsafe_allow_html=True) st.markdown( "[Get your API key](https://openrouter.ai/keys)", unsafe_allow_html=True, ) openrouter_api_key = st.text_input( "OpenRouter API Key", type="password", help="Get your API key from OpenRouter" ) st.divider() # Input for GitHub URL github_url = st.text_input("GitHub Repository URL") # Button to load and process the GitHub repository process_button = st.button("Load") message_container = st.empty() # Placeholder for dynamic messages if process_button and github_url: if not openrouter_api_key: st.error("Please provide OpenRouter API Key") st.stop() if not codex_api_key: st.error("Please provide Codex API Key") st.stop() owner, repo = parse_github_url(github_url) if validate_owner_repo(owner, repo): with st.spinner(f"Loading {repo} repository by {owner}..."): try: # Initialize Codex project project = initialize_codex_project(codex_api_key) # input_dir_path = f"/teamspace/studios/this_studio/{repo}" input_dir_path = os.path.join(os.getcwd(), repo) if not os.path.exists(input_dir_path): subprocess.run( ["git", "clone", github_url], check=True, text=True, capture_output=True, ) if os.path.exists(input_dir_path): file_types = { ".md": "markdown", ".py": "python", ".ipynb": "python", ".js": "javascript", ".ts": "typescript", } nodes = [] for ext, language in file_types.items(): nodes += parse_docs_by_file_types( ext, language, input_dir_path ) else: st.error( "Error occurred while cloning the repository, carefully check the url" ) st.stop() # Setting up the embedding model Settings.embed_model = HuggingFaceEmbedding( model_name="BAAI/bge-base-en-v1.5" ) try: index = create_index(nodes) except: index = VectorStoreIndex(nodes=nodes) # ====== Setup a query engine ====== Settings.llm = load_llm( model_name="qwen/qwen3-coder:free", api_key=openrouter_api_key ) query_engine = index.as_query_engine( streaming=True, similarity_top_k=4 ) # ====== Customise prompt template ====== qa_prompt_tmpl_str = ( "Context information is below.\n" "---------------------\n" "{context_str}\n" "---------------------\n" "Given the context information above, I want you to think step by step to answer the query in a crisp manner. " "First, carefully check if the answer can be found in the provided context. " "If the answer is available in the context, use that information to respond. " "If the answer is not available in the context or the context is insufficient, " "you may use your own knowledge to provide a helpful response. " "Only say 'I don't know!' if you cannot answer the question using either the context or your general knowledge.\n" "Query: {query_str}\n" "Answer: " ) qa_prompt_tmpl = PromptTemplate(qa_prompt_tmpl_str) query_engine.update_prompts( {"response_synthesizer:text_qa_template": qa_prompt_tmpl} ) if nodes: message_container.success("Data loaded successfully!!") else: message_container.write( "No data found, check if the repository is not empty!" ) st.session_state.query_engine = query_engine st.session_state.project = project except Exception as e: st.error(f"An error occurred: {e}") st.stop() st.success("Ready to Chat!") else: st.error("Invalid owner or repository") st.stop() col1, col2 = st.columns([6, 1]) with col1: st.header(f"Chat with Code using Qwen3-Coder!") powered_by_html = """
Powered by and
""" st.markdown(powered_by_html, unsafe_allow_html=True) with col2: st.button("Clear ↺", on_click=reset_chat) # Initialize chat history if "messages" not in st.session_state: reset_chat() # Display chat messages from history on app rerun for message in st.session_state.messages: with st.chat_message(message["role"]): st.markdown(message["content"]) # Accept user input if prompt := st.chat_input("What's up?"): # Check if query engine and project are available if "query_engine" not in st.session_state or "project" not in st.session_state: st.error("Please load a repository first!") st.stop() # Add user message to chat history st.session_state.messages.append({"role": "user", "content": prompt}) # Display user message in chat message container with st.chat_message("user"): st.markdown(prompt) # Display assistant response in chat message container with st.chat_message("assistant"): message_placeholder = st.empty() # context = st.session_state.context query_engine = st.session_state.query_engine project = st.session_state.project # Simulate stream of response with milliseconds delay emoji, trust_score, streaming_response = codex_validated_query( query_engine=query_engine, project=project, user_query=prompt ) # Streaming full_response = "" for char in streaming_response: full_response += char message_placeholder.markdown(full_response + "▌") time.sleep(0.01) # Adjust speed as needed message_placeholder.markdown(full_response) st.markdown(f"{emoji} **Trust Score**: `{trust_score}`") # st.session_state.context = ctx # Add assistant response to chat history st.session_state.messages.append({"role": "assistant", "content": full_response})