Initial commits

This commit is contained in:
Hardik Sankhla
2025-06-27 05:56:40 +05:30
commit 202e51c8a1
22 changed files with 916 additions and 0 deletions

48
backend/.env Normal file
View File

@@ -0,0 +1,48 @@
# D:\GNPL-Private\TimeCraft\backend\.env
# .env - Environment variables for the FastAPI Backend
# --- Frontend URL for CORS configuration ---
# Set this to the URL where your React frontend is hosted.
# For local development, it's typically http://localhost:3000.
FRONTEND_URL=http://localhost:3000
# --- LLM Provider Configuration ---
# Choose which Large Language Model provider to use.
# Options:
# - GENERIC_API: For any LLM accessible via a standard HTTP API (e.g., OpenAI, Google Gemini, custom APIs)
# - HUGGINGFACE: For models loaded locally using the 'transformers' library
# - OLLAMA: For local Ollama server deployments
LLM_PROVIDER=OLLAMA
# --- Settings for GENERIC_API Provider (currently commented out as LLM_PROVIDER is OLLAMA) ---
# These variables are required if LLM_PROVIDER is set to GENERIC_API.
# LLM_API_KEY: Your API key for the chosen generic LLM service.
# LLM_API_URL: The API endpoint URL for the chosen generic LLM service.
# Examples:
# LLM_API_KEY=sk-your_openai_api_key_here
# LLM_API_URL=https://api.openai.com/v1/chat/completions
# For Google Gemini API (if using with a generic setup, though often has specific client libraries)
# LLM_API_KEY=your_google_gemini_api_key_here
# LLM_API_URL=https://generativelanguage.googleapis.com/v1beta/models/gemini-pro:generateContent
# LLM_API_KEY=your_generic_llm_api_key_here
# LLM_API_URL=https://api.openai.com/v1/chat/completions
# --- Settings for HUGGINGFACE Provider (currently commented out as LLM_PROVIDER is OLLAMA) ---
# These variables are required if LLM_PROVIDER is set to HUGGINGFACE.
# HF_MODEL_NAME: The model identifier from Hugging Face Hub (e.g., google/gemma-2b-it, meta-llama/Llama-2-7b-chat-hf).
# Ensure you have sufficient RAM/VRAM for the chosen model.
# HUGGING_FACE_HUB_TOKEN: Your Hugging Face access token, often required for private models or higher rate limits.
# Uncomment and set this if necessary.
# HF_MODEL_NAME=google/gemma-2b-it
# HUGGING_FACE_HUB_TOKEN=hf_your_hugging_face_token_here
# --- Settings for OLLAMA Provider (currently active as LLM_PROVIDER is OLLAMA) ---
# These variables are required if LLM_PROVIDER is set to OLLAMA.
# OLLAMA_MODEL_NAME: The name of the model downloaded in your local Ollama instance (e.g., llama2, mistral).
# OLLAMA_API_URL: The URL of your local Ollama API. Default is typically http://localhost:11434/api/generate.
# NOTE: Use "ollama" as the hostname inside Docker Compose, not "localhost"
OLLAMA_MODEL_NAME=qwen3:4b # CHANGED: Model updated to deepseek-r1:1.5b
OLLAMA_API_URL=http://ollama:11434/api/generate

25
backend/Dockerfile Normal file
View File

@@ -0,0 +1,25 @@
# D:\GNPL-Private\TimeCraft\backend\Dockerfile
# Use a slim, official Python image to reduce overall image size
FROM python:3.10-slim-buster
# Set the working directory inside the container
WORKDIR /app
# Copy the requirements file into the container
COPY requirements.txt .
# Install dependencies without caching to keep the image lightweight
RUN pip install --no-cache-dir -r requirements.txt
# Copy the application source code into the container
COPY ./app /app/app
COPY main.py /app/main.py
# Expose the port Uvicorn will use to serve FastAPI
EXPOSE 8000
# Define the container startup command to run the FastAPI app via Uvicorn
# - main:app -> refers to 'app' inside main.py
# - 0.0.0.0 -> binds to all interfaces so it's accessible outside container
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]

177
backend/README.md Normal file
View File

