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
38 changes: 35 additions & 3 deletions Backend/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
from routes.weight import weight_bp
from routes.blood_pressure import bp_bp
from routes.discharge import discharge_bp
from agent.agent import get_agent

app = Flask(__name__)
CORS(app)
Expand All @@ -32,10 +31,21 @@ def teardown_db(exception):
db_path = os.path.join(os.path.dirname(__file__), "db", "database.db")
first_time_setup() # This needs to be called before initializing the agent

agent = get_agent(db_path)
# Check if agent initialization should be skipped
skip_agent_init = os.getenv('SKIP_AGENT_INIT', '0').lower() in ('1', 'true', 'yes')
if skip_agent_init:
agent = None
print("⚠️ Agent initialization skipped (SKIP_AGENT_INIT=1)")
else:
from agent.agent import get_agent
agent = get_agent(db_path)
print("🤖 Agent initialized successfully")

@app.route("/agent", methods=["POST"])
def run_agent():
if agent is None:
return jsonify({"error": "Agent not initialized"}), 503

if not request.is_json:
return jsonify({"error": "Invalid JSON format"}), 400

Expand All @@ -59,6 +69,9 @@ def run_agent():
@app.route("/agent/cache/status", methods=["GET"])
def get_cache_status():
"""Get cache status information."""
if agent is None:
return jsonify({"error": "Agent not initialized"}), 503

try:
user_id = request.args.get("user_id", "default")
user_context = agent.get_user_context(user_id)
Expand All @@ -83,6 +96,9 @@ def get_cache_status():
@app.route("/agent/context", methods=["GET"])
def get_agent_context():
"""Get the current agent context for frontend use."""
if agent is None:
return jsonify({"error": "Agent not initialized"}), 503

try:
user_id = request.args.get("user_id", "default")
context = agent.get_user_context(user_id)
Expand All @@ -109,6 +125,9 @@ def get_agent_context():
@app.route("/agent/tasks/recommendations", methods=["GET"])
def get_task_recommendations():
"""Get LLM-powered task recommendations based on current context."""
if agent is None:
return jsonify({"error": "Agent not initialized"}), 503

try:
user_id = request.args.get("user_id", "default")
week = request.args.get("week")
Expand Down Expand Up @@ -140,6 +159,9 @@ def get_task_recommendations():
@app.route("/agent/cache/stats", methods=["GET"])
def get_cache_statistics():
"""Get detailed cache statistics for monitoring."""
if agent is None:
return jsonify({"error": "Agent not initialized"}), 503

