abdibrahem commited on
Commit
cdee10f
·
1 Parent(s): 55666e4

Inital commit

Browse files
Files changed (6) hide show
  1. Dockerfile +51 -0
  2. README.md +90 -11
  3. endpoints_documentation.py +193 -0
  4. final.py +708 -0
  5. main.py +42 -0
  6. requirements.txt +81 -0
Dockerfile ADDED
@@ -0,0 +1,51 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Use Ubuntu as base image for better compatibility
2
+ FROM ubuntu:22.04
3
+
4
+ # Set environment variables
5
+ ENV DEBIAN_FRONTEND=noninteractive
6
+ ENV PYTHONUNBUFFERED=1
7
+
8
+ # Install system dependencies
9
+ RUN apt-get update && apt-get install -y \
10
+ curl \
11
+ wget \
12
+ git \
13
+ python3.12 \
14
+ python3-pip \
15
+ && rm -rf /var/lib/apt/lists/*
16
+
17
+ # Install Ollama
18
+ RUN curl -fsSL https://ollama.com/install.sh | sh
19
+
20
+ # Set working directory
21
+ WORKDIR /app
22
+
23
+ # Copy requirements first to leverage Docker cache
24
+ COPY requirements.txt .
25
+
26
+ # Install Python dependencies
27
+ RUN pip3 install --no-cache-dir -r requirements.txt
28
+
29
+ # Copy the application code
30
+ COPY . .
31
+
32
+ # Create a startup script
33
+ RUN echo '#!/bin/bash\n\
34
+ # Start Ollama in the background\n\
35
+ ollama serve &\n\
36
+ \n\
37
+ # Wait for Ollama to start\n\
38
+ sleep 10\n\
39
+ \n\
40
+ # Pull Mistral model\n\
41
+ ollama pull mistral\n\
42
+ \n\
43
+ # Start FastAPI application\n\
44
+ uvicorn main:app --host 0.0.0.0 --port 8000\n\
45
+ ' > /app/start.sh && chmod +x /app/start.sh
46
+
47
+ # Expose ports
48
+ EXPOSE 8000 11434
49
+
50
+ # Set the entrypoint
51
+ ENTRYPOINT ["uvicorn", "main:app --port 8000 --reload"]
README.md CHANGED
@@ -1,11 +1,90 @@
1
- ---
2
- title: Ai Agent
3
- emoji: 🐨
4
- colorFrom: red
5
- colorTo: yellow
6
- sdk: docker
7
- pinned: false
8
- short_description: 'This is a simple ai agent for health care management system '
9
- ---
10
-
11
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Healthcare AI Assistant
2
+
3
+ A FastAPI-based AI assistant that handles healthcare appointment booking and queries in multiple languages.
4
+
5
+ ## Features
6
+
7
+ - Multi-language support (English and Arabic)
8
+ - Appointment booking and cancellation
9
+ - Sentiment analysis
10
+ - Language detection
11
+ - User-friendly responses
12
+
13
+ ## Setup
14
+
15
+ 1. Create a virtual environment:
16
+ ```bash
17
+ python -m venv venv
18
+ source venv/bin/activate # On Windows: venv\Scripts\activate
19
+ ```
20
+
21
+ 2. Install dependencies:
22
+ ```bash
23
+ pip install -r requirements.txt
24
+ ```
25
+
26
+ 3. Set up environment variables:
27
+ Create a `.env` file in the root directory with the following variables:
28
+ ```
29
+ API_BASE_URL=your_api_base_url
30
+ API_KEY=your_api_key
31
+ ```
32
+
33
+ ## Running the Application
34
+
35
+ Start the FastAPI server:
36
+ ```bash
37
+ uvicorn main:app --reload
38
+ ```
39
+
40
+ The API will be available at `http://localhost:8000`
41
+
42
+ ## API Documentation
43
+
44
+ Once the server is running, you can access:
45
+ - Interactive API documentation: `http://localhost:8000/docs`
46
+ - Alternative API documentation: `http://localhost:8000/redoc`
47
+
48
+ ## API Endpoints
49
+
50
+ ### POST /query
51
+ Process a user query and return a response.
52
+
53
+ Request body:
54
+ ```json
55
+ {
56
+ "query": "string",
57
+ "language": "string (optional)"
58
+ }
59
+ ```
60
+
61
+ Response:
62
+ ```json
63
+ {
64
+ "routing_info": {
65
+ "endpoint": "string",
66
+ "method": "string",
67
+ "parameters": {}
68
+ },
69
+ "api_response": {},
70
+ "user_friendly_response": "string",
71
+ "detected_language": "string",
72
+ "sentiment": {
73
+ "compound": float,
74
+ "pos": float,
75
+ "neu": float,
76
+ "neg": float
77
+ }
78
+ }
79
+ ```
80
+
81
+ ### GET /health
82
+ Health check endpoint.
83
+
84
+ Response:
85
+ ```json
86
+ {
87
+ "status": "healthy",
88
+ "service": "healthcare-ai-assistant"
89
+ }
90
+ ```
endpoints_documentation.py ADDED
@@ -0,0 +1,193 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ endpoints_documentation = {
2
+ "root": {
3
+ "path": "/",
4
+ "method": "GET",
5
+ "description": "API root endpoint",
6
+ "parameters": None,
7
+ "request_body": None,
8
+ "response": {"message": "Clinic Reservation API - Use /docs for documentation"}
9
+ },
10
+ "get_reservations": {
11
+ "path": "/reservations",
12
+ "method": "GET",
13
+ "description": "Get all reservations",
14
+ "parameters": None,
15
+ "request_body": None,
16
+ "response": {
17
+ "description": "List of all reservations",
18
+ "model": "List[Reservation]",
19
+ "filters": "Only returns reservations with status 'scheduled'"
20
+ }
21
+ },
22
+ "get_reservations_by_filters": {
23
+ "path": "/reservations/",
24
+ "method": "GET",
25
+ "description": "Get reservations filtered by patient ID and/or doctor name (only scheduled)",
26
+ "parameters": {
27
+ "patient_id": {
28
+ "type": "str",
29
+ "required": False,
30
+ "description": "Filter by patient ID"
31
+ },
32
+ "doctor_name": {
33
+ "type": "str",
34
+ "required": False,
35
+ "description": "Filter by doctor name (case-insensitive partial match)"
36
+ }
37
+ },
38
+ "request_body": None,
39
+ "response": {
40
+ "description": "List of matching scheduled reservations",
41
+ "model": "List[Reservation]",
42
+ "error": "404 if no scheduled reservations match criteria"
43
+ }
44
+ },
45
+ "create_reservation": {
46
+ "path": "/create-reservation",
47
+ "method": "POST",
48
+ "description": "Create a new reservation",
49
+ "parameters": None,
50
+ "request_body": {
51
+ "model": "CreateReservation",
52
+ "required_fields": {
53
+ "patient_id": "str",
54
+ "doctor_name": "str",
55
+ "date_time": "datetime like this 2023-10-16T19:00:00"
56
+ },
57
+ "optional_fields": {
58
+ "reason": "str (optional)"
59
+ }
60
+ },
61
+ "response": {
62
+ "description": "Created reservation with status 'scheduled'",
63
+ "model": "Reservation",
64
+ "error": "404 if doctor not found"
65
+ }
66
+ },
67
+ "reschedule_reservation": {
68
+ "path": "/reservations/reschedule",
69
+ "method": "PUT",
70
+ "description": "Reschedule an existing reservation",
71
+ "parameters": {
72
+ "doctor_name": {
73
+ "type": "str",
74
+ "required": True,
75
+ "description": "ID of the reservation to reschedule"
76
+ },
77
+ "new_date_time": {
78
+ "type": "str",
79
+ "required": True,
80
+ "description": "New date/time to reschedule to"
81
+ }
82
+ },
83
+ "response": {
84
+ "description": "Updated reservation with new date/time",
85
+ "model": "Reservation",
86
+ "error": "404 if reservation not found"
87
+ }
88
+ },
89
+ "cancel_reservation": {
90
+ "path": "/reservations/cancel",
91
+ "method": "PUT",
92
+ "description": "Cancel a reservation",
93
+ "parameters": {
94
+ "doctor_name": {
95
+ "type": "str",
96
+ "required": True,
97
+ "description": "ID of the reservation to cancel"
98
+ }
99
+ },
100
+ "request_body": None,
101
+ "response": {
102
+ "description": "Canceled reservation (status changed to 'canceled')",
103
+ "model": "Reservation",
104
+ "error": "404 if reservation not found"
105
+ }
106
+ },
107
+ "get_doctors": {
108
+ "path": "/doctors",
109
+ "method": "GET",
110
+ "description": "Get list of all available doctors",
111
+ "parameters": None,
112
+ "request_body": None,
113
+ "response": {
114
+ "description": "List of doctors with their specialties",
115
+ "model": "List[dict]",
116
+ "example": [
117
+ {"id": 1, "name": "Dr. Smith", "specialty": "General Medicine"}
118
+ ]
119
+ }
120
+ },
121
+ "get_doctor_availability": {
122
+ "path": "/doctors/{doctor_id}/availability",
123
+ "method": "GET",
124
+ "description": "Get doctor's fake availability slots (30-min intervals from 9am-5pm)",
125
+ "parameters": {
126
+ "doctor_id": {
127
+ "type": "int",
128
+ "required": True,
129
+ "description": "ID of the doctor"
130
+ },
131
+ "date": {
132
+ "type": "str (YYYY-MM-DD)",
133
+ "required": False,
134
+ "description": "Date to check availability (default: today)"
135
+ }
136
+ },
137
+ "request_body": None,
138
+ "response": {
139
+ "description": "Doctor's availability slots with random availability",
140
+ "model": "dict",
141
+ "example": {
142
+ "doctor_id": 1,
143
+ "date": "2023-10-15",
144
+ "slots": [
145
+ {
146
+ "start": "2023-10-15T09:00:00",
147
+ "end": "2023-10-15T09:30:00",
148
+ "available": True
149
+ }
150
+ ]
151
+ },
152
+ "error": "404 if doctor not found"
153
+ }
154
+ },
155
+ "get_patients": {
156
+ "path": "/patients",
157
+ "method": "GET",
158
+ "description": "Get list of all patients",
159
+ "parameters": None,
160
+ "request_body": None,
161
+ "response": {
162
+ "description": "List of patients with contact information",
163
+ "model": "List[Patient]",
164
+ "example": [
165
+ {"id": "uuid-123", "name": "John Doe", "phone": "555-0101", "email": "john@example.com"}
166
+ ]
167
+ }
168
+ },
169
+ "get_patient_reservations": {
170
+ "path": "/patients/reservations",
171
+ "method": "GET",
172
+ "description": "Get my all reservations",
173
+ "parameters": {
174
+ "patient_id": {
175
+ "type": "str",
176
+ "required": True,
177
+ "description": "ID of the patient"
178
+ }
179
+ },
180
+ "request_body": None,
181
+ "response": {
182
+ "description": "List of patient's reservations (all statuses)",
183
+ "model": "List[Reservation]",
184
+ "note": "Returns empty list if no reservations found (no 404 error)"
185
+ }
186
+ },
187
+ "get_hospitals": {
188
+ "path": "/hospitals",
189
+ "method": "GET",
190
+ "description": "Get list of all hospitals and thier working hours (start time and end time) and location",
191
+ "parameters": None,
192
+ }
193
+ }
final.py ADDED
@@ -0,0 +1,708 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import re
2
+ import json
3
+ import requests
4
+ import traceback
5
+ import time
6
+ import os
7
+ from typing import Dict, Any, List, Optional
8
+ from datetime import datetime, timedelta
9
+
10
+ # Updated imports for pydantic
11
+ from pydantic import BaseModel, Field
12
+
13
+ # Updated imports for LangChain
14
+ from langchain_core.prompts import PromptTemplate, ChatPromptTemplate
15
+ from langchain_core.output_parsers import JsonOutputParser
16
+ from langchain_ollama import OllamaLLM
17
+ from langchain.chains import LLMChain
18
+ from langchain.callbacks.manager import CallbackManager
19
+ from langchain.callbacks.streaming_stdout import StreamingStdOutCallbackHandler
20
+ from langchain_huggingface.embeddings import HuggingFaceEmbeddings
21
+
22
+ # Enhanced HuggingFace imports for improved functionality
23
+ from transformers import pipeline, AutoTokenizer, AutoModelForSequenceClassification
24
+ import numpy as np
25
+
26
+ # Import endpoints documentation
27
+ from endpoints_documentation import endpoints_documentation
28
+
29
+ # Set environment variables for HuggingFace
30
+ # os.environ["HF_HOME"] = "/tmp/huggingface"
31
+ if os.name == 'posix' and os.uname().sysname == 'Darwin': # Check if running on macOS
32
+ # Use macOS appropriate paths
33
+ os.environ["HF_HOME"] = os.path.expanduser("~/Library/Caches/huggingface")
34
+ os.environ["TRANSFORMERS_CACHE"] = os.path.expanduser("~/Library/Caches/huggingface/transformers")
35
+ else:
36
+ # Default paths for Linux/Windows
37
+ os.environ["HF_HOME"] = "/tmp/huggingface"
38
+ os.environ["HF_HUB_DISABLE_SYMLINKS_WARNING"] = "1"
39
+
40
+ class EndpointRequest(BaseModel):
41
+ """Data model for API endpoint requests"""
42
+ endpoint: str = Field(..., description="The API endpoint path to call")
43
+ method: str = Field(..., description="The HTTP method to use (GET or POST)")
44
+ params: Dict[str, Any] = Field(default_factory=dict, description="Parameters for the API call")
45
+ missing_required: List[str] = Field(default_factory=list, description="Any required parameters that are missing")
46
+
47
+
48
+ class AIAgent:
49
+ def __init__(self):
50
+ self.endpoints_documentation = endpoints_documentation
51
+ self.ollama_base_url = "http://localhost:11434" # Default Ollama URL
52
+ self.model_name = "mistral" # Using mistral model for better multilingual support
53
+ # self.model_name = 'llama3'
54
+ self.BASE_URL = 'https://agent.serveo.net'
55
+ self.headers = {
56
+ 'Content-type': 'application/json'
57
+ }
58
+ self.user_id = 'd8507df8-cec6-49f9-adcc-367b13805e73'
59
+ self.max_retries = 3
60
+ self.retry_delay = 2 # seconds
61
+
62
+ # Enhanced language detection using HuggingFace models
63
+ self._initialize_language_tools()
64
+
65
+ # Initialize LangChain components
66
+ self._initialize_llm()
67
+ self._initialize_parsers_and_chains()
68
+
69
+ # Add date parsing capabilities
70
+ self._initialize_date_parser()
71
+
72
+ def _initialize_language_tools(self):
73
+ """Initialize more sophisticated language processing tools"""
74
+ # Use multilingual embeddings for semantic understanding
75
+ self.embeddings = HuggingFaceEmbeddings(model_name="intfloat/multilingual-e5-large")
76
+
77
+ # Initialize language identification model
78
+ try:
79
+ self.language_classifier = pipeline(
80
+ "text-classification",
81
+ model="papluca/xlm-roberta-base-language-detection",
82
+ top_k=1
83
+ )
84
+ print("Language classification model loaded successfully")
85
+ except Exception as e:
86
+ print(f"Failed to load language classification model: {e}")
87
+ # Fallback to basic regex detection if model fails to load
88
+ self.language_classifier = None
89
+
90
+ # Add sentiment analysis for enhanced response generation
91
+ try:
92
+ self.sentiment_analyzer = pipeline(
93
+ "sentiment-analysis",
94
+ model="cardiffnlp/twitter-xlm-roberta-base-sentiment"
95
+ )
96
+ print("Sentiment analysis model loaded successfully")
97
+ except Exception as e:
98
+ print(f"Failed to load sentiment analysis model: {e}")
99
+ self.sentiment_analyzer = None
100
+
101
+ def _initialize_date_parser(self):
102
+ """Initialize date parsing model for handling relative date expressions"""
103
+ try:
104
+ self.date_parser = pipeline(
105
+ "token-classification",
106
+ model="Jean-Baptiste/roberta-large-ner-english",
107
+ aggregation_strategy="simple"
108
+ )
109
+ print("Date parsing model loaded successfully")
110
+ except Exception as e:
111
+ print(f"Failed to load date parsing model: {e}")
112
+ self.date_parser = None
113
+
114
+ def detect_language(self, text):
115
+ """
116
+ Enhanced language detection using HuggingFace models
117
+ """
118
+ # First try using the HuggingFace language classification model if available
119
+ if self.language_classifier and len(text.strip()) > 3:
120
+ try:
121
+ result = self.language_classifier(text)
122
+ detected_lang = result[0][0]['label']
123
+ confidence = result[0][0]['score']
124
+
125
+ print(f"Language detected: {detected_lang} with confidence {confidence:.4f}")
126
+
127
+ # Map the detected language to our simplified language set
128
+ if detected_lang in ['ar', 'arabic']:
129
+ return "arabic"
130
+ elif detected_lang in ['en', 'english']:
131
+ return "english"
132
+ elif confidence > 0.8: # If confident but not English/Arabic
133
+ # We currently only support English/Arabic, but log other languages
134
+ print(f"Detected unsupported language: {detected_lang}")
135
+ # Default to English for other languages for now
136
+ return "english"
137
+ except Exception as e:
138
+ print(f"Error in language detection model: {e}")
139
+ # Continue to fallback methods
140
+
141
+ # Fallback: Basic detection of Arabic text using regex
142
+ arabic_pattern = re.compile(r'[\u0600-\u06FF\u0750-\u077F\u08A0-\u08FF]+')
143
+ if arabic_pattern.search(text):
144
+ return "arabic"
145
+
146
+ # Default to English
147
+ return "english"
148
+
149
+ def analyze_sentiment(self, text):
150
+ """Analyze the sentiment of the input text"""
151
+ if self.sentiment_analyzer and len(text.strip()) > 3:
152
+ try:
153
+ result = self.sentiment_analyzer(text)
154
+ sentiment = result[0]['label']
155
+ score = result[0]['score']
156
+ return {
157
+ "sentiment": sentiment,
158
+ "score": score
159
+ }
160
+ except Exception as e:
161
+ print(f"Error in sentiment analysis: {e}")
162
+
163
+ # Default neutral sentiment if analysis fails
164
+ return {"sentiment": "NEUTRAL", "score": 0.5}
165
+
166
+ def extract_semantic_keywords(self, text, top_n=5):
167
+ """Extract semantic keywords from text using embeddings"""
168
+ try:
169
+ # Simple keyword extraction using embeddings comparison
170
+ # This is a basic implementation - could be enhanced further
171
+ words = re.findall(r'\b\w+\b', text.lower())
172
+ unique_words = list(set([w for w in words if len(w) > 3]))
173
+
174
+ if not unique_words:
175
+ return []
176
+
177
+ # Get embeddings for all words
178
+ embeddings_list = []
179
+ for word in unique_words:
180
+ try:
181
+ emb = self.embeddings.embed_query(word)
182
+ embeddings_list.append((word, emb))
183
+ except Exception as e:
184
+ print(f"Error embedding word {word}: {e}")
185
+
186
+ # Get embedding for full text
187
+ text_embedding = self.embeddings.embed_query(text)
188
+
189
+ # Calculate similarity to full text
190
+ similarities = []
191
+ for word, emb in embeddings_list:
192
+ similarity = np.dot(emb, text_embedding) / (np.linalg.norm(emb) * np.linalg.norm(text_embedding))
193
+ similarities.append((word, similarity))
194
+
195
+ # Sort by similarity
196
+ similarities.sort(key=lambda x: x[1], reverse=True)
197
+
198
+ # Return top N keywords
199
+ return [word for word, _ in similarities[:top_n]]
200
+
201
+ except Exception as e:
202
+ print(f"Error extracting keywords: {e}")
203
+ return []
204
+
205
+ def _initialize_llm(self):
206
+ """Initialize the LLM with appropriate configuration"""
207
+ # Set up the callback manager for streaming (optional)
208
+ callbacks = [StreamingStdOutCallbackHandler()]
209
+
210
+ # Initialize the Ollama LLM with updated parameters
211
+ self.llm = OllamaLLM(
212
+ model=self.model_name,
213
+ base_url=self.ollama_base_url,
214
+ callbacks=callbacks,
215
+ temperature=0.7,
216
+ num_ctx=8192, # Increased context window
217
+ top_p=0.9,
218
+ request_timeout=60, # Timeout in seconds
219
+ )
220
+
221
+ def _initialize_parsers_and_chains(self):
222
+ """Initialize output parsers and LLM chains"""
223
+ # Setup JSON parser for structured output
224
+ self.json_parser = JsonOutputParser(pydantic_object=EndpointRequest)
225
+
226
+ # Create multilingual router prompt template with enhanced context
227
+ self.router_prompt_template = PromptTemplate(
228
+ template="""
229
+ You are a precise API routing assistant. Your job is to analyze user queries and select the correct API endpoint with proper parameters.
230
+
231
+ === ENDPOINT DOCUMENTATION ===
232
+ {endpoints_documentation}
233
+
234
+ === USER REQUEST ANALYSIS ===
235
+ User Query: {user_query}
236
+ Language: {detected_language}
237
+ Keywords: {extracted_keywords}
238
+ Sentiment: {sentiment_analysis}
239
+
240
+ === ROUTING PROCESS ===
241
+ Follow these steps in order:
242
+
243
+ STEP 1: INTENT ANALYSIS
244
+ - What is the user trying to accomplish?
245
+ - What type of operation are they requesting? (create, read, update, delete, search, etc.)
246
+ - What entity/resource are they working with?
247
+
248
+ STEP 2: ENDPOINT MATCHING
249
+ - Review each endpoint in the documentation
250
+ - Match the user's intent to the endpoint's PURPOSE/DESCRIPTION
251
+ - Consider the HTTP method (GET for retrieval, POST for creation, etc.)
252
+ - Verify the endpoint can handle the user's specific request
253
+
254
+ STEP 3: PARAMETER EXTRACTION
255
+ - Identify ALL required parameters from the endpoint documentation
256
+ - Extract parameter values from the user query
257
+ - Convert data types as needed (dates to ISO 8601, numbers to integers, etc.)
258
+ - Set appropriate defaults for optional parameters if beneficial
259
+
260
+ STEP 4: VALIDATION
261
+ - Ensure ALL required parameters are provided or identified as missing
262
+ - Verify parameter formats match documentation requirements
263
+ - Check that the selected endpoint actually solves the user's problem
264
+
265
+ === RESPONSE FORMAT ===
266
+ Provide your analysis and decision in this exact JSON structure:
267
+
268
+ {{
269
+ "reasoning": {{
270
+ "user_intent": "Brief description of what the user wants to accomplish",
271
+ "selected_endpoint": "Why this endpoint was chosen over others",
272
+ "parameter_mapping": "How user query maps to endpoint parameters"
273
+ }},
274
+ "endpoint": "/exact_endpoint_path_from_documentation",
275
+ "method": "HTTP_METHOD",
276
+ "params": {{
277
+ "required_param_1": "extracted_or_converted_value",
278
+ "required_param_2": "extracted_or_converted_value",
279
+ "optional_param": "value_if_applicable"
280
+ }},
281
+ "missing_required": ["list", "of", "missing", "required", "parameters"],
282
+ "confidence": 0.95
283
+ }}
284
+
285
+ === CRITICAL RULES ===
286
+ 1. ONLY select endpoints that exist in the provided documentation
287
+ 2. NEVER fabricate or assume endpoint parameters not in documentation
288
+ 3. ALL required parameters MUST be included or listed as missing
289
+ 4. Convert dates/times to ISO 8601 format (YYYY-MM-DDTHH:MM:SS)
290
+ 5. If patient_id is required and not provided, add it to missing_required
291
+ 6. Match endpoints by PURPOSE, not just keywords in the path
292
+ 7. If multiple endpoints could work, choose the most specific one
293
+ 8. If no endpoint matches, set endpoint to null and explain in reasoning
294
+
295
+ === EXAMPLES OF GOOD MATCHING ===
296
+ - User wants "patient records" → Use patient retrieval endpoint, not general search
297
+ - User wants to "schedule appointment" → Use appointment creation endpoint
298
+ - User asks "what appointments today" → Use appointment listing with date filter
299
+ - User wants to "update medication" → Use medication update endpoint with patient_id
300
+
301
+ Think step by step and be precise with your endpoint selection and parameter extraction.
302
+ """,
303
+ input_variables=["endpoints_documentation", "user_query", "detected_language",
304
+ "extracted_keywords", "sentiment_analysis"],
305
+ partial_variables={"format_instructions": self.json_parser.get_format_instructions()}
306
+ )
307
+
308
+ # # Create user-friendly response template with enhanced context awareness
309
+ # self.user_response_template = PromptTemplate(
310
+ # template="""
311
+ # You are a professional and friendly virtual assistant for a healthcare system.
312
+ # Your task is to generate clear, concise, and professional responses to user queries.
313
+
314
+ # IMPORTANT RULES:
315
+ # - Respond ONLY in {detected_language}
316
+ # - For Arabic, use Modern Standard Arabic (فصحى)
317
+ # - Keep responses SHORT and DIRECT
318
+ # - Include ONLY essential information
319
+ # - NEVER mix languages
320
+ # - ALWAYS use the EXACT data from the system response
321
+ # - NEVER make up or modify hospital information
322
+ # - Use professional and polite tone
323
+
324
+ # Original query: {user_query}
325
+ # System result: {api_response}
326
+ # User sentiment: {sentiment_analysis}
327
+
328
+ # ARABIC RESPONSE RULES:
329
+ # - Use Arabic numbers (١، ٢، ٣)
330
+ # - Use proper date format (١٥ مايو ٢٠٢٥)
331
+ # - Use proper time format (الساعة ٨ صباحاً)
332
+ # - Use formal medical terms
333
+ # - Keep sentences short and clear
334
+ # - Use exact hospital names and addresses from the data
335
+ # - Use exact working hours from the data
336
+ # - Use professional healthcare terminology
337
+
338
+ # ENGLISH RESPONSE RULES:
339
+ # - Use clear, direct language
340
+ # - Include only essential details
341
+ # - Use proper medical terms
342
+ # - Keep responses concise
343
+ # - Use exact hospital names and addresses from the data
344
+ # - Use exact working hours from the data
345
+ # - Use professional healthcare terminology
346
+
347
+ # Remember:
348
+ # - Keep responses SHORT and FOCUSED
349
+ # - Use ONLY data from the system response
350
+ # - NEVER modify or make up hospital information
351
+ # - Include only what's necessary to answer the query
352
+ # - Maintain professional and polite tone
353
+ # - Use proper healthcare terminology
354
+ # """,
355
+ # input_variables=["user_query", "api_response", "detected_language",
356
+ # "sentiment_analysis", "extracted_keywords"]
357
+ # )
358
+ # Create user-friendly response template with enhanced context awareness
359
+ # Create user-friendly response template with enhanced context awareness
360
+ # Create user-friendly response template with enhanced context awareness
361
+ self.user_response_template = PromptTemplate(
362
+ template="""
363
+ You are a professional healthcare assistant. Generate clear, accurate responses using EXACT data from the system.
364
+
365
+ === STRICT REQUIREMENTS ===
366
+ - Respond ONLY in {detected_language}
367
+ - Use EXACT information from api_response - NO modifications
368
+ - Keep responses SHORT, SIMPLE, and DIRECT
369
+ - Use professional healthcare tone
370
+ - NEVER mix languages or make up information
371
+
372
+ === ORIGINAL REQUEST ===
373
+ User Query: {user_query}
374
+ User Sentiment: {sentiment_analysis}
375
+
376
+ === SYSTEM DATA ===
377
+ {api_response}
378
+
379
+ === LANGUAGE-SPECIFIC FORMATTING ===
380
+
381
+ FOR ARABIC RESPONSES:
382
+ - Use Modern Standard Arabic (الفصحى)
383
+ - Use Arabic numerals: ١، ٢، ٣، ٤، ٥، ٦، ٧، ٨، ٩، ١٠
384
+ - Time format: "من الساعة ٨:٠٠ صباحاً إلى ٥:٠٠ مساءً"
385
+ - Date format: "١٥ مايو ٢٠٢٥"
386
+ - Use proper Arabic medical terminology
387
+ - Keep sentences short and grammatically correct
388
+ - Example format for hospitals:
389
+ "مستشفى [الاسم] - العنوان: [العنوان الكامل] - أوقات العمل: من [الوقت] إلى [الوقت]"
390
+
391
+ FOR ENGLISH RESPONSES:
392
+ - Use clear, professional language
393
+ - Time format: "8:00 AM to 5:00 PM"
394
+ - Date format: "May 15, 2025"
395
+ - Keep sentences concise and direct
396
+ - Example format for hospitals:
397
+ "[Hospital Name] - Address: [Full Address] - Hours: [Opening Time] to [Closing Time]"
398
+
399
+ === RESPONSE STRUCTURE ===
400
+ 1. Direct answer to the user's question
401
+ 2. Essential details only (names, addresses, hours, contact info)
402
+ 3. Brief helpful note if needed
403
+ 4. No unnecessary introductions or conclusions
404
+
405
+ === CRITICAL RULES ===
406
+ - Extract information EXACTLY as provided in api_response
407
+ - Do NOT include technical URLs, IDs, or system codes in the response
408
+ - Do NOT show raw links or booking URLs to users
409
+ - Present information in natural, conversational language
410
+ - Do NOT use bullet points or technical formatting
411
+ - Write as if you're speaking to the patient directly
412
+ - If data is missing, state "المعلومات غير متوفرة" (Arabic) or "Information not available" (English)
413
+ - Convert technical data into human-readable format
414
+ - NEVER add translations or explanations in other languages
415
+ - NEVER include "Translated response" or similar phrases
416
+ - END your response immediately after providing the requested information
417
+ - Do NOT add any English translation when responding in Arabic
418
+ - Do NOT add any Arabic translation when responding in English
419
+
420
+ === HUMAN-LIKE FORMATTING RULES ===
421
+ FOR ARABIC:
422
+ - Instead of "رابط الحجز: [URL]" → say "تم حجز موعدك بنجاح"
423
+ - Instead of "الأزمة: غير متوفرة" → omit or say "بدون أعراض محددة"
424
+ - Use natural sentences like "موعدك مع الدكتور [Name] يوم [Date] في تمام الساعة [Time]"
425
+ - Avoid technical terms and system language
426
+
427
+ FOR ENGLISH:
428
+ - Instead of "Booking URL: [link]" → say "Your appointment has been scheduled"
429
+ - Use natural sentences like "You have an appointment with Dr. [Name] on [Date] at [Time]"
430
+ - Avoid showing raw URLs, IDs, or technical data
431
+
432
+ === QUALITY CHECKS ===
433
+ Before responding, verify:
434
+ ✓ Response sounds natural and conversational
435
+ ✓ No technical URLs, IDs, or system codes are shown
436
+ ✓ Information is presented in human-friendly language
437
+ ✓ Grammar is correct in the target language
438
+ ✓ Response directly answers the user's question
439
+ ✓ No bullet points or technical formatting
440
+ ✓ Sounds like a helpful human assistant, not a system
441
+
442
+ Generate a response that is accurate, helpful, and professionally formatted.
443
+
444
+ === FINAL INSTRUCTION ===
445
+ Respond ONLY in the requested language. Do NOT provide translations, explanations, or additional text in any other language. Stop immediately after answering the user's question.
446
+ """,
447
+ input_variables=["user_query", "api_response", "detected_language",
448
+ "sentiment_analysis", "extracted_keywords"]
449
+ )
450
+
451
+ # Create LLM chains
452
+ self.router_chain = LLMChain(
453
+ llm=self.llm,
454
+ prompt=self.router_prompt_template,
455
+ output_key="route_result"
456
+ )
457
+
458
+ self.user_response_chain = LLMChain(
459
+ llm=self.llm,
460
+ prompt=self.user_response_template,
461
+ output_key="user_friendly_response"
462
+ )
463
+
464
+ def parse_relative_date(self, text, detected_language):
465
+ """
466
+ Parse relative dates from text using a combination of methods
467
+ """
468
+ today = datetime.now()
469
+
470
+ # Handle common relative date patterns in English and Arabic
471
+ tomorrow_patterns = {
472
+ 'english': [r'\btomorrow\b', r'\bnext day\b'],
473
+ 'arabic': [r'\bغدا\b', r'\bبكرة\b', r'\bغدًا\b', r'\bالغد\b']
474
+ }
475
+
476
+ next_week_patterns = {
477
+ 'english': [r'\bnext week\b'],
478
+ 'arabic': [r'\bالأسبوع القادم\b', r'\bالأسبوع المقبل\b', r'\bالاسبوع الجاي\b']
479
+ }
480
+
481
+ # Check for "tomorrow" patterns
482
+ for pattern in tomorrow_patterns.get(detected_language, []) + tomorrow_patterns.get('english', []):
483
+ if re.search(pattern, text, re.IGNORECASE):
484
+ return (today + timedelta(days=1)).strftime('%Y-%m-%dT%H:%M:%S')
485
+
486
+ # Check for "next week" patterns
487
+ for pattern in next_week_patterns.get(detected_language, []) + next_week_patterns.get('english', []):
488
+ if re.search(pattern, text, re.IGNORECASE):
489
+ return (today + timedelta(days=7)).strftime('%Y-%m-%dT%H:%M:%S')
490
+
491
+ # If NER model is available, use it to extract date entities
492
+ if self.date_parser and detected_language == 'english':
493
+ try:
494
+ date_entities = self.date_parser(text)
495
+ for entity in date_entities:
496
+ if entity['entity_group'] == 'DATE':
497
+ # Here you would need more complex date parsing logic
498
+ # This is just a placeholder
499
+ print(f"Found date entity: {entity['word']}")
500
+ # For now, just default to tomorrow if we detect any date
501
+ return (today + timedelta(days=1)).strftime('%Y-%m-%dT%H:%M:%S')
502
+ except Exception as e:
503
+ print(f"Error in date parsing: {e}")
504
+
505
+ # Default return None if no date pattern is recognized
506
+ return None
507
+
508
+ def process_user_query(self, user_query: str) -> Dict[str, Any]:
509
+ """
510
+ Process the user query through the LangChain pipeline and return a response
511
+ """
512
+ try:
513
+ start_time = time.time()
514
+
515
+ # Detect language of the query
516
+ detected_language = self.detect_language(user_query)
517
+ print(f"Detected language: {detected_language}")
518
+
519
+ # Enhanced context using Hugging Face models
520
+ sentiment_result = self.analyze_sentiment(user_query)
521
+ print(f"Sentiment analysis: {sentiment_result}")
522
+
523
+ extracted_keywords = self.extract_semantic_keywords(user_query)
524
+ print(f"Extracted keywords: {extracted_keywords}")
525
+
526
+ # Try to extract dates from query
527
+ parsed_date = self.parse_relative_date(user_query, detected_language)
528
+ if parsed_date:
529
+ print(f"Parsed relative date: {parsed_date}")
530
+
531
+ # 1. Route the query to determine which API endpoint to call
532
+ router_result = self.router_chain.invoke({
533
+ "endpoints_documentation": json.dumps(self.endpoints_documentation, indent=2),
534
+ "user_query": user_query,
535
+ "detected_language": detected_language,
536
+ "extracted_keywords": ", ".join(extracted_keywords),
537
+ "sentiment_analysis": json.dumps(sentiment_result)
538
+ })
539
+
540
+ # 2. Parse the router response
541
+ route_result = router_result["route_result"]
542
+ parsed_route = None
543
+
544
+ # Clean the response first
545
+ cleaned_response = route_result
546
+
547
+ # Remove any comments (both single-line and multi-line)
548
+ cleaned_response = re.sub(r'//.*?$', '', cleaned_response, flags=re.MULTILINE)
549
+ cleaned_response = re.sub(r'/\*.*?\*/', '', cleaned_response, flags=re.DOTALL)
550
+
551
+ # Remove any trailing commas
552
+ cleaned_response = re.sub(r',(\s*[}\]])', r'\1', cleaned_response)
553
+
554
+ # Try different methods to parse the JSON response
555
+ try:
556
+ # First attempt: direct JSON parsing of cleaned response
557
+ parsed_route = json.loads(cleaned_response)
558
+ except json.JSONDecodeError:
559
+ try:
560
+ # Second attempt: extract JSON from markdown code block
561
+ json_match = re.search(r'```(?:json)?\s*(\{.*?\})\s*```', cleaned_response, re.DOTALL)
562
+ if json_match:
563
+ parsed_route = json.loads(json_match.group(1))
564
+ except (json.JSONDecodeError, AttributeError):
565
+ try:
566
+ # Third attempt: find JSON-like content using regex
567
+ json_pattern = r'\{\s*"endpoint"\s*:.*?\}'
568
+ json_match = re.search(json_pattern, cleaned_response, re.DOTALL)
569
+ if json_match:
570
+ json_str = json_match.group(0)
571
+ # Additional cleaning for the extracted JSON
572
+ json_str = re.sub(r'//.*?$', '', json_str, flags=re.MULTILINE)
573
+ json_str = re.sub(r',(\s*[}\]])', r'\1', json_str)
574
+ parsed_route = json.loads(json_str)
575
+ except (json.JSONDecodeError, AttributeError):
576
+ print(f"Failed to parse JSON. Raw response: {route_result}")
577
+ print(f"Cleaned response: {cleaned_response}")
578
+ raise ValueError("Could not extract valid JSON from LLM response")
579
+
580
+ if not parsed_route:
581
+ raise ValueError("Failed to parse LLM response into valid JSON")
582
+
583
+ # Replace any placeholder values and inject parsed dates if available
584
+ if 'params' in parsed_route:
585
+ if 'patient_id' in parsed_route['params']:
586
+ parsed_route['params']['patient_id'] = self.user_id
587
+
588
+ # Inject parsed date if available and a date parameter exists
589
+ date_params = ['appointment_date', 'date', 'schedule_date', 'date_time', 'new_date_time']
590
+ if parsed_date:
591
+ for param in date_params:
592
+ if param in parsed_route['params']:
593
+ parsed_route['params'][param] = parsed_date
594
+
595
+ print('Parsed route: ', parsed_route)
596
+ print(f"Routing completed in {time.time() - start_time:.2f} seconds")
597
+
598
+ # 3. Make the backend API call
599
+ backend_response = self.backend_call(parsed_route)
600
+
601
+ # 4. Generate user-friendly response
602
+ user_friendly_result = self.user_response_chain.invoke({
603
+ "user_query": user_query,
604
+ "api_response": json.dumps(backend_response, indent=2),
605
+ "detected_language": detected_language,
606
+ "sentiment_analysis": json.dumps(sentiment_result),
607
+ "extracted_keywords": ", ".join(extracted_keywords)
608
+ })
609
+ print('user response: ', user_friendly_result["user_friendly_response"])
610
+
611
+ print(f"Total processing time: {time.time() - start_time:.2f} seconds")
612
+
613
+ return {
614
+ "routing_info": parsed_route,
615
+ "api_response": backend_response,
616
+ "user_friendly_response": user_friendly_result["user_friendly_response"],
617
+ "detected_language": detected_language,
618
+ "sentiment": sentiment_result,
619
+ "keywords": extracted_keywords
620
+ }
621
+
622
+ except Exception as e:
623
+ error_detail = {
624
+ "error": f"Error processing query: {str(e)}",
625
+ "type": type(e).__name__,
626
+ "traceback": traceback.format_exc()
627
+ }
628
+ print(f"Error: {error_detail['error']}")
629
+ print(f"Traceback: {error_detail['traceback']}")
630
+ return error_detail
631
+
632
+ def backend_call(self, data: Dict[str, Any]) -> Dict[str, Any]:
633
+ """
634
+ Make the actual API call to the backend with retry logic
635
+ """
636
+ endpoint_url = data.get('endpoint')
637
+ endpoint_method = data.get('method')
638
+ endpoint_params = data.get('params', {}).copy() # Create a copy to avoid modifying the original
639
+
640
+ print('Endpoint url: ' + endpoint_url)
641
+ print('Method: ', endpoint_method)
642
+ print('Params: ', endpoint_params)
643
+
644
+ # Add retry logic for more robust API calls
645
+ retries = 0
646
+ while retries < self.max_retries:
647
+ try:
648
+ if endpoint_method.upper() == 'GET':
649
+ response = requests.get(
650
+ self.BASE_URL + endpoint_url,
651
+ params=endpoint_params,
652
+ headers=self.headers,
653
+ timeout=10 # Add timeout for backend calls
654
+ )
655
+ elif endpoint_method.upper() == 'POST': # POST or other methods
656
+ response = requests.post(
657
+ self.BASE_URL + endpoint_url,
658
+ json=endpoint_params,
659
+ headers=self.headers,
660
+ timeout=10
661
+ )
662
+ elif endpoint_method.upper() == 'PUT':
663
+ response = requests.put(
664
+ self.BASE_URL + endpoint_url,
665
+ json=endpoint_params,
666
+ headers=self.headers,
667
+ timeout=10
668
+ )
669
+
670
+ # Check if response status is success
671
+ response.raise_for_status()
672
+ return response.json()
673
+
674
+ except requests.exceptions.RequestException as e:
675
+ retries += 1
676
+ if retries >= self.max_retries:
677
+ return {
678
+ "error": "Backend API call failed after multiple retries",
679
+ "details": str(e),
680
+ "status_code": getattr(e.response, 'status_code', None) if hasattr(e, 'response') else None
681
+ }
682
+
683
+ print(f"API call attempt {retries} failed, retrying in {self.retry_delay} seconds...")
684
+ time.sleep(self.retry_delay)
685
+
686
+
687
+ # Initialize the AI agent singleton
688
+ # ai_agent = AIAgent()
689
+
690
+
691
+ # Test the agent directly
692
+ if __name__ == "__main__":
693
+ agent = AIAgent()
694
+
695
+ # Test with English query
696
+ # print("\n---Testing English Query---")
697
+ # english_response = agent.process_user_query("I need to book an appointment with Dr. Smith tomorrow at 8 PM")
698
+ # print("\nEnglish response:")
699
+ # print(english_response["user_friendly_response"])
700
+
701
+ # Test with Arabic query
702
+ print("\n---Testing Arabic Query---")
703
+ # arabic_response = agent.process_user_query(" اريد الغاء الحجز مع الدكتور Smith")
704
+ arabic_response = agent.process_user_query("اريد حجز ميعاد غدا في الساعه الثامنه مساء مع الدكتور Smith")
705
+ # arabic_response = agent.process_user_query("متى يفتح المستشفى؟")
706
+ # arabic_response = agent.process_user_query("اريد معرفه كل الحجوزات الخاصه بي")
707
+ print("\nArabic response:")
708
+ print(arabic_response["user_friendly_response"])
main.py ADDED
@@ -0,0 +1,42 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from fastapi import FastAPI, HTTPException
2
+ from pydantic import BaseModel
3
+ from typing import Dict, Any, Optional
4
+ from agent.final import AIAgent
5
+
6
+ app = FastAPI(
7
+ title="Healthcare AI Assistant",
8
+ description="An AI-powered healthcare assistant that handles appointment booking and queries",
9
+ version="1.0.0"
10
+ )
11
+
12
+ # Initialize the AI agent
13
+ agent = AIAgent()
14
+
15
+ class QueryRequest(BaseModel):
16
+ query: str
17
+ language: Optional[str] = None
18
+
19
+ class QueryResponse(BaseModel):
20
+ routing_info: Dict[str, Any]
21
+ api_response: Dict[str, Any]
22
+ user_friendly_response: str
23
+ detected_language: str
24
+ sentiment: Dict[str, Any]
25
+
26
+ @app.post("/query", response_model=QueryResponse)
27
+ async def process_query(request: QueryRequest):
28
+ """
29
+ Process a user query and return a response
30
+ """
31
+ try:
32
+ response = agent.process_user_query(request.query)
33
+ return response
34
+ except Exception as e:
35
+ raise HTTPException(status_code=500, detail=str(e))
36
+
37
+ @app.get("/health")
38
+ async def health_check():
39
+ """
40
+ Health check endpoint
41
+ """
42
+ return {"status": "healthy", "service": "healthcare-ai-assistant"}
requirements.txt ADDED
@@ -0,0 +1,81 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ aiohappyeyeballs==2.6.1
2
+ aiohttp==3.11.18
3
+ aiosignal==1.3.2
4
+ annotated-types==0.7.0
5
+ anyio==4.9.0
6
+ attrs==25.3.0
7
+ certifi==2025.4.26
8
+ charset-normalizer==3.4.2
9
+ click==8.2.1
10
+ dataclasses-json==0.6.7
11
+ distro==1.9.0
12
+ fastapi==0.115.12
13
+ filelock==3.18.0
14
+ frozenlist==1.6.0
15
+ fsspec==2025.3.2
16
+ greenlet==3.2.2
17
+ h11==0.16.0
18
+ httpcore==1.0.9
19
+ httpx==0.28.1
20
+ httpx-sse==0.4.0
21
+ huggingface-hub==0.31.4
22
+ idna==3.10
23
+ Jinja2==3.1.6
24
+ jiter==0.9.0
25
+ joblib==1.5.0
26
+ jsonpatch==1.33
27
+ jsonpointer==3.0.0
28
+ langchain==0.3.25
29
+ langchain-community==0.3.24
30
+ langchain-core==0.3.59
31
+ langchain-huggingface==0.2.0
32
+ langchain-ollama==0.3.2
33
+ langchain-openai==0.3.16
34
+ langchain-text-splitters==0.3.8
35
+ langdetect==1.0.9
36
+ langsmith==0.3.42
37
+ MarkupSafe==3.0.2
38
+ marshmallow==3.26.1
39
+ mpmath==1.3.0
40
+ multidict==6.4.3
41
+ mypy_extensions==1.1.0
42
+ networkx==3.4.2
43
+ numpy==1.26.4
44
+ ollama==0.4.8
45
+ openai==1.78.1
46
+ orjson==3.10.18
47
+ packaging==24.2
48
+ pillow==11.2.1
49
+ propcache==0.3.1
50
+ pydantic==2.11.4
51
+ pydantic-settings==2.9.1
52
+ pydantic_core==2.33.2
53
+ python-dotenv==1.1.0
54
+ PyYAML==6.0.2
55
+ regex==2024.11.6
56
+ requests==2.32.3
57
+ requests-toolbelt==1.0.0
58
+ safetensors==0.5.3
59
+ scikit-learn==1.6.1
60
+ scipy==1.15.3
61
+ sentence-transformers==4.1.0
62
+ six==1.17.0
63
+ sniffio==1.3.1
64
+ SQLAlchemy==2.0.40
65
+ starlette==0.46.2
66
+ sympy==1.14.0
67
+ tenacity==9.1.2
68
+ threadpoolctl==3.6.0
69
+ tiktoken==0.9.0
70
+ tokenizers==0.21.1
71
+ torch==2.2.2
72
+ tqdm==4.67.1
73
+ transformers==4.51.3
74
+ typing-inspect==0.9.0
75
+ typing-inspection==0.4.0
76
+ typing_extensions==4.13.2
77
+ urllib3==2.4.0
78
+ uvicorn==0.34.2
79
+ vaderSentiment==3.3.2
80
+ yarl==1.20.0
81
+ zstandard==0.23.0