1
0

[kimi] Add /api/matrix/thoughts endpoint for recent thought stream (#677) (#739)

This commit is contained in:
2026-03-21 14:44:46 +00:00
parent ada0774ca6
commit 8fc8e0fc3d
2 changed files with 228 additions and 0 deletions

View File

@@ -1059,6 +1059,164 @@ lighting:
assert lights[0]["position"] == {"x": 1, "y": 2, "z": 3}
# ---------------------------------------------------------------------------
# Matrix Thoughts Endpoint (/api/matrix/thoughts)
# ---------------------------------------------------------------------------
class TestMatrixThoughtsEndpoint:
"""Tests for the Matrix thoughts endpoint."""
def test_thoughts_endpoint_returns_json(self, matrix_client):
"""GET /api/matrix/thoughts returns JSON list."""
with patch("timmy.thinking.thinking_engine") as mock_engine:
mock_engine.get_recent_thoughts.return_value = [
MagicMock(
id="test-1",
content="First thought",
created_at="2026-03-21T10:00:00Z",
parent_id=None,
),
]
resp = matrix_client.get("/api/matrix/thoughts")
assert resp.status_code == 200
data = resp.json()
assert isinstance(data, list)
assert len(data) == 1
assert resp.headers["cache-control"] == "no-cache, no-store"
def test_thoughts_endpoint_default_limit(self, matrix_client):
"""Default limit is 10 thoughts."""
with patch("timmy.thinking.thinking_engine") as mock_engine:
mock_engine.get_recent_thoughts.return_value = []
matrix_client.get("/api/matrix/thoughts")
# Should call with default limit of 10
mock_engine.get_recent_thoughts.assert_called_once_with(limit=10)
def test_thoughts_endpoint_custom_limit(self, matrix_client):
"""Custom limit can be specified via query param."""
with patch("timmy.thinking.thinking_engine") as mock_engine:
mock_engine.get_recent_thoughts.return_value = []
matrix_client.get("/api/matrix/thoughts?limit=25")
mock_engine.get_recent_thoughts.assert_called_once_with(limit=25)
def test_thoughts_endpoint_max_limit_capped(self, matrix_client):
"""Limit is capped at 50 maximum."""
with patch("timmy.thinking.thinking_engine") as mock_engine:
mock_engine.get_recent_thoughts.return_value = []
matrix_client.get("/api/matrix/thoughts?limit=100")
mock_engine.get_recent_thoughts.assert_called_once_with(limit=50)
def test_thoughts_endpoint_min_limit(self, matrix_client):
"""Limit less than 1 is clamped to 1."""
with patch("timmy.thinking.thinking_engine") as mock_engine:
mock_engine.get_recent_thoughts.return_value = []
matrix_client.get("/api/matrix/thoughts?limit=0")
mock_engine.get_recent_thoughts.assert_called_once_with(limit=1)
def test_thoughts_endpoint_response_structure(self, matrix_client):
"""Response has all required fields with correct types."""
with patch("timmy.thinking.thinking_engine") as mock_engine:
mock_engine.get_recent_thoughts.return_value = [
MagicMock(
id="thought-uuid-1",
content="This is a test thought",
created_at="2026-03-21T10:00:00Z",
parent_id="parent-uuid",
),
]
resp = matrix_client.get("/api/matrix/thoughts")
data = resp.json()
assert len(data) == 1
thought = data[0]
assert thought["id"] == "thought-uuid-1"
assert thought["text"] == "This is a test thought"
assert thought["created_at"] == "2026-03-21T10:00:00Z"
assert thought["chain_id"] == "parent-uuid"
def test_thoughts_endpoint_null_parent_id(self, matrix_client):
"""Root thoughts have chain_id as null."""
with patch("timmy.thinking.thinking_engine") as mock_engine:
mock_engine.get_recent_thoughts.return_value = [
MagicMock(
id="root-thought",
content="Root thought",
created_at="2026-03-21T10:00:00Z",
parent_id=None,
),
]
resp = matrix_client.get("/api/matrix/thoughts")
data = resp.json()
assert data[0]["chain_id"] is None
def test_thoughts_endpoint_text_truncation(self, matrix_client):
"""Text is truncated to 500 characters."""
long_content = "A" * 1000
with patch("timmy.thinking.thinking_engine") as mock_engine:
mock_engine.get_recent_thoughts.return_value = [
MagicMock(
id="long-thought",
content=long_content,
created_at="2026-03-21T10:00:00Z",
parent_id=None,
),
]
resp = matrix_client.get("/api/matrix/thoughts")
data = resp.json()
assert len(data[0]["text"]) == 500
assert data[0]["text"] == "A" * 500
def test_thoughts_endpoint_empty_list(self, matrix_client):
"""Endpoint returns 200 with empty list when no thoughts."""
with patch("timmy.thinking.thinking_engine") as mock_engine:
mock_engine.get_recent_thoughts.return_value = []
resp = matrix_client.get("/api/matrix/thoughts")
assert resp.status_code == 200
assert resp.json() == []
def test_thoughts_endpoint_graceful_degradation(self, matrix_client):
"""Returns empty list when thinking engine fails."""
with patch("timmy.thinking.thinking_engine") as mock_engine:
mock_engine.get_recent_thoughts.side_effect = RuntimeError("Engine down")
resp = matrix_client.get("/api/matrix/thoughts")
assert resp.status_code == 200
assert resp.json() == []
def test_thoughts_endpoint_multiple_thoughts(self, matrix_client):
"""Multiple thoughts are returned in order from thinking engine."""
with patch("timmy.thinking.thinking_engine") as mock_engine:
mock_engine.get_recent_thoughts.return_value = [
MagicMock(
id="t1",
content="First",
created_at="2026-03-21T10:00:00Z",
parent_id=None,
),
MagicMock(
id="t2",
content="Second",
created_at="2026-03-21T10:01:00Z",
parent_id="t1",
),
]
resp = matrix_client.get("/api/matrix/thoughts")
data = resp.json()
assert len(data) == 2
assert data[0]["id"] == "t1"
assert data[1]["id"] == "t2"
# ---------------------------------------------------------------------------
# Matrix Bark Endpoint (/api/matrix/bark)
# ---------------------------------------------------------------------------