@@ -0,0 +1,177 @@
# 🧠 AI-Powered Schedule and Task Planning Backend
This repository contains the backend service for an intelligent schedule and task planning application. It leverages a **Large Language Model (LLM)** (currently using **Qwen1.5–4B via Ollama**) to transform natural language tasks with complex recurrence rules into structured, conflict-free daily schedules.
---
## ✨ Features
- **Intelligent Scheduling**: Uses a powerful LLM to understand and structure time-bound tasks.
- **Flexible Task Input**: Supports daily, weekly, monthly recurrence with specific time rules.
- **Advanced Recurrence Handling**: Supports “every 2nd Saturday” or “last Monday of month”.
- **Structured JSON Output**: Returns clean machine-readable schedules.
- **Pluggable LLM Backend**: Switch between Ollama, HuggingFace, or any generic API.
- **FastAPI + Swagger UI**: For live API testing and documentation.
- **Dockerized**: Easily run the entire system with Docker Compose.
---
## 🚀 Technologies Used
- **FastAPI** – high-performance API framework
- **Pydantic** – data validation & schema
- **HTTPLX** – async HTTP client for LLM
- **Ollama** – running Qwen1.5–4B locally
- **Docker + Compose** – containerization
- **Python 3.10+**
---
## 📂 Project Structure
```bash
backend/
├── main.py # FastAPI entry point
├── requirements.txt # Python dependencies
├── Dockerfile # Docker image config
├── docker-compose.yml # Docker orchestration
├── .env # Runtime config
└── app/
├── models/
│ └── task.py # Task schemas
├── api/
│ └── endpoints.py # FastAPI endpoints
└── services/
└── llm_service.py # LLM interaction layer
```
---
## 🔧 Configuration
`.env` example:
```env
FRONTEND_URL=http://localhost:3000
LLM_PROVIDER=OLLAMA
OLLAMA_MODEL_NAME=qwen:4b
OLLAMA_API_URL=http://localhost:11434/api/generate
```
---
## 📅 Weekly Task Schedule Visualization (Sample Week: July 14–20, 2025)
| Time | Mon (14) | Tue (15) | Wed (16) | Thu (17) | Fri (18) | Sat (19) | Sun (20) |
|--------------|----------------------------------|----------------------------------|----------------------------------|----------------------------------|----------------------------------|----------------------------------|----------------------------------|
| 06:00–07:00 | 🟢 🧘 Workout (Flexible) | 🟢 🧘 Workout (Flexible) | 🟢 🧘 Workout (Flexible) | 🟢 🧘 Workout (Flexible) | 🟢 🧘 Workout (Flexible) | 🟢 🧘 Workout (Flexible) | 🟢 🧘 Workout (Flexible) |
| 07:00–08:00 | ⭐ 🧘 Workout (Preferred) | ⭐ 🧘 Workout (Preferred) | ⭐ 🧘 Workout (Preferred) | ⭐ 🧘 Workout (Preferred) | ⭐ 🧘 Workout (Preferred) | ⭐ 🧘 Workout (Preferred) | ⭐ 🧘 Workout (Preferred) |
| 08:30–09:00 | ☕ Coffee Break | ☕ Coffee Break | ☕ Coffee Break | ☕ Coffee Break | ☕ Coffee Break | | |
| 09:00–09:15 | 🔒 ⚠️ Stand-up + Scrum 🎯 | 🔒 ⚠️ Stand-up + Scrum 🎯 | 🔒 ⚠️ Stand-up + Scrum 🎯 | 🔒 ⚠️ Stand-up + Scrum 🎯 | 🔒 ⚠️ Stand-up + Scrum 🎯 | | |
| 09:15–10:00 | ⚠️ Scrum Continues 🎯 | ⚠️ Scrum Continues 🎯 | ⚠️ Scrum Continues 🎯 | ⚠️ Scrum Continues 🎯 | ⚠️ Scrum Continues 🎯 | | |
| 10:00–11:00 | 🟢 Flex / Board Review | 🔒 🧑‍💼 Exec Meeting | 🟢 Financial Reporting | 🔒 🧑‍💼 Deep Code Review | 🟢 🎯 Team Activity | | |
| 11:00–12:00 | 🟢 Report Prep ⭐ | 🟢 Report Prep ⭐ | 🟢 Code Review / Reporting | 🔒 Code Finalization 🧑‍💼 | 🟢 🎯 Team Activity Cont. | 🔒 🧘 HR Audit (2nd Sat) | |
| 12:00–13:00 | 🍽 Lunch | 🍽 Lunch | 🍽 Lunch | 🍽 Lunch | 🍽 Lunch | 🍽 Lunch | 🍽 Lunch |
| 13:00–14:00 | ⭐ Board Meeting Prep | ⭐ Board Meeting Prep | ⭐ Board Meeting Prep | 🟢 Buffer | 🟢 Buffer | 🟢 Flex Task | 🟢 Flex Task |
| 14:00–17:00 | 🟢 Deep Work Block | 🟢 Deep Work | 🟢 Deep Work | 🟢 Deep Work | 🟢 Deep Work | 🟢 Flex Time | 🟢 Flex Time |
| 18:00–19:00 | 🧘 Workout (Fallback) | 🧘 Workout (Fallback) | 🧘 Workout (Fallback) | 🧘 Workout (Fallback) | 🧘 Workout (Fallback) | 🧘 Workout (Fallback) | 🧘 Workout (Fallback) |
| 21:00–22:00 | 🧘 Wind-down | 🧘 Wind-down | 🧘 Wind-down | 🧘 Wind-down | 🧘 Wind-down | 🧘 Wind-down | 🧘 Wind-down |
**Legend**:
🔒 Fixed • ⚠️ Conflict • ⭐ Preferred • 🟢 Flexible • 🧘 Personal • 🧑‍💼 Work • 🎯 High Impact
---
## 📬 API Endpoint: `/api/v1/schedule/tasks`
**POST Request** with list of tasks → returns intelligent JSON schedule.
Example input:
```json
[
{
"type": "daily",
"task_name": "Daily Stand-up",
"task_description": "Critical morning sync.",
"time_settings": {
"start_time": "09:00",
"end_time": "09:15"
},
"category": "work",
"repeat_on_1": "Every weekday",
"repeat_on_2": ["M", "T", "W", "T", "F"],
"reminder_on": {
"is_active": true,
"numeric_input": 10,
"time_unit": "Minutes"
}
}
]
```
Example output:
```json
{
"scheduled_tasks": [
{
"task_name": "Daily Stand-up",
"date": "2025-07-14",
"start_time": "09:00",
"end_time": "09:15",
"category": "work",
"original_task_id": "task1"
}
]
}
```
---
## 🧪 Testing
### Swagger UI
Visit: `http://localhost:8000/docs` → Try `/api/v1/schedule/tasks`
### Curl
```bash
curl -X POST http://localhost:8000/api/v1/schedule/tasks \
-H "Content-Type: application/json" \
-d '[{"type":"daily", "task_name":"Daily Stand-up", "task_description":"Sync","time_settings":{"start_time":"09:00","end_time":"09:15"},"category":"work","repeat_on_1":"Every weekday","repeat_on_2":["M","T","W","T","F"],"reminder_on":{"is_active":true,"numeric_input":10,"time_unit":"Minutes"}}]'
```
---
## ⚙️ LLM Configuration
**Current Provider**: `OLLAMA`
**Model Used**: `qwen:4b`
**API URL**: `http://localhost:11434/api/generate`
Switch to `HUGGINGFACE` or `GENERIC_API` by editing `.env`.
---
## 🧠 Troubleshooting
- **422 Unprocessable Entity**: Add fallback logic in `llm_service.py` for null times.
- **500 Server Error**: LLM may not be running or wrong model name.
- **Fix Conflicts**: Use better prompts or larger models for conflict resolution.
---
## 💡 Future Enhancements
- Conflict resolver + dependency chains
- User sessions & database storage
- Calendar frontend (React)
- Notification/reminders
- Team role scheduling
---
## 📄 License
MIT License – See `LICENSE` file.

