Skip to content

Commit deeabae

Browse files
committed
Toward clean up with Claude
1 parent 43076a8 commit deeabae

File tree

4 files changed

+138
-5
lines changed

4 files changed

+138
-5
lines changed

gonotego/settings/secure_settings_template.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
DROPBOX_ACCESS_TOKEN = '<DROPBOX_ACCESS_TOKEN>'
3636

3737
OPENAI_API_KEY = '<OPENAI_API_KEY>'
38+
ANTHROPIC_API_KEY = '<ANTHROPIC_API_KEY>'
3839

3940
WIFI_NETWORKS = []
4041
CUSTOM_COMMAND_PATHS = []

gonotego/settings/server.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
'EMAIL_PASSWORD',
3535
'DROPBOX_ACCESS_TOKEN',
3636
'OPENAI_API_KEY',
37+
'ANTHROPIC_API_KEY',
3738
]
3839

3940
class SettingsCombinedHandler(BaseHTTPRequestHandler):

gonotego/uploader/slack/slack_uploader.py

Lines changed: 135 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
"""Uploader for Slack workspace channels."""
22

3+
import anthropic
34
import logging
4-
from typing import List, Optional
5+
from concurrent.futures import ThreadPoolExecutor
6+
from typing import List, Optional, Dict
57

68
from slack_sdk import WebClient
79
from slack_sdk.errors import SlackApiError
@@ -21,6 +23,10 @@ def __init__(self):
2123
self._thread_ts: Optional[str] = None
2224
self._session_started: bool = False
2325
self._indent_level: int = 0
26+
self._message_timestamps: List[str] = []
27+
self._session_messages: List[Dict[str, str]] = []
28+
self._executor = ThreadPoolExecutor(max_workers=5)
29+
self._anthropic_client = None
2430

2531
@property
2632
def client(self) -> WebClient:
@@ -38,6 +44,18 @@ def client(self) -> WebClient:
3844
self._client = WebClient(token=token)
3945
return self._client
4046

47+
def _get_anthropic_client(self):
48+
"""Get or create the Anthropic client."""
49+
if self._anthropic_client:
50+
return self._anthropic_client
51+
52+
api_key = settings.get('ANTHROPIC_API_KEY')
53+
if api_key and api_key != '<ANTHROPIC_API_KEY>':
54+
self._anthropic_client = anthropic.Anthropic(api_key=api_key)
55+
return self._anthropic_client
56+
57+
return None
58+
4159
def _get_channel_id(self) -> str:
4260
"""Get the channel ID for the configured channel name."""
4361
if self._channel_id:
@@ -73,13 +91,19 @@ def _start_session(self, first_note: str) -> bool:
7391

7492
# Create the initial message with the note content
7593
try:
76-
message_text = f"{first_note}\n\n:keyboard: Go Note Go thread."
94+
message_text = f":wip: {first_note}"
7795
response = self.client.chat_postMessage(
7896
channel=channel_id,
7997
text=message_text
8098
)
8199
self._thread_ts = response['ts']
82100
self._session_started = True
101+
self._message_timestamps = [response['ts']]
102+
self._session_messages = [{'ts': response['ts'], 'text': first_note}]
103+
104+
# Schedule cleanup for first message
105+
self._executor.submit(self._cleanup_message_async, first_note, response['ts'])
106+
83107
return True
84108
except SlackApiError as e:
85109
logger.error(f"Error starting session: {e}")
@@ -102,16 +126,108 @@ def _send_note_to_thread(self, text: str, indent_level: int = 0) -> bool:
102126
formatted_text = f"{indentation}{bullet} {text}"
103127

