proscenium 0.0.1__py3-none-any.whl → 0.0.3__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.
- proscenium/__init__.py +3 -0
- proscenium/admin/__init__.py +37 -0
- proscenium/bin/bot.py +142 -0
- proscenium/core/__init__.py +152 -0
- proscenium/interfaces/__init__.py +3 -0
- proscenium/interfaces/slack.py +265 -0
- proscenium/patterns/__init__.py +3 -0
- proscenium/patterns/chunk_space.py +51 -0
- proscenium/{scripts → patterns}/document_enricher.py +15 -11
- proscenium/{scripts → patterns}/entity_resolver.py +24 -18
- proscenium/patterns/graph_rag.py +61 -0
- proscenium/{scripts → patterns}/knowledge_graph.py +4 -2
- proscenium/{scripts → patterns}/rag.py +6 -12
- proscenium/{scripts → patterns}/tools.py +13 -45
- proscenium/verbs/__init__.py +3 -0
- proscenium/verbs/chunk.py +2 -0
- proscenium/verbs/complete.py +24 -28
- proscenium/verbs/display/__init__.py +1 -1
- proscenium/verbs/display.py +3 -0
- proscenium/verbs/extract.py +8 -4
- proscenium/verbs/invoke.py +3 -0
- proscenium/verbs/read.py +6 -8
- proscenium/verbs/remember.py +5 -0
- proscenium/verbs/vector_database.py +13 -20
- proscenium/verbs/write.py +3 -0
- {proscenium-0.0.1.dist-info → proscenium-0.0.3.dist-info}/METADATA +5 -8
- proscenium-0.0.3.dist-info/RECORD +34 -0
- {proscenium-0.0.1.dist-info → proscenium-0.0.3.dist-info}/WHEEL +1 -1
- proscenium-0.0.3.dist-info/entry_points.txt +3 -0
- proscenium/scripts/__init__.py +0 -0
- proscenium/scripts/chunk_space.py +0 -33
- proscenium/scripts/graph_rag.py +0 -43
- proscenium/verbs/display/huggingface.py +0 -0
- proscenium/verbs/know.py +0 -9
- proscenium-0.0.1.dist-info/RECORD +0 -30
- {proscenium-0.0.1.dist-info → proscenium-0.0.3.dist-info}/LICENSE +0 -0
proscenium/__init__.py
CHANGED
@@ -0,0 +1,37 @@
|
|
1
|
+
from typing import Generator
|
2
|
+
from typing import List
|
3
|
+
from typing import Optional
|
4
|
+
|
5
|
+
import logging
|
6
|
+
|
7
|
+
from proscenium.core import Prop
|
8
|
+
from proscenium.core import Character
|
9
|
+
from rich.console import Console
|
10
|
+
|
11
|
+
logging.getLogger(__name__).addHandler(logging.NullHandler())
|
12
|
+
|
13
|
+
log = logging.getLogger(__name__)
|
14
|
+
|
15
|
+
system_message = """
|
16
|
+
You are an administrator of a chatbot.
|
17
|
+
"""
|
18
|
+
|
19
|
+
|
20
|
+
def props(console: Optional[Console]) -> List[Prop]:
|
21
|
+
|
22
|
+
return []
|
23
|
+
|
24
|
+
|
25
|
+
class Admin(Character):
|
26
|
+
|
27
|
+
def __init__(self, admin_channel_id: str):
|
28
|
+
super().__init__(admin_channel_id)
|
29
|
+
self.admin_channel_id = admin_channel_id
|
30
|
+
|
31
|
+
def handle(
|
32
|
+
channel_id: str,
|
33
|
+
speaker_id: str,
|
34
|
+
question: str,
|
35
|
+
) -> Generator[tuple[str, str], None, None]:
|
36
|
+
|
37
|
+
yield channel_id, "I am the administrator of this chat system."
|
proscenium/bin/bot.py
ADDED
@@ -0,0 +1,142 @@
|
|
1
|
+
#!/usr/bin/env python3
|
2
|
+
|
3
|
+
import os
|
4
|
+
import sys
|
5
|
+
import logging
|
6
|
+
import typer
|
7
|
+
import importlib
|
8
|
+
from rich.console import Console
|
9
|
+
|
10
|
+
from proscenium.admin import Admin
|
11
|
+
|
12
|
+
from proscenium.interfaces.slack import (
|
13
|
+
get_slack_auth,
|
14
|
+
channel_table,
|
15
|
+
bot_user_id,
|
16
|
+
places_table,
|
17
|
+
channel_maps,
|
18
|
+
make_slack_listener,
|
19
|
+
connect,
|
20
|
+
send_curtain_up,
|
21
|
+
listen,
|
22
|
+
send_curtain_down,
|
23
|
+
shutdown,
|
24
|
+
)
|
25
|
+
|
26
|
+
from proscenium.verbs.display import header
|
27
|
+
|
28
|
+
logging.basicConfig(
|
29
|
+
stream=sys.stdout,
|
30
|
+
format="%(asctime)s %(levelname)-8s %(name)s: %(message)s",
|
31
|
+
level=logging.WARNING,
|
32
|
+
)
|
33
|
+
|
34
|
+
logging.basicConfig(
|
35
|
+
stream=sys.stdout,
|
36
|
+
format="%(asctime)s %(levelname)-8s %(name)s: %(message)s",
|
37
|
+
level=logging.WARNING,
|
38
|
+
)
|
39
|
+
|
40
|
+
app = typer.Typer(help="Proscenium Bot")
|
41
|
+
|
42
|
+
log = logging.getLogger(__name__)
|
43
|
+
|
44
|
+
|
45
|
+
@app.command(help="""Start the Proscenium Bot.""")
|
46
|
+
def start(
|
47
|
+
verbose: bool = False,
|
48
|
+
production_module_name: str = typer.Option(
|
49
|
+
"demo.production",
|
50
|
+
"-p",
|
51
|
+
"--production",
|
52
|
+
help="The name of the python module in PYTHONPATH in which the variable production of type proscenium.core.Production is defined.",
|
53
|
+
),
|
54
|
+
force_rebuild: bool = False,
|
55
|
+
):
|
56
|
+
|
57
|
+
console = Console()
|
58
|
+
sub_console = None
|
59
|
+
|
60
|
+
if verbose:
|
61
|
+
log.setLevel(logging.INFO)
|
62
|
+
logging.getLogger("proscenium").setLevel(logging.INFO)
|
63
|
+
logging.getLogger("demo").setLevel(logging.INFO)
|
64
|
+
sub_console = console
|
65
|
+
|
66
|
+
console.print(header())
|
67
|
+
|
68
|
+
production_module = importlib.import_module(production_module_name, package=None)
|
69
|
+
|
70
|
+
slack_admin_channel_id = os.environ.get("SLACK_ADMIN_CHANNEL_ID")
|
71
|
+
# Note that the checking of the existence of the admin channel id is delayed
|
72
|
+
# until after the subscribed channels are shown.
|
73
|
+
|
74
|
+
production = production_module.make_production(slack_admin_channel_id, sub_console)
|
75
|
+
|
76
|
+
console.print("Preparing props...")
|
77
|
+
production.prepare_props()
|
78
|
+
console.print("Props are up-to-date.")
|
79
|
+
|
80
|
+
slack_app_token, slack_bot_token = get_slack_auth()
|
81
|
+
|
82
|
+
socket_mode_client = connect(slack_app_token, slack_bot_token)
|
83
|
+
|
84
|
+
user_id = bot_user_id(socket_mode_client, console)
|
85
|
+
console.print()
|
86
|
+
|
87
|
+
channels_by_id, channel_name_to_id = channel_maps(socket_mode_client)
|
88
|
+
console.print(channel_table(channels_by_id))
|
89
|
+
console.print()
|
90
|
+
|
91
|
+
if slack_admin_channel_id is None:
|
92
|
+
raise ValueError(
|
93
|
+
"SLACK_ADMIN_CHANNEL_ID environment variable not set. "
|
94
|
+
"Please set it to the channel ID of the Proscenium admin channel."
|
95
|
+
)
|
96
|
+
if slack_admin_channel_id not in channels_by_id:
|
97
|
+
raise ValueError(
|
98
|
+
f"Admin channel {slack_admin_channel_id} not found in subscribed channels."
|
99
|
+
)
|
100
|
+
|
101
|
+
admin = Admin(slack_admin_channel_id)
|
102
|
+
log.info("Admin handler started.")
|
103
|
+
|
104
|
+
log.info("Places, please!")
|
105
|
+
channel_id_to_character = production.places(channel_name_to_id)
|
106
|
+
channel_id_to_character[slack_admin_channel_id] = admin
|
107
|
+
|
108
|
+
console.print(places_table(channel_id_to_character, channels_by_id))
|
109
|
+
console.print()
|
110
|
+
|
111
|
+
slack_listener = make_slack_listener(
|
112
|
+
user_id,
|
113
|
+
slack_admin_channel_id,
|
114
|
+
channels_by_id,
|
115
|
+
channel_id_to_character,
|
116
|
+
console,
|
117
|
+
)
|
118
|
+
|
119
|
+
send_curtain_up(socket_mode_client, production, slack_admin_channel_id)
|
120
|
+
|
121
|
+
console.print("Starting the show. Listening for events...")
|
122
|
+
listen(
|
123
|
+
socket_mode_client,
|
124
|
+
slack_listener,
|
125
|
+
user_id,
|
126
|
+
console,
|
127
|
+
)
|
128
|
+
|
129
|
+
send_curtain_down(socket_mode_client, slack_admin_channel_id)
|
130
|
+
|
131
|
+
shutdown(
|
132
|
+
socket_mode_client,
|
133
|
+
slack_listener,
|
134
|
+
user_id,
|
135
|
+
production,
|
136
|
+
console,
|
137
|
+
)
|
138
|
+
|
139
|
+
|
140
|
+
if __name__ == "__main__":
|
141
|
+
|
142
|
+
app()
|
@@ -0,0 +1,152 @@
|
|
1
|
+
from typing import Generator
|
2
|
+
from typing import Optional
|
3
|
+
import logging
|
4
|
+
from rich.console import Console
|
5
|
+
|
6
|
+
logging.getLogger(__name__).addHandler(logging.NullHandler())
|
7
|
+
|
8
|
+
log = logging.getLogger(__name__)
|
9
|
+
|
10
|
+
|
11
|
+
class Prop:
|
12
|
+
"""
|
13
|
+
A `Prop` is a resource available to the `Character`s in a `Scene`.
|
14
|
+
"""
|
15
|
+
|
16
|
+
def __init__(
|
17
|
+
self,
|
18
|
+
console: Optional[Console] = None,
|
19
|
+
):
|
20
|
+
self.console = console
|
21
|
+
|
22
|
+
def name(self) -> str:
|
23
|
+
return self.__class__.__name__
|
24
|
+
|
25
|
+
def description(self) -> str:
|
26
|
+
return self.__doc__ or ""
|
27
|
+
|
28
|
+
def curtain_up_message(self) -> str:
|
29
|
+
return f"- {self.name()}, {self.description().strip()}"
|
30
|
+
|
31
|
+
def already_built(self) -> bool:
|
32
|
+
return False
|
33
|
+
|
34
|
+
def build(self) -> None:
|
35
|
+
pass
|
36
|
+
|
37
|
+
|
38
|
+
class Character:
|
39
|
+
"""
|
40
|
+
A `Character` is a participant in a `Scene` that `handle`s utterances from the
|
41
|
+
scene by producing its own utterances."""
|
42
|
+
|
43
|
+
def __init__(self, admin_channel_id: str):
|
44
|
+
self.admin_channel_id = admin_channel_id
|
45
|
+
|
46
|
+
def name(self) -> str:
|
47
|
+
return self.__class__.__name__
|
48
|
+
|
49
|
+
def description(self) -> str:
|
50
|
+
return self.__doc__ or ""
|
51
|
+
|
52
|
+
def curtain_up_message(self) -> str:
|
53
|
+
return f"- {self.name()}, {self.description().strip()}"
|
54
|
+
|
55
|
+
def handle(
|
56
|
+
channel_id: str, speaker_id: str, utterance: str
|
57
|
+
) -> Generator[tuple[str, str], None, None]:
|
58
|
+
pass
|
59
|
+
|
60
|
+
|
61
|
+
class Scene:
|
62
|
+
"""
|
63
|
+
A `Scene` is a setting in which `Character`s interact with each other and
|
64
|
+
with `Prop`s. It is a container for `Character`s and `Prop`s.
|
65
|
+
"""
|
66
|
+
|
67
|
+
def __init__(self):
|
68
|
+
pass
|
69
|
+
|
70
|
+
def name(self) -> str:
|
71
|
+
return self.__class__.__name__
|
72
|
+
|
73
|
+
def description(self) -> str:
|
74
|
+
return self.__doc__ or ""
|
75
|
+
|
76
|
+
def curtain_up_message(self) -> str:
|
77
|
+
|
78
|
+
characters_msg = "\n".join(
|
79
|
+
[character.curtain_up_message() for character in self.characters()]
|
80
|
+
)
|
81
|
+
|
82
|
+
props_msg = "\n".join([prop.curtain_up_message() for prop in self.props()])
|
83
|
+
|
84
|
+
return f"""
|
85
|
+
Scene: {self.name()}, {self.description().strip()}
|
86
|
+
|
87
|
+
Characters:
|
88
|
+
{characters_msg}
|
89
|
+
|
90
|
+
Props:
|
91
|
+
{props_msg}
|
92
|
+
"""
|
93
|
+
|
94
|
+
def props(self) -> list[Prop]:
|
95
|
+
return []
|
96
|
+
|
97
|
+
def prepare_props(self, force_rebuild: bool = False) -> None:
|
98
|
+
for prop in self.props():
|
99
|
+
if force_rebuild:
|
100
|
+
prop.build()
|
101
|
+
elif not prop.already_built():
|
102
|
+
log.info("Prop %s not built. Building it now.", prop.name())
|
103
|
+
prop.build()
|
104
|
+
|
105
|
+
def characters(self) -> list[Character]:
|
106
|
+
return []
|
107
|
+
|
108
|
+
def places(self) -> dict[str, Character]:
|
109
|
+
pass
|
110
|
+
|
111
|
+
def curtain(self) -> None:
|
112
|
+
pass
|
113
|
+
|
114
|
+
|
115
|
+
class Production:
|
116
|
+
"""
|
117
|
+
A `Production` is a collection of `Scene`s."""
|
118
|
+
|
119
|
+
def __init__(self):
|
120
|
+
pass
|
121
|
+
|
122
|
+
def name(self) -> str:
|
123
|
+
return self.__class__.__name__
|
124
|
+
|
125
|
+
def description(self) -> str:
|
126
|
+
return self.__doc__ or ""
|
127
|
+
|
128
|
+
def prepare_props(self, force_rebuild: bool = False) -> None:
|
129
|
+
if force_rebuild:
|
130
|
+
log.info("Forcing rebuild of all props.")
|
131
|
+
else:
|
132
|
+
log.info("Building any missing props...")
|
133
|
+
|
134
|
+
for scene in self.scenes():
|
135
|
+
scene.prepare_props(force_rebuild=force_rebuild)
|
136
|
+
|
137
|
+
def curtain_up_message(self) -> str:
|
138
|
+
|
139
|
+
scenes_msg = "\n\n".join(
|
140
|
+
[scene.curtain_up_message() for scene in self.scenes()]
|
141
|
+
)
|
142
|
+
|
143
|
+
return f"""Production: {self.name()}, {self.description().strip()}
|
144
|
+
|
145
|
+
{scenes_msg}"""
|
146
|
+
|
147
|
+
def scenes(self) -> list[Scene]:
|
148
|
+
return []
|
149
|
+
|
150
|
+
def curtain(self) -> None:
|
151
|
+
for scene in self.scenes():
|
152
|
+
scene.curtain()
|
@@ -0,0 +1,265 @@
|
|
1
|
+
from typing import Callable
|
2
|
+
|
3
|
+
from typing import Generator
|
4
|
+
import time
|
5
|
+
import logging
|
6
|
+
import os
|
7
|
+
from rich.console import Console
|
8
|
+
from rich.table import Table
|
9
|
+
|
10
|
+
from slack_sdk.web import WebClient
|
11
|
+
from slack_sdk.socket_mode import SocketModeClient
|
12
|
+
from slack_sdk.socket_mode.request import SocketModeRequest
|
13
|
+
from slack_sdk.socket_mode.response import SocketModeResponse
|
14
|
+
from slack_sdk.socket_mode.listeners import SocketModeRequestListener
|
15
|
+
|
16
|
+
from proscenium.core import Production
|
17
|
+
from proscenium.core import Character
|
18
|
+
|
19
|
+
log = logging.getLogger(__name__)
|
20
|
+
|
21
|
+
|
22
|
+
def get_slack_auth() -> tuple[str, str]:
|
23
|
+
|
24
|
+
slack_app_token = os.environ.get("SLACK_APP_TOKEN")
|
25
|
+
if slack_app_token is None:
|
26
|
+
raise ValueError(
|
27
|
+
"SLACK_APP_TOKEN environment variable not set. "
|
28
|
+
"Please set it to the app token of the Proscenium Slack app."
|
29
|
+
)
|
30
|
+
slack_bot_token = os.environ.get("SLACK_BOT_TOKEN")
|
31
|
+
if slack_bot_token is None:
|
32
|
+
raise ValueError(
|
33
|
+
"SLACK_BOT_TOKEN environment variable not set. "
|
34
|
+
"Please set it to the bot token of the Proscenium Slack app."
|
35
|
+
)
|
36
|
+
|
37
|
+
return slack_app_token, slack_bot_token
|
38
|
+
|
39
|
+
|
40
|
+
def connect(app_token: str, bot_token: str) -> SocketModeClient:
|
41
|
+
|
42
|
+
web_client = WebClient(token=bot_token)
|
43
|
+
socket_mode_client = SocketModeClient(app_token=app_token, web_client=web_client)
|
44
|
+
|
45
|
+
socket_mode_client.connect()
|
46
|
+
log.info("Connected to Slack.")
|
47
|
+
|
48
|
+
return socket_mode_client
|
49
|
+
|
50
|
+
|
51
|
+
def make_slack_listener(
|
52
|
+
proscenium_user_id: str,
|
53
|
+
admin_channel_id: str,
|
54
|
+
channels_by_id: dict,
|
55
|
+
channel_id_to_handler: dict[
|
56
|
+
str, Callable[[str, str, str], Generator[tuple[str, str], None, None]]
|
57
|
+
],
|
58
|
+
console: Console,
|
59
|
+
):
|
60
|
+
|
61
|
+
def process(client: SocketModeClient, req: SocketModeRequest):
|
62
|
+
|
63
|
+
if req.type == "events_api":
|
64
|
+
|
65
|
+
event = req.payload["event"]
|
66
|
+
|
67
|
+
response = SocketModeResponse(envelope_id=req.envelope_id)
|
68
|
+
client.send_socket_mode_response(response)
|
69
|
+
|
70
|
+
if event.get("type") in [
|
71
|
+
"message",
|
72
|
+
"app_mention",
|
73
|
+
]:
|
74
|
+
speaker_id = event.get("user")
|
75
|
+
if speaker_id == proscenium_user_id:
|
76
|
+
return
|
77
|
+
|
78
|
+
text = event.get("text")
|
79
|
+
channel_id = event.get("channel")
|
80
|
+
console.print(f"{speaker_id} in {channel_id} said something")
|
81
|
+
|
82
|
+
channel = channels_by_id.get(channel_id, None)
|
83
|
+
|
84
|
+
if channel is None:
|
85
|
+
|
86
|
+
# TODO: channels_by_id will get stale
|
87
|
+
log.info("No handler for channel id %s", channel_id)
|
88
|
+
|
89
|
+
else:
|
90
|
+
|
91
|
+
character = channel_id_to_handler[channel_id]
|
92
|
+
log.info("Handler defined for channel id %s", channel_id)
|
93
|
+
|
94
|
+
# TODO determine whether the handler has a good chance of being useful
|
95
|
+
|
96
|
+
for receiving_channel_id, response in character.handle(
|
97
|
+
channel_id, speaker_id, text
|
98
|
+
):
|
99
|
+
response_response = client.web_client.chat_postMessage(
|
100
|
+
channel=receiving_channel_id, text=response
|
101
|
+
)
|
102
|
+
log.info(
|
103
|
+
"Response sent to channel %s",
|
104
|
+
receiving_channel_id,
|
105
|
+
)
|
106
|
+
if receiving_channel_id == admin_channel_id:
|
107
|
+
continue
|
108
|
+
|
109
|
+
permalink = client.web_client.chat_getPermalink(
|
110
|
+
channel=receiving_channel_id,
|
111
|
+
message_ts=response_response["ts"],
|
112
|
+
)["permalink"]
|
113
|
+
log.info(
|
114
|
+
"Response sent to channel %s link %s",
|
115
|
+
receiving_channel_id,
|
116
|
+
permalink,
|
117
|
+
)
|
118
|
+
client.web_client.chat_postMessage(
|
119
|
+
channel=admin_channel_id,
|
120
|
+
text=permalink,
|
121
|
+
)
|
122
|
+
|
123
|
+
elif req.type == "interactive":
|
124
|
+
pass
|
125
|
+
elif req.type == "slash_commands":
|
126
|
+
pass
|
127
|
+
elif req.type == "app_home_opened":
|
128
|
+
pass
|
129
|
+
elif req.type == "block_actions":
|
130
|
+
pass
|
131
|
+
elif req.type == "message_actions":
|
132
|
+
pass
|
133
|
+
|
134
|
+
return process
|
135
|
+
|
136
|
+
|
137
|
+
def channel_maps(
|
138
|
+
socket_mode_client: SocketModeClient,
|
139
|
+
) -> tuple[dict[str, dict], dict[str, str]]:
|
140
|
+
|
141
|
+
subscribed_channels = socket_mode_client.web_client.users_conversations(
|
142
|
+
types="public_channel,private_channel,mpim,im",
|
143
|
+
limit=100,
|
144
|
+
)
|
145
|
+
log.info(
|
146
|
+
"Subscribed channels count: %s",
|
147
|
+
len(subscribed_channels["channels"]),
|
148
|
+
)
|
149
|
+
|
150
|
+
channels_by_id = {
|
151
|
+
channel["id"]: channel for channel in subscribed_channels["channels"]
|
152
|
+
}
|
153
|
+
|
154
|
+
channel_name_to_id = {
|
155
|
+
channel["name"]: channel["id"]
|
156
|
+
for channel in channels_by_id.values()
|
157
|
+
if channel.get("name")
|
158
|
+
}
|
159
|
+
|
160
|
+
return channels_by_id, channel_name_to_id
|
161
|
+
|
162
|
+
|
163
|
+
def channel_table(channels_by_id) -> Table:
|
164
|
+
channel_table = Table(title="Subscribed channels")
|
165
|
+
channel_table.add_column("Channel ID", justify="left")
|
166
|
+
channel_table.add_column("Name", justify="left")
|
167
|
+
for channel_id, channel in channels_by_id.items():
|
168
|
+
channel_table.add_row(
|
169
|
+
channel_id,
|
170
|
+
channel.get("name", "-"),
|
171
|
+
)
|
172
|
+
return channel_table
|
173
|
+
|
174
|
+
|
175
|
+
def bot_user_id(socket_mode_client: SocketModeClient, console: Console):
|
176
|
+
|
177
|
+
auth_response = socket_mode_client.web_client.auth_test()
|
178
|
+
|
179
|
+
console.print(auth_response["url"])
|
180
|
+
console.print()
|
181
|
+
console.print(f"Team '{auth_response["team"]}' ({auth_response["team_id"]})")
|
182
|
+
console.print(f"User '{auth_response["user"]}' ({auth_response["user_id"]})")
|
183
|
+
|
184
|
+
user_id = auth_response["user_id"]
|
185
|
+
console.print("Bot id", auth_response["bot_id"])
|
186
|
+
|
187
|
+
return user_id
|
188
|
+
|
189
|
+
|
190
|
+
def places_table(
|
191
|
+
channel_id_to_character: dict[str, Character], channels_by_id: dict[str, dict]
|
192
|
+
) -> Table:
|
193
|
+
|
194
|
+
table = Table(title="Characters in place")
|
195
|
+
table.add_column("Channel ID", justify="left")
|
196
|
+
table.add_column("Channel Name", justify="left")
|
197
|
+
table.add_column("Character", justify="left")
|
198
|
+
for channel_id, character in channel_id_to_character.items():
|
199
|
+
channel = channels_by_id[channel_id]
|
200
|
+
table.add_row(channel_id, channel["name"], character.name())
|
201
|
+
|
202
|
+
return table
|
203
|
+
|
204
|
+
|
205
|
+
def send_curtain_up(
|
206
|
+
socket_mode_client: SocketModeClient,
|
207
|
+
production: Production,
|
208
|
+
slack_admin_channel_id: str,
|
209
|
+
) -> None:
|
210
|
+
|
211
|
+
curtain_up_message = f"""
|
212
|
+
Proscenium 🎭 https://the-ai-alliance.github.io/proscenium/
|
213
|
+
|
214
|
+
```
|
215
|
+
{production.curtain_up_message()}
|
216
|
+
```
|
217
|
+
|
218
|
+
Curtain up.
|
219
|
+
"""
|
220
|
+
|
221
|
+
socket_mode_client.web_client.chat_postMessage(
|
222
|
+
channel=slack_admin_channel_id,
|
223
|
+
text=curtain_up_message,
|
224
|
+
)
|
225
|
+
|
226
|
+
|
227
|
+
def listen(
|
228
|
+
socket_mode_client: SocketModeClient,
|
229
|
+
slack_listener: SocketModeRequestListener,
|
230
|
+
user_id: str,
|
231
|
+
console: Console,
|
232
|
+
):
|
233
|
+
socket_mode_client.socket_mode_request_listeners.append(slack_listener)
|
234
|
+
|
235
|
+
try:
|
236
|
+
while True:
|
237
|
+
time.sleep(1)
|
238
|
+
except KeyboardInterrupt:
|
239
|
+
console.print("Exiting...")
|
240
|
+
|
241
|
+
|
242
|
+
def send_curtain_down(
|
243
|
+
socket_mode_client: SocketModeClient, slack_admin_channel_id: str
|
244
|
+
) -> None:
|
245
|
+
socket_mode_client.web_client.chat_postMessage(
|
246
|
+
channel=slack_admin_channel_id,
|
247
|
+
text="""Curtain down. We hope you enjoyed the show!""",
|
248
|
+
)
|
249
|
+
|
250
|
+
|
251
|
+
def shutdown(
|
252
|
+
socket_mode_client: SocketModeClient,
|
253
|
+
slack_listener: SocketModeRequestListener,
|
254
|
+
user_id: str,
|
255
|
+
production: Production,
|
256
|
+
console: Console,
|
257
|
+
):
|
258
|
+
|
259
|
+
socket_mode_client.socket_mode_request_listeners.remove(slack_listener)
|
260
|
+
socket_mode_client.disconnect()
|
261
|
+
console.print("Disconnected from Slack.")
|
262
|
+
|
263
|
+
production.curtain()
|
264
|
+
|
265
|
+
console.print("Handlers stopped.")
|
@@ -0,0 +1,51 @@
|
|
1
|
+
from typing import Optional
|
2
|
+
import logging
|
3
|
+
from rich.console import Console
|
4
|
+
from pymilvus import model
|
5
|
+
|
6
|
+
from proscenium.verbs.read import load_file
|
7
|
+
from proscenium.verbs.chunk import documents_to_chunks_by_characters
|
8
|
+
from proscenium.verbs.display.milvus import collection_panel
|
9
|
+
from proscenium.verbs.vector_database import vector_db
|
10
|
+
from proscenium.verbs.vector_database import create_collection
|
11
|
+
from proscenium.verbs.vector_database import add_chunks_to_vector_db
|
12
|
+
|
13
|
+
log = logging.getLogger(__name__)
|
14
|
+
|
15
|
+
|
16
|
+
def load_chunks_from_files(
|
17
|
+
data_files: list[str],
|
18
|
+
milvus_uri: str,
|
19
|
+
embedding_fn: model.dense.SentenceTransformerEmbeddingFunction,
|
20
|
+
collection_name: str,
|
21
|
+
console: Optional[Console] = None,
|
22
|
+
) -> None:
|
23
|
+
|
24
|
+
vector_db_client = vector_db(milvus_uri)
|
25
|
+
log.info("Vector db stored at %s", milvus_uri)
|
26
|
+
|
27
|
+
for data_file in data_files:
|
28
|
+
|
29
|
+
log.info(
|
30
|
+
"Loading data file %s into vector db %s collection %s",
|
31
|
+
data_file,
|
32
|
+
milvus_uri,
|
33
|
+
collection_name,
|
34
|
+
)
|
35
|
+
create_collection(vector_db_client, embedding_fn, collection_name)
|
36
|
+
|
37
|
+
documents = load_file(data_file)
|
38
|
+
chunks = documents_to_chunks_by_characters(documents)
|
39
|
+
log.info("Data file %s has %s chunks", data_file, len(chunks))
|
40
|
+
|
41
|
+
info = add_chunks_to_vector_db(
|
42
|
+
vector_db_client,
|
43
|
+
embedding_fn,
|
44
|
+
chunks,
|
45
|
+
collection_name,
|
46
|
+
)
|
47
|
+
log.info("%s chunks inserted ", info["insert_count"])
|
48
|
+
if console is not None:
|
49
|
+
console.print(collection_panel(vector_db_client, collection_name))
|
50
|
+
|
51
|
+
vector_db_client.close()
|