Binary file not shown.

0
backend/app/__init__.py Normal file
View File

Binary file not shown.

View File

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,65 @@
# D:\GNPL-Private\TimeCraft\backend\app\api\endpoints.py
from fastapi import APIRouter, HTTPException, status
from typing import List
from app.models.task import TaskCreateRequest, ScheduleResponse
from app.services.llm_service import get_scheduled_tasks
# Initialize the API router
router = APIRouter()
@router.post(
"/schedule/tasks",
response_model=ScheduleResponse, # Define the response model for automatic serialization/validation
status_code=status.HTTP_200_OK, # Set the default successful status code
summary="Create and Schedule Tasks using LLM",
description="Receives a list of user-defined tasks, sends them to an LLM for intelligent scheduling, and returns a structured schedule."
)
async def create_and_schedule_tasks(tasks: List[TaskCreateRequest]):
"""
Handles the POST request to create and schedule tasks.
Args:
tasks (List[TaskCreateRequest]): A list of task objects received from the frontend.
Returns:
ScheduleResponse: A structured response containing the scheduled tasks.
Raises:
HTTPException: If no tasks are provided, or if there's an error during LLM interaction
or internal processing.
"""
# Validate if any tasks were provided in the request body
if not tasks:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="No tasks provided for scheduling. Please submit at least one task."
)
try:
# Call the LLM service to get the scheduled tasks
scheduled_response = await get_scheduled_tasks(tasks)
return scheduled_response
except RuntimeError as e:
# Catch errors specifically from LLM interaction issues (e.g., network, API issues)
print(f"LLM interaction failed: {e}") # Log the error for debugging
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=f"Failed to schedule tasks due to an issue with the LLM service. Please try again later. Details: {e}"
)
except ValueError as e:
# Catch errors related to invalid data or parsing issues from the LLM's response
print(f"Data validation or parsing error: {e}") # Log the error
raise HTTPException(
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY, # 422 for semantically erroneous request syntax
detail=f"The LLM returned an invalid schedule structure or there was an internal data processing error. Details: {e}"
)
except Exception as e:
# Catch any other unexpected errors
print(f"An unexpected error occurred in endpoint: {e}") # Log the error
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=f"An unexpected error occurred while processing your request. Please contact support. Details: {e}"
)

View File

Binary file not shown.

View File

