Refactor Nexus One AI product structure and roadmap
- Add Workstation as an additive standalone category, kept out of TIER_ORDER so it never enters the Server S/M/L/Max upgrade ladder - Add Server S/M/L/Max commercial labels for existing starter/basic/pro/max tiers (slugs, license format, and APIs unchanged) - Rename portal UI terminology: Connectors -> MCP & Enterprise Connectors, Audit Log -> Governance & Audit, Model Manager -> Model Catalog, Agent Builder -> Agent Studio, Workflow Automation -> Workflow Designer, Multimodal Chat -> AI Workspace - Fix branding.js workflow.html tier-gating bug and entry.yml missing tier var - Document 7-capability platform roadmap and Workstation/Server structure in README.md and docs/README.md - Ignore __pycache__/*.pyc build artifacts
This commit is contained in:
1 parent
e5b2496b97
commit
12d9ea84f6
58 files changed
+587
-366
No files matched your search
@@ -13,6 +13,7 @@
|
||||
cuda_version: "12.4"
|
||||
skip_roles: "" # comma-separated list of role names to skip (set by install.sh)
|
||||
gpu_available: false
|
||||
tier: "basic" # Server M (commercial label) — internal slug unchanged
|
||||
|
||||
roles:
|
||||
- role: base
|
||||
|
||||
@@ -19,10 +19,46 @@ TIER_ALIASES = {
|
||||
"basic tier": "basic",
|
||||
"pro tier": "pro",
|
||||
"max tier": "max",
|
||||
# Server S/M/L/Max commercial labels — synonyms for the same four Server
|
||||
# tiers/ranks above (starter/basic/pro/max). Additive only; TIER_ORDER and
|
||||
# rank comparisons are unchanged.
|
||||
"server s": "starter",
|
||||
"server s tier": "starter",
|
||||
"server m": "basic",
|
||||
"server m tier": "basic",
|
||||
"server l": "pro",
|
||||
"server l tier": "pro",
|
||||
"server max": "max",
|
||||
"server max tier": "max",
|
||||
}
|
||||
STAGING_MAX_TIER = "basic"
|
||||
SUPPORTED_SIGNATURE_ALGS = {"rsa-sha256", "sha256-rsa"}
|
||||
|
||||
# ── Product category: Workstation (standalone, NOT part of the Server ladder) ──
|
||||
# Nexus One AI Workstation is a personal AI appliance (1-3 active users, 5 max),
|
||||
# distinct from the Server S/M/L/Max ladder above (TIER_ORDER). It is additive:
|
||||
# it does not appear in TIER_ORDER and must never be passed through tier_rank(),
|
||||
# tier_lte(), min_tier(), evaluate_license()/build_tier_options() ladder logic —
|
||||
# those functions and STAGING_MAX_TIER/TIER_ALIASES above are unchanged and only
|
||||
# ever resolve to one of TIER_ORDER's four Server tiers. Workstation entitlement
|
||||
# is looked up independently via TIER_MATRIX["workstation"] in main.py.
|
||||
WORKSTATION_TIER = "workstation"
|
||||
CATEGORY_SERVER = "server"
|
||||
CATEGORY_WORKSTATION = "workstation"
|
||||
WORKSTATION_ALIASES = {"workstation tier", "personal", "personal ai", "nexus one ai workstation"}
|
||||
|
||||
|
||||
def is_workstation_tier(value):
|
||||
"""True if `value` refers to the Workstation category (never a Server tier)."""
|
||||
return (value or "").strip().lower() in ({WORKSTATION_TIER} | WORKSTATION_ALIASES)
|
||||
|
||||
|
||||
def tier_category(value):
|
||||
"""Return CATEGORY_WORKSTATION or CATEGORY_SERVER for a tier/category string.
|
||||
Server tiers (starter/basic/pro/max — displayed as Server S/M/L/Max) rank via
|
||||
TIER_ORDER; Workstation has no rank and is resolved independently."""
|
||||
return CATEGORY_WORKSTATION if is_workstation_tier(value) else CATEGORY_SERVER
|
||||
|
||||
|
||||
def utcnow_iso():
|
||||
return datetime.now(timezone.utc).replace(microsecond=0).isoformat().replace("+00:00", "Z")
|
||||
|
||||
@@ -29,10 +29,15 @@ from apscheduler.triggers.cron import CronTrigger
|
||||
from apscheduler.triggers.interval import IntervalTrigger
|
||||
from cezen_license import (
|
||||
STAGING_MAX_TIER,
|
||||
WORKSTATION_TIER,
|
||||
CATEGORY_SERVER,
|
||||
CATEGORY_WORKSTATION,
|
||||
collect_entitlement,
|
||||
evaluate_license,
|
||||
is_workstation_tier,
|
||||
normalize_tier as license_normalize_tier,
|
||||
read_json_file,
|
||||
tier_category,
|
||||
tier_lte,
|
||||
write_json_file,
|
||||
)
|
||||
@@ -72,6 +77,8 @@ pwd_ctx = _passlib.CryptContext(schemes=["bcrypt"], deprecated="auto")
|
||||
TIER_MATRIX = {
|
||||
"starter": {
|
||||
"label": "Starter",
|
||||
"commercial_label": "Server S",
|
||||
"category": CATEGORY_SERVER,
|
||||
"positioning": "Private AI entry package for one department or small team.",
|
||||
"max_users": 10,
|
||||
"features": {
|
||||
@@ -92,6 +99,8 @@ TIER_MATRIX = {
|
||||
},
|
||||
"basic": {
|
||||
"label": "Basic",
|
||||
"commercial_label": "Server M",
|
||||
"category": CATEGORY_SERVER,
|
||||
"positioning": "Department AI suite with users, RAG, meeting assistant, and audit basics.",
|
||||
"max_users": 25,
|
||||
"features": {
|
||||
@@ -112,6 +121,8 @@ TIER_MATRIX = {
|
||||
},
|
||||
"pro": {
|
||||
"label": "Pro",
|
||||
"commercial_label": "Server L",
|
||||
"category": CATEGORY_SERVER,
|
||||
"positioning": "Production private AI platform for multiple teams and automation workflows.",
|
||||
"max_users": 100,
|
||||
"features": {
|
||||
@@ -132,6 +143,8 @@ TIER_MATRIX = {
|
||||
},
|
||||
"max": {
|
||||
"label": "Max",
|
||||
"commercial_label": "Server Max",
|
||||
"category": CATEGORY_SERVER,
|
||||
"positioning": "Custom high-performance AI platform for large GPU systems and enterprise controls.",
|
||||
"max_users": None,
|
||||
"features": {
|
||||
@@ -150,6 +163,43 @@ TIER_MATRIX = {
|
||||
"deepspeed": "custom",
|
||||
},
|
||||
},
|
||||
# ── Nexus One AI Workstation — additive, standalone product category ──
|
||||
# NOT part of the Server S/M/L/Max ladder: intentionally absent from
|
||||
# cezen_license.TIER_ORDER, so it is unreachable through the license
|
||||
# evaluation/upgrade-path ladder (_current_tier, collect_entitlement,
|
||||
# build_tier_options all resolve exclusively to starter/basic/pro/max).
|
||||
# This entry exists purely so the Workstation feature set is documented
|
||||
# and available for future, explicitly-built activation flows to read —
|
||||
# it does not change behavior for any existing Server install/license.
|
||||
"workstation": {
|
||||
"label": "Workstation",
|
||||
"commercial_label": "Nexus One AI Workstation",
|
||||
"category": CATEGORY_WORKSTATION,
|
||||
"positioning": "Personal AI appliance for individual developers, researchers, and knowledge workers — not a shared server.",
|
||||
"max_users": 5,
|
||||
"features": {
|
||||
"portal": True,
|
||||
"chat": True,
|
||||
"rag": "personal",
|
||||
"meeting_assistant": False,
|
||||
"workflows": False,
|
||||
"connectors": False,
|
||||
"model_router": False,
|
||||
"audit_report": False,
|
||||
"backup_restore": True,
|
||||
"guardrails": "basic",
|
||||
"gpu_inference": True,
|
||||
"fine_tuning": False,
|
||||
"deepspeed": False,
|
||||
"document_intelligence": True,
|
||||
"personal_rag": True,
|
||||
"prompt_studio": True,
|
||||
"model_catalog": True,
|
||||
"local_ai_workspace": True,
|
||||
"api_access": True,
|
||||
"personal_knowledge_base": True,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
TIER_ALIASES = {
|
||||
@@ -850,13 +900,22 @@ def _public_license_record(record: dict) -> dict:
|
||||
return public
|
||||
|
||||
def _current_tier() -> str:
|
||||
# Workstation is checked first and explicitly, rather than falling through
|
||||
# to _normalize_tier() (which only knows the Server ladder and would
|
||||
# silently alias an unrecognized "workstation" string to "basic"/Server M).
|
||||
# This is what keeps Workstation from ever inheriting Server behavior.
|
||||
if CEZEN_TIER:
|
||||
if is_workstation_tier(CEZEN_TIER):
|
||||
return WORKSTATION_TIER
|
||||
return _normalize_tier(CEZEN_TIER)
|
||||
entitlement = _entitlement_summary()
|
||||
licensed_tier = (entitlement.get("licensed_tier") or "").strip()
|
||||
if licensed_tier:
|
||||
return _normalize_tier(licensed_tier)
|
||||
return _normalize_tier(_setting_value("tier_label", "Basic"))
|
||||
tier_label_setting = _setting_value("tier_label", "Basic")
|
||||
if is_workstation_tier(tier_label_setting):
|
||||
return WORKSTATION_TIER
|
||||
return _normalize_tier(tier_label_setting)
|
||||
|
||||
def _entitlement_summary(feasibility: Optional[dict] = None) -> dict:
|
||||
feasibility = feasibility or _cached_feasibility()
|
||||
@@ -876,6 +935,8 @@ def _tier_payload() -> dict:
|
||||
return {
|
||||
"tier": tier,
|
||||
"label": info["label"],
|
||||
"commercial_label": info.get("commercial_label", info["label"]),
|
||||
"category": info.get("category", CATEGORY_SERVER),
|
||||
"locked": bool(CEZEN_TIER or license_record.get("allowed_tier") or license_record.get("tier")),
|
||||
"positioning": info["positioning"],
|
||||
"max_users": info["max_users"],
|
||||
|
||||
Reference in new issue
Block a user