hostel-protocol-python 0.3.0__tar.gz → 0.4.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.
- {hostel_protocol_python-0.3.0 → hostel_protocol_python-0.4.0}/PKG-INFO +2 -2
- {hostel_protocol_python-0.3.0 → hostel_protocol_python-0.4.0}/pyproject.toml +2 -2
- {hostel_protocol_python-0.3.0 → hostel_protocol_python-0.4.0}/src/hostel/protocol/converter.py +52 -1
- {hostel_protocol_python-0.3.0 → hostel_protocol_python-0.4.0}/src/hostel/protocol/models.py +66 -0
- {hostel_protocol_python-0.3.0 → hostel_protocol_python-0.4.0}/.github/workflows/ci.yml +0 -0
- {hostel_protocol_python-0.3.0 → hostel_protocol_python-0.4.0}/.github/workflows/publish.yml +0 -0
- {hostel_protocol_python-0.3.0 → hostel_protocol_python-0.4.0}/.gitignore +0 -0
- {hostel_protocol_python-0.3.0 → hostel_protocol_python-0.4.0}/Makefile +0 -0
- {hostel_protocol_python-0.3.0 → hostel_protocol_python-0.4.0}/src/hostel/client/__init__.py +0 -0
- {hostel_protocol_python-0.3.0 → hostel_protocol_python-0.4.0}/src/hostel/client/client.py +0 -0
- {hostel_protocol_python-0.3.0 → hostel_protocol_python-0.4.0}/src/hostel/protocol/__init__.py +0 -0
- {hostel_protocol_python-0.3.0 → hostel_protocol_python-0.4.0}/src/hostel/py.typed +0 -0
- {hostel_protocol_python-0.3.0 → hostel_protocol_python-0.4.0}/src/hostel/transport/__init__.py +0 -0
- {hostel_protocol_python-0.3.0 → hostel_protocol_python-0.4.0}/src/hostel/transport/transport.py +0 -0
- {hostel_protocol_python-0.3.0 → hostel_protocol_python-0.4.0}/src/hostel/transport/zeromq.py +0 -0
- {hostel_protocol_python-0.3.0 → hostel_protocol_python-0.4.0}/tests/__init__.py +0 -0
- {hostel_protocol_python-0.3.0 → hostel_protocol_python-0.4.0}/tests/test_client.py +0 -0
- {hostel_protocol_python-0.3.0 → hostel_protocol_python-0.4.0}/tests/test_converter.py +0 -0
- {hostel_protocol_python-0.3.0 → hostel_protocol_python-0.4.0}/tests/test_models.py +0 -0
- {hostel_protocol_python-0.3.0 → hostel_protocol_python-0.4.0}/tests/test_transport.py +0 -0
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: hostel-protocol-python
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.0
|
|
4
4
|
Summary: Pydantic models, transport and client for the Hostel protocol
|
|
5
5
|
Requires-Python: >=3.12
|
|
6
|
-
Requires-Dist: hostel-protocol>=0.
|
|
6
|
+
Requires-Dist: hostel-protocol>=0.4.0
|
|
7
7
|
Requires-Dist: protobuf<8,>=7.35.0
|
|
8
8
|
Requires-Dist: pydantic>=2.5.0
|
|
9
9
|
Provides-Extra: client
|
|
@@ -4,11 +4,11 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "hostel-protocol-python"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.4.0"
|
|
8
8
|
description = "Pydantic models, transport and client for the Hostel protocol"
|
|
9
9
|
requires-python = ">=3.12"
|
|
10
10
|
dependencies = [
|
|
11
|
-
"hostel-protocol>=0.
|
|
11
|
+
"hostel-protocol>=0.4.0",
|
|
12
12
|
"pydantic>=2.5.0",
|
|
13
13
|
"protobuf>=7.35.0,<8",
|
|
14
14
|
]
|
{hostel_protocol_python-0.3.0 → hostel_protocol_python-0.4.0}/src/hostel/protocol/converter.py
RENAMED
|
@@ -30,6 +30,15 @@ _PYDANTIC_TO_PROTO: dict[type[BaseModel], type[Message]] = {
|
|
|
30
30
|
models.ChatMessage: chat_pb2.ChatMessage,
|
|
31
31
|
models.ChatRequest: chat_pb2.ChatRequest,
|
|
32
32
|
models.ChatResponse: chat_pb2.ChatResponse,
|
|
33
|
+
# Conversation
|
|
34
|
+
models.ConversationSummary: chat_pb2.ConversationSummary,
|
|
35
|
+
models.ConversationMessage: chat_pb2.ConversationMessage,
|
|
36
|
+
models.ListConversationsRequest: chat_pb2.ListConversationsRequest,
|
|
37
|
+
models.ListConversationsResponse: chat_pb2.ListConversationsResponse,
|
|
38
|
+
models.GetConversationRequest: chat_pb2.GetConversationRequest,
|
|
39
|
+
models.GetConversationResponse: chat_pb2.GetConversationResponse,
|
|
40
|
+
models.DeleteConversationRequest: chat_pb2.DeleteConversationRequest,
|
|
41
|
+
models.DeleteConversationResponse: chat_pb2.DeleteConversationResponse,
|
|
33
42
|
# Agent
|
|
34
43
|
models.ListAgentsRequest: agent_pb2.ListAgentsRequest,
|
|
35
44
|
models.ListAgentsResponse: agent_pb2.ListAgentsResponse,
|
|
@@ -62,6 +71,14 @@ _PYDANTIC_TO_PROTO: dict[type[BaseModel], type[Message]] = {
|
|
|
62
71
|
|
|
63
72
|
_PROTO_TO_PYDANTIC: dict[type[Message], type[BaseModel]] = {v: k for k, v in _PYDANTIC_TO_PROTO.items()}
|
|
64
73
|
|
|
74
|
+
# Fields in each proto that are plain strings but should be treated as optional
|
|
75
|
+
# (empty string on the wire maps to None in Pydantic)
|
|
76
|
+
_OPTIONAL_STRING_FIELDS: dict[type[Message], set[str]] = {
|
|
77
|
+
chat_pb2.ChatRequest: {"conversation_id"},
|
|
78
|
+
chat_pb2.ChatResponse: {"conversation_id"},
|
|
79
|
+
chat_pb2.ConversationMessage: {"tool_name", "tool_call_id", "parameters_json"},
|
|
80
|
+
}
|
|
81
|
+
|
|
65
82
|
# Fields in each proto that use google.protobuf.StringValue (nullable strings)
|
|
66
83
|
_STRING_VALUE_FIELDS: dict[type[Message], set[str]] = {
|
|
67
84
|
task_pb2.TaskData: {"webhook_url", "status", "response", "created_at", "updated_at", "executed_at"},
|
|
@@ -87,17 +104,28 @@ _VALUE_FIELDS: dict[type[Message], set[str]] = {
|
|
|
87
104
|
_REPEATED_MSG_FIELDS: dict[type[Message], dict[str, type[Message]]] = {
|
|
88
105
|
agent_pb2.ListAgentsResponse: {"agents": struct_pb2.Struct},
|
|
89
106
|
chat_pb2.ChatRequest: {"messages": chat_pb2.ChatMessage},
|
|
107
|
+
chat_pb2.ListConversationsResponse: {"conversations": chat_pb2.ConversationSummary},
|
|
108
|
+
chat_pb2.GetConversationResponse: {"messages": chat_pb2.ConversationMessage},
|
|
90
109
|
component_pb2.ListComponentsResponse: {"components": struct_pb2.Struct},
|
|
91
110
|
task_pb2.ListTasksResponse: {"tasks": task_pb2.TaskData},
|
|
92
111
|
}
|
|
93
112
|
|
|
94
113
|
# Fields that hold a single sub-message (non-oneof)
|
|
95
114
|
_SUB_MSG_FIELDS: dict[type[Message], dict[str, type[Message]]] = {
|
|
115
|
+
chat_pb2.GetConversationResponse: {"conversation": chat_pb2.ConversationSummary},
|
|
96
116
|
task_pb2.CreateTaskResponse: {"task": task_pb2.TaskData},
|
|
97
117
|
task_pb2.GetTaskResponse: {"task": task_pb2.TaskData},
|
|
98
118
|
task_pb2.UpdateTaskResponse: {"task": task_pb2.TaskData},
|
|
99
119
|
}
|
|
100
120
|
|
|
121
|
+
# Enum fields: proto class -> {field_name: {int_value: str_value}}
|
|
122
|
+
# Used to convert between proto integer enum values and Pydantic string literals.
|
|
123
|
+
_ENUM_FIELDS: dict[type[Message], dict[str, dict[int, str]]] = {
|
|
124
|
+
chat_pb2.ChatRequest: {
|
|
125
|
+
"planning_mode": {0: "react", 1: "plan_and_execute"},
|
|
126
|
+
},
|
|
127
|
+
}
|
|
128
|
+
|
|
101
129
|
# oneof groups: proto class -> {field_name: proto sub-message class}
|
|
102
130
|
_ONEOF_FIELDS: dict[type[Message], dict[str, type[Message]]] = {
|
|
103
131
|
chat_pb2.ChatMessage: {
|
|
@@ -114,6 +142,12 @@ _HOSTEL_MESSAGE_PAYLOAD_FIELDS: dict[str, type[Message]] = {
|
|
|
114
142
|
"system_payload": struct_pb2.Struct,
|
|
115
143
|
"chat_request": chat_pb2.ChatRequest,
|
|
116
144
|
"chat_response_chunk": chat_pb2.ChatResponse,
|
|
145
|
+
"conversation_list_request": chat_pb2.ListConversationsRequest,
|
|
146
|
+
"conversation_list_response": chat_pb2.ListConversationsResponse,
|
|
147
|
+
"conversation_get_request": chat_pb2.GetConversationRequest,
|
|
148
|
+
"conversation_get_response": chat_pb2.GetConversationResponse,
|
|
149
|
+
"conversation_delete_request": chat_pb2.DeleteConversationRequest,
|
|
150
|
+
"conversation_delete_response": chat_pb2.DeleteConversationResponse,
|
|
117
151
|
"task_create": task_pb2.CreateTaskRequest,
|
|
118
152
|
"task_create_response": task_pb2.CreateTaskResponse,
|
|
119
153
|
"task_list": task_pb2.ListTasksRequest,
|
|
@@ -191,11 +225,13 @@ def pydantic_to_proto(model: BaseModel) -> Message:
|
|
|
191
225
|
def _pydantic_to_proto_inner(model: BaseModel, proto_cls: type[Message]) -> Message:
|
|
192
226
|
proto = proto_cls()
|
|
193
227
|
string_value_fields = _STRING_VALUE_FIELDS.get(proto_cls, set())
|
|
228
|
+
optional_string_fields = _OPTIONAL_STRING_FIELDS.get(proto_cls, set())
|
|
194
229
|
struct_fields = _STRUCT_FIELDS.get(proto_cls, set())
|
|
195
230
|
value_fields = _VALUE_FIELDS.get(proto_cls, set())
|
|
196
231
|
repeated_fields = _REPEATED_MSG_FIELDS.get(proto_cls, {})
|
|
197
232
|
sub_msg_fields = _SUB_MSG_FIELDS.get(proto_cls, {})
|
|
198
233
|
oneof_fields = _ONEOF_FIELDS.get(proto_cls, {})
|
|
234
|
+
enum_fields = _ENUM_FIELDS.get(proto_cls, {})
|
|
199
235
|
|
|
200
236
|
# Special handling for HostelMessage payload oneof
|
|
201
237
|
is_envelope = proto_cls is message_pb2.HostelMessage
|
|
@@ -252,6 +288,11 @@ def _pydantic_to_proto_inner(model: BaseModel, proto_cls: type[Message]) -> Mess
|
|
|
252
288
|
if child_pydantic_cls and isinstance(value, child_pydantic_cls):
|
|
253
289
|
getattr(proto, field_name).CopyFrom(_pydantic_to_proto_inner(value, child_proto_cls))
|
|
254
290
|
|
|
291
|
+
# Enum fields (string literal -> int)
|
|
292
|
+
elif field_name in enum_fields:
|
|
293
|
+
str_to_int = {v: k for k, v in enum_fields[field_name].items()}
|
|
294
|
+
setattr(proto, field_name, str_to_int[value])
|
|
295
|
+
|
|
255
296
|
# Scalar fields
|
|
256
297
|
else:
|
|
257
298
|
setattr(proto, field_name, value)
|
|
@@ -275,11 +316,13 @@ def proto_to_pydantic(proto: Message) -> BaseModel:
|
|
|
275
316
|
def _proto_to_pydantic_inner(proto: Message, pydantic_cls: type[BaseModel]) -> BaseModel:
|
|
276
317
|
proto_cls = type(proto)
|
|
277
318
|
string_value_fields = _STRING_VALUE_FIELDS.get(proto_cls, set())
|
|
319
|
+
optional_string_fields = _OPTIONAL_STRING_FIELDS.get(proto_cls, set())
|
|
278
320
|
struct_fields = _STRUCT_FIELDS.get(proto_cls, set())
|
|
279
321
|
value_fields = _VALUE_FIELDS.get(proto_cls, set())
|
|
280
322
|
repeated_fields = _REPEATED_MSG_FIELDS.get(proto_cls, {})
|
|
281
323
|
sub_msg_fields = _SUB_MSG_FIELDS.get(proto_cls, {})
|
|
282
324
|
oneof_fields = _ONEOF_FIELDS.get(proto_cls, {})
|
|
325
|
+
enum_fields = _ENUM_FIELDS.get(proto_cls, {})
|
|
283
326
|
|
|
284
327
|
is_envelope = proto_cls is message_pb2.HostelMessage
|
|
285
328
|
|
|
@@ -361,8 +404,16 @@ def _proto_to_pydantic_inner(proto: Message, pydantic_cls: type[BaseModel]) -> B
|
|
|
361
404
|
else:
|
|
362
405
|
kwargs[field_name] = None
|
|
363
406
|
|
|
407
|
+
# Enum fields (int -> string literal)
|
|
408
|
+
elif field_name in enum_fields:
|
|
409
|
+
kwargs[field_name] = enum_fields[field_name][getattr(proto, field_name)]
|
|
410
|
+
|
|
364
411
|
# Scalar fields
|
|
365
412
|
else:
|
|
366
|
-
|
|
413
|
+
raw = getattr(proto, field_name)
|
|
414
|
+
if field_name in optional_string_fields:
|
|
415
|
+
kwargs[field_name] = raw if raw != "" else None
|
|
416
|
+
else:
|
|
417
|
+
kwargs[field_name] = raw
|
|
367
418
|
|
|
368
419
|
return pydantic_cls(**kwargs)
|
|
@@ -42,6 +42,7 @@ class ChatRequest(BaseModel):
|
|
|
42
42
|
messages: list[ChatMessage] = []
|
|
43
43
|
planning_mode: Literal["react", "plan_and_execute"] = "react"
|
|
44
44
|
require_plan_approval: bool = True
|
|
45
|
+
conversation_id: str | None = None
|
|
45
46
|
|
|
46
47
|
|
|
47
48
|
class ChatResponse(BaseModel):
|
|
@@ -49,6 +50,7 @@ class ChatResponse(BaseModel):
|
|
|
49
50
|
role: str = ""
|
|
50
51
|
scope: str = ""
|
|
51
52
|
content: Any = None
|
|
53
|
+
conversation_id: str | None = None
|
|
52
54
|
|
|
53
55
|
|
|
54
56
|
# ---------------------------------------------------------------------------
|
|
@@ -209,6 +211,62 @@ class DeleteTaskResponse(BaseModel):
|
|
|
209
211
|
success: bool = False
|
|
210
212
|
|
|
211
213
|
|
|
214
|
+
# ---------------------------------------------------------------------------
|
|
215
|
+
# Conversation
|
|
216
|
+
# ---------------------------------------------------------------------------
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
class ConversationSummary(BaseModel):
|
|
220
|
+
model_config = ConfigDict(populate_by_name=True)
|
|
221
|
+
id: str = ""
|
|
222
|
+
agent_name: str = ""
|
|
223
|
+
created_at: str = ""
|
|
224
|
+
updated_at: str = ""
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
class ConversationMessage(BaseModel):
|
|
228
|
+
model_config = ConfigDict(populate_by_name=True)
|
|
229
|
+
id: str = ""
|
|
230
|
+
conversation_id: str = ""
|
|
231
|
+
role: Literal["human", "ai", "tool_call", "tool_response"] = "human"
|
|
232
|
+
content: str = ""
|
|
233
|
+
tool_name: str | None = None
|
|
234
|
+
tool_call_id: str | None = None
|
|
235
|
+
parameters_json: str | None = None
|
|
236
|
+
sequence: int = 0
|
|
237
|
+
is_complete: bool = True
|
|
238
|
+
created_at: str = ""
|
|
239
|
+
|
|
240
|
+
|
|
241
|
+
class ListConversationsRequest(BaseModel):
|
|
242
|
+
model_config = ConfigDict(populate_by_name=True)
|
|
243
|
+
|
|
244
|
+
|
|
245
|
+
class ListConversationsResponse(BaseModel):
|
|
246
|
+
model_config = ConfigDict(populate_by_name=True)
|
|
247
|
+
conversations: list[ConversationSummary] = []
|
|
248
|
+
|
|
249
|
+
|
|
250
|
+
class GetConversationRequest(BaseModel):
|
|
251
|
+
model_config = ConfigDict(populate_by_name=True)
|
|
252
|
+
conversation_id: str
|
|
253
|
+
|
|
254
|
+
|
|
255
|
+
class GetConversationResponse(BaseModel):
|
|
256
|
+
model_config = ConfigDict(populate_by_name=True)
|
|
257
|
+
conversation: ConversationSummary | None = None
|
|
258
|
+
messages: list[ConversationMessage] = []
|
|
259
|
+
|
|
260
|
+
|
|
261
|
+
class DeleteConversationRequest(BaseModel):
|
|
262
|
+
model_config = ConfigDict(populate_by_name=True)
|
|
263
|
+
conversation_id: str
|
|
264
|
+
|
|
265
|
+
|
|
266
|
+
class DeleteConversationResponse(BaseModel):
|
|
267
|
+
model_config = ConfigDict(populate_by_name=True)
|
|
268
|
+
|
|
269
|
+
|
|
212
270
|
# ---------------------------------------------------------------------------
|
|
213
271
|
# Envelope (HostelMessage)
|
|
214
272
|
# ---------------------------------------------------------------------------
|
|
@@ -241,6 +299,14 @@ class HostelMessage(BaseModel):
|
|
|
241
299
|
chat_request: ChatRequest | None = None
|
|
242
300
|
chat_response_chunk: ChatResponse | None = None
|
|
243
301
|
|
|
302
|
+
# oneof payload – Conversation
|
|
303
|
+
conversation_list_request: ListConversationsRequest | None = None
|
|
304
|
+
conversation_list_response: ListConversationsResponse | None = None
|
|
305
|
+
conversation_get_request: GetConversationRequest | None = None
|
|
306
|
+
conversation_get_response: GetConversationResponse | None = None
|
|
307
|
+
conversation_delete_request: DeleteConversationRequest | None = None
|
|
308
|
+
conversation_delete_response: DeleteConversationResponse | None = None
|
|
309
|
+
|
|
244
310
|
# oneof payload – Task
|
|
245
311
|
task_create: CreateTaskRequest | None = None
|
|
246
312
|
task_create_response: CreateTaskResponse | None = None
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{hostel_protocol_python-0.3.0 → hostel_protocol_python-0.4.0}/src/hostel/protocol/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
{hostel_protocol_python-0.3.0 → hostel_protocol_python-0.4.0}/src/hostel/transport/__init__.py
RENAMED
|
File without changes
|
{hostel_protocol_python-0.3.0 → hostel_protocol_python-0.4.0}/src/hostel/transport/transport.py
RENAMED
|
File without changes
|
{hostel_protocol_python-0.3.0 → hostel_protocol_python-0.4.0}/src/hostel/transport/zeromq.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|