Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
159 changes: 159 additions & 0 deletions backend/app/backend_err_ascii.txt
Original file line number Diff line number Diff line change
@@ -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 "<frozen runpy>", line
198, in _run_module_as_main
File "<frozen runpy>", 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 <module>
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 "<frozen
importlib._bootstrap>", line
1387, in _gcd_import
File "<frozen
importlib._bootstrap>", line
1360, in _find_and_load
File "<frozen
importlib._bootstrap>", line
1310, in _find_and_load_unlocked
File "<frozen
importlib._bootstrap>", line 488,
in _call_with_frames_removed
File "<frozen
importlib._bootstrap>", line
1387, in _gcd_import
File "<frozen
importlib._bootstrap>", line
1360, in _find_and_load
File "<frozen
importlib._bootstrap>", line
1324, in _find_and_load_unlocked
ModuleNotFoundError: No module
named 'app'
4 changes: 3 additions & 1 deletion backend/app/core/config.py
Original file line number Diff line number Diff line change
@@ -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

Expand All @@ -24,4 +25,5 @@ class Settings(BaseSettings):
LANGFUSE_PUBLIC_KEY: str
LANGFUSE_HOST: str


settings = Settings()
2 changes: 1 addition & 1 deletion backend/app/core/singleton.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
6 changes: 4 additions & 2 deletions backend/app/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down
2 changes: 1 addition & 1 deletion backend/app/services/knowledge_base_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
59 changes: 33 additions & 26 deletions backend/app/services/vcelldb_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,26 +15,26 @@
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.
"""
# Remove only ImageData tags and their content using regex
# This pattern matches <ImageData ...> ... </ImageData> including nested content
# The pattern handles multiline content and preserves the rest of the XML structure
sanitized_content = re.sub(
r'<ImageData[^>]*>.*?</ImageData>',
'',
r"<ImageData[^>]*>.*?</ImageData>",
"",
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

Expand Down Expand Up @@ -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
Expand All @@ -342,38 +342,45 @@ 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)}")
raise e
except Exception as e:
logger.error(f"Unexpected error fetching publications: {str(e)}")
raise e

11 changes: 11 additions & 0 deletions backend/app/utils/system_prompt.py
Original file line number Diff line number Diff line change
Expand Up @@ -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/<bmKey>`
* 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/<bmKey>)`
* 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).
Expand Down
File renamed without changes.
3 changes: 2 additions & 1 deletion frontend/app/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -76,9 +76,10 @@ export default function LandingPage() {

<div className="flex flex-col sm:flex-row gap-4 justify-center items-center">
<Link href="/chat">
{/* The indigo color creates a subtle contrast against the predominantly blue theme, drawing more attention to this primary action button while maintaining aesthetic coherence. */}
<Button
size="lg"
className="bg-blue-600 hover:bg-blue-700 text-white px-8 py-4 text-lg font-semibold rounded-lg shadow-lg hover:shadow-xl transition-all duration-200"
className="bg-indigo-600 hover:bg-indigo-700 text-white px-8 py-4 text-lg font-semibold rounded-lg shadow-lg hover:shadow-xl transition-all duration-200"
>
Start Exploring
<ArrowRight className="ml-2 h-5 w-5" />
Expand Down
6 changes: 3 additions & 3 deletions frontend/components/json-viewer.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"use client";

import { useState } from "react";
import React, { useState } from "react";
import { Copy, Check } from "lucide-react";
import { Button } from "@/components/ui/button";

Expand All @@ -21,8 +21,8 @@ export function JsonViewer({ data }: JsonViewerProps) {
}
};

const formatJson = (obj: any, indent = 0): JSX.Element[] => {
const elements: JSX.Element[] = [];
const formatJson = (obj: any, indent = 0): React.JSX.Element[] => {
const elements: React.JSX.Element[] = [];
const indentStr = " ".repeat(indent);

if (Array.isArray(obj)) {
Expand Down
Loading