@@ -0,0 +1,69 @@
# D:\GNPL-Private\TimeCraft\backend\app\models\task.py
from datetime import time
from enum import Enum
from typing import List, Optional, Union
from pydantic import BaseModel, Field, conint, conlist
# Type aliases for Pydantic constrained types for better readability
PositiveInt = conint(gt=0)
WeekdayList = conlist(str, min_length=0, max_length=7)
# Enum for primary recurrence periods
class RecurrenceType(str, Enum):
daily = "daily"
weekly = "weekly"
monthly = "monthly"
# Enum for task categories
class TaskCategory(str, Enum):
personal = "personal"
work = "work"
errands = "errands"
extra = "extra"
# Enum for time units used in reminders or durations
class TimeUnit(str, Enum):
minutes = "Minutes"
hours = "Hours"
days = "Days"
# Pydantic model for time settings of a task
class TimeSettings(BaseModel):
start_time: Optional[time] = Field(None, description="Specific start time (HH:MM)")
end_time: Optional[time] = Field(None, description="Specific end time (HH:MM)")
duration: Optional[str] = Field(None, description="Duration of the task (e.g., '1 Hour', '30 Minutes'). Used if start_time and end_time are empty or to validate them.")
# Pydantic model for reminder settings
class ReminderOn(BaseModel):
is_active: bool = Field(False, description="Whether a reminder is set (true/false)")
numeric_input: Optional[PositiveInt] = Field(None, description="Numeric value for reminder (e.g., 10 minutes before)")
time_unit: Optional[TimeUnit] = Field(None, description="Unit for reminder time (Minutes, Hours, Days)")
# Main Pydantic model for the task creation request from the frontend
class TaskCreateRequest(BaseModel):
type: RecurrenceType = Field(..., description="Defines the primary recurrence period (daily, weekly, monthly)")
task_name: str = Field(..., min_length=1, description="A descriptive name for the task")
task_description: Optional[str] = Field(None, description="Detailed notes or description of the task")
time_settings: TimeSettings = Field(..., description="Time settings for the task, including start, end, and duration")
category: TaskCategory = Field(..., description="Categorizes the task (personal, work, errands, extra)")
repeat_on_1: Optional[str] = Field(None, description="CRITICAL for complex recurrence patterns. Free-form text field for custom recurrence rules (e.g., 'Every second Monday')")
repeat_on_2: Optional[WeekdayList] = Field(
None,
description="Checkbox selector for days of the week (S, M, T, W, T, F, S). Users can select multiple days."
)
reminder_on: ReminderOn = Field(..., description="Reminder settings for the task")
# Pydantic model for a single scheduled task returned by the LLM
class ScheduledTask(BaseModel):
task_name: str = Field(..., description="Name of the scheduled task")
date: str = Field(..., description="Scheduled date in ISO-MM-DD format")
start_time: Optional[str] = Field(None, description="Scheduled start time in HH:MM format")
end_time: Optional[str] = Field(None, description="Scheduled end time in HH:MM format")
category: TaskCategory = Field(..., description="Category of the scheduled task")
original_task_id: Optional[str] = Field(None, description="Optional ID to link back to the original task request, useful for updates/tracking")
# reminder_on: Optional[ReminderOn] = Field(None, description="Reminder settings for the scheduled task") # Uncomment if LLM output includes this
# Pydantic model for the overall schedule response from the LLM
class ScheduleResponse(BaseModel):
scheduled_tasks: List[ScheduledTask] = Field(..., description="List of intelligently assigned scheduled tasks")

View File

View File

