Skip to content

Commit 5bd662b

Browse files
author
Paweł Kędzia
committed
Merge branch 'features/web'
2 parents 99bd1b4 + f21006e commit 5bd662b

File tree

14 files changed

+603
-95
lines changed

14 files changed

+603
-95
lines changed

.version

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
0.2.2
1+
0.2.3

CHANGELOG.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,5 @@
1010
| 0.1.1 | Prometheus metrics logging. Workers/Threads/Workers class is able to set by environments. Streaming fixes. Multi-providers for single model with default-balanced strategy. |
1111
| 0.2.0 | Add balancing strategies: `balanced`, `weighted`, `dynamic_weighted` and `first_available` which works for streaming and non streaming requests. Included Prometheus metrics logging via `/metrics` endpoint. First stage of `llm_router_lib` library, to simply usage of `llm-router-api`. |
1212
| 0.2.1 | Fix stream: OpenAI->Ollama, Ollama->OpenAI. Add Redis caching of availability of model providers (when using `first_available` strategy). Add `llm_router_web` module with simple flask-based frontend to manage llm-router config files. |
13-
| 0.2.2 | Update dockerfile and requirements. Fix routing with vLLM. |
13+
| 0.2.2 | Update dockerfile and requirements. Fix routing with vLLM. |
14+
| 0.2.3 | New web configurator: Handling projects, configs for each user separately. |

llm_router_web/web/__init__.py

Lines changed: 32 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# web/__init__.py
22
import os
3-
from flask import Flask
4-
from .models import db
3+
from flask import Flask, session, request, flash, redirect, url_for
4+
from .models import db, Project, User
55
from .routes import bp as web_bp
66
from .utils import _ensure_provider_order_column
77

@@ -40,14 +40,41 @@ def create_app() -> Flask:
4040
# ---- Register blueprint ---------------------------------------------
4141
app.register_blueprint(web_bp)
4242

43-
# --------------------------------------------------------------------
44-
# **Create plain‑name aliases for every blueprint endpoint**
43+
# ---- Context processor – makes project data available to all templates
44+
@app.context_processor
45+
def inject_project_data():
46+
"""
47+
Provides:
48+
* Project – the SQLAlchemy model (so templates can query it if needed)
49+
* user_projects – list of Project objects belonging to the logged‑in user
50+
* current_project_name – name of the project stored in the session
51+
"""
52+
user_id = session.get("user_id")
53+
proj_id = session.get("project_id")
54+
user_projects = []
55+
current_name = "—"
56+
57+
if user_id:
58+
user_projects = (
59+
Project.query.filter_by(user_id=user_id).order_by(Project.name).all()
60+
)
61+
if proj_id:
62+
cur = next((p for p in user_projects if p.id == proj_id), None)
63+
if cur:
64+
current_name = cur.name
65+
66+
return {
67+
"Project": Project,
68+
"user_projects": user_projects,
69+
"current_project_name": current_name,
70+
}
71+
72+
# ---- Plain‑name aliases for every blueprint endpoint ----------------
4573
# After the blueprint is registered Flask knows the full endpoint name
4674
# (e.g. "web.new_config") and the corresponding URL rule.
4775
# We iterate over the map and register the same view function under the
4876
# short name ("new_config") so templates can keep using the original
4977
# `url_for('new_config')` syntax.
50-
# --------------------------------------------------------------------
5178
for rule in list(app.url_map.iter_rules()):
5279
# Only handle endpoints that belong to the "web" blueprint
5380
if rule.endpoint.startswith("web."):

llm_router_web/web/models.py

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,37 @@
66
db = SQLAlchemy()
77

88

9+
# --------------------------------------------------------------
10+
# Project – groups configs per user
11+
# --------------------------------------------------------------
12+
class Project(db.Model):
13+
__tablename__ = "project"
14+
id = db.Column(db.Integer, primary_key=True)
15+
name = db.Column(db.String(160), nullable=False)
16+
user_id = db.Column(
17+
db.Integer,
18+
db.ForeignKey("user.id"),
19+
nullable=False,
20+
index=True,
21+
)
22+
# A project can be marked as the user's default
23+
is_default = db.Column(db.Boolean, default=False, nullable=False)
24+
25+
# free‑text description for the project
26+
description = db.Column(db.String(500), nullable=False, default="")
27+
28+
# One‑to‑many relationship: a project owns many configs
29+
configs = db.relationship(
30+
"Config", backref="project", cascade="all, delete-orphan"
31+
)
32+
33+
def __repr__(self):
34+
return f"<Project {self.name} (user_id={self.user_id})>"
35+
36+
937
class Config(db.Model):
1038
id = db.Column(db.Integer, primary_key=True)
11-
name = db.Column(db.String(160), unique=True, nullable=False)
39+
name = db.Column(db.String(160), nullable=False)
1240
is_active = db.Column(db.Boolean, default=False, nullable=False)
1341
created_at = db.Column(db.DateTime, default=datetime.utcnow, nullable=False)
1442
updated_at = db.Column(
@@ -23,6 +51,16 @@ class Config(db.Model):
2351
nullable=False,
2452
index=True,
2553
)
54+
project_id = db.Column(
55+
db.Integer,
56+
db.ForeignKey("project.id"),
57+
nullable=False,
58+
index=True,
59+
)
60+
61+
# Free‑text description for the configuration
62+
description = db.Column(db.String(500), nullable=False, default="")
63+
2664
# ------------------------------------------------------------------
2765
models = db.relationship("Model", backref="config", cascade="all, delete-orphan")
2866
actives = db.relationship(
@@ -96,6 +134,12 @@ class User(db.Model):
96134
)
97135
created_at = db.Column(db.DateTime, default=datetime.utcnow, nullable=False)
98136

137+
projects = db.relationship(
138+
"Project",
139+
backref="owner",
140+
cascade="all, delete-orphan",
141+
)
142+
99143
configs = db.relationship(
100144
"Config",
101145
backref="owner", # access via cfg.owner

0 commit comments

Comments
 (0)