-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathapp.py
More file actions
177 lines (153 loc) · 5.66 KB
/
Copy pathapp.py
File metadata and controls
177 lines (153 loc) · 5.66 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
from __future__ import annotations
import os
from contextlib import asynccontextmanager
from dataclasses import dataclass
import fastapi
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
import contact_tag_jobs
import conversations
import daily_briefing_jobs
import document_tag_jobs
import event_tag_jobs
import meeting_transcript_jobs
import proposed_event_jobs
from db import get_conn
from db_migrations import run_pending_migrations
from llm_helpers import warm_configured_chat_models
from observability.log_stream import configure_logging, install_stdout_logger
from observability.logger import get_runtime_logger
from routes.automation import create_automation_router
from routes.chat import create_chat_router
from routes.contacts import create_contacts_router
from routes.daily_briefing import create_daily_briefing_router
from routes.documents import create_documents_router
from routes.evals import create_evals_router
from routes.events import create_events_router
from routes.generated_pdfs import create_generated_pdfs_router
from routes.news import create_news_router
from routes.places import create_places_router
from routes.proposed_events import create_proposed_events_router
from routes.system import create_system_router
from routes.todos import create_todos_router
from routes.user import create_user_router
from schemas import AskIn
logger = get_runtime_logger(__name__)
@dataclass
class _SessionContext:
session_id: str
question: str
is_new_session: bool
is_reset_only: bool
user_email: str
original_question: str
def _strip_command_prefix(message: str) -> str:
from commands.parser import parse_command
text = (message or "").strip()
parsed = parse_command(text)
if not parsed:
return text
return parsed.args
def _resolve_session_context(
payload: AskIn,
user_email: str,
*,
force_new_session: bool = False,
) -> _SessionContext:
"""Compatibility helper kept for command stripping regressions."""
from commands.parser import parse_command
requested_thread_id = payload.thread_id or payload.session_id
question = payload.question
is_new_session = False
parsed_command = parse_command(question)
reset_requested = parsed_command is not None and parsed_command.command == "new"
if parsed_command is not None and reset_requested:
question = parsed_command.args or ""
force_new_session = True
if requested_thread_id and not force_new_session:
try:
thread = conversations.ensure_thread(requested_thread_id, user_email)
except LookupError as exc:
raise fastapi.HTTPException(status_code=404, detail="Conversation thread not found") from exc
except PermissionError as exc:
raise fastapi.HTTPException(
status_code=403,
detail="Conversation thread does not belong to user",
) from exc
elif requested_thread_id and force_new_session:
thread = conversations.ensure_thread(None, user_email)
is_new_session = True
else:
if force_new_session:
question = f"/new {question}".strip()
thread, is_new_session, question = conversations.resolve_main_session(user_email, question)
question = _strip_command_prefix(question)
session_id = thread["id"]
is_reset_only = is_new_session and not question.strip()
return _SessionContext(
session_id=session_id,
question=question,
is_new_session=is_new_session,
is_reset_only=is_reset_only,
user_email=user_email,
original_question=payload.question,
)
@asynccontextmanager
async def lifespan(_app: FastAPI):
configure_logging()
install_stdout_logger()
try:
run_pending_migrations()
except Exception:
if os.getenv("DB_AUTO_MIGRATE_FAIL_FAST", "true").strip().lower() in {
"1",
"true",
"yes",
"on",
}:
raise
logger.exception("Database migration failed; continuing startup")
with get_conn():
pass
try:
warmed_models = warm_configured_chat_models()
logger.info("Warmed configured chat models: %s", ", ".join(warmed_models) or "none")
except Exception:
logger.exception("Chat-model warmup failed; continuing startup")
meeting_transcript_jobs.start_worker()
event_tag_jobs.start_worker()
document_tag_jobs.start_worker()
contact_tag_jobs.start_worker()
daily_briefing_jobs.start_worker()
proposed_event_jobs.start_worker()
try:
yield
finally:
proposed_event_jobs.stop_worker()
daily_briefing_jobs.stop_worker()
contact_tag_jobs.stop_worker()
document_tag_jobs.stop_worker()
event_tag_jobs.stop_worker()
meeting_transcript_jobs.stop_worker()
api = FastAPI(title="Personal Memory Orchestrator", version="0.3.1", lifespan=lifespan)
api.include_router(create_daily_briefing_router())
api.include_router(create_news_router())
api.include_router(create_chat_router())
api.include_router(create_contacts_router())
api.include_router(create_places_router())
api.include_router(create_proposed_events_router())
api.include_router(create_todos_router())
api.include_router(create_events_router())
api.include_router(create_documents_router())
api.include_router(create_generated_pdfs_router())
api.include_router(create_evals_router())
api.include_router(create_system_router())
api.include_router(create_user_router())
api.include_router(create_automation_router())
api.add_middleware(
CORSMiddleware,
allow_origins=["http://localhost:3000"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)