nora-lib-impl 1.0.0__py3-none-any.whl
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/__init__.py +0 -0
- nora_lib/impl/__init__.py +0 -0
- nora_lib/impl/context/__init__.py +0 -0
- nora_lib/impl/context/agent_context.py +43 -0
- nora_lib/impl/context/context_service.py +33 -0
- nora_lib/impl/context/models.py +11 -0
- nora_lib/impl/interactions/__init__.py +0 -0
- nora_lib/impl/interactions/interactions_service.py +561 -0
- nora_lib/impl/interactions/models.py +414 -0
- nora_lib/impl/interactions/step_progress.py +69 -0
- nora_lib/impl/pubsub.py +140 -0
- nora_lib/impl/tasks/__init__.py +0 -0
- nora_lib/impl/tasks/state.py +156 -0
- nora_lib/progress/__init__.py +0 -0
- nora_lib/progress/models.py +47 -0
- nora_lib/progress/reporter.py +122 -0
- nora_lib/py.typed +0 -0
- nora_lib/serializers.py +10 -0
- nora_lib/tasks/__init__.py +0 -0
- nora_lib/tasks/models.py +25 -0
- nora_lib/tasks/state.py +71 -0
- nora_lib_impl-1.0.0.dist-info/METADATA +20 -0
- nora_lib_impl-1.0.0.dist-info/RECORD +25 -0
- nora_lib_impl-1.0.0.dist-info/WHEEL +5 -0
- nora_lib_impl-1.0.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Handles IO for asynchronous task-related state.
|
|
3
|
+
|
|
4
|
+
A StateManager instance must be initialized with
|
|
5
|
+
a concrete subclass of `AsyncTaskState`, as implemented
|
|
6
|
+
by dependent projects.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
import logging
|
|
10
|
+
from uuid import UUID
|
|
11
|
+
from datetime import datetime, timezone
|
|
12
|
+
from typing import Optional, Any
|
|
13
|
+
from pydantic import BaseModel
|
|
14
|
+
|
|
15
|
+
from nora_lib.tasks.models import AsyncTaskState, R
|
|
16
|
+
from nora_lib.tasks.state import (
|
|
17
|
+
IStateManager,
|
|
18
|
+
TaskStateFetchException,
|
|
19
|
+
NoSuchTaskException,
|
|
20
|
+
)
|
|
21
|
+
from nora_lib.impl.interactions.interactions_service import InteractionsService
|
|
22
|
+
from nora_lib.impl.interactions.models import Event, ReturnedEvent
|
|
23
|
+
from nora_lib.impl.pubsub import PubsubService
|
|
24
|
+
from nora_lib.impl.context.agent_context import AgentContext
|
|
25
|
+
|
|
26
|
+
TASK_STATE_CHANGE_TOPIC = "istore:event:task_state"
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class RemoteStateManagerFactory:
|
|
30
|
+
"""
|
|
31
|
+
Stores task state in the interaction store
|
|
32
|
+
"""
|
|
33
|
+
|
|
34
|
+
def __init__(
|
|
35
|
+
self,
|
|
36
|
+
agent_name: str,
|
|
37
|
+
actor_id: UUID,
|
|
38
|
+
interactions_service: InteractionsService,
|
|
39
|
+
pubsub_service: PubsubService,
|
|
40
|
+
):
|
|
41
|
+
"""
|
|
42
|
+
:param agent_name: Used to form the event type that will hold the task state in the interactions store
|
|
43
|
+
:param actor_id: Associated with the events written to the interactions store
|
|
44
|
+
:param interactions_service:
|
|
45
|
+
"""
|
|
46
|
+
self.agent_name = agent_name
|
|
47
|
+
self.actor_id = actor_id
|
|
48
|
+
self.interactions_service = interactions_service
|
|
49
|
+
self.pubsub_service = pubsub_service
|
|
50
|
+
|
|
51
|
+
def for_message(self, message_id: str) -> IStateManager[R]:
|
|
52
|
+
return RemoteStateManager(
|
|
53
|
+
self.agent_name,
|
|
54
|
+
self.actor_id,
|
|
55
|
+
self.interactions_service,
|
|
56
|
+
self.pubsub_service,
|
|
57
|
+
message_id,
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
def for_agent_context(self, context: AgentContext) -> IStateManager[R]:
|
|
61
|
+
return RemoteStateManager(
|
|
62
|
+
self.agent_name,
|
|
63
|
+
self.actor_id,
|
|
64
|
+
self.interactions_service,
|
|
65
|
+
PubsubService(context.pubsub.base_url, context.pubsub.namespace),
|
|
66
|
+
context.message.message_id,
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
class RemoteStateManager(IStateManager[R]):
|
|
71
|
+
"""
|
|
72
|
+
Stores task state in the interaction store
|
|
73
|
+
"""
|
|
74
|
+
|
|
75
|
+
_TASK_STATE_EVENT_TYPE = "agent:{}:task_state"
|
|
76
|
+
|
|
77
|
+
def __init__(
|
|
78
|
+
self,
|
|
79
|
+
agent_name: str,
|
|
80
|
+
actor_id: UUID,
|
|
81
|
+
interactions_service: InteractionsService,
|
|
82
|
+
pubsub_service: PubsubService,
|
|
83
|
+
message_id: str,
|
|
84
|
+
):
|
|
85
|
+
"""
|
|
86
|
+
:param agent_name: Agent that saved the task
|
|
87
|
+
:param actor_id: ID for the agent (ignored when reading)
|
|
88
|
+
:param message_id: The message that initiated the request for task status
|
|
89
|
+
"""
|
|
90
|
+
self.agent_name = agent_name
|
|
91
|
+
self.actor_id = actor_id
|
|
92
|
+
self.message_id = message_id
|
|
93
|
+
self.interactions_service = interactions_service
|
|
94
|
+
self.pubsub_service = pubsub_service
|
|
95
|
+
|
|
96
|
+
def read_state(self, task_id: str) -> AsyncTaskState[R]:
|
|
97
|
+
event_type = RemoteStateManager._TASK_STATE_EVENT_TYPE.format(self.agent_name)
|
|
98
|
+
response = (
|
|
99
|
+
self.interactions_service.fetch_thread_messages_and_events_for_message(
|
|
100
|
+
self.message_id, [event_type]
|
|
101
|
+
)
|
|
102
|
+
)
|
|
103
|
+
latest_state: Optional[AsyncTaskState[R]] = None
|
|
104
|
+
latest_timestamp = None
|
|
105
|
+
for msg in response.messages or []:
|
|
106
|
+
for event in msg.events or []:
|
|
107
|
+
try:
|
|
108
|
+
state = AsyncTaskState[Any].model_validate(event.data)
|
|
109
|
+
except Exception as e:
|
|
110
|
+
# Event json blob has unexpected format
|
|
111
|
+
raise TaskStateFetchException(
|
|
112
|
+
f"Event of type {event_type} for message {self.message_id} does not deserialize to AsyncTaskState: {e}"
|
|
113
|
+
)
|
|
114
|
+
if state.task_id != task_id:
|
|
115
|
+
continue
|
|
116
|
+
if latest_state is None or (
|
|
117
|
+
latest_timestamp and event.timestamp > latest_timestamp
|
|
118
|
+
):
|
|
119
|
+
latest_state = state
|
|
120
|
+
latest_timestamp = event.timestamp
|
|
121
|
+
|
|
122
|
+
if not latest_state:
|
|
123
|
+
raise NoSuchTaskException(task_id)
|
|
124
|
+
return latest_state
|
|
125
|
+
|
|
126
|
+
def write_state(self, state: AsyncTaskState[R]) -> None:
|
|
127
|
+
event_type = RemoteStateManager._TASK_STATE_EVENT_TYPE.format(self.agent_name)
|
|
128
|
+
event = Event(
|
|
129
|
+
type=event_type,
|
|
130
|
+
actor_id=self.actor_id,
|
|
131
|
+
timestamp=datetime.now(tz=timezone.utc),
|
|
132
|
+
message_id=self.message_id,
|
|
133
|
+
data=state.model_dump(),
|
|
134
|
+
)
|
|
135
|
+
event_id = self.interactions_service.save_event(event)
|
|
136
|
+
returned_event = ReturnedEvent(
|
|
137
|
+
event_id=event_id,
|
|
138
|
+
type=event.type,
|
|
139
|
+
actor_id=event.actor_id,
|
|
140
|
+
message_id=event.message_id,
|
|
141
|
+
timestamp=event.timestamp,
|
|
142
|
+
)
|
|
143
|
+
payload = TaskStateChangeNotification(
|
|
144
|
+
agent=self.agent_name, event=returned_event
|
|
145
|
+
)
|
|
146
|
+
try:
|
|
147
|
+
self.pubsub_service.publish(TASK_STATE_CHANGE_TOPIC, payload.model_dump())
|
|
148
|
+
except Exception as e:
|
|
149
|
+
logging.exception(
|
|
150
|
+
f"Failed to publish event to pubsub topic {TASK_STATE_CHANGE_TOPIC} at {self.pubsub_service.base_url}"
|
|
151
|
+
)
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
class TaskStateChangeNotification(BaseModel):
|
|
155
|
+
agent: str
|
|
156
|
+
event: ReturnedEvent
|
|
File without changes
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
from enum import Enum
|
|
2
|
+
import uuid
|
|
3
|
+
from typing import Optional
|
|
4
|
+
|
|
5
|
+
from pydantic import BaseModel, Field
|
|
6
|
+
|
|
7
|
+
from nora_lib.serializers import (
|
|
8
|
+
UuidWithSerializer,
|
|
9
|
+
DatetimeWithSerializer,
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class RunState(str, Enum):
|
|
14
|
+
"""State of a step"""
|
|
15
|
+
|
|
16
|
+
CREATED = "created"
|
|
17
|
+
RUNNING = "running"
|
|
18
|
+
SUCCEEDED = "succeeded"
|
|
19
|
+
FAILED = "failed"
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class StepProgress(BaseModel):
|
|
23
|
+
"""Data class for step progress. This goes into the `data` field in `Event`."""
|
|
24
|
+
|
|
25
|
+
# A short message, e.g. "searching for $query". Recommend < 100 chars.
|
|
26
|
+
short_desc: str
|
|
27
|
+
# Detailed message.
|
|
28
|
+
long_desc: Optional[str] = None
|
|
29
|
+
# Updates on the same unit of work have the same step_id.
|
|
30
|
+
step_id: UuidWithSerializer = Field(default_factory=uuid.uuid4)
|
|
31
|
+
# Inner steps can be constituent to some outer step, effectively a tree.
|
|
32
|
+
parent_step_id: Optional[UuidWithSerializer] = None
|
|
33
|
+
# Populated if this step is due to an async task.
|
|
34
|
+
task_id: Optional[str] = None
|
|
35
|
+
|
|
36
|
+
# Enum of possible states.
|
|
37
|
+
run_state: RunState = RunState.CREATED
|
|
38
|
+
# DB timestamp when this step was defined/created.
|
|
39
|
+
created_at: Optional[DatetimeWithSerializer] = None
|
|
40
|
+
# When this step started running.
|
|
41
|
+
started_at: Optional[DatetimeWithSerializer] = None
|
|
42
|
+
# Estimated finish time, if available.
|
|
43
|
+
finish_est: Optional[DatetimeWithSerializer] = None
|
|
44
|
+
# When this step stopped running, whether that was due to success or failure.
|
|
45
|
+
finished_at: Optional[DatetimeWithSerializer] = None
|
|
46
|
+
# Error message in case of terminal step failure.
|
|
47
|
+
error_message: Optional[str] = None
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
from abc import ABC, abstractmethod
|
|
2
|
+
from typing import Optional
|
|
3
|
+
from datetime import datetime, timezone
|
|
4
|
+
|
|
5
|
+
from nora_lib.progress.models import StepProgress, RunState
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class StepProgressWriter(ABC):
|
|
9
|
+
"""Writes step progress to a store or service"""
|
|
10
|
+
|
|
11
|
+
def write(self, step_progress: StepProgress):
|
|
12
|
+
"""Default no-op implementation"""
|
|
13
|
+
pass
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class StepProgressReporter:
|
|
17
|
+
"""
|
|
18
|
+
Tracks the lifecycle of a task and records incremental progress to some external store
|
|
19
|
+
|
|
20
|
+
Usage:
|
|
21
|
+
# Create/define a step
|
|
22
|
+
find_papers_progress = StepProgressReporter(...)
|
|
23
|
+
find_papers_progress.create()
|
|
24
|
+
|
|
25
|
+
# Do something
|
|
26
|
+
...
|
|
27
|
+
|
|
28
|
+
# Start the step
|
|
29
|
+
find_papers_progress.start()
|
|
30
|
+
|
|
31
|
+
# Describe a part of the work of that outer step as a child step.
|
|
32
|
+
...
|
|
33
|
+
count_citation_progress = find_papers_progress.create_child_step(short_desc="Count citations")
|
|
34
|
+
count_citation_progress.create()
|
|
35
|
+
...
|
|
36
|
+
count_citation_progress.start()
|
|
37
|
+
...
|
|
38
|
+
count_citation_progress.finish(is_success=True)
|
|
39
|
+
...
|
|
40
|
+
|
|
41
|
+
# Finish the outer step
|
|
42
|
+
find_papers_progress.finish(is_success=False, error_message="Something went wrong")
|
|
43
|
+
|
|
44
|
+
# Alternatively, you can use this as a context. Step state transitions are managed for you,
|
|
45
|
+
# so you should NOT call any of the create/start/finish methods.
|
|
46
|
+
|
|
47
|
+
with StepProgressReporter(...) as spr:
|
|
48
|
+
# Do something
|
|
49
|
+
...
|
|
50
|
+
|
|
51
|
+
# This step will be automatically created, started, and finished when the context exits.
|
|
52
|
+
# If an exception is raised, the step will be marked as failed
|
|
53
|
+
# and the exception message will be recorded in the error_message field
|
|
54
|
+
"""
|
|
55
|
+
|
|
56
|
+
def __init__(self, step_progress: StepProgress, writer: StepProgressWriter):
|
|
57
|
+
self.step_progress = step_progress
|
|
58
|
+
self.writer = writer
|
|
59
|
+
|
|
60
|
+
def __enter__(self):
|
|
61
|
+
if self.step_progress.created_at is None:
|
|
62
|
+
self.create()
|
|
63
|
+
self.start()
|
|
64
|
+
return self
|
|
65
|
+
|
|
66
|
+
def __exit__(self, error_type, value, traceback):
|
|
67
|
+
is_success = error_type is None
|
|
68
|
+
self.finish(is_success=is_success, error_message=str(value))
|
|
69
|
+
return True
|
|
70
|
+
|
|
71
|
+
def create(self):
|
|
72
|
+
"""Create a step, but don't start it yet. This is useful for defining plans."""
|
|
73
|
+
if self.step_progress.run_state in [
|
|
74
|
+
RunState.RUNNING,
|
|
75
|
+
RunState.SUCCEEDED,
|
|
76
|
+
RunState.FAILED,
|
|
77
|
+
]:
|
|
78
|
+
return
|
|
79
|
+
|
|
80
|
+
self.step_progress.run_state = RunState.CREATED
|
|
81
|
+
self.step_progress.created_at = datetime.now(timezone.utc)
|
|
82
|
+
self.writer.write(self.step_progress)
|
|
83
|
+
|
|
84
|
+
def start(self):
|
|
85
|
+
"""Start a step"""
|
|
86
|
+
if self.step_progress.run_state in [
|
|
87
|
+
RunState.RUNNING,
|
|
88
|
+
RunState.SUCCEEDED,
|
|
89
|
+
RunState.FAILED,
|
|
90
|
+
]:
|
|
91
|
+
return
|
|
92
|
+
|
|
93
|
+
self.step_progress.started_at = datetime.now(timezone.utc)
|
|
94
|
+
self.step_progress.run_state = RunState.RUNNING
|
|
95
|
+
self.writer.write(self.step_progress)
|
|
96
|
+
|
|
97
|
+
def finish(self, is_success: bool, error_message: Optional[str] = None):
|
|
98
|
+
"""Finish a step whether it was successful or not"""
|
|
99
|
+
if self.step_progress.run_state != RunState.RUNNING:
|
|
100
|
+
return
|
|
101
|
+
else:
|
|
102
|
+
self.step_progress.finished_at = datetime.now(timezone.utc)
|
|
103
|
+
self.step_progress.run_state = (
|
|
104
|
+
RunState.SUCCEEDED if is_success else RunState.FAILED
|
|
105
|
+
)
|
|
106
|
+
self.step_progress.error_message = error_message if error_message else None
|
|
107
|
+
self.writer.write(self.step_progress)
|
|
108
|
+
|
|
109
|
+
def create_child_step(
|
|
110
|
+
self, short_desc: str, **step_progress_kwargs
|
|
111
|
+
) -> "StepProgressReporter":
|
|
112
|
+
"""Create a child step"""
|
|
113
|
+
child_step_progress = StepProgress(
|
|
114
|
+
short_desc=short_desc,
|
|
115
|
+
parent_step_id=self.step_progress.step_id,
|
|
116
|
+
task_id=self.step_progress.task_id,
|
|
117
|
+
**step_progress_kwargs,
|
|
118
|
+
)
|
|
119
|
+
child_reporter = StepProgressReporter(
|
|
120
|
+
step_progress=child_step_progress, writer=self.writer
|
|
121
|
+
)
|
|
122
|
+
return child_reporter
|
nora_lib/py.typed
ADDED
|
File without changes
|
nora_lib/serializers.py
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
from datetime import datetime
|
|
2
|
+
from uuid import UUID
|
|
3
|
+
|
|
4
|
+
from pydantic import PlainSerializer
|
|
5
|
+
from typing_extensions import Annotated
|
|
6
|
+
|
|
7
|
+
UuidWithSerializer = Annotated[UUID, PlainSerializer(lambda x: str(x), return_type=str)]
|
|
8
|
+
DatetimeWithSerializer = Annotated[
|
|
9
|
+
datetime, PlainSerializer(lambda x: x.isoformat(), return_type=str)
|
|
10
|
+
]
|
|
File without changes
|
nora_lib/tasks/models.py
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
from typing import Any, Dict, Generic, Optional, TypeVar
|
|
2
|
+
from pydantic import BaseModel, Field
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
R = TypeVar("R", bound=BaseModel)
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
TASK_STATUSES = dict(STARTED="STARTED", FAILED="FAILED", COMPLETED="COMPLETED")
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class AsyncTaskState(BaseModel, Generic[R]):
|
|
12
|
+
"""Models the current state of an asynchronous request."""
|
|
13
|
+
|
|
14
|
+
task_id: str = Field(
|
|
15
|
+
"Identifies the long-running task so that its status and eventual result"
|
|
16
|
+
"can be checked in follow-up calls."
|
|
17
|
+
)
|
|
18
|
+
estimated_time: str = Field(
|
|
19
|
+
description="How long we expect this task to take from start to finish."
|
|
20
|
+
)
|
|
21
|
+
task_status: str = Field(description="Current human-readable status of the task.")
|
|
22
|
+
task_result: Optional[R] = Field(description="Final result of the task.")
|
|
23
|
+
extra_state: Dict[str, Any] = Field(
|
|
24
|
+
description="Any extra task-specific state can go in here as free-form JSON-serializable dictionary."
|
|
25
|
+
)
|
nora_lib/tasks/state.py
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Handles IO for asynchronous task-related state.
|
|
3
|
+
|
|
4
|
+
A StateManager instance must be initialized with
|
|
5
|
+
a concrete subclass of `AsyncTaskState`, as implemented
|
|
6
|
+
by dependent projects.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
import json
|
|
10
|
+
import os
|
|
11
|
+
from typing import Generic, Type
|
|
12
|
+
from abc import ABC, abstractmethod
|
|
13
|
+
|
|
14
|
+
from nora_lib.tasks.models import AsyncTaskState, R, TASK_STATUSES
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class NoSuchTaskException(Exception):
|
|
18
|
+
def __init__(self, task_id: str):
|
|
19
|
+
self._task_id = task_id
|
|
20
|
+
|
|
21
|
+
def __str__(self):
|
|
22
|
+
return f"No record found for task {self._task_id}"
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class IStateManager(ABC, Generic[R]):
|
|
26
|
+
@abstractmethod
|
|
27
|
+
def read_state(self, task_id: str) -> AsyncTaskState[R]:
|
|
28
|
+
pass
|
|
29
|
+
|
|
30
|
+
@abstractmethod
|
|
31
|
+
def write_state(self, state: AsyncTaskState[R]) -> None:
|
|
32
|
+
pass
|
|
33
|
+
|
|
34
|
+
def update_status(self, task_id: str, new_status: str) -> None:
|
|
35
|
+
state = self.read_state(task_id)
|
|
36
|
+
state.task_status = new_status
|
|
37
|
+
self.write_state(state)
|
|
38
|
+
|
|
39
|
+
def save_result(self, task_id: str, task_result: R) -> None:
|
|
40
|
+
state = self.read_state(task_id)
|
|
41
|
+
state.task_status = TASK_STATUSES["COMPLETED"]
|
|
42
|
+
state.task_result = task_result
|
|
43
|
+
self.write_state(state)
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
class StateManager(IStateManager[R]):
|
|
47
|
+
"""
|
|
48
|
+
Stores task state on local disk
|
|
49
|
+
"""
|
|
50
|
+
|
|
51
|
+
def __init__(self, task_state_class: Type[AsyncTaskState[R]], state_dir) -> None:
|
|
52
|
+
self._task_state_class = task_state_class
|
|
53
|
+
self._state_dir = state_dir
|
|
54
|
+
|
|
55
|
+
def read_state(self, task_id: str) -> AsyncTaskState[R]:
|
|
56
|
+
task_state_path = os.path.join(self._state_dir, f"{task_id}.json")
|
|
57
|
+
if not os.path.isfile(task_state_path):
|
|
58
|
+
raise NoSuchTaskException(task_id)
|
|
59
|
+
|
|
60
|
+
with open(task_state_path, "r") as f:
|
|
61
|
+
return self._task_state_class(**json.loads(f.read()))
|
|
62
|
+
|
|
63
|
+
def write_state(self, state: AsyncTaskState[R]) -> None:
|
|
64
|
+
task_state_path = os.path.join(self._state_dir, f"{state.task_id}.json")
|
|
65
|
+
with open(task_state_path, "w") as f:
|
|
66
|
+
json.dump(state.model_dump(), f)
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
class TaskStateFetchException(Exception):
|
|
70
|
+
def __init__(self, message: str):
|
|
71
|
+
super().__init__(message)
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
Metadata-Version: 2.2
|
|
2
|
+
Name: nora_lib-impl
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: For making and coordinating agents and tools
|
|
5
|
+
Home-page: https://github.com/allenai/nora_lib/impl
|
|
6
|
+
Requires-Python: >=3.9
|
|
7
|
+
Requires-Dist: pydantic<3,>=2
|
|
8
|
+
Requires-Dist: requests
|
|
9
|
+
Requires-Dist: boto3
|
|
10
|
+
Requires-Dist: aws_requests_auth
|
|
11
|
+
Provides-Extra: dev
|
|
12
|
+
Requires-Dist: mypy; extra == "dev"
|
|
13
|
+
Requires-Dist: pytest; extra == "dev"
|
|
14
|
+
Requires-Dist: black; extra == "dev"
|
|
15
|
+
Requires-Dist: types-requests; extra == "dev"
|
|
16
|
+
Dynamic: home-page
|
|
17
|
+
Dynamic: provides-extra
|
|
18
|
+
Dynamic: requires-dist
|
|
19
|
+
Dynamic: requires-python
|
|
20
|
+
Dynamic: summary
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
nora_lib/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
+
nora_lib/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
3
|
+
nora_lib/serializers.py,sha256=8l8pZWyVfVvtm2HjkvBwIrNRclFVmtuQZg16eD1LlNE,330
|
|
4
|
+
nora_lib/impl/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
5
|
+
nora_lib/impl/pubsub.py,sha256=0tmFfdOf4J-YZSxdpgXct0GpO9VatzqAfr7-5DB7s2Y,4779
|
|
6
|
+
nora_lib/impl/context/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
7
|
+
nora_lib/impl/context/agent_context.py,sha256=I6e24JGpDZTJaxrawx5FnkOVnpr1c2D5YTqs8zweRyU,864
|
|
8
|
+
nora_lib/impl/context/context_service.py,sha256=VfWBXDqGyR2mkv8sEVs4BFBtkmmjEM5oQSGUJFH2hi4,1189
|
|
9
|
+
nora_lib/impl/context/models.py,sha256=QQ4LnAemHL0N4YK9vm8DQIqFyRzaQFuX4Ngn_XKzUF0,356
|
|
10
|
+
nora_lib/impl/interactions/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
11
|
+
nora_lib/impl/interactions/interactions_service.py,sha256=0EJTTcRQeEbmC0CW-2v_J61cG-UQM34nFJc1s0ITqOM,19660
|
|
12
|
+
nora_lib/impl/interactions/models.py,sha256=rgZUft0yz2KpYVCkQizo9evN2cnsES3NPo8pzQEx2w4,12532
|
|
13
|
+
nora_lib/impl/interactions/step_progress.py,sha256=ZlxD488P7gHZdOOQhsFvk_bNJMRIyW912IWY9dTHyD8,2374
|
|
14
|
+
nora_lib/impl/tasks/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
15
|
+
nora_lib/impl/tasks/state.py,sha256=WJnmzqAzhqLzPbx1O6R7j1bbsqhjT5hwPb0CaUfNS_E,5374
|
|
16
|
+
nora_lib/progress/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
17
|
+
nora_lib/progress/models.py,sha256=YNwI_gFM7vzQBQaH6GIIQ28MXQDdL6m1_syFWvvMSWM,1581
|
|
18
|
+
nora_lib/progress/reporter.py,sha256=JK4taghGTUt0zWpEWAznGaxnNtQf4yEwnjT_wrtYQBI,4045
|
|
19
|
+
nora_lib/tasks/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
20
|
+
nora_lib/tasks/models.py,sha256=Guz4PC8DCrZ_JPOK6IvxHDNS1itTTwPmSMQHDsfbwdA,925
|
|
21
|
+
nora_lib/tasks/state.py,sha256=blgv4xxz3GCxd7KN9avbtAJaSvJB3lH-Cbcxywnr-gE,2142
|
|
22
|
+
nora_lib_impl-1.0.0.dist-info/METADATA,sha256=WPMniVqUGucqHt98vkRxGzQqr3hwdmXhTobMRtE9ZPI,579
|
|
23
|
+
nora_lib_impl-1.0.0.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
|
|
24
|
+
nora_lib_impl-1.0.0.dist-info/top_level.txt,sha256=BXasX5xI5NPFNqJsU63ojSi4Y3CPeH2ueaBR-WxxoaA,9
|
|
25
|
+
nora_lib_impl-1.0.0.dist-info/RECORD,,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
nora_lib
|