[kimi] Add produce_thought() to stream thinking to Matrix (#672) (#734)
Some checks failed
Tests / lint (push) Has been cancelled
Tests / test (push) Has been cancelled

This commit was merged in pull request #734.
This commit is contained in:
2026-03-21 14:09:19 +00:00
parent 2fa5b23c0c
commit 646eaefa3e
2 changed files with 140 additions and 1 deletions

View File

@@ -65,6 +65,57 @@ def produce_bark(agent_id: str, text: str, reply_to: str = None, style: str = "s
}
def produce_thought(
agent_id: str, thought_text: str, thought_id: int, chain_id: str = None
) -> dict:
"""Format a thinking engine thought as a Matrix thought message.
Thoughts appear as subtle floating text in the 3D world, streaming from
Timmy's thinking engine (/thinking/api). This function wraps thoughts in
Matrix protocol format.
Parameters
----------
agent_id:
Unique identifier for the agent (e.g. ``"timmy"``).
thought_text:
The thought text to display. Truncated to 500 characters.
thought_id:
Unique identifier for this thought (sequence number).
chain_id:
Optional chain identifier grouping related thoughts.
Returns
-------
dict
Thought message with keys ``type``, ``agent_id``, ``data`` (containing
``text``, ``thought_id``, ``chain_id``), and ``ts``.
Examples
--------
>>> produce_thought("timmy", "Considering the options...", 42, "chain-123")
{
"type": "thought",
"agent_id": "timmy",
"data": {"text": "Considering the options...", "thought_id": 42, "chain_id": "chain-123"},
"ts": 1742529600,
}
"""
# Truncate text to 500 characters (thoughts can be longer than barks)
truncated_text = thought_text[:500] if thought_text else ""
return {
"type": "thought",
"agent_id": agent_id,
"data": {
"text": truncated_text,
"thought_id": thought_id,
"chain_id": chain_id,
},
"ts": int(time.time()),
}
def serialize_presence(presence: dict) -> dict:
"""Transform an ADR-023 presence dict into the world-state API shape.

View File

@@ -4,7 +4,12 @@ from unittest.mock import patch
import pytest
from infrastructure.presence import produce_agent_state, produce_bark, serialize_presence
from infrastructure.presence import (
produce_agent_state,
produce_bark,
produce_thought,
serialize_presence,
)
class TestSerializePresence:
@@ -269,3 +274,86 @@ class TestProduceBark:
assert result["data"]["text"] == "Running test suite..."
assert result["data"]["reply_to"] == "parent-msg-456"
assert result["data"]["style"] == "thought"
class TestProduceThought:
"""Tests for produce_thought() — Matrix thought message producer."""
@patch("infrastructure.presence.time")
def test_full_message_structure(self, mock_time):
"""Returns dict with type, agent_id, data, and ts keys."""
mock_time.time.return_value = 1742529600
result = produce_thought("timmy", "Considering the options...", 42)
assert result["type"] == "thought"
assert result["agent_id"] == "timmy"
assert result["ts"] == 1742529600
assert isinstance(result["data"], dict)
def test_data_fields(self):
"""data dict contains text, thought_id, and chain_id."""
result = produce_thought("timmy", "Considering...", 42, chain_id="chain-123")
data = result["data"]
assert data["text"] == "Considering..."
assert data["thought_id"] == 42
assert data["chain_id"] == "chain-123"
def test_default_chain_id_is_none(self):
"""When chain_id is not provided, defaults to None."""
result = produce_thought("timmy", "Thinking...", 1)
assert result["data"]["chain_id"] is None
def test_text_truncated_to_500_chars(self):
"""Text longer than 500 chars is truncated."""
long_text = "A" * 600
result = produce_thought("timmy", long_text, 1)
assert len(result["data"]["text"]) == 500
assert result["data"]["text"] == "A" * 500
def test_text_exactly_500_chars_not_truncated(self):
"""Text exactly 500 chars is not truncated."""
text = "B" * 500
result = produce_thought("timmy", text, 1)
assert result["data"]["text"] == text
def test_text_shorter_than_500_not_padded(self):
"""Text shorter than 500 chars is not padded."""
result = produce_thought("timmy", "Short thought", 1)
assert result["data"]["text"] == "Short thought"
def test_empty_text_handled(self):
"""Empty text is handled gracefully."""
result = produce_thought("timmy", "", 1)
assert result["data"]["text"] == ""
def test_ts_is_unix_timestamp(self):
"""ts should be an integer Unix timestamp."""
result = produce_thought("timmy", "Hello!", 1)
assert isinstance(result["ts"], int)
assert result["ts"] > 0
def test_agent_id_passed_through(self):
"""agent_id appears in the top-level message."""
result = produce_thought("spark", "Hello!", 1)
assert result["agent_id"] == "spark"
def test_thought_id_passed_through(self):
"""thought_id appears in the data."""
result = produce_thought("timmy", "Hello!", 999)
assert result["data"]["thought_id"] == 999
def test_with_all_parameters(self):
"""Full parameter set produces expected output."""
result = produce_thought(
agent_id="timmy",
thought_text="Analyzing the situation...",
thought_id=42,
chain_id="chain-abc",
)
assert result["type"] == "thought"
assert result["agent_id"] == "timmy"
assert result["data"]["text"] == "Analyzing the situation..."
assert result["data"]["thought_id"] == 42
assert result["data"]["chain_id"] == "chain-abc"