cube-chat 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
cube_chat-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "cube-chat"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "Concrete chat session implementations for cube-standard"
|
|
5
|
+
requires-python = ">=3.12"
|
|
6
|
+
dependencies = [
|
|
7
|
+
"cube-standard",
|
|
8
|
+
]
|
|
9
|
+
|
|
10
|
+
[project.optional-dependencies]
|
|
11
|
+
dev = [
|
|
12
|
+
"pytest>=8.0.0",
|
|
13
|
+
]
|
|
14
|
+
|
|
15
|
+
[build-system]
|
|
16
|
+
requires = ["uv_build>=0.6.0,<0.7.0"]
|
|
17
|
+
build-backend = "uv_build"
|
|
18
|
+
|
|
19
|
+
[tool.uv.build-backend]
|
|
20
|
+
module-name = "cube_chat"
|
|
21
|
+
|
|
22
|
+
[tool.ruff]
|
|
23
|
+
fix = true
|
|
24
|
+
line-length = 120
|
|
25
|
+
indent-width = 4
|
|
26
|
+
|
|
27
|
+
[tool.ruff.format]
|
|
28
|
+
quote-style = "double"
|
|
29
|
+
indent-style = "space"
|
|
30
|
+
skip-magic-trailing-comma = false
|
|
31
|
+
line-ending = "auto"
|
|
32
|
+
|
|
33
|
+
[tool.ruff.lint]
|
|
34
|
+
extend-select = ["I"]
|
|
35
|
+
|
|
36
|
+
[tool.pytest.ini_options]
|
|
37
|
+
testpaths = ["tests"]
|
|
38
|
+
addopts = "-rs"
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
"""In-memory chat session implementation using a queue."""
|
|
2
|
+
|
|
3
|
+
import queue
|
|
4
|
+
import time
|
|
5
|
+
|
|
6
|
+
from cube.resources.chat_session import ChatConfig, ChatMessage, ChatRole, ChatSession
|
|
7
|
+
|
|
8
|
+
_STORED_ROLES = {"user", "assistant", "infeasible"}
|
|
9
|
+
_VALID_ROLES = {"user", "assistant", "info", "infeasible"}
|
|
10
|
+
_STOP_SENTINEL = None
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class SessionStoppedError(Exception):
|
|
14
|
+
"""Raised by wait_for_user_message() when stop() has been called."""
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class BasicChatConfig(ChatConfig):
|
|
18
|
+
"""Configuration for an in-memory BasicChatSession.
|
|
19
|
+
|
|
20
|
+
No configuration fields are needed for the in-memory implementation.
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
def make(self) -> "BasicChatSession":
|
|
24
|
+
"""Create and return a new BasicChatSession."""
|
|
25
|
+
return BasicChatSession()
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class BasicChatSession(ChatSession):
|
|
29
|
+
"""In-memory chat session backed by a queue.
|
|
30
|
+
|
|
31
|
+
Messages posted with add_message() for roles "user", "assistant", and
|
|
32
|
+
"infeasible" are stored in history. The "info" role is silently dropped
|
|
33
|
+
(not stored). send_message() appends an "assistant" message and unblocks
|
|
34
|
+
any caller waiting in wait_for_user_message().
|
|
35
|
+
"""
|
|
36
|
+
|
|
37
|
+
def __init__(self) -> None:
|
|
38
|
+
self._messages: list[ChatMessage] = []
|
|
39
|
+
self._queue: queue.SimpleQueue[str | None] = queue.SimpleQueue()
|
|
40
|
+
|
|
41
|
+
@property
|
|
42
|
+
def messages(self) -> list[ChatMessage]:
|
|
43
|
+
"""Return a copy of the full message history."""
|
|
44
|
+
return self._messages[:]
|
|
45
|
+
|
|
46
|
+
def add_message(self, role: ChatRole, msg: str) -> None:
|
|
47
|
+
"""Post a message to the chat from the task side.
|
|
48
|
+
|
|
49
|
+
Parameters
|
|
50
|
+
----------
|
|
51
|
+
role : ChatRole
|
|
52
|
+
Role of the message sender. "info" messages are not stored.
|
|
53
|
+
msg : str
|
|
54
|
+
Message content.
|
|
55
|
+
|
|
56
|
+
Raises
|
|
57
|
+
------
|
|
58
|
+
ValueError
|
|
59
|
+
If role is not a valid ChatRole.
|
|
60
|
+
"""
|
|
61
|
+
if role not in _VALID_ROLES:
|
|
62
|
+
raise ValueError(f"Invalid role '{role}'. Must be one of: {sorted(_VALID_ROLES)}")
|
|
63
|
+
if role in _STORED_ROLES:
|
|
64
|
+
self._messages.append(ChatMessage(role=role, timestamp=time.time(), message=msg))
|
|
65
|
+
|
|
66
|
+
def wait_for_user_message(self) -> str:
|
|
67
|
+
"""Block until the agent sends a message and return it.
|
|
68
|
+
|
|
69
|
+
Returns
|
|
70
|
+
-------
|
|
71
|
+
str
|
|
72
|
+
The agent's message text.
|
|
73
|
+
|
|
74
|
+
Raises
|
|
75
|
+
------
|
|
76
|
+
SessionStoppedError
|
|
77
|
+
If stop() was called while blocked or before this call.
|
|
78
|
+
"""
|
|
79
|
+
msg = self._queue.get()
|
|
80
|
+
if msg is _STOP_SENTINEL:
|
|
81
|
+
raise SessionStoppedError("Session has been stopped")
|
|
82
|
+
return msg
|
|
83
|
+
|
|
84
|
+
def send_message(self, text: str) -> None:
|
|
85
|
+
"""Send a message from the agent side, unblocking wait_for_user_message().
|
|
86
|
+
|
|
87
|
+
Appends an "assistant" message to history and puts the text into the queue.
|
|
88
|
+
|
|
89
|
+
Parameters
|
|
90
|
+
----------
|
|
91
|
+
text : str
|
|
92
|
+
Message content to send.
|
|
93
|
+
"""
|
|
94
|
+
self._messages.append(ChatMessage(role="assistant", timestamp=time.time(), message=text))
|
|
95
|
+
self._queue.put(text)
|
|
96
|
+
|
|
97
|
+
def stop(self) -> None:
|
|
98
|
+
"""Unblock any thread waiting in wait_for_user_message() and mark the session stopped."""
|
|
99
|
+
self._queue.put(_STOP_SENTINEL)
|