@@ -0,0 +1,153 @@
# D:\GNPL-Private\TimeCraft\backend\app\services\llm_service.py
import os
import json
import httpx
from typing import List
from datetime import date
from dotenv import load_dotenv
from app.models.task import TaskCreateRequest, ScheduleResponse
load_dotenv()
LLM_PROVIDER = os.getenv("LLM_PROVIDER", "OLLAMA")
LLM_API_KEY = os.getenv("LLM_API_KEY")
LLM_API_URL = os.getenv("LLM_API_URL")
HF_MODEL_NAME = os.getenv("HF_MODEL_NAME", "google/gemma-2b-it")
OLLAMA_MODEL_NAME = os.getenv("OLLAMA_MODEL_NAME", "deepseek-r1:1.5b")
OLLAMA_API_URL = os.getenv("OLLAMA_API_URL", "http://localhost:11434/api/generate")
tokenizer = None
model = None
if LLM_PROVIDER == "HUGGINGFACE":
try:
from transformers import AutoTokenizer, AutoModelForCausalLM
import torch
tokenizer = AutoTokenizer.from_pretrained(HF_MODEL_NAME)
model = AutoModelForCausalLM.from_pretrained(HF_MODEL_NAME, torch_dtype=torch.float16)
if torch.cuda.is_available():
model.to('cuda')
print(f"[LLM] Hugging Face model '{HF_MODEL_NAME}' loaded.")
except Exception as e:
print(f"[ERROR] Failed to load Hugging Face model: {e}")
raise RuntimeError("Hugging Face model loading failed.")
def infer_priority(task: TaskCreateRequest) -> str:
desc = task.task_description.lower()
if any(k in desc for k in ["non-negotiable", "critical", "mandatory", "must", "cannot be moved", "very high priority"]):
return "high"
if any(k in desc for k in ["optional", "nice to have", "if possible", "fun"]):
return "low"
return "medium"
async def get_scheduled_tasks(tasks: List[TaskCreateRequest]) -> ScheduleResponse:
today_str = date.today().isoformat()
prompt_tasks_data = [
{
**json.loads(task.model_dump_json()),
"original_task_id": f"task{index + 1}",
"priority": infer_priority(task)
}
for index, task in enumerate(tasks)
]
system_message = (
f"You are an AI-powered schedule and task planning assistant. Your job is to create a conflict-free, "
f"intelligent 30-day schedule from today ({today_str}).\n\n"
"Parse task details precisely: type, time_settings (start_time, end_time, duration), category, and priority.\n"
"Avoid time conflicts. If two fixed-time tasks clash, prioritize the 'high' priority one. Try to shift "
"'medium' priority tasks to nearby times. Skip low priority if no space.\n\n"
"Respect fixed times first. For flexible tasks, use hints.\n"
"Return strictly valid JSON:\n"
"{\n"
" \"scheduled_tasks\": [\n"
" {\n"
" \"task_name\": \"string\",\n"
" \"date\": \"YYYY-MM-DD\",\n"
" \"start_time\": \"HH:MM\",\n"
" \"end_time\": \"HH:MM\",\n"
" \"category\": \"personal | work | errands | extra\",\n"
" \"original_task_id\": \"string\"\n"
" }\n"
" ]\n"
"}"
)
user_message = f"Here are the tasks to schedule:\n{json.dumps(prompt_tasks_data, indent=2)}"
full_prompt = f"{system_message}\n\n{user_message}"
llm_output_content = ""
try:
if LLM_PROVIDER == "GENERIC_API":
if not LLM_API_URL or not LLM_API_KEY:
raise ValueError("Missing LLM_API_URL or LLM_API_KEY in .env")
headers = {
"Content-Type": "application/json",
"Authorization": f"Bearer {LLM_API_KEY}"
}
payload = {
"model": "your-preferred-llm-model-name",
"messages": [
{"role": "system", "content": system_message},
{"role": "user", "content": user_message}
],
"response_format": {"type": "json_object"}
}
async with httpx.AsyncClient(timeout=httpx.Timeout(600.0, connect=60.0)) as client:
response = await client.post(LLM_API_URL, headers=headers, json=payload)
response.raise_for_status()
llm_output_content = response.json().get("choices", [{}])[0].get("message", {}).get("content", "")
elif LLM_PROVIDER == "HUGGINGFACE":
import torch
input_ids = tokenizer(full_prompt, return_tensors="pt").input_ids
if torch.cuda.is_available():
input_ids = input_ids.to("cuda")
outputs = model.generate(input_ids, max_new_tokens=1024, pad_token_id=tokenizer.eos_token_id)
llm_output_content = tokenizer.decode(outputs[0], skip_special_tokens=True)
elif LLM_PROVIDER == "OLLAMA":
payload = {
"model": OLLAMA_MODEL_NAME,
"prompt": full_prompt,
"stream": False,
"format": "json"
}
async with httpx.AsyncClient(timeout=httpx.Timeout(600.0, connect=60.0)) as client:
print("[LLM] Sending prompt to Ollama...")
response = await client.post(OLLAMA_API_URL, json=payload)
response.raise_for_status()
llm_output_content = response.json().get("response", "")
print("[LLM] Received response (first 500 chars):")
print(llm_output_content[:500])
else:
raise ValueError(f"Unsupported LLM_PROVIDER: {LLM_PROVIDER}")
json_start = llm_output_content.find('{')
json_end = llm_output_content.rfind('}')
if json_start == -1 or json_end == -1:
raise ValueError("No valid JSON object found in LLM output.")
json_string = llm_output_content[json_start:json_end + 1]
parsed_data = json.loads(json_string)
return ScheduleResponse(**parsed_data)
except httpx.HTTPStatusError as e:
print(f"[ERROR] Ollama returned HTTP {e.response.status_code}: {e.response.text}")
raise RuntimeError(f"LLM API returned error {e.response.status_code}: {e.response.text}")
except httpx.RequestError as e:
print(f"[ERROR] Request to LLM service failed: {e}")
raise RuntimeError(f"Failed to connect to LLM service. Details: {e}")
except json.JSONDecodeError as e:
print(f"[ERROR] JSON decoding failed: {e}")
print(f"[DEBUG] Raw output: {llm_output_content}")
raise ValueError("LLM did not return valid JSON.")
except Exception as e:
print(f"[ERROR] Unexpected exception: {e}")
raise RuntimeError(f"Failed to schedule tasks due to an issue with the LLM service. Details: {e}")

View File

@@ -0,0 +1,40 @@
services:
backend:
build:
context: .
ports:
- "8000:8000"
volumes:
- ./.env:/app/.env
restart: unless-stopped
depends_on:
ollama:
condition: service_healthy
ollama:
image: ollama/ollama
ports:
- "11434:11434"
volumes:
- ollama_data:/root/.ollama
container_name: ollama
restart: unless-stopped
# Override default entrypoint to allow shell command chaining
entrypoint: ["/bin/sh", "-c"]
command: >
"ollama serve &
sleep 5 &&
until ollama list > /dev/null 2>&1; do sleep 3; done &&
ollama pull qwen3:4b &&
wait"
healthcheck:
test: ["CMD", "ollama", "list"]
interval: 15s
timeout: 10s
retries: 6
start_period: 120s
volumes:
ollama_data:

55
backend/main.py Normal file
View File

@@ -0,0 +1,55 @@
# D:\GNPL-Private\TimeCraft\backend\main.py
import os
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from dotenv import load_dotenv # For loading environment variables
from app.api import endpoints # Import your API router
# Load environment variables from the .env file.
# This should be called early in your application's lifecycle.
load_dotenv()
# Initialize the FastAPI application instance
# Add additional metadata for professional API documentation
app = FastAPI(
title="AI-Powered Schedule and Task Planning Backend",
description="FastAPI backend for ingesting user tasks, using an LLM for intelligent scheduling, and returning structured schedules.",
version="1.0.0",
docs_url="/docs", # Enable interactive API documentation (Swagger UI)
redoc_url="/redoc", # Enable ReDoc documentation
openapi_url="/openapi.json" # Specify the OpenAPI schema URL
)
# Configure CORS (Cross-Origin Resource Sharing) middleware.
# This is crucial for allowing your React frontend (running on a different port/domain)
# to make requests to your FastAPI backend.
origins = [
os.getenv("FRONTEND_URL", "http://localhost:3000"), # Get frontend URL from .env, default to React's development port
# Add other origins if your frontend is deployed elsewhere or has multiple access points
# e.g., "https://your-production-frontend.com",
# "http://localhost:8080" # If testing with another local frontend setup
]
app.add_middleware(
CORSMiddleware,
allow_origins=origins, # List of allowed origins
allow_credentials=True, # Allow cookies and authentication headers to be sent
allow_methods=["*"], # Allow all HTTP methods (GET, POST, PUT, DELETE, etc.)
allow_headers=["*"], # Allow all headers in the request
)
# Include the API router defined in app.api.endpoints.
# All routes defined in endpoints.py will be prefixed with "/api/v1".
app.include_router(endpoints.router, prefix="/api/v1")
# Define a root endpoint for basic health check or welcome message
@app.get("/", summary="Root Endpoint", description="Provides a welcome message for the API.")
async def root():
"""
Root endpoint for the API.
Returns a simple welcome message to indicate the API is running.
"""
return {"message": "Welcome to the AI-Powered Schedule and Task Planning API! Access docs at /docs"}

9
backend/requirements.txt Normal file
View File

@@ -0,0 +1,9 @@
# D:\GNPL-Private\TimeCraft\backend\requirements.txt
fastapi
uvicorn[standard]
pydantic # Specify a compatible Pydantic version
python-dotenv
httpx
#transformers
#torch

275
documentation/README.md Normal file
View File

@@ -0,0 +1,275 @@
# Workflow
## Input: Json
```json
COMPLEX COMPLEX COMPLEX COMPLEX COMPLEX COMPLEX COMPLEX COMPLEX COMPLEX COMPLEX COMPLEX
COMPLEX COMPLEX COMPLEX COMPLEX COMPLEX COMPLEX COMPLEX COMPLEX COMPLEX COMPLEX COMPLEX
COMPLEX COMPLEX COMPLEX COMPLEX COMPLEX COMPLEX COMPLEX COMPLEX COMPLEX COMPLEX COMPLEX
[
{
"type": "daily",
"task_name": "Critical Daily Stand-up",
"task_description": "Non-negotiable daily sync for core project team. Early birds only.",
"time_settings": {
"start_time": "09:00",
"end_time": "09:15",
"duration": null
},
"category": "work",
"repeat_on_1": "Every weekday, but skip public holidays (LLM to infer)",
"repeat_on_2": ["M", "T", "W", "T", "F"],
"reminder_on": {
"is_active": true,
"numeric_input": 5,
"time_unit": "Minutes"
}
},
{
"type": "daily",
"task_name": "Daily Team Scrum",
"task_description": "New mandatory daily team scrum, might conflict with other meetings. Needs 1 hour.",
"time_settings": {
"start_time": "09:00",
"end_time": "10:00",
"duration": null
},
"category": "work",
"repeat_on_1": "Every Monday to Friday. Very high priority.",
"repeat_on_2": ["M", "T", "W", "T", "F"],
"reminder_on": {
"is_active": true,
"numeric_input": 10,
"time_unit": "Minutes"
}
},
{
"type": "weekly",
"task_name": "Executive Leadership Meeting",
"task_description": "Mandatory weekly meeting with executive board. Fixed slot.",
"time_settings": {
"start_time": "09:00",
"end_time": "10:00",
"duration": null
},
"category": "work",
"repeat_on_1": "Every Tuesday, but starting from the 3rd week of the month only. Cannot be moved.",
"repeat_on_2": ["T"],
"reminder_on": {
"is_active": true,
"numeric_input": 15,
"time_unit": "Minutes"
}
},
{
"type": "monthly",
"task_name": "Monthly HR Check-in",
"task_description": "Meet with HR for personal development. Fixed slot, 1 hour.",
"time_settings": {
"start_time": "11:00",
"end_time": "12:00",
"duration": null
},
"category": "personal",
"repeat_on_1": "Every second Saturday of the month.",
"repeat_on_2": ["S"],
"reminder_on": {
"is_active": true,
"numeric_input": 1,
"time_unit": "Days"
}
},
{
"type": "monthly",
"task_name": "Board Meeting Prep & Review",
"task_description": "Extensive preparation for monthly board meeting. Flexible, 8-hour task, needs continuous block if possible.",
"time_settings": {
"start_time": null,
"end_time": null,
"duration": "8 Hours"
},
"category": "work",
"repeat_on_1": "Any weekday in the first week of every month, as long as it's not Monday or Friday. Prefer early morning. Highly flexible on exact time within that day.",
"repeat_on_2": ["T", "W", "T"],
"reminder_on": {
"is_active": true,
"numeric_input": 2,
"time_unit": "Days"
}
},
{
"type": "daily",
"task_name": "Personal Workout Session",
"task_description": "Daily exercise, needs 45 minutes, flexible around work hours. Prioritize morning but accept evening.",
"time_settings": {
"start_time": null,
"end_time": null,
"duration": "45 Minutes"
},
"category": "personal",
"repeat_on_1": "Daily, prefer 07:00-08:00 AM OR 18:00-19:00 PM, but can be done anytime between 6 AM and 10 PM. Prioritize morning.",
"repeat_on_2": ["S", "M", "T", "W", "T", "F", "S"],
"reminder_on": {
"is_active": true,
"numeric_input": 30,
"time_unit": "Minutes"
}
},
{
"type": "weekly",
"task_name": "Team Building Activity",
"task_description": "Organize a fun team activity. Needs 2 hours, afternoon only.",
"time_settings": {
"start_time": null,
"end_time": null,
"duration": "2 Hours"
},
"category": "extra",
"repeat_on_1": "Last Friday of every second month (starting from this month), flexible between 2 PM and 5 PM.",
"repeat_on_2": ["F"],
"reminder_on": {
"is_active": true,
"numeric_input": 1,
"time_unit": "Days"
}
},
{
"type": "monthly",
"task_name": "Complex Financial Report",
"task_description": "Prepare the complex financial report for audit. Requires 5 hours, ideally split over two consecutive days if necessary.",
"time_settings": {
"start_time": null,
"end_time": null,
"duration": "5 Hours"
},
"category": "work",
"repeat_on_1": "Any two consecutive weekdays in the third week of the month, prioritizing Tuesday-Wednesday or Wednesday-Thursday. Flexible 09:00-17:00.",
"repeat_on_2": ["M", "T", "W", "T", "F"],
"reminder_on": {
"is_active": true,
"numeric_input": 7,
"time_unit": "Days"
}
},
{
"type": "daily",
"task_name": "Morning Coffee Break",
"task_description": "A short 15-minute break. Highly flexible, but must be before 11 AM, avoiding any fixed meetings.",
"time_settings": {
"start_time": null,
"end_time": null,
"duration": "15 Minutes"
},
"category": "personal",
"repeat_on_1": "Daily, any time between 09:15 and 11:00 AM, avoiding other fixed meetings.",
"repeat_on_2": ["M", "T", "W", "T", "F"],
"reminder_on": {
"is_active": false,
"numeric_input": null,
"time_unit": null
}
},
{
"type": "weekly",
"task_name": "Deep Dive Code Review",
"task_description": "Dedicated slot for reviewing complex PRs. Needs 3 hours, strictly afternoon.",
"time_settings": {
"start_time": null,
"end_time": null,
"duration": "3 Hours"
},
"category": "work",
"repeat_on_1": "Every Thursday, after 11:00 AM, but before 4:00 PM. Cannot overlap with any other work meetings.",
"repeat_on_2": ["T"],
"reminder_on": {
"is_active": true,
"numeric_input": 4,
"time_unit": "Hours"
}
},
{
"type": "monthly",
"task_name": "Personal Finance Audit",
"task_description": "Review personal budget and investments. 1 hour, weekday only.",
"time_settings": {
"start_time": null,
"end_time": null,
"duration": "1 Hour"
},
"category": "personal",
"repeat_on_1": "The 15th of every month, if it's a weekday. Otherwise, the closest preceding weekday.",
"repeat_on_2": ["M", "T", "W", "T", "F"],
"reminder_on": {
"is_active": true,
"numeric_input": 24,
"time_unit": "Hours"
}
},
{
"type": "daily",
"task_name": "Evening Wind-Down",
"task_description": "Read a book or meditate. 30 minutes, before bed. No fixed time.",
"time_settings": {
"start_time": null,
"end_time": null,
"duration": "30 Minutes"
},
"category": "personal",
"repeat_on_1": "Daily, between 9 PM and 11 PM. Avoid any fixed late-night calls.",
"repeat_on_2": ["S", "M", "T", "W", "T", "F", "S"],
"reminder_on": {
"is_active": false,
"numeric_input": null,
"time_unit": null
}
}
]
```
# 📅 Weekly Task Schedule Visualization
**Sample Week: July 14–20, 2025**
| Time | Mon (14) | Tue (15) | Wed (16) | Thu (17) | Fri (18) | Sat (19) | Sun (20) |
|-------------|---------------------------------|---------------------------------|---------------------------------|---------------------------------|---------------------------------|---------------------------------|---------------------------------|
| 06:00–07:00 | 🟢 🧘 Workout (Flexible) | 🟢 🧘 Workout (Flexible) | 🟢 🧘 Workout (Flexible) | 🟢 🧘 Workout (Flexible) | 🟢 🧘 Workout (Flexible) | 🟢 🧘 Workout (Flexible) | 🟢 🧘 Workout (Flexible) |
| 07:00–08:00 | ⭐ 🧘 Workout (Preferred) | ⭐ 🧘 Workout (Preferred) | ⭐ 🧘 Workout (Preferred) | ⭐ 🧘 Workout (Preferred) | ⭐ 🧘 Workout (Preferred) | ⭐ 🧘 Workout (Preferred) | ⭐ 🧘 Workout (Preferred) |
| 08:30–09:00 | 🟢 ☕ Coffee Break | 🟢 ☕ Coffee Break | 🟢 ☕ Coffee Break | 🟢 ☕ Coffee Break | 🟢 ☕ Coffee Break | | |
| 09:00–09:15 | 🔒 ⚠️ Stand-up + Scrum 🎯 | 🔒 ⚠️ Stand-up + Scrum 🎯 | 🔒 ⚠️ Stand-up + Scrum 🎯 | 🔒 ⚠️ Stand-up + Scrum 🎯 | 🔒 ⚠️ Stand-up + Scrum 🎯 | | |
| 09:15–10:00 | ⚠️ Scrum Continues 🎯 | ⚠️ Scrum Continues 🎯 | ⚠️ Scrum Continues 🎯 | ⚠️ Scrum Continues 🎯 | ⚠️ Scrum Continues 🎯 | | |
| 10:00–11:00 | 🟢 Board Prep / Overflow Slot | 🔒 🧑‍💼 Exec Leadership Meeting | 🟢 Report Split (Day 2) | 🔒 🧑‍💼 Deep Code Review | 🟢 🎯 Team Activity Slot | | |
| 11:00–12:00 | 🟢 Report Prep ⭐ | 🟢 Report Prep ⭐ | 🟢 Report or Code Review | 🔒 🧑‍💼 Code Review Ends | 🟢 Team Activity Cont. | 🔒 🧘 HR Check-in (2nd Sat) | |
| 12:00–13:00 | 🟢 🍽 Lunch / Overflow | 🟢 🍽 Lunch / Overflow | 🟢 🍽 Lunch / Overflow | 🟢 🍽 Lunch / Overflow | 🟢 🍽 Lunch / Overflow | 🟢 🍽 Lunch / Overflow | 🟢 🍽 Lunch / Overflow |
| 13:00–14:00 | 🟢 ⭐ Board Meeting Prep | 🟢 ⭐ Board Meeting Prep | 🟢 ⭐ Board Meeting Prep | 🟢 Board Prep / Flex | 🟢 Team Activity Overflow | 🟢 Free | 🟢 Free |
| 14:00–17:00 | 🟢 Deep Work / Report Block | 🟢 Deep Work | 🟢 Deep Work | 🟢 Deep Work | 🟢 Deep Work | 🟢 Flex Time | 🟢 Flex Time |
| 18:00–19:00 | 🧘 Workout (Fallback) | 🧘 Workout (Fallback) | 🧘 Workout (Fallback) | 🧘 Workout (Fallback) | 🧘 Workout (Fallback) | 🧘 Workout (Fallback) | 🧘 Workout (Fallback) |
| 21:00–22:00 | 🧘 Evening Wind-Down | 🧘 Evening Wind-Down | 🧘 Evening Wind-Down | 🧘 Evening Wind-Down | 🧘 Evening Wind-Down | 🧘 Evening Wind-Down | 🧘 Evening Wind-Down |
---
### 🔵 Legend
- 🔒 = Fixed time slot
- ⚠️ = Conflict or overlap
- ⭐ = Preferred or high-priority
- 🧘 = Personal task
- 🧑‍💼 = Work task
- 🎯 = High-impact / urgent
- 🟢 = Flexible slot
- ☕ = Break
- 🍽 = Lunch
---
### 🧠 Notes & Insights
- **09:00 Conflict:** Critical Daily Stand-up and Team Scrum overlap every weekday → consider merging or shifting.
- **Board Prep & Financial Reports** are scheduled with max flexibility to avoid blocking.
- **Workouts** are morning-preferred with evening fallback for sustainability.
- **Personal wellness tasks** are spread across early morning and late evening with no overlaps.
---
Want this in `.csv`, `.ics`, or as a visual HTML calendar? Just let me know.