try:
stats = agent.get_cache_stats()
return jsonify({
Expand All @@ -163,6 +185,9 @@ def get_cache_statistics():
@app.route("/agent/cache/cleanup", methods=["POST"])
def cleanup_cache():
"""Manually trigger cache cleanup."""
if agent is None:
return jsonify({"error": "Agent not initialized"}), 503

try:
agent.cleanup_cache()
stats = agent.get_cache_stats()
Expand All @@ -182,7 +207,14 @@ def index():
task_db = get_tasks()
return appointment_db

@app.route('/health')
def health():
return jsonify({
'status': 'ok',
'agent_initialized': agent is not None
})

if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000, debug=True)
app.run(host='0.0.0.0', port=5000, debug=False)


2 changes: 1 addition & 1 deletion Backend/db/db.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from flask import g

DATABASE = "db/database.db"
SCHEMA_FILE = "schema.sql"
SCHEMA_FILE = os.path.join(os.path.dirname(__file__), "..", "schema.sql")

def open_db():
if "db" not in g:
Expand Down
Binary file modified Backend/requirements.txt
Binary file not shown.
14 changes: 14 additions & 0 deletions Frontend/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,20 @@ The model is configured in `src/model/model.jsx`:
- `POST /agent/refresh` - Refresh context cache
- `GET /agent/cache/status` - Check cache status

### Agent Availability

The app gracefully handles scenarios where the backend agent is unavailable:

- **Fast Development Mode**: When `SKIP_AGENT_INIT=1` is set in backend
- **Backend Down**: When Flask server is not running
- **Agent Initialization Failure**: When agent fails to load

**Behavior when agent is unavailable:**
- Shows a clear banner: "Backend agent is disabled (fast dev mode). Using local model only – some context-aware features may be unavailable."
- Chat falls back to local GGUF model for general conversations
- Structured commands (appointments, health tracking) still work via direct API calls
- Context-aware features are disabled but app remains functional

## 📱 App Structure

### Core Components
Expand Down
40 changes: 37 additions & 3 deletions Frontend/src/Screens/ChatScreen.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import { conversationContext } from '../services/ConversationContext';
export default function ChatScreen() {
const navigation = useNavigation();
const { theme } = useTheme();
const { context, refreshContext, initializeContext, isInitialized } = useAgentContext();
const { context, refreshContext, initializeContext, isInitialized, agentAvailable, healthChecked, checkHealth } = useAgentContext();

// Toggle command examples with animation
const toggleCommandExamples = () => {
Expand Down Expand Up @@ -162,6 +162,10 @@ export default function ChatScreen() {
}
}, [conversation.length, showCommandExamples]);

// Check agent health on component mount
useEffect(() => {
checkHealth();
}, []);

const handleSendMessage = async () => {
if (!userInput.trim()) {
Expand Down Expand Up @@ -206,10 +210,10 @@ export default function ChatScreen() {
// RAG Mode (Robot) - Process structured commands
if (conversationContext.hasPendingFollowUp()) {
console.log('🤖 Processing follow-up response with RAG...');
result = await conversationContext.processFollowUpResponse(userInput, ragService);
result = await conversationContext.processFollowUpResponse(userInput, ragService, agentAvailable);
} else {
console.log('🤖 Processing new query with RAG...');
result = await ragService.processQuery(userInput, context);
result = await ragService.processQuery(userInput, context, agentAvailable);
}
} else {
// Model Mode (Phone) - Use backend model for general chat
Expand Down Expand Up @@ -520,6 +524,16 @@ export default function ChatScreen() {
</TouchableOpacity>
</View>

{/* Agent Availability Banner */}
{healthChecked && !agentAvailable && (
<View style={[styles.agentBanner, { backgroundColor: theme.warning || '#fff3cd' }]}>
<Icon name="info" size={20} color={theme.warningText || '#856404'} />
<Text style={[styles.agentBannerText, { color: theme.warningText || '#856404' }]}>
Backend agent is disabled (fast dev mode). Using local model only – some context-aware features may be unavailable.
</Text>
</View>
)}

{/* Compact Command Examples - Always Visible */}
<Animated.View
style={{
Expand Down Expand Up @@ -709,6 +723,26 @@ const styles = StyleSheet.create({
headerSpacer: {
width: 40, // Same width as the back button to center the title
},
agentBanner: {
flexDirection: 'row',
alignItems: 'center',
paddingHorizontal: 16,
paddingVertical: 8,
marginHorizontal: 16,
marginTop: 8,
borderRadius: 8,
elevation: 1,
shadowColor: '#000',
shadowOffset: { width: 0, height: 1 },
shadowOpacity: 0.2,
shadowRadius: 2,
},
agentBannerText: {
fontSize: 12,
marginLeft: 8,
flex: 1,
lineHeight: 16,
},
modeToggleButton: {
padding: 8,
borderRadius: 20,
Expand Down
29 changes: 29 additions & 0 deletions Frontend/src/context/AgentContext.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ export const AgentProvider = ({ children }) => {
const [error, setError] = useState(null);
const [lastUpdated, setLastUpdated] = useState(null);
const [isInitialized, setIsInitialized] = useState(false);
const [agentAvailable, setAgentAvailable] = useState(true); // Assume available by default
const [healthChecked, setHealthChecked] = useState(false);

const fetchContext = async (user_id = "default", force = false) => {
// Don't fetch if already initialized and not forced
Expand Down Expand Up @@ -65,6 +67,30 @@ export const AgentProvider = ({ children }) => {
}
};

const checkHealth = async () => {
try {
const response = await fetch(`${BASE_URL}/health`, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
},
});

if (response.ok) {
const data = await response.json();
setAgentAvailable(data.agent_initialized);
} else {
// If health endpoint fails, assume agent is unavailable
setAgentAvailable(false);
}
} catch (err) {
console.warn('Health check failed, assuming agent unavailable:', err.message);
setAgentAvailable(false);
} finally {
setHealthChecked(true);
}
};

const refreshContext = async (user_id = "default") => {
try {
const response = await fetch(`${BASE_URL}/agent/refresh`, {
Expand Down Expand Up @@ -158,10 +184,13 @@ export const AgentProvider = ({ children }) => {
error,
lastUpdated,
isInitialized,
agentAvailable,
healthChecked,
fetchContext,
refreshContext,
initializeContext,
isContextReady,
checkHealth,
getTaskRecommendations,
getCacheStatus,
};
Expand Down
4 changes: 2 additions & 2 deletions Frontend/src/services/ConversationContext.js
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ class ConversationContext {
/**
* Process follow-up response
*/
async processFollowUpResponse(userQuery, ragService) {
async processFollowUpResponse(userQuery, ragService, agentAvailable = true) {
if (!this.pendingFollowUp) {
return null;
}
Expand Down Expand Up @@ -183,7 +183,7 @@ class ConversationContext {
// All information gathered, execute the action
const intent = this.pendingFollowUp.intent;
this.clearPendingFollowUp();
return await ragService.executeAction(intent, mergedData, this.userContext);
return await ragService.executeAction(intent, mergedData, this.userContext, agentAvailable);
}
} catch (error) {
console.error('Follow-up processing error:', error);
Expand Down
20 changes: 14 additions & 6 deletions Frontend/src/services/RAGService.js
Original file line number Diff line number Diff line change
Expand Up @@ -630,7 +630,7 @@ class RAGService {
/**
* Process user query with semantic understanding
*/
async processQuery(userQuery, userContext = {}) {
async processQuery(userQuery, userContext = {}, agentAvailable = true) {
if (!this.isInitialized) {
await this.initialize();
}
Expand All @@ -653,7 +653,7 @@ class RAGService {
}

// 4. Execute the action
return await this.executeAction(bestIntent, extractedData, userContext);
return await this.executeAction(bestIntent, extractedData, userContext, agentAvailable);

} catch (error) {
console.error('RAG processing error:', error);
Expand Down Expand Up @@ -1357,7 +1357,6 @@ class RAGService {
'symptom': '• **What symptoms** are you experiencing? (e.g., "nausea", "headache")',
'systolic': '• **What\'s your systolic pressure**? (e.g., "120")',
'diastolic': '• **What\'s your diastolic pressure**? (e.g., "80")',
'name': '• **What medicine** did you take? (e.g., "paracetamol", "iron")',
'dose': '• **What dose** did you take? (e.g., "500mg", "2 tablets")',
'type': '• **What type of discharge**? (e.g., "normal", "spotting")',
'color': '• **What color** is it? (e.g., "clear", "white", "pink")',
Expand Down Expand Up @@ -1447,7 +1446,7 @@ class RAGService {
/**
* Execute the determined action
*/
async executeAction(intent, data, userContext) {
async executeAction(intent, data, userContext, agentAvailable = true) {
try {
switch (intent.action) {
case 'createAppointment':
Expand Down Expand Up @@ -1497,7 +1496,7 @@ class RAGService {
case 'logout':
return await this.logout(data, userContext);
case 'generalChat':
return await this.handleGeneralChat(data, userContext);
return await this.handleGeneralChat(data, userContext, agentAvailable);

// Medicine CRUD operations
case 'updateMedicine':
Expand Down Expand Up @@ -2355,7 +2354,16 @@ class RAGService {
/**
* Handle general chat
*/
async handleGeneralChat(data, userContext) {
async handleGeneralChat(data, userContext, agentAvailable = true) {
// If agent is not available, skip backend call and return a generic response
if (!agentAvailable) {
return {
success: true,
message: 'I\'m here to help with your pregnancy journey! How can I assist you today?',
action: 'generalChat'
};
}

try {
const response = await fetch(`${BASE_URL}/agent`, {
method: 'POST',
Expand Down
3 changes: 1 addition & 2 deletions Frontend/src/theme/ThemeContext.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,7 @@ const themes = {
button: '#9e2a2b',
factcardprimary: '#e09f3e',
factcardsecondary: '#e09f3e',
appointment: '#fff3b0',
iconText:'#FFFFFF'
appointment: '#fff3b0'
},
pastel: {
primary: '#AC87C5',
Expand Down
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,14 @@
- **AI & NLP**: Python, LangChain
- **Database**: SQLite

## Quick Start

1. Clone the repository and navigate to the project directory.
2. For frontend: `cd Frontend && npm install && npx react-native run-android` (or `run-ios`).
3. For backend: `cd Backend && python -m venv .venv && source .venv/bin/activate && pip install -r requirements.txt && python app.py`.

For detailed setup instructions, see [Setup.md](Setup.md).

## Usage

- Open the mobile app on an emulator or device.
Expand Down
Loading