104128
try:
105-
self.client.chat_postMessage(
129+
response = self.client.chat_postMessage(
106130
channel=channel_id,
107131
text=formatted_text,
108132
thread_ts=self._thread_ts
109133
)
134+
135+
# Track the message
136+
self._message_timestamps.append(response['ts'])
137+
self._session_messages.append({'ts': response['ts'], 'text': text})
138+
139+
# Schedule cleanup for this message
140+
self._executor.submit(self._cleanup_message_async, text, response['ts'])
141+
110142
return True
111143
except SlackApiError as e:
112144
logger.error(f"Error sending note to thread: {e}")
113145
return False
114146

147+
def _cleanup_message_async(self, original_text: str, message_ts: str):
148+
"""Clean up a message using Claude in the background."""
149+
try:
150+
client = self._get_anthropic_client()
151+
if not client:
152+
logger.debug("Anthropic client not available, skipping cleanup")
153+
return
154+
155+
# Call Claude to clean up the message
156+
prompt = f"""Clean up this voice-transcribed note to be clear and concise. Fix any transcription errors, grammar, and formatting. Keep the core meaning intact but make it more readable. Return only the cleaned text without any explanation or metadata.
157+
158+
Original text: {original_text}"""
159+
160+
message = client.messages.create(
161+
model="claude-4-opus",
162+
max_tokens=5000,
163+
temperature=0.7,
164+
messages=[
165+
{"role": "user", "content": prompt}
166+
]
167+
)
168+
169+
cleaned_text = message.content[0].text.strip()
170+
171+
# Update the message in Slack
172+
channel_id = self._get_channel_id()
173+
self._update_message(channel_id, message_ts, cleaned_text)
174+
175+
except Exception as e:
176+
logger.error(f"Error cleaning up message: {e}")
177+
178+
def _summarize_session_async(self):
179+
"""Summarize the entire session using Claude Opus 4."""
180+
try:
181+
client = self._get_anthropic_client()
182+
if not client:
183+
logger.debug("Anthropic client not available, skipping summarization")
184+
return
185+
186+
if not self._session_messages or not self._thread_ts:
187+
logger.debug("No messages to summarize")
188+
return
189+
190+
# Compile all messages into a thread
191+
thread_text = "\n".join([msg['text'] for msg in self._session_messages])
192+
193+
prompt = f"""Please provide a concise summary of this note-taking session. Identify the main topics, key points, and any action items. Format the summary clearly with bullet points where appropriate.
194+
195+
Session notes:
196+
{thread_text}"""
197+
198+
message = client.messages.create(
199+
model="claude-3-5-opus-20241022",
200+
max_tokens=1000,
201+
temperature=0.3,
202+
messages=[
203+
{"role": "user", "content": prompt}
204+
]
205+
)
206+
207+
summary = message.content[0].text.strip()
208+
209+
# Update the top-level message with the summary
210+
channel_id = self._get_channel_id()
211+
original_text = self._session_messages[0]['text'] if self._session_messages else ""
212+
updated_text = f":memo: **Session Summary**\n\n{summary}\n\n---\n_Original first note: {original_text}_"
213+
214+
self._update_message(channel_id, self._thread_ts, updated_text)
215+
216+
except Exception as e:
217+
logger.error(f"Error summarizing session: {e}")
218+
219+
def _update_message(self, channel_id: str, ts: str, new_text: str):
220+
"""Update a Slack message with new text."""
221+
try:
222+
self.client.chat_update(
223+
channel=channel_id,
224+
ts=ts,
225+
text=new_text
226+
)
227+
logger.debug(f"Updated message {ts}")
228+
except SlackApiError as e:
229+
logger.error(f"Error updating message: {e}")
230+
115231
def upload(self, note_events: List[events.NoteEvent]) -> bool:
116232
"""Upload note events to Slack.
117233
@@ -160,16 +276,30 @@ def upload(self, note_events: List[events.NoteEvent]) -> bool:
160276

161277
def end_session(self) -> None:
162278
"""End the current session."""
279+
# Schedule session summarization before clearing
280+
if self._session_started and self._session_messages:
281+
self._executor.submit(self._summarize_session_async)
282+
283+
# Clear session state
163284
self._thread_ts = None
164285
self._session_started = False
165286
self._indent_level = 0
287+
self._message_timestamps = []
288+
self._session_messages = []
166289

167290
def handle_inactivity(self) -> None:
168291
"""Handle inactivity by ending the session and clearing client."""
169-
self._client = None
170292
self.end_session()
293+
self._client = None
294+
self._anthropic_client = None
171295

172296
def handle_disconnect(self) -> None:
173297
"""Handle disconnection by ending the session and clearing client."""
174-
self._client = None
175298
self.end_session()
299+
self._client = None
300+
self._anthropic_client = None
301+
302+
def __del__(self):
303+
"""Cleanup executor on deletion."""
304+
if hasattr(self, '_executor'):
305+
self._executor.shutdown(wait=False)

pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ classifiers = [
2020

2121
dependencies = [
2222
'absl-py<=2.1.0',
23+
'anthropic<=0.40.0',
2324
'apscheduler<=3.10.4',
2425
'dropbox<=12.0.2',
2526
'fire<=0.7.0',

0 commit comments

Comments
 (0)