nora-lib 0.0.5.dev0__tar.gz → 0.0.5.dev2__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.
- {nora_lib-0.0.5.dev0 → nora_lib-0.0.5.dev2}/PKG-INFO +1 -1
- nora_lib-0.0.5.dev2/nora_lib/interactions/interactions_service.py +239 -0
- {nora_lib-0.0.5.dev0 → nora_lib-0.0.5.dev2}/nora_lib/interactions/models.py +21 -6
- {nora_lib-0.0.5.dev0 → nora_lib-0.0.5.dev2}/nora_lib.egg-info/PKG-INFO +1 -1
- {nora_lib-0.0.5.dev0 → nora_lib-0.0.5.dev2}/setup.py +1 -1
- nora_lib-0.0.5.dev0/nora_lib/interactions/interactions_service.py +0 -146
- {nora_lib-0.0.5.dev0 → nora_lib-0.0.5.dev2}/README.md +0 -0
- {nora_lib-0.0.5.dev0 → nora_lib-0.0.5.dev2}/nora_lib/__init__.py +0 -0
- {nora_lib-0.0.5.dev0 → nora_lib-0.0.5.dev2}/nora_lib/context/__init__.py +0 -0
- {nora_lib-0.0.5.dev0 → nora_lib-0.0.5.dev2}/nora_lib/context/context_service.py +0 -0
- {nora_lib-0.0.5.dev0 → nora_lib-0.0.5.dev2}/nora_lib/context/models.py +0 -0
- {nora_lib-0.0.5.dev0 → nora_lib-0.0.5.dev2}/nora_lib/interactions/__init__.py +0 -0
- {nora_lib-0.0.5.dev0 → nora_lib-0.0.5.dev2}/nora_lib/py.typed +0 -0
- {nora_lib-0.0.5.dev0 → nora_lib-0.0.5.dev2}/nora_lib/tasks/__init__.py +0 -0
- {nora_lib-0.0.5.dev0 → nora_lib-0.0.5.dev2}/nora_lib/tasks/models.py +0 -0
- {nora_lib-0.0.5.dev0 → nora_lib-0.0.5.dev2}/nora_lib/tasks/state.py +0 -0
- {nora_lib-0.0.5.dev0 → nora_lib-0.0.5.dev2}/nora_lib.egg-info/SOURCES.txt +0 -0
- {nora_lib-0.0.5.dev0 → nora_lib-0.0.5.dev2}/nora_lib.egg-info/dependency_links.txt +0 -0
- {nora_lib-0.0.5.dev0 → nora_lib-0.0.5.dev2}/nora_lib.egg-info/requires.txt +0 -0
- {nora_lib-0.0.5.dev0 → nora_lib-0.0.5.dev2}/nora_lib.egg-info/top_level.txt +0 -0
- {nora_lib-0.0.5.dev0 → nora_lib-0.0.5.dev2}/pyproject.toml +0 -0
- {nora_lib-0.0.5.dev0 → nora_lib-0.0.5.dev2}/setup.cfg +0 -0
- {nora_lib-0.0.5.dev0 → nora_lib-0.0.5.dev2}/tests/tasks/__init__.py +0 -0
- {nora_lib-0.0.5.dev0 → nora_lib-0.0.5.dev2}/tests/tasks/test_state.py +0 -0
- {nora_lib-0.0.5.dev0 → nora_lib-0.0.5.dev2}/tests/test_placeholder.py +0 -0
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
from datetime import datetime
|
|
2
|
+
import logging
|
|
3
|
+
import requests
|
|
4
|
+
from typing import List, Optional
|
|
5
|
+
|
|
6
|
+
from nora_lib.interactions.models import (
|
|
7
|
+
AnnotationBatch,
|
|
8
|
+
Event,
|
|
9
|
+
EventType,
|
|
10
|
+
Message,
|
|
11
|
+
ReturnedMessage,
|
|
12
|
+
ThreadRelationsResponse,
|
|
13
|
+
ThreadForkEventData,
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class InteractionsService:
|
|
18
|
+
"""
|
|
19
|
+
Service which saves interactions to the Interactions API
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
def __init__(self, base_url: str, timeout: int = 30, token: Optional[str] = None):
|
|
23
|
+
self.base_url = base_url
|
|
24
|
+
self.timeout = timeout
|
|
25
|
+
self.headers = {"Authorization": f"Bearer {token}"} if token else None
|
|
26
|
+
|
|
27
|
+
def save_message(self, message: Message) -> None:
|
|
28
|
+
"""Save a message to the Interactions API"""
|
|
29
|
+
message_url = f"{self.base_url}/interaction/v1/message"
|
|
30
|
+
response = requests.post(
|
|
31
|
+
message_url,
|
|
32
|
+
json=message.model_dump(),
|
|
33
|
+
headers=self.headers,
|
|
34
|
+
timeout=int(self.timeout),
|
|
35
|
+
)
|
|
36
|
+
response.raise_for_status()
|
|
37
|
+
|
|
38
|
+
def save_event(self, event: Event) -> None:
|
|
39
|
+
"""Save an event to the Interactions API"""
|
|
40
|
+
event_url = f"{self.base_url}/interaction/v1/event"
|
|
41
|
+
response = requests.post(
|
|
42
|
+
event_url,
|
|
43
|
+
json=event.model_dump(),
|
|
44
|
+
headers=self.headers,
|
|
45
|
+
timeout=int(self.timeout),
|
|
46
|
+
)
|
|
47
|
+
response.raise_for_status()
|
|
48
|
+
|
|
49
|
+
def save_annotation(self, annotation: AnnotationBatch) -> None:
|
|
50
|
+
"""Save an annotation to the Interactions API"""
|
|
51
|
+
annotation_url = f"{self.base_url}/interaction/v1/annotation"
|
|
52
|
+
response = requests.post(
|
|
53
|
+
annotation_url,
|
|
54
|
+
json=annotation.model_dump(),
|
|
55
|
+
headers=self.headers,
|
|
56
|
+
timeout=int(self.timeout),
|
|
57
|
+
)
|
|
58
|
+
response.raise_for_status()
|
|
59
|
+
|
|
60
|
+
def get_message(self, message_id: str) -> ReturnedMessage:
|
|
61
|
+
"""Fetch a message from the Interactions API"""
|
|
62
|
+
message_url = f"{self.base_url}/interaction/v1/search/message"
|
|
63
|
+
request_body = {
|
|
64
|
+
"id": message_id,
|
|
65
|
+
"relations": {"thread": {}, "channel": {}, "events": {}, "annotations": {}},
|
|
66
|
+
}
|
|
67
|
+
response = requests.post(
|
|
68
|
+
message_url,
|
|
69
|
+
json=request_body,
|
|
70
|
+
headers=self.headers,
|
|
71
|
+
timeout=int(self.timeout),
|
|
72
|
+
)
|
|
73
|
+
response.raise_for_status()
|
|
74
|
+
res_dict = response.json()["message"]
|
|
75
|
+
res = ReturnedMessage.model_validate(res_dict)
|
|
76
|
+
|
|
77
|
+
# thread_id and channel_id are for some reason nested in the response
|
|
78
|
+
if not res.thread_id:
|
|
79
|
+
res.thread_id = res_dict.get("thread", {}).get("thread_id")
|
|
80
|
+
if not res.channel_id:
|
|
81
|
+
res.channel_id = res_dict.get("channel", {}).get("channel_id")
|
|
82
|
+
|
|
83
|
+
return res
|
|
84
|
+
|
|
85
|
+
def fetch_all_threads_by_channel(self, channel_id: str, min_timestamp: str) -> dict:
|
|
86
|
+
"""Fetch a message from the Interactions API"""
|
|
87
|
+
message_url = f"{self.base_url}/interaction/v1/search/channel"
|
|
88
|
+
request_body = self._channel_lookup_request(
|
|
89
|
+
channel_id=channel_id, min_timestamp=min_timestamp
|
|
90
|
+
)
|
|
91
|
+
response = requests.post(
|
|
92
|
+
message_url,
|
|
93
|
+
json=request_body,
|
|
94
|
+
headers=self.headers,
|
|
95
|
+
timeout=int(self.timeout),
|
|
96
|
+
)
|
|
97
|
+
response.raise_for_status()
|
|
98
|
+
return response.json()
|
|
99
|
+
|
|
100
|
+
def fetch_messages_and_agent_context_events_for_thread(
|
|
101
|
+
self, message_id: str, event_type: str
|
|
102
|
+
) -> List[ReturnedMessage]:
|
|
103
|
+
"""Build a history of messages for a given message including associated events.
|
|
104
|
+
This includes messages from pre-forked threads."""
|
|
105
|
+
messages_with_events: List[ReturnedMessage] = []
|
|
106
|
+
|
|
107
|
+
messages_for_thread: ThreadRelationsResponse = (
|
|
108
|
+
self.fetch_thread_messages_and_events_for_message(
|
|
109
|
+
message_id, [event_type, EventType.THREAD_FORK.value]
|
|
110
|
+
)
|
|
111
|
+
)
|
|
112
|
+
messages_with_events.extend(messages_for_thread.messages)
|
|
113
|
+
|
|
114
|
+
# Process any thread_fork events
|
|
115
|
+
try:
|
|
116
|
+
for msg in messages_for_thread.messages:
|
|
117
|
+
for event in msg.events:
|
|
118
|
+
if event.type == EventType.THREAD_FORK.value:
|
|
119
|
+
event_data = ThreadForkEventData.model_validate(event.data)
|
|
120
|
+
forked_thread: ThreadRelationsResponse = (
|
|
121
|
+
self.fetch_thread_messages_and_events_for_message(
|
|
122
|
+
event_data.previous_message_id, [event_type]
|
|
123
|
+
)
|
|
124
|
+
)
|
|
125
|
+
messages_with_events.extend(forked_thread.messages)
|
|
126
|
+
except Exception as e: # pylint: disable=broad-except
|
|
127
|
+
logging.exception(
|
|
128
|
+
"Failed to fetch forked thread messages for message %s: %s",
|
|
129
|
+
message_id,
|
|
130
|
+
e,
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
messages_with_events.sort(key=lambda x: datetime.fromisoformat(x.ts))
|
|
134
|
+
return messages_with_events
|
|
135
|
+
|
|
136
|
+
def fetch_thread_messages_and_events_for_message(
|
|
137
|
+
self, message_id: str, event_types: list[str]
|
|
138
|
+
) -> ThreadRelationsResponse:
|
|
139
|
+
"""Fetch messages sorted by timestamp and events for agent context"""
|
|
140
|
+
message_url = f"{self.base_url}/interaction/v1/search/message"
|
|
141
|
+
request_body = self._thread_lookup_request(message_id, event_types=event_types)
|
|
142
|
+
response = requests.post(
|
|
143
|
+
message_url,
|
|
144
|
+
json=request_body,
|
|
145
|
+
headers=self.headers,
|
|
146
|
+
timeout=int(self.timeout),
|
|
147
|
+
)
|
|
148
|
+
response.raise_for_status()
|
|
149
|
+
json_response = response.json()
|
|
150
|
+
|
|
151
|
+
return ThreadRelationsResponse.model_validate(
|
|
152
|
+
json_response.get("message", {}).get("thread", {})
|
|
153
|
+
)
|
|
154
|
+
|
|
155
|
+
def fetch_messages_and_events_for_thread(
|
|
156
|
+
self,
|
|
157
|
+
thread_id: str,
|
|
158
|
+
event_type: Optional[str] = None,
|
|
159
|
+
min_timestamp: Optional[str] = None,
|
|
160
|
+
) -> dict:
|
|
161
|
+
"""Fetch messages and events for the given thread from the Interactions API"""
|
|
162
|
+
thread_search_url = f"{self.base_url}/interaction/v1/search/thread"
|
|
163
|
+
request_body = {
|
|
164
|
+
"id": thread_id,
|
|
165
|
+
"relations": {
|
|
166
|
+
"messages": (
|
|
167
|
+
{"filter": {"min_timestamp": min_timestamp}}
|
|
168
|
+
if min_timestamp
|
|
169
|
+
else {}
|
|
170
|
+
),
|
|
171
|
+
"events": {"filter": {"type": event_type}} if event_type else {},
|
|
172
|
+
},
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
response = requests.post(
|
|
176
|
+
thread_search_url,
|
|
177
|
+
json=request_body,
|
|
178
|
+
headers=self.headers,
|
|
179
|
+
timeout=int(self.timeout),
|
|
180
|
+
)
|
|
181
|
+
response.raise_for_status()
|
|
182
|
+
return response.json()
|
|
183
|
+
|
|
184
|
+
def fetch_events_for_message(
|
|
185
|
+
self,
|
|
186
|
+
message_id: str,
|
|
187
|
+
event_type: Optional[str] = None,
|
|
188
|
+
) -> dict:
|
|
189
|
+
"""Fetch messages and events for the thread containing a given message from the Interactions API"""
|
|
190
|
+
message_search_url = f"{self.base_url}/interaction/v1/search/message"
|
|
191
|
+
request_body = {
|
|
192
|
+
"id": message_id,
|
|
193
|
+
"relations": {
|
|
194
|
+
"events": {"filter": {"type": event_type}} if event_type else {},
|
|
195
|
+
},
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
response = requests.post(
|
|
199
|
+
message_search_url,
|
|
200
|
+
json=request_body,
|
|
201
|
+
headers=self.headers,
|
|
202
|
+
timeout=int(self.timeout),
|
|
203
|
+
)
|
|
204
|
+
response.raise_for_status()
|
|
205
|
+
return response.json()
|
|
206
|
+
|
|
207
|
+
@staticmethod
|
|
208
|
+
def _channel_lookup_request(channel_id: str, min_timestamp: str) -> dict:
|
|
209
|
+
"""Interaction service API request to get threads and messages for a channel"""
|
|
210
|
+
return {
|
|
211
|
+
"id": channel_id,
|
|
212
|
+
"relations": {
|
|
213
|
+
"threads": {
|
|
214
|
+
"relations": {
|
|
215
|
+
"messages": {
|
|
216
|
+
"filter": {"min_timestamp": min_timestamp},
|
|
217
|
+
"apply_annotations_from_actors": ["*"],
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
},
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
@staticmethod
|
|
225
|
+
def _thread_lookup_request(message_id: str, event_types: list[str]) -> dict:
|
|
226
|
+
"""will return all messages for the thread containing the given message and events associated with each message"""
|
|
227
|
+
return {
|
|
228
|
+
"id": message_id,
|
|
229
|
+
"relations": {
|
|
230
|
+
"thread": {
|
|
231
|
+
"relations": {
|
|
232
|
+
"messages": {
|
|
233
|
+
"relations": {"events": {"filter": {"type": event_types}}},
|
|
234
|
+
"apply_annotations_from_actors": ["*"],
|
|
235
|
+
},
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
},
|
|
239
|
+
}
|
|
@@ -25,6 +25,16 @@ class Annotation(BaseModel):
|
|
|
25
25
|
attributes: Optional[Dict[str, str]] = None
|
|
26
26
|
|
|
27
27
|
|
|
28
|
+
class AnnotationBatch(BaseModel):
|
|
29
|
+
actor_id: UUID
|
|
30
|
+
message_id: str
|
|
31
|
+
annotations: List[Annotation]
|
|
32
|
+
|
|
33
|
+
@field_serializer("actor_id")
|
|
34
|
+
def serialize_actor_id(self, actor_id: UUID):
|
|
35
|
+
return str(actor_id)
|
|
36
|
+
|
|
37
|
+
|
|
28
38
|
class Message(BaseModel):
|
|
29
39
|
message_id: str
|
|
30
40
|
actor_id: UUID
|
|
@@ -60,7 +70,7 @@ class Event(BaseModel):
|
|
|
60
70
|
)
|
|
61
71
|
timestamp: datetime
|
|
62
72
|
text: Optional[str] = None
|
|
63
|
-
data:
|
|
73
|
+
data: dict = Field(default_factory=dict)
|
|
64
74
|
message_id: Optional[str] = None
|
|
65
75
|
thread_id: Optional[str] = None
|
|
66
76
|
channel_id: Optional[str] = None
|
|
@@ -82,7 +92,10 @@ class ReturnedMessage(BaseModel):
|
|
|
82
92
|
text: str
|
|
83
93
|
ts: str
|
|
84
94
|
annotated_text: Optional[str] = None
|
|
85
|
-
events:
|
|
95
|
+
events: List[Event] = Field(default_factory=list)
|
|
96
|
+
thread_id: Optional[str] = None
|
|
97
|
+
channel_id: Optional[str] = None
|
|
98
|
+
annotations: Optional[List[Annotation]] = None
|
|
86
99
|
|
|
87
100
|
|
|
88
101
|
class AgentMessageData(BaseModel):
|
|
@@ -125,10 +138,12 @@ class ThreadRelationsResponse(BaseModel):
|
|
|
125
138
|
"""Thread format returned by interaction service for thread relations in a search response"""
|
|
126
139
|
|
|
127
140
|
thread_id: str
|
|
128
|
-
events:
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
141
|
+
events: List[Event] = Field(
|
|
142
|
+
default_factory=list
|
|
143
|
+
) # events associated only with the thread
|
|
144
|
+
messages: List[ReturnedMessage] = Field(
|
|
145
|
+
default_factory=list
|
|
146
|
+
) # includes events associated with each message
|
|
132
147
|
|
|
133
148
|
|
|
134
149
|
def thread_message_lookup_request(message_id: str, event_type: str) -> dict:
|
|
@@ -7,7 +7,7 @@ dev_requirements = ["mypy", "pytest", "black", "types-requests"]
|
|
|
7
7
|
|
|
8
8
|
setuptools.setup(
|
|
9
9
|
name="nora_lib",
|
|
10
|
-
version="0.0.5.
|
|
10
|
+
version="0.0.5.dev2",
|
|
11
11
|
description="For making and coordinating agents and tools",
|
|
12
12
|
url="https://github.com/allenai/nora_lib",
|
|
13
13
|
packages=setuptools.find_packages(exclude=(["tests"])),
|
|
@@ -1,146 +0,0 @@
|
|
|
1
|
-
from datetime import datetime
|
|
2
|
-
import requests
|
|
3
|
-
from typing import List, Optional
|
|
4
|
-
|
|
5
|
-
from nora_lib.interactions.models import (
|
|
6
|
-
Event,
|
|
7
|
-
EventType,
|
|
8
|
-
Message,
|
|
9
|
-
ReturnedMessage,
|
|
10
|
-
ThreadRelationsResponse,
|
|
11
|
-
ThreadForkEventData,
|
|
12
|
-
thread_message_lookup_request,
|
|
13
|
-
)
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
class InteractionsService:
|
|
17
|
-
"""
|
|
18
|
-
Service which saves interactions to the Interactions API
|
|
19
|
-
"""
|
|
20
|
-
|
|
21
|
-
def __init__(self, base_url, timeout, token):
|
|
22
|
-
self.base_url = base_url
|
|
23
|
-
self.timeout = timeout
|
|
24
|
-
self.headers = {"Authorization": f"Bearer {token}"}
|
|
25
|
-
|
|
26
|
-
def save_message(self, message: Message) -> None:
|
|
27
|
-
"""Save a message to the Interactions API"""
|
|
28
|
-
message_url = f"{self.base_url}/interaction/v1/message"
|
|
29
|
-
response = requests.post(
|
|
30
|
-
message_url,
|
|
31
|
-
json=message.model_dump(),
|
|
32
|
-
headers=self.headers,
|
|
33
|
-
timeout=int(self.timeout),
|
|
34
|
-
)
|
|
35
|
-
response.raise_for_status()
|
|
36
|
-
|
|
37
|
-
def save_event(self, event: Event) -> None:
|
|
38
|
-
"""Save an event to the Interactions API"""
|
|
39
|
-
event_url = f"{self.base_url}/interaction/v1/event"
|
|
40
|
-
response = requests.post(
|
|
41
|
-
event_url,
|
|
42
|
-
json=event.model_dump(),
|
|
43
|
-
headers=self.headers,
|
|
44
|
-
timeout=int(self.timeout),
|
|
45
|
-
)
|
|
46
|
-
response.raise_for_status()
|
|
47
|
-
|
|
48
|
-
def get_message(self, message_id: str) -> ReturnedMessage:
|
|
49
|
-
"""Fetch a message from the Interactions API"""
|
|
50
|
-
message_url = f"{self.base_url}/interaction/v1/search/message"
|
|
51
|
-
request_body = {
|
|
52
|
-
"id": message_id,
|
|
53
|
-
}
|
|
54
|
-
response = requests.post(
|
|
55
|
-
message_url,
|
|
56
|
-
json=request_body,
|
|
57
|
-
headers=self.headers,
|
|
58
|
-
timeout=int(self.timeout),
|
|
59
|
-
)
|
|
60
|
-
response.raise_for_status()
|
|
61
|
-
res_dict = response.json()["message"]
|
|
62
|
-
|
|
63
|
-
return ReturnedMessage.model_validate(res_dict)
|
|
64
|
-
|
|
65
|
-
def fetch_thread_messages_and_events_for_message(
|
|
66
|
-
self, message_id: str, event_type: str
|
|
67
|
-
) -> ThreadRelationsResponse:
|
|
68
|
-
"""Fetch messages and associated events from the same thread as provided messagev id"""
|
|
69
|
-
message_url = f"{self.base_url}/interaction/v1/search/message"
|
|
70
|
-
request_body = thread_message_lookup_request(message_id, event_type=event_type)
|
|
71
|
-
response = requests.post(
|
|
72
|
-
message_url,
|
|
73
|
-
json=request_body,
|
|
74
|
-
headers=self.headers,
|
|
75
|
-
timeout=int(self.timeout),
|
|
76
|
-
)
|
|
77
|
-
response.raise_for_status()
|
|
78
|
-
json_response = response.json()
|
|
79
|
-
|
|
80
|
-
return ThreadRelationsResponse.model_validate(
|
|
81
|
-
json_response.get("message", {}).get("thread", {})
|
|
82
|
-
)
|
|
83
|
-
|
|
84
|
-
def fetch_messages_and_events_for_thread(
|
|
85
|
-
self,
|
|
86
|
-
thread_id: str,
|
|
87
|
-
event_type: Optional[str] = None,
|
|
88
|
-
min_timestamp: Optional[str] = None,
|
|
89
|
-
) -> dict:
|
|
90
|
-
"""Fetch messages and events for the thread containing a given message from the Interactions API"""
|
|
91
|
-
THREAD_SEARCH_URL = f"{self.base_url}/interaction/v1/search/thread"
|
|
92
|
-
request_body = {
|
|
93
|
-
"id": thread_id,
|
|
94
|
-
"relations": {
|
|
95
|
-
"messages": (
|
|
96
|
-
{"filter": {"min_timestamp": min_timestamp}}
|
|
97
|
-
if min_timestamp
|
|
98
|
-
else {}
|
|
99
|
-
),
|
|
100
|
-
"events": {"filter": {"type": event_type}} if event_type else {},
|
|
101
|
-
},
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
response = requests.post(
|
|
105
|
-
THREAD_SEARCH_URL,
|
|
106
|
-
json=request_body,
|
|
107
|
-
headers=self.headers,
|
|
108
|
-
timeout=int(self.timeout),
|
|
109
|
-
)
|
|
110
|
-
response.raise_for_status()
|
|
111
|
-
return response.json()
|
|
112
|
-
|
|
113
|
-
def fetch_messages_and_events_for_forked_thread(
|
|
114
|
-
self, message_id: str, event_type: str
|
|
115
|
-
) -> List[ReturnedMessage]:
|
|
116
|
-
"""Build a history of messages for a given message including associated events.
|
|
117
|
-
This includes messages from pre-forked threads."""
|
|
118
|
-
returned_messages: List[ReturnedMessage] = []
|
|
119
|
-
|
|
120
|
-
messages_for_thread: ThreadRelationsResponse = (
|
|
121
|
-
self.fetch_thread_messages_and_events_for_message(message_id, event_type)
|
|
122
|
-
)
|
|
123
|
-
if messages_for_thread.messages:
|
|
124
|
-
returned_messages.extend(messages_for_thread.messages)
|
|
125
|
-
|
|
126
|
-
# Lookup any thread_fork events (conversation across surfaces)
|
|
127
|
-
thread_fork_events = self.fetch_messages_and_events_for_thread(
|
|
128
|
-
messages_for_thread.thread_id, EventType.THREAD_FORK.value
|
|
129
|
-
)
|
|
130
|
-
for forked_thread_event in thread_fork_events.get("thread", {}).get(
|
|
131
|
-
"events", []
|
|
132
|
-
):
|
|
133
|
-
event_data = ThreadForkEventData.model_validate(
|
|
134
|
-
forked_thread_event.get("data", {})
|
|
135
|
-
)
|
|
136
|
-
forked_thread: ThreadRelationsResponse = (
|
|
137
|
-
self.fetch_thread_messages_and_events_for_message(
|
|
138
|
-
event_data.previous_message_id, event_type
|
|
139
|
-
)
|
|
140
|
-
)
|
|
141
|
-
if forked_thread.messages:
|
|
142
|
-
returned_messages.extend(forked_thread.messages)
|
|
143
|
-
|
|
144
|
-
returned_messages.sort(key=lambda x: datetime.fromisoformat(x.ts))
|
|
145
|
-
|
|
146
|
-
return returned_messages
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|