Skip to content

Commit e68f280

Browse files
committed
load convo store
1 parent 7fcfd7b commit e68f280

File tree

1 file changed

+53
-8
lines changed

1 file changed

+53
-8
lines changed

oneping/interface/textual.py

Lines changed: 53 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
# textual chat interface
22

3+
import re
4+
import os
5+
36
from textual import on, work
47
from textual.app import App, ComposeResult
58
from textual.widget import Widget
@@ -29,7 +32,6 @@
2932
## sidebar
3033
##
3134

32-
3335
class Sidebar(Widget):
3436
DEFAULT_CSS = """
3537
Sidebar {
@@ -63,11 +65,16 @@ class Sidebar(Widget):
6365
}
6466
"""
6567

68+
def __init__(self, convo, **kwargs):
69+
super().__init__(**kwargs)
70+
self.convo = convo
71+
6672
def compose(self):
6773
with Vertical():
6874
yield Label("Chat History", id='history_title')
69-
yield Label("Is Jupiter a planet?")
70-
yield Label("What is the capital of France?")
75+
for title in self.convo:
76+
yield Label(title)
77+
7178

7279
##
7380
## widgets
@@ -185,34 +192,72 @@ async def pipe_stream(self, generate, setter):
185192
async for reply in cumcat(generate):
186193
self.app.call_from_thread(setter, reply)
187194

195+
class ConvoStore:
196+
def __init__(self, store):
197+
self.store = store
198+
self.load_store()
199+
200+
@staticmethod
201+
def parse_convo(markdown):
202+
# match title (#!)
203+
title_match = re.match(r'^#! (.*)\n', markdown)
204+
if title_match is None: return None
205+
title = title_match.group(1)
206+
207+
# match messages
208+
chunks = re.split(r'\n\n(SYSTEM|USER|ASSISTANT): ', markdown)
209+
messages = [
210+
{'role': role, 'text': text}
211+
for role, text in zip(chunks[1::2], chunks[2::2])
212+
]
213+
214+
# return title and messages
215+
return title, messages
216+
217+
def load_store(self):
218+
self.convo = {}
219+
for file in os.listdir(self.store):
220+
with open(os.path.join(self.store, file), 'r') as fid:
221+
markdown = fid.read().strip()
222+
result = ConvoStore.parse_convo(markdown)
223+
if result is None: continue
224+
title, messages = result
225+
self.convo[title] = messages
226+
188227
class TextualChat(App):
189228
BINDINGS = [("ctrl+s", "toggle_sidebar", "Toggle Sidebar")]
190229

191230
show_sidebar = reactive(False)
192231

193-
def __init__(self, chat, **kwargs):
232+
def __init__(self, chat, store=None, **kwargs):
194233
super().__init__(**kwargs)
195234
self.chat = chat
235+
236+
# load conversation history
237+
self.store = ConvoStore(store) if store is not None else None
238+
239+
# set window title
196240
provider = self.chat.kwargs.get('provider', 'local')
197241
self.title = f'oneping: {provider}'
198242

199243
def compose(self):
200244
yield Header(id='header')
201-
yield Sidebar()
245+
yield Sidebar(convo=self.store.convo)
202246
yield ChatWindow(self.chat.stream_async, system=self.chat.system)
203247

204248
def on_mount(self):
205249
query = self.query_one('BareQuery')
206250
self.set_focus(query)
207251

208252
def action_toggle_sidebar(self):
209-
self.show_sidebar = not self.show_sidebar
253+
if self.store is not None:
254+
self.show_sidebar = not self.show_sidebar
210255

211256
def watch_show_sidebar(self, show_sidebar):
212257
self.query_one(Sidebar).set_class(show_sidebar, "-visible")
213258

214259
# textual powered chat interface
215-
def main(**kwargs):
260+
def main(store=None, **kwargs):
216261
chat = Chat(**kwargs)
217-
app = TextualChat(chat)
262+
app = TextualChat(chat, store=store)
218263
app.run()

0 commit comments

Comments
 (0)