nora-lib 0.0.3__tar.gz → 0.0.5.dev0__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.3 → nora_lib-0.0.5.dev0}/PKG-INFO +1 -1
- nora_lib-0.0.5.dev0/nora_lib/context/context_service.py +42 -0
- nora_lib-0.0.5.dev0/nora_lib/context/models.py +11 -0
- {nora_lib-0.0.3 → nora_lib-0.0.5.dev0}/nora_lib/interactions/interactions_service.py +29 -0
- {nora_lib-0.0.3 → nora_lib-0.0.5.dev0}/nora_lib/interactions/models.py +47 -13
- {nora_lib-0.0.3 → nora_lib-0.0.5.dev0}/nora_lib.egg-info/PKG-INFO +1 -1
- {nora_lib-0.0.3 → nora_lib-0.0.5.dev0}/setup.py +1 -1
- nora_lib-0.0.3/nora_lib/context/context_service.py +0 -88
- nora_lib-0.0.3/nora_lib/context/models.py +0 -21
- {nora_lib-0.0.3 → nora_lib-0.0.5.dev0}/README.md +0 -0
- {nora_lib-0.0.3 → nora_lib-0.0.5.dev0}/nora_lib/__init__.py +0 -0
- {nora_lib-0.0.3 → nora_lib-0.0.5.dev0}/nora_lib/context/__init__.py +0 -0
- {nora_lib-0.0.3 → nora_lib-0.0.5.dev0}/nora_lib/interactions/__init__.py +0 -0
- {nora_lib-0.0.3 → nora_lib-0.0.5.dev0}/nora_lib/py.typed +0 -0
- {nora_lib-0.0.3 → nora_lib-0.0.5.dev0}/nora_lib/tasks/__init__.py +0 -0
- {nora_lib-0.0.3 → nora_lib-0.0.5.dev0}/nora_lib/tasks/models.py +0 -0
- {nora_lib-0.0.3 → nora_lib-0.0.5.dev0}/nora_lib/tasks/state.py +0 -0
- {nora_lib-0.0.3 → nora_lib-0.0.5.dev0}/nora_lib.egg-info/SOURCES.txt +0 -0
- {nora_lib-0.0.3 → nora_lib-0.0.5.dev0}/nora_lib.egg-info/dependency_links.txt +0 -0
- {nora_lib-0.0.3 → nora_lib-0.0.5.dev0}/nora_lib.egg-info/requires.txt +0 -0
- {nora_lib-0.0.3 → nora_lib-0.0.5.dev0}/nora_lib.egg-info/top_level.txt +0 -0
- {nora_lib-0.0.3 → nora_lib-0.0.5.dev0}/pyproject.toml +0 -0
- {nora_lib-0.0.3 → nora_lib-0.0.5.dev0}/setup.cfg +0 -0
- {nora_lib-0.0.3 → nora_lib-0.0.5.dev0}/tests/tasks/__init__.py +0 -0
- {nora_lib-0.0.3 → nora_lib-0.0.5.dev0}/tests/tasks/test_state.py +0 -0
- {nora_lib-0.0.3 → nora_lib-0.0.5.dev0}/tests/test_placeholder.py +0 -0
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
from datetime import datetime, timezone
|
|
2
|
+
from typing import List, Optional
|
|
3
|
+
from uuid import UUID
|
|
4
|
+
|
|
5
|
+
from nora_lib.interactions.interactions_service import InteractionsService
|
|
6
|
+
from nora_lib.interactions.models import (
|
|
7
|
+
ReturnedMessage,
|
|
8
|
+
ReturnedAgentContextMessage,
|
|
9
|
+
ReturnedAgentContextEvent,
|
|
10
|
+
EventType,
|
|
11
|
+
AgentMessageData,
|
|
12
|
+
Event,
|
|
13
|
+
)
|
|
14
|
+
from nora_lib.context.models import WrappedTaskObject
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class ContextService:
|
|
18
|
+
"""
|
|
19
|
+
Save and retrieve task agent context from interaction store
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
def __init__(
|
|
23
|
+
self,
|
|
24
|
+
agent_actor_id: str, # uuid representing this agent in interaction store
|
|
25
|
+
interactions_base_url: str,
|
|
26
|
+
interactions_bearer_token: Optional[str],
|
|
27
|
+
timeout: int = 30,
|
|
28
|
+
):
|
|
29
|
+
self.interactions_service = self._get_interactions_service(
|
|
30
|
+
interactions_base_url, interactions_bearer_token, timeout
|
|
31
|
+
)
|
|
32
|
+
self.agent_actor_id = agent_actor_id
|
|
33
|
+
|
|
34
|
+
def _get_interactions_service(self, url, token, timeout) -> InteractionsService:
|
|
35
|
+
return InteractionsService(url, timeout, token)
|
|
36
|
+
|
|
37
|
+
def get_message(self, message_id: str) -> str:
|
|
38
|
+
message: ReturnedMessage = self.interactions_service.get_message(message_id)
|
|
39
|
+
if message.annotated_text:
|
|
40
|
+
return message.annotated_text
|
|
41
|
+
else:
|
|
42
|
+
return message.text
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
from typing import Optional
|
|
2
|
+
from pydantic import BaseModel, Field
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class WrappedTaskObject(BaseModel):
|
|
6
|
+
"""Encloses request or response object with additional metadata"""
|
|
7
|
+
|
|
8
|
+
message_id: str = Field(
|
|
9
|
+
description="id of originating message; key for istore retrieval"
|
|
10
|
+
)
|
|
11
|
+
data: dict = Field(description="Tool-defined request or response")
|
|
@@ -5,6 +5,7 @@ from typing import List, Optional
|
|
|
5
5
|
from nora_lib.interactions.models import (
|
|
6
6
|
Event,
|
|
7
7
|
EventType,
|
|
8
|
+
Message,
|
|
8
9
|
ReturnedMessage,
|
|
9
10
|
ThreadRelationsResponse,
|
|
10
11
|
ThreadForkEventData,
|
|
@@ -22,6 +23,17 @@ class InteractionsService:
|
|
|
22
23
|
self.timeout = timeout
|
|
23
24
|
self.headers = {"Authorization": f"Bearer {token}"}
|
|
24
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
|
+
|
|
25
37
|
def save_event(self, event: Event) -> None:
|
|
26
38
|
"""Save an event to the Interactions API"""
|
|
27
39
|
event_url = f"{self.base_url}/interaction/v1/event"
|
|
@@ -33,6 +45,23 @@ class InteractionsService:
|
|
|
33
45
|
)
|
|
34
46
|
response.raise_for_status()
|
|
35
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
|
+
|
|
36
65
|
def fetch_thread_messages_and_events_for_message(
|
|
37
66
|
self, message_id: str, event_type: str
|
|
38
67
|
) -> ThreadRelationsResponse:
|
|
@@ -4,10 +4,44 @@ Model for interactions to be sent to the interactions service.
|
|
|
4
4
|
|
|
5
5
|
from datetime import datetime
|
|
6
6
|
from enum import Enum
|
|
7
|
-
from typing import Optional,
|
|
7
|
+
from typing import Dict, List, Optional, Tuple
|
|
8
8
|
from uuid import UUID
|
|
9
9
|
|
|
10
|
-
from pydantic import BaseModel, Field, field_serializer
|
|
10
|
+
from pydantic import BaseModel, Field, field_serializer, ConfigDict
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class Surface(str, Enum):
|
|
14
|
+
SLACK = "Slack"
|
|
15
|
+
WEB = "NoraWebapp"
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class Annotation(BaseModel):
|
|
19
|
+
# Need this config to stringify numeric values in attributes.
|
|
20
|
+
# Otherwise, we'll get 'Input should be a valid string' error.
|
|
21
|
+
model_config = ConfigDict(coerce_numbers_to_str=True)
|
|
22
|
+
|
|
23
|
+
tag: str
|
|
24
|
+
span: Tuple[int, int]
|
|
25
|
+
attributes: Optional[Dict[str, str]] = None
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class Message(BaseModel):
|
|
29
|
+
message_id: str
|
|
30
|
+
actor_id: UUID
|
|
31
|
+
text: str
|
|
32
|
+
thread_id: Optional[str]
|
|
33
|
+
channel_id: str
|
|
34
|
+
surface: Surface
|
|
35
|
+
ts: datetime
|
|
36
|
+
annotations: Optional[List[Annotation]] = None
|
|
37
|
+
|
|
38
|
+
@field_serializer("actor_id")
|
|
39
|
+
def serialize_actor_id(self, actor_id: UUID):
|
|
40
|
+
return str(actor_id)
|
|
41
|
+
|
|
42
|
+
@field_serializer("ts")
|
|
43
|
+
def serialize_ts(self, ts: datetime):
|
|
44
|
+
return ts.isoformat()
|
|
11
45
|
|
|
12
46
|
|
|
13
47
|
class EventType(str, Enum):
|
|
@@ -40,6 +74,17 @@ class Event(BaseModel):
|
|
|
40
74
|
return timestamp.isoformat()
|
|
41
75
|
|
|
42
76
|
|
|
77
|
+
class ReturnedMessage(BaseModel):
|
|
78
|
+
"""Message format returned by interaction service"""
|
|
79
|
+
|
|
80
|
+
message_id: str
|
|
81
|
+
actor_id: str
|
|
82
|
+
text: str
|
|
83
|
+
ts: str
|
|
84
|
+
annotated_text: Optional[str] = None
|
|
85
|
+
events: Optional[List[dict]] = None
|
|
86
|
+
|
|
87
|
+
|
|
43
88
|
class AgentMessageData(BaseModel):
|
|
44
89
|
"""capture requests to and responses from tools within Events"""
|
|
45
90
|
|
|
@@ -76,17 +121,6 @@ class ThreadForkEventData(BaseModel):
|
|
|
76
121
|
previous_message_id: str
|
|
77
122
|
|
|
78
123
|
|
|
79
|
-
class ReturnedMessage(BaseModel):
|
|
80
|
-
"""Message format returned by interaction service for search by thread"""
|
|
81
|
-
|
|
82
|
-
message_id: str
|
|
83
|
-
actor_id: str
|
|
84
|
-
text: str
|
|
85
|
-
ts: str
|
|
86
|
-
annotated_text: Optional[str] = None
|
|
87
|
-
events: Optional[List[dict]] = None
|
|
88
|
-
|
|
89
|
-
|
|
90
124
|
class ThreadRelationsResponse(BaseModel):
|
|
91
125
|
"""Thread format returned by interaction service for thread relations in a search response"""
|
|
92
126
|
|
|
@@ -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.
|
|
10
|
+
version="0.0.5.dev0",
|
|
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,88 +0,0 @@
|
|
|
1
|
-
from datetime import datetime, timezone
|
|
2
|
-
from typing import List, Optional
|
|
3
|
-
from uuid import UUID
|
|
4
|
-
|
|
5
|
-
from nora_lib.interactions.interactions_service import InteractionsService
|
|
6
|
-
from nora_lib.interactions.models import (
|
|
7
|
-
ReturnedMessage,
|
|
8
|
-
ReturnedAgentContextMessage,
|
|
9
|
-
ReturnedAgentContextEvent,
|
|
10
|
-
EventType,
|
|
11
|
-
AgentMessageData,
|
|
12
|
-
Event,
|
|
13
|
-
)
|
|
14
|
-
from nora_lib.context.models import WrappedTaskObject
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
class ContextService:
|
|
18
|
-
"""
|
|
19
|
-
Save and retrieve task agent context from interaction store
|
|
20
|
-
"""
|
|
21
|
-
|
|
22
|
-
def __init__(
|
|
23
|
-
self,
|
|
24
|
-
agent_actor_id: str, # uuid representing this agent in interaction store
|
|
25
|
-
interactions_base_url: str,
|
|
26
|
-
interactions_bearer_token: Optional[str],
|
|
27
|
-
timeout: int = 30,
|
|
28
|
-
):
|
|
29
|
-
self.interactions_service = self._get_interactions_service(
|
|
30
|
-
interactions_base_url, interactions_bearer_token, timeout
|
|
31
|
-
)
|
|
32
|
-
self.agent_actor_id = agent_actor_id
|
|
33
|
-
|
|
34
|
-
def _get_interactions_service(self, url, token, timeout) -> InteractionsService:
|
|
35
|
-
return InteractionsService(url, timeout, token)
|
|
36
|
-
|
|
37
|
-
def fetch_context(
|
|
38
|
-
self, request: WrappedTaskObject
|
|
39
|
-
) -> List[ReturnedAgentContextMessage]:
|
|
40
|
-
message_id = request.message_id
|
|
41
|
-
|
|
42
|
-
returned_messages: List[ReturnedMessage] = (
|
|
43
|
-
self.interactions_service.fetch_messages_and_events_for_forked_thread(
|
|
44
|
-
message_id, EventType.AGENT_CONTEXT
|
|
45
|
-
)
|
|
46
|
-
)
|
|
47
|
-
|
|
48
|
-
messages_with_filtered_events: List[ReturnedAgentContextMessage] = []
|
|
49
|
-
for message in returned_messages:
|
|
50
|
-
events_saved_by_this_agent: List[ReturnedAgentContextEvent] = []
|
|
51
|
-
if message.events:
|
|
52
|
-
for event in message.events:
|
|
53
|
-
context_event = ReturnedAgentContextEvent.model_validate(event)
|
|
54
|
-
if context_event.actor_id == self.agent_actor_id:
|
|
55
|
-
events_saved_by_this_agent.append(context_event)
|
|
56
|
-
|
|
57
|
-
events_saved_by_this_agent.sort(
|
|
58
|
-
key=lambda event: datetime.fromisoformat(event.timestamp)
|
|
59
|
-
)
|
|
60
|
-
|
|
61
|
-
updated_message = ReturnedAgentContextMessage(
|
|
62
|
-
message_id=message.message_id,
|
|
63
|
-
actor_id=message.actor_id,
|
|
64
|
-
text=message.text,
|
|
65
|
-
ts=message.ts,
|
|
66
|
-
annotated_text=message.annotated_text,
|
|
67
|
-
events=events_saved_by_this_agent,
|
|
68
|
-
)
|
|
69
|
-
|
|
70
|
-
messages_with_filtered_events.append(updated_message)
|
|
71
|
-
|
|
72
|
-
return messages_with_filtered_events
|
|
73
|
-
|
|
74
|
-
def save_context(self, event_data: WrappedTaskObject):
|
|
75
|
-
agent_data = AgentMessageData(
|
|
76
|
-
message_data=event_data.model_dump(),
|
|
77
|
-
data_sender_actor_id=event_data.sender_actor_id,
|
|
78
|
-
virtual_thread_id=event_data.virtual_thread_id,
|
|
79
|
-
)
|
|
80
|
-
event = Event(
|
|
81
|
-
type=EventType.AGENT_CONTEXT,
|
|
82
|
-
actor_id=UUID(self.agent_actor_id),
|
|
83
|
-
timestamp=datetime.now(timezone.utc),
|
|
84
|
-
data=agent_data.model_dump(),
|
|
85
|
-
message_id=event_data.message_id,
|
|
86
|
-
)
|
|
87
|
-
|
|
88
|
-
self.interactions_service.save_event(event)
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
from typing import Generic, Optional, TypeVar
|
|
2
|
-
from pydantic import BaseModel, Field
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
R = TypeVar("R", bound=BaseModel)
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
class WrappedTaskObject(BaseModel, Generic[R]):
|
|
9
|
-
"""Encloses request or response object with additional metadata"""
|
|
10
|
-
|
|
11
|
-
message_id: str = Field(
|
|
12
|
-
description="id of originating message; key for istore retrieval"
|
|
13
|
-
)
|
|
14
|
-
sender_actor_id: str = Field(
|
|
15
|
-
description="string representation of uuid identifying agent sending data"
|
|
16
|
-
)
|
|
17
|
-
virtual_thread_id: Optional[str] = Field(
|
|
18
|
-
description="Tool-defined local thread to associate follow up requests"
|
|
19
|
-
)
|
|
20
|
-
task_id: Optional[str] = Field(description="Reference to a long-running task")
|
|
21
|
-
data: R = Field(description="Tool-defined request or response")
|
|
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
|