|
4 | 4 |
|
5 | 5 | from prefect.assets import Asset, AssetProperties, add_asset_metadata, materialize |
6 | 6 | from pydantic import BaseModel |
| 7 | +from pydantic_ai import RunContext |
7 | 8 | from pydantic_ai.messages import ModelMessage, SystemPromptPart |
| 9 | +from raggy.documents import Document |
| 10 | +from raggy.vectorstores.tpuf import TurboPuffer |
8 | 11 |
|
9 | 12 | from marvin import cast_async |
| 13 | +from slackbot.settings import settings |
10 | 14 | from slackbot.slack import get_channel_name |
11 | 15 | from slackbot.types import UserContext |
12 | 16 |
|
@@ -70,6 +74,54 @@ def thread_summary_asset( |
70 | 74 | ) |
71 | 75 |
|
72 | 76 |
|
| 77 | +def user_facts_asset(user_context: UserContext) -> Asset: |
| 78 | + user_id = user_context["user_id"] |
| 79 | + workspace_name = user_context["workspace_name"] |
| 80 | + bot_id = user_context["bot_id"] |
| 81 | + return Asset( |
| 82 | + key=f"slack://{workspace_name}/bot/{bot_id}/facts/{user_id}", |
| 83 | + properties=AssetProperties( |
| 84 | + name=f"User Facts {user_id}", |
| 85 | + description=f"Facts learned about user {user_id} by bot {bot_id}", |
| 86 | + owners=["slackbot"], |
| 87 | + ), |
| 88 | + ) |
| 89 | + |
| 90 | + |
| 91 | +async def store_user_facts(ctx: RunContext[UserContext], facts: list[str]) -> str: |
| 92 | + """Store facts extracted from a Slack thread using context for namespacing.""" |
| 93 | + |
| 94 | + with TurboPuffer( |
| 95 | + namespace=f"{settings.user_facts_namespace_prefix}{ctx.deps['user_id']}" |
| 96 | + ) as tpuf: |
| 97 | + tpuf.upsert(documents=[Document(text=fact) for fact in facts]) |
| 98 | + |
| 99 | + user_facts = user_facts_asset(ctx.deps) |
| 100 | + |
| 101 | + slack_thread = await slack_thread_asset(ctx.deps) |
| 102 | + slackbot = slackbot_asset(ctx.deps) |
| 103 | + |
| 104 | + @materialize(user_facts, asset_deps=[slack_thread, slackbot]) |
| 105 | + async def materialize_user_facts(): |
| 106 | + add_asset_metadata( |
| 107 | + user_facts, |
| 108 | + { |
| 109 | + "user_id": ctx.deps["user_id"], |
| 110 | + "fact_count": len(facts), |
| 111 | + "timestamp": datetime.now().isoformat(), |
| 112 | + "namespace": f"{settings.user_facts_namespace_prefix}{ctx.deps['user_id']}", |
| 113 | + "thread_ts": ctx.deps["thread_ts"], |
| 114 | + "workspace_name": ctx.deps["workspace_name"], |
| 115 | + "channel_id": ctx.deps["channel_id"], |
| 116 | + "bot_id": ctx.deps["bot_id"], |
| 117 | + "facts": facts, |
| 118 | + }, |
| 119 | + ) |
| 120 | + return f"Stored {len(facts)} facts about user {ctx.deps['user_id']} from thread {ctx.deps['thread_ts']}" |
| 121 | + |
| 122 | + return await materialize_user_facts() |
| 123 | + |
| 124 | + |
73 | 125 | async def summarize_thread( |
74 | 126 | user_context: UserContext, conversation: list[ModelMessage] |
75 | 127 | ) -> ThreadSummary: |
|
0 commit comments