1
0

[kimi] Add /api/matrix/config endpoint for world configuration (#674) (#736)

This commit is contained in:
2026-03-21 14:25:19 +00:00
parent c9601ba32c
commit 9d4ac8e7cc
3 changed files with 321 additions and 0 deletions

28
config/matrix.yaml Normal file
View File

@@ -0,0 +1,28 @@
# Matrix World Configuration
# Serves lighting, environment, and feature settings to the Matrix frontend.
lighting:
ambient_color: "#1a1a2e"
ambient_intensity: 0.4
point_lights:
- color: "#FFD700"
intensity: 1.2
position: { x: 0, y: 5, z: 0 }
- color: "#3B82F6"
intensity: 0.8
position: { x: -5, y: 3, z: -5 }
- color: "#A855F7"
intensity: 0.6
position: { x: 5, y: 3, z: 5 }
environment:
rain_enabled: false
starfield_enabled: true
fog_color: "#0f0f23"
fog_density: 0.02
features:
chat_enabled: true
visitor_avatars: true
pip_familiar: true
workshop_portal: true

View File

@@ -22,11 +22,14 @@ import re
import time
from collections import deque
from datetime import UTC, datetime
from pathlib import Path
from typing import Any
import yaml
from fastapi import APIRouter, WebSocket
from fastapi.responses import JSONResponse
from config import settings
from infrastructure.presence import serialize_presence
from timmy.workshop_state import PRESENCE_FILE
@@ -489,6 +492,102 @@ async def _generate_bark(visitor_text: str) -> str:
return "Hmm, my thoughts are a bit tangled right now."
# ---------------------------------------------------------------------------
# Matrix Configuration Endpoint
# ---------------------------------------------------------------------------
# Default Matrix configuration (fallback when matrix.yaml is missing/corrupt)
_DEFAULT_MATRIX_CONFIG: dict[str, Any] = {
"lighting": {
"ambient_color": "#1a1a2e",
"ambient_intensity": 0.4,
"point_lights": [
{"color": "#FFD700", "intensity": 1.2, "position": {"x": 0, "y": 5, "z": 0}},
{"color": "#3B82F6", "intensity": 0.8, "position": {"x": -5, "y": 3, "z": -5}},
{"color": "#A855F7", "intensity": 0.6, "position": {"x": 5, "y": 3, "z": 5}},
],
},
"environment": {
"rain_enabled": False,
"starfield_enabled": True,
"fog_color": "#0f0f23",
"fog_density": 0.02,
},
"features": {
"chat_enabled": True,
"visitor_avatars": True,
"pip_familiar": True,
"workshop_portal": True,
},
}
def _load_matrix_config() -> dict[str, Any]:
"""Load Matrix world configuration from matrix.yaml with fallback to defaults.
Returns a dict with sections: lighting, environment, features.
If the config file is missing or invalid, returns sensible defaults.
"""
try:
config_path = Path(settings.repo_root) / "config" / "matrix.yaml"
if not config_path.exists():
logger.debug("matrix.yaml not found, using default config")
return _DEFAULT_MATRIX_CONFIG.copy()
raw = config_path.read_text()
config = yaml.safe_load(raw)
if not isinstance(config, dict):
logger.warning("matrix.yaml invalid format, using defaults")
return _DEFAULT_MATRIX_CONFIG.copy()
# Merge with defaults to ensure all required fields exist
result: dict[str, Any] = {
"lighting": {
**_DEFAULT_MATRIX_CONFIG["lighting"],
**config.get("lighting", {}),
},
"environment": {
**_DEFAULT_MATRIX_CONFIG["environment"],
**config.get("environment", {}),
},
"features": {
**_DEFAULT_MATRIX_CONFIG["features"],
**config.get("features", {}),
},
}
# Ensure point_lights is a list
if "point_lights" in config.get("lighting", {}):
result["lighting"]["point_lights"] = config["lighting"]["point_lights"]
else:
result["lighting"]["point_lights"] = _DEFAULT_MATRIX_CONFIG["lighting"]["point_lights"]
return result
except Exception as exc:
logger.warning("Failed to load matrix config: %s, using defaults", exc)
return _DEFAULT_MATRIX_CONFIG.copy()
@matrix_router.get("/config")
async def get_matrix_config() -> JSONResponse:
"""Return Matrix world configuration.
Serves lighting presets, environment settings, and feature flags
to the Matrix frontend so it can be config-driven rather than
hardcoded. Reads from config/matrix.yaml with sensible defaults.
Response structure:
- lighting: ambient_color, ambient_intensity, point_lights[]
- environment: rain_enabled, starfield_enabled, fog_color, fog_density
- features: chat_enabled, visitor_avatars, pip_familiar, workshop_portal
"""
config = _load_matrix_config()
return JSONResponse(
content=config,
headers={"Cache-Control": "no-cache, no-store"},
)
# ---------------------------------------------------------------------------
# Matrix Agent Registry Endpoint
# ---------------------------------------------------------------------------

View File

@@ -863,3 +863,197 @@ def test_matrix_agents_endpoint_graceful_degradation(matrix_client):
assert resp.status_code == 200
assert resp.json() == []
# ---------------------------------------------------------------------------
# Matrix Configuration Endpoint (/api/matrix/config)
# ---------------------------------------------------------------------------
class TestMatrixConfigEndpoint:
"""Tests for the Matrix configuration endpoint."""
def test_matrix_config_endpoint_returns_json(self, matrix_client):
"""GET /api/matrix/config returns JSON config."""
resp = matrix_client.get("/api/matrix/config")
assert resp.status_code == 200
data = resp.json()
assert isinstance(data, dict)
assert "lighting" in data
assert "environment" in data
assert "features" in data
assert resp.headers["cache-control"] == "no-cache, no-store"
def test_matrix_config_lighting_structure(self, matrix_client):
"""Config has correct lighting section structure."""
resp = matrix_client.get("/api/matrix/config")
data = resp.json()
lighting = data["lighting"]
assert "ambient_color" in lighting
assert "ambient_intensity" in lighting
assert "point_lights" in lighting
assert isinstance(lighting["point_lights"], list)
# Check first point light structure
if lighting["point_lights"]:
pl = lighting["point_lights"][0]
assert "color" in pl
assert "intensity" in pl
assert "position" in pl
assert "x" in pl["position"]
assert "y" in pl["position"]
assert "z" in pl["position"]
def test_matrix_config_environment_structure(self, matrix_client):
"""Config has correct environment section structure."""
resp = matrix_client.get("/api/matrix/config")
data = resp.json()
env = data["environment"]
assert "rain_enabled" in env
assert "starfield_enabled" in env
assert "fog_color" in env
assert "fog_density" in env
assert isinstance(env["rain_enabled"], bool)
assert isinstance(env["starfield_enabled"], bool)
def test_matrix_config_features_structure(self, matrix_client):
"""Config has correct features section structure."""
resp = matrix_client.get("/api/matrix/config")
data = resp.json()
features = data["features"]
assert "chat_enabled" in features
assert "visitor_avatars" in features
assert "pip_familiar" in features
assert "workshop_portal" in features
assert isinstance(features["chat_enabled"], bool)
class TestMatrixConfigLoading:
"""Tests for _load_matrix_config function."""
def test_load_matrix_config_returns_dict(self):
"""_load_matrix_config returns a dictionary."""
from dashboard.routes.world import _load_matrix_config
config = _load_matrix_config()
assert isinstance(config, dict)
assert "lighting" in config
assert "environment" in config
assert "features" in config
def test_load_matrix_config_has_all_required_sections(self):
"""Config contains all required sections."""
from dashboard.routes.world import _load_matrix_config
config = _load_matrix_config()
lighting = config["lighting"]
env = config["environment"]
features = config["features"]
# Lighting fields
assert "ambient_color" in lighting
assert "ambient_intensity" in lighting
assert "point_lights" in lighting
# Environment fields
assert "rain_enabled" in env
assert "starfield_enabled" in env
assert "fog_color" in env
assert "fog_density" in env
# Features fields
assert "chat_enabled" in features
assert "visitor_avatars" in features
assert "pip_familiar" in features
assert "workshop_portal" in features
def test_load_matrix_config_fallback_on_missing_file(self, tmp_path):
"""Returns defaults when matrix.yaml is missing."""
from dashboard.routes.world import _load_matrix_config
with patch("dashboard.routes.world.settings") as mock_settings:
mock_settings.repo_root = str(tmp_path)
config = _load_matrix_config()
# Should return defaults
assert config["lighting"]["ambient_color"] == "#1a1a2e"
assert config["environment"]["rain_enabled"] is False
assert config["features"]["chat_enabled"] is True
def test_load_matrix_config_merges_with_defaults(self, tmp_path):
"""Partial config file is merged with defaults."""
from dashboard.routes.world import _load_matrix_config
# Create a partial config file
config_dir = tmp_path / "config"
config_dir.mkdir()
config_file = config_dir / "matrix.yaml"
config_file.write_text("""
lighting:
ambient_color: "#ff0000"
ambient_intensity: 0.8
environment:
rain_enabled: true
""")
with patch("dashboard.routes.world.settings") as mock_settings:
mock_settings.repo_root = str(tmp_path)
config = _load_matrix_config()
# Custom values
assert config["lighting"]["ambient_color"] == "#ff0000"
assert config["lighting"]["ambient_intensity"] == 0.8
assert config["environment"]["rain_enabled"] is True
# Defaults preserved
assert config["features"]["chat_enabled"] is True
assert config["environment"]["starfield_enabled"] is True
assert len(config["lighting"]["point_lights"]) == 3
def test_load_matrix_config_handles_invalid_yaml(self, tmp_path):
"""Returns defaults when YAML is invalid."""
from dashboard.routes.world import _load_matrix_config
config_dir = tmp_path / "config"
config_dir.mkdir()
config_file = config_dir / "matrix.yaml"
config_file.write_text("not: valid: yaml: [{")
with patch("dashboard.routes.world.settings") as mock_settings:
mock_settings.repo_root = str(tmp_path)
config = _load_matrix_config()
# Should return defaults despite invalid YAML
assert "lighting" in config
assert "environment" in config
assert "features" in config
def test_load_matrix_config_custom_point_lights(self, tmp_path):
"""Custom point lights override defaults completely."""
from dashboard.routes.world import _load_matrix_config
config_dir = tmp_path / "config"
config_dir.mkdir()
config_file = config_dir / "matrix.yaml"
config_file.write_text("""
lighting:
point_lights:
- color: "#FFFFFF"
intensity: 2.0
position: { x: 1, y: 2, z: 3 }
""")
with patch("dashboard.routes.world.settings") as mock_settings:
mock_settings.repo_root = str(tmp_path)
config = _load_matrix_config()
# Should have custom point lights, not defaults
lights = config["lighting"]["point_lights"]
assert len(lights) == 1
assert lights[0]["color"] == "#FFFFFF"
assert lights[0]["intensity"] == 2.0
assert lights[0]["position"] == {"x": 1, "y": 2, "z": 3}