summaryrefslogtreecommitdiff
path: root/Omni/Agent/Tools/Todos.hs
diff options
context:
space:
mode:
authorBen Sima <ben@bensima.com>2025-12-12 23:30:04 -0500
committerBen Sima <ben@bensima.com>2025-12-12 23:30:04 -0500
commit817bdb1f33e9825946a2da2aa1ff8f91b6166366 (patch)
tree32af363a03de72964e999ce437a7e01bfc80a85a /Omni/Agent/Tools/Todos.hs
parentbfa50a5a755e13c0ee2394d89280092a639d8f0d (diff)
telegram bot: refactor + multimedia + reply support
Refactor Telegram.hs into submodules to reduce file size: - Types.hs: data types, JSON parsing - Media.hs: file downloads, image/voice analysis - Reminders.hs: reminder loop, user chat persistence Multimedia improvements: - Vision uses third-person to avoid LLM confusion - Better message framing for embedded descriptions - Size validation (10MB images, 20MB voice) - MIME type validation for voice messages New features: - Reply support: bot sees context when users reply - Web search: default 5->10, max 10->20 results - Guardrails: duplicate tool limit 3->10 for research - Timezone: todos parse/display in Eastern time (ET)
Diffstat (limited to 'Omni/Agent/Tools/Todos.hs')
-rw-r--r--Omni/Agent/Tools/Todos.hs26
1 files changed, 18 insertions, 8 deletions
diff --git a/Omni/Agent/Tools/Todos.hs b/Omni/Agent/Tools/Todos.hs
index 4c7d2be..2aacacc 100644
--- a/Omni/Agent/Tools/Todos.hs
+++ b/Omni/Agent/Tools/Todos.hs
@@ -45,8 +45,8 @@ import Alpha
import Data.Aeson ((.!=), (.:), (.:?), (.=))
import qualified Data.Aeson as Aeson
import qualified Data.Text as Text
-import Data.Time (NominalDiffTime, UTCTime, addUTCTime, getCurrentTime)
-import Data.Time.Format (defaultTimeLocale, parseTimeM)
+import Data.Time (LocalTime, NominalDiffTime, TimeZone, UTCTime, addUTCTime, getCurrentTime, localTimeToUTC, minutesToTimeZone, utcToLocalTime)
+import Data.Time.Format (defaultTimeLocale, formatTime, parseTimeM)
import qualified Database.SQLite.Simple as SQL
import qualified Omni.Agent.Engine as Engine
import qualified Omni.Agent.Memory as Memory
@@ -165,12 +165,18 @@ migrateTodosTable conn = do
unless ("last_reminded_at" `elem` colNames) <| do
SQL.execute_ conn "ALTER TABLE todos ADD COLUMN last_reminded_at TIMESTAMP"
+easternTimeZone :: TimeZone
+easternTimeZone = minutesToTimeZone (-300)
+
parseDueDate :: Text -> Maybe UTCTime
parseDueDate txt =
let s = Text.unpack txt
- in parseTimeM True defaultTimeLocale "%Y-%m-%d %H:%M" s
- <|> parseTimeM True defaultTimeLocale "%Y-%m-%d" s
- <|> parseTimeM True defaultTimeLocale "%Y-%m-%dT%H:%M:%S" s
+ parseLocal :: Maybe LocalTime
+ parseLocal =
+ parseTimeM True defaultTimeLocale "%Y-%m-%d %H:%M" s
+ <|> parseTimeM True defaultTimeLocale "%Y-%m-%d" s
+ <|> parseTimeM True defaultTimeLocale "%Y-%m-%dT%H:%M:%S" s
+ in fmap (localTimeToUTC easternTimeZone) parseLocal
<|> parseTimeM True defaultTimeLocale "%Y-%m-%dT%H:%M:%SZ" s
createTodo :: Text -> Text -> Maybe Text -> IO Todo
@@ -301,7 +307,7 @@ todoAddTool uid =
"due_date"
.= Aeson.object
[ "type" .= ("string" :: Text),
- "description" .= ("Optional due date: 'YYYY-MM-DD' or 'YYYY-MM-DD HH:MM'" :: Text)
+ "description" .= ("Optional due date in Eastern time: 'YYYY-MM-DD' or 'YYYY-MM-DD HH:MM'" :: Text)
]
],
"required" .= (["title"] :: [Text])
@@ -316,7 +322,9 @@ executeTodoAdd uid v =
Aeson.Success (args :: TodoAddArgs) -> do
td <- createTodo uid (taTitle args) (taDueDate args)
let dueDateMsg = case todoDueDate td of
- Just d -> " (due: " <> tshow d <> ")"
+ Just d ->
+ let localTime = utcToLocalTime easternTimeZone d
+ in " (due: " <> Text.pack (formatTime defaultTimeLocale "%Y-%m-%d %H:%M ET" localTime) <> ")"
Nothing -> ""
pure
( Aeson.object
@@ -392,7 +400,9 @@ formatTodosForLLM todos =
formatTodo td =
let status = if todoCompleted td then "[x]" else "[ ]"
dueStr = case todoDueDate td of
- Just d -> " (due: " <> Text.pack (show d) <> ")"
+ Just d ->
+ let localTime = utcToLocalTime easternTimeZone d
+ in " (due: " <> Text.pack (formatTime defaultTimeLocale "%Y-%m-%d %H:%M ET" localTime) <> ")"
Nothing -> ""
in status <> " " <> todoTitle td <> dueStr <> " (id: " <> tshow (todoId td) <> ")"