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
21 changes: 21 additions & 0 deletions forecast_dashboard/.dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Keep the Docker build context small + avoid baking local junk into the image.
.git
.venv
__pycache__/
*.py[cod]
*.egg-info/
.ipynb_checkpoints/
.pytest_cache/

# Local data / secrets / logs (the image uses /tmp + build-time HF cache)
.env
*.db
*.sqlite
*.log

# The image clones Kronos itself at build time; don't ship a local clone.
Kronos/

# Deploy helpers + docs not needed inside the container
deploy/_*
*.md
44 changes: 44 additions & 0 deletions forecast_dashboard/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# ----------------------------------------------------------------------------
# Kronos Forecasting Dashboard — environment template
# Copy to .env and fill in. NEVER commit the real .env.
# ----------------------------------------------------------------------------

# --- Kronos repo location -----------------------------------------------------
# Path to the cloned Kronos repo root (the dir containing `model/`).
# Defaults to ./Kronos next to this project if unset.
KRONOS_DIR=./Kronos

# --- Model config -------------------------------------------------------------
KRONOS_MODEL=NeoQuasar/Kronos-small
KRONOS_TOKENIZER=NeoQuasar/Kronos-Tokenizer-base
# device: cpu | cuda:0 | mps
KRONOS_DEVICE=cpu

# --- Forecast defaults --------------------------------------------------------
DEFAULT_PRED_LEN=30
DEFAULT_N_SAMPLES=30
DEFAULT_LOOKBACK=400

# --- Storage ------------------------------------------------------------------
# SQLite file for forecast logging + accuracy tracking.
FORECAST_DB=./forecasts.db

# --- Web server (server.py) ---------------------------------------------------
# Bind address/port for the bespoke FastAPI dashboard.
# For GCP, bind 0.0.0.0 and reach it via SSH port-forward (see README).
HOST=127.0.0.1
PORT=8000
# Prewarm all four forecasts in the background on startup (1=on, 0=off).
# Hides slow first-run CPU inference from the first visitor.
KRONOS_PREWARM=1

# --- News / sentiment (OPTIONAL) ---------------------------------------------
# Used ONLY to summarize recent headlines as "things the model did NOT see".
# This NEVER feeds the forecast and NEVER produces a confidence score.
# Leave blank to disable the news panel (the dashboard still works fully).
ANTHROPIC_API_KEY=
ANTHROPIC_MODEL=claude-3-5-haiku-latest

# Optional web-search provider for headlines (else news panel shows a notice).
# Supported: tavily (set TAVILY_API_KEY). Leave blank to skip live search.
TAVILY_API_KEY=
16 changes: 16 additions & 0 deletions forecast_dashboard/.gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# Shell scripts and deploy config MUST stay LF — they run on the Linux VM and
# bash/systemd break on CRLF (\r). Force LF regardless of core.autocrlf.
*.sh text eol=lf
deploy/config.env text eol=lf
deploy/vm_setup.sh text eol=lf

# Keep these LF too for cross-platform consistency.
*.py text eol=lf
*.js text eol=lf
*.css text eol=lf
*.html text eol=lf
*.md text eol=lf

# PowerShell deploy scripts are Windows-only; CRLF is the native convention and
# PowerShell reads either, so pin CRLF for clarity.
*.ps1 text eol=crlf
30 changes: 30 additions & 0 deletions forecast_dashboard/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# Secrets / config
.env

# Data / artifacts
forecasts.db
*.db
*.sqlite
*.log

# Optional standalone sibling clone of Kronos (when this folder is used outside
# the Kronos repo). Inside the repo the model code is the parent dir, not here.
Kronos/

# Python
__pycache__/
*.py[cod]
*.egg-info/
.ipynb_checkpoints/
.pytest_cache/

# OS / editor
.DS_Store
Thumbs.db
.vscode/
.idea/

# Throwaway deploy helper scripts + their captured output (see CLAUDE notes).
# deploy/_common.ps1 is a real shared helper, so keep it tracked.
deploy/_*
!deploy/_common.ps1
60 changes: 60 additions & 0 deletions forecast_dashboard/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
# syntax=docker/dockerfile:1
# ============================================================================
# Cloud Run image for the Kronos Forecasting Dashboard.
#
# Notes specific to Cloud Run:
# - Cloud Run injects $PORT; uvicorn must bind 0.0.0.0:$PORT.
# - The container filesystem is read-only except /tmp, so the SQLite DB and
# the HuggingFace cache are pointed at /tmp.
# - Kronos is cloned at BUILD time (not runtime) and model weights are
# pre-downloaded into the image so cold starts don't pay that cost.
# - CPU-only torch (Cloud Run general compute has no GPU). Forecasts are slow
# (~minutes); raise --timeout and concurrency accordingly when deploying.
# ============================================================================
FROM python:3.11-slim

ENV PYTHONUNBUFFERED=1 \
PIP_NO_CACHE_DIR=1 \
PIP_DISABLE_PIP_VERSION_CHECK=1 \
HF_HOME=/opt/hf_cache \
KRONOS_DIR=/app/Kronos \
KRONOS_DEVICE=cpu \
KRONOS_MODEL=NeoQuasar/Kronos-small \
KRONOS_TOKENIZER=NeoQuasar/Kronos-Tokenizer-base \
FORECAST_DB=/tmp/forecasts.db \
KRONOS_PREWARM=0

WORKDIR /app

# System deps: git to clone Kronos. Build tools only if a wheel needs compiling.
RUN apt-get update && apt-get install -y --no-install-recommends \
git curl ca-certificates \
&& rm -rf /var/lib/apt/lists/*

# --- Python deps -----------------------------------------------------------
# CPU-only torch first (smaller, no CUDA), then the rest of the app deps.
COPY requirements.txt .
RUN pip install --index-url https://download.pytorch.org/whl/cpu torch \
&& pip install -r requirements.txt

# --- Kronos model code (cloned at build time, baked into the image) --------
RUN git clone --depth 1 https://github.com/shiyu-coder/Kronos.git /app/Kronos

# --- App code --------------------------------------------------------------
COPY . /app

# --- Pre-download model + tokenizer weights into the image -----------------
# So the first request after a cold start doesn't wait on a ~100MB HF download.
RUN python -c "import os,sys; sys.path.insert(0,'/app/Kronos'); \
from model import Kronos, KronosTokenizer; \
KronosTokenizer.from_pretrained(os.environ['KRONOS_TOKENIZER']); \
Kronos.from_pretrained(os.environ['KRONOS_MODEL']); \
print('weights cached into image OK')"

# Cloud Run sets $PORT (default 8080). Bind it.
ENV PORT=8080
EXPOSE 8080

# Single worker: the in-process forecast cache + per-key locks assume one
# process. Scale via Cloud Run instances, not workers.
CMD exec uvicorn server:app --host 0.0.0.0 --port ${PORT} --workers 1
Loading