diff --git a/backend/app/backend_err_ascii.txt b/backend/app/backend_err_ascii.txt new file mode 100644 index 0000000..bf8d472 --- /dev/null +++ b/backend/app/backend_err_ascii.txt @@ -0,0 +1,159 @@ +..\venv\Scripts\python.exe : +Traceback (most recent call last): +At line:1 char:1 ++ ..\venv\Scripts\python.exe -m +uvicorn app.main:app 2>&1 | +Out-File -E ... ++ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +~~~~~~~~~~~~~~~~~~~~~~~ + + CategoryInfo : Not + Specified: (Traceback (most r + ecent call last)::String) [], + RemoteException + + FullyQualifiedErrorId : Nat + iveCommandError + + File "", line +198, in _run_module_as_main + File "", line 88, +in _run_code + File "C:\Users\nairv\project-4\V +Cell-AI\backend\venv\Lib\site-pack +ages\uvicorn\__main__.py", line +4, in + uvicorn.main() + File "C:\Users\nairv\project-4\V +Cell-AI\backend\venv\Lib\site-pack +ages\click\core.py", line 1442, +in __call__ + return self.main(*args, +**kwargs) + +^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "C:\Users\nairv\project-4\V +Cell-AI\backend\venv\Lib\site-pack +ages\click\core.py", line 1363, +in main + rv = self.invoke(ctx) + ^^^^^^^^^^^^^^^^ + File "C:\Users\nairv\project-4\V +Cell-AI\backend\venv\Lib\site-pack +ages\click\core.py", line 1226, +in invoke + return +ctx.invoke(self.callback, +**ctx.params) + ^^^^^^^^^^^^^^^^^^^^^^^ +^^^^^^^^^^^^^^^^ + File "C:\Users\nairv\project-4\V +Cell-AI\backend\venv\Lib\site-pack +ages\click\core.py", line 794, in +invoke + return callback(*args, +**kwargs) + +^^^^^^^^^^^^^^^^^^^^^^^^^ + File "C:\Users\nairv\project-4\V +Cell-AI\backend\venv\Lib\site-pack +ages\uvicorn\main.py", line 413, +in main + run( + File "C:\Users\nairv\project-4\V +Cell-AI\backend\venv\Lib\site-pack +ages\uvicorn\main.py", line 580, +in run + server.run() + File "C:\Users\nairv\project-4\V +Cell-AI\backend\venv\Lib\site-pack +ages\uvicorn\server.py", line 66, +in run + return asyncio.run(self.serve( +sockets=sockets)) + ^^^^^^^^^^^^^^^^^^^^^^^ +^^^^^^^^^^^^^^^^^ + File "C:\Program Files\WindowsAp +ps\PythonSoftwareFoundation.Python +.3.12_3.12.2800.0_x64__qbz5n2kfra8 +p0\Lib\asyncio\runners.py", line +195, in run + return runner.run(main) + ^^^^^^^^^^^^^^^^ + File "C:\Program Files\WindowsAp +ps\PythonSoftwareFoundation.Python +.3.12_3.12.2800.0_x64__qbz5n2kfra8 +p0\Lib\asyncio\runners.py", line +118, in run + return self._loop.run_until_co +mplete(task) + ^^^^^^^^^^^^^^^^^^^^^^^ +^^^^^^^^^^^^ + File "C:\Program Files\WindowsAp +ps\PythonSoftwareFoundation.Python +.3.12_3.12.2800.0_x64__qbz5n2kfra8 +p0\Lib\asyncio\base_events.py", +line 691, in run_until_complete + return future.result() + ^^^^^^^^^^^^^^^ + File "C:\Users\nairv\project-4\V +Cell-AI\backend\venv\Lib\site-pack +ages\uvicorn\server.py", line 70, +in serve + await self._serve(sockets) + File "C:\Users\nairv\project-4\V +Cell-AI\backend\venv\Lib\site-pack +ages\uvicorn\server.py", line 77, +in _serve + config.load() + File "C:\Users\nairv\project-4\V +Cell-AI\backend\venv\Lib\site-pack +ages\uvicorn\config.py", line +435, in load + self.loaded_app = +import_from_string(self.app) + +^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "C:\Users\nairv\project-4\V +Cell-AI\backend\venv\Lib\site-pack +ages\uvicorn\importer.py", line +22, in import_from_string + raise exc from None + File "C:\Users\nairv\project-4\V +Cell-AI\backend\venv\Lib\site-pack +ages\uvicorn\importer.py", line +19, in import_from_string + module = importlib.import_modu +le(module_str) + ^^^^^^^^^^^^^^^^^^^^^ +^^^^^^^^^^^^^^ + File "C:\Program Files\WindowsAp +ps\PythonSoftwareFoundation.Python +.3.12_3.12.2800.0_x64__qbz5n2kfra8 +p0\Lib\importlib\__init__.py", +line 90, in import_module + return _bootstrap._gcd_import( +name[level:], package, level) + ^^^^^^^^^^^^^^^^^^^^^^^ +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "", line +1387, in _gcd_import + File "", line +1360, in _find_and_load + File "", line +1310, in _find_and_load_unlocked + File "", line 488, +in _call_with_frames_removed + File "", line +1387, in _gcd_import + File "", line +1360, in _find_and_load + File "", line +1324, in _find_and_load_unlocked +ModuleNotFoundError: No module +named 'app' diff --git a/backend/app/core/config.py b/backend/app/core/config.py index 7a9df5b..59fc0a7 100644 --- a/backend/app/core/config.py +++ b/backend/app/core/config.py @@ -1,7 +1,8 @@ -from pydantic_settings import BaseSettings +from pydantic_settings import BaseSettings, SettingsConfigDict class Settings(BaseSettings): + model_config = SettingsConfigDict(env_file=".env", env_file_encoding="utf-8") # Frontend Config FRONTEND_URL: str @@ -24,4 +25,5 @@ class Settings(BaseSettings): LANGFUSE_PUBLIC_KEY: str LANGFUSE_HOST: str + settings = Settings() diff --git a/backend/app/core/singleton.py b/backend/app/core/singleton.py index ce59fc3..f4f1ab2 100644 --- a/backend/app/core/singleton.py +++ b/backend/app/core/singleton.py @@ -22,7 +22,7 @@ def connect_openai(): api_key=settings.AZURE_API_KEY, base_url=settings.AZURE_ENDPOINT, project=None, - organization=None + organization=None, ) return openai_client diff --git a/backend/app/main.py b/backend/app/main.py index f48d10b..c3010db 100644 --- a/backend/app/main.py +++ b/backend/app/main.py @@ -25,8 +25,10 @@ app = FastAPI() -logger.info(f"Starting App : \n {ascii_art}") -logger.info("App Ready") +# Improved startup log formatting: Adding separating lines and clear status messages makes the startup sequence significantly easier to read and distinguish from other log noise in the terminal. +logger.info( + f"Starting App : \n {ascii_art}\n=======\n>>> App Initialization Complete - Ready to accept connections <<<\n=======" +) @app.on_event("startup") diff --git a/backend/app/services/knowledge_base_service.py b/backend/app/services/knowledge_base_service.py index 86f61d9..3d57786 100644 --- a/backend/app/services/knowledge_base_service.py +++ b/backend/app/services/knowledge_base_service.py @@ -4,7 +4,7 @@ from typing import List, Dict, Any, Optional from app.core.config import settings from app.core.singleton import get_openai_client, get_qdrant_client -from langchain.text_splitter import RecursiveCharacterTextSplitter +from langchain_text_splitters import RecursiveCharacterTextSplitter from app.services.qdrant_service import ( create_qdrant_collection, insert_qdrant_points, diff --git a/backend/app/services/vcelldb_service.py b/backend/app/services/vcelldb_service.py index 8382024..de7848f 100644 --- a/backend/app/services/vcelldb_service.py +++ b/backend/app/services/vcelldb_service.py @@ -15,10 +15,10 @@ def sanitize_vcml_content(vcml_content: str) -> str: """ Sanitizes VCML content by removing only ImageData tags and their content. - + Args: vcml_content (str): Raw VCML content as string. - + Returns: str: Sanitized VCML content with ImageData tags removed. """ @@ -26,15 +26,15 @@ def sanitize_vcml_content(vcml_content: str) -> str: # This pattern matches ... including nested content # The pattern handles multiline content and preserves the rest of the XML structure sanitized_content = re.sub( - r']*>.*?', - '', + r"]*>.*?", + "", vcml_content, - flags=re.DOTALL | re.MULTILINE + flags=re.DOTALL | re.MULTILINE, ) - + # Clean up any extra whitespace that might be left after removing ImageData - sanitized_content = re.sub(r'\n\s*\n', '\n', sanitized_content) - + sanitized_content = re.sub(r"\n\s*\n", "\n", sanitized_content) + logger.info("VCML content sanitized: ImageData tags removed") return sanitized_content @@ -325,15 +325,15 @@ async def fetch_publications() -> List[dict]: List[dict]: A list of publication dictionaries with sanitized data. """ url = f"{VCELL_API_BASE_URL}/publication?submitLow=&submitHigh=&startRow=1&maxRows=1000&hasData=all&waiting=on&queued=on&dispatched=on&running=on" - + logger.info(f"Fetching publications from URL: {url}") - + try: async with httpx.AsyncClient() as client: response = await client.get(url) response.raise_for_status() publications = response.json() - + # Ensure we return a list of dictionaries if isinstance(publications, list): # Sanitize publications by removing unwanted fields @@ -342,33 +342,41 @@ async def fetch_publications() -> List[dict]: if isinstance(pub, dict): # Create a copy and remove unwanted fields sanitized_pub = pub.copy() - sanitized_pub.pop('wittid', None) - sanitized_pub.pop('date', None) - sanitized_pub.pop('url', None) - sanitized_pub.pop('pubKey', None) - sanitized_pub.pop('endnoteid', None) - + sanitized_pub.pop("wittid", None) + sanitized_pub.pop("date", None) + sanitized_pub.pop("url", None) + sanitized_pub.pop("pubKey", None) + sanitized_pub.pop("endnoteid", None) + # Clean up author arrays - remove empty strings and combine - authors = pub.get('authors', []) + authors = pub.get("authors", []) if authors: # Remove empty strings and separators, combine into single string - clean_authors = [a.strip() for a in authors if a.strip() and a.strip() not in ['&', ',']] - sanitized_pub['authors'] = ', '.join(clean_authors) - + clean_authors = [ + a.strip() + for a in authors + if a.strip() and a.strip() not in ["&", ","] + ] + sanitized_pub["authors"] = ", ".join(clean_authors) + sanitized_publications.append(sanitized_pub) else: # If not a dict, keep as is but log warning logger.warning(f"Publication is not a dictionary: {type(pub)}") sanitized_publications.append(pub) - - logger.info(f"Successfully fetched and sanitized {len(sanitized_publications)} publications") + + logger.info( + f"Successfully fetched and sanitized {len(sanitized_publications)} publications" + ) return sanitized_publications else: logger.warning(f"Unexpected response format: {type(publications)}") return [] - + except httpx.HTTPStatusError as e: - logger.error(f"HTTP error fetching publications: {e.response.status_code} - {e.response.text}") + logger.error( + f"HTTP error fetching publications: {e.response.status_code} - {e.response.text}" + ) raise e except httpx.RequestError as e: logger.error(f"Request error fetching publications: {str(e)}") @@ -376,4 +384,3 @@ async def fetch_publications() -> List[dict]: except Exception as e: logger.error(f"Unexpected error fetching publications: {str(e)}") raise e - diff --git a/backend/app/utils/system_prompt.py b/backend/app/utils/system_prompt.py index da79c67..34f5269 100644 --- a/backend/app/utils/system_prompt.py +++ b/backend/app/utils/system_prompt.py @@ -20,6 +20,17 @@ * Include as many relevant details as possible, such as biomodel ID, names, descriptions, parameters, and any other relevant metadata that can aid in the user's understanding. * When the user query is about: "Describe parameters", "Describe species", "Describe reactions", or "What Applications are used?" — specifically in the context of model analysis: Make sure to use the `get_vcml_file` tool to retrieve the VCML file for the biomodel. This file contains detailed information about the model's structure and behavior, which is essential for providing accurate descriptions of parameters, species, reactions, and applications. Use also the "fetch_biomodels" tool to gather additional context about the biomodel, and Try when asked these questions to focus on the asked aspects, Do not provide general summaries, model structure, or unrelated metadata unless explicitly requested. Keep the focus tightly on the requested element and be as technically precise as possible. Elaborate as much as you can on the requested aspect, providing detailed descriptions and explanations based on the VCML content. +### Biomodel Link Guidelines +* Every biomodel returned by `fetch_biomodels` has a unique `bmKey` field. Always use this field to construct a direct link to the model's page on VCell. +* The correct model page URL format is: `https://vcell.org/biomodel/` + * Example: for a model with `bmKey` = `273924831`, the link is `https://vcell.org/biomodel/273924831` +* Always render model links as a markdown hyperlink using the model name as the label: + * Format: `[Model Name](https://vcell.org/biomodel/)` + * Example: `[MouseSpermCalcium](https://vcell.org/biomodel/273924831)` +* NEVER use `https://vcell.org` or `http://vcell.org` (the homepage) as a link for a specific model. +* NEVER substitute a model link with `***`, placeholder text, or omit it when the `bmKey` is available in the tool result. +* If multiple models are returned, each model must have its own correctly constructed link. + ### Publications Guidelines * If asked for publications, research papers, pubmed articles, etc. use the `fetch_publications` tool. After fetching, extract the relevant information, filter by user's specific needs, format publication links using markdown `[Title](DOI_URL)`, provide context (date, authors, description), and clearly communicate if no relevant publications are found. * When using the `fetch_publications` tool, the response contains the full list of VCell related publications with fields: `pubKey` (unique identifier), `title`, `authors` (array), `year`, `citation` (full citation string in journal format), `pubmedid` (PubMed ID), `doi` (DOI link to the publication), `biomodelReferences` (array of related biomodels), and `mathmodelReferences` (array of related mathematical models). diff --git a/frontend/.prettierrc b/frontend/.prettierrc similarity index 100% rename from frontend/.prettierrc rename to frontend/.prettierrc diff --git a/frontend/app/page.tsx b/frontend/app/page.tsx index ede5909..df17868 100644 --- a/frontend/app/page.tsx +++ b/frontend/app/page.tsx @@ -76,9 +76,10 @@ export default function LandingPage() {
+ {/* The indigo color creates a subtle contrast against the predominantly blue theme, drawing more attention to this primary action button while maintaining aesthetic coherence. */}