Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion lifetrace/llm/vector_db.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ def _initialize(self):
if self.embedding_model_name:
if SentenceTransformer is None:
raise RuntimeError("SentenceTransformer not available")
self.logger.info(f"Loading embedding model: {self.embedding_model_name}")
self.logger.info(f"正在加载 embedding 模型 ({self.embedding_model_name}),首次下载可能需要几分钟...")
self.embedding_model = SentenceTransformer(self.embedding_model_name)
else:
self.logger.info("Skipping embedding model initialization (multimodal mode)")
Expand Down
2 changes: 1 addition & 1 deletion lifetrace/routers/chat/context.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ async def chat_with_context_stream(
"X-Session-Id": session_id, # 返回 session_id 供前端使用
}
return StreamingResponse(
token_generator, media_type="text/plain; charset=utf-8", headers=headers
token_generator, media_type="text/event-stream", headers=headers
)

except Exception as e:
Expand Down
2 changes: 1 addition & 1 deletion lifetrace/routers/chat/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ async def chat_with_llm_stream(
"X-Session-Id": session_id, # 返回 session_id 供前端使用
}
return StreamingResponse(
token_generator, media_type="text/plain; charset=utf-8", headers=headers
token_generator, media_type="text/event-stream", headers=headers
)

except Exception as e:
Expand Down
24 changes: 22 additions & 2 deletions lifetrace/routers/todo.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
from fastapi import APIRouter, Depends, File, HTTPException, Path, Query, Response, UploadFile
from fastapi.responses import FileResponse

from lifetrace.core.dependencies import get_todo_service
from lifetrace.core.dependencies import get_db_session, get_todo_service
from lifetrace.schemas.todo import (
TodoAttachmentResponse,
TodoCreate,
Expand All @@ -24,12 +24,24 @@
from lifetrace.util.path_utils import get_attachments_dir

if TYPE_CHECKING:
from sqlalchemy.orm import Session

from lifetrace.services.todo_service import TodoService

router = APIRouter(prefix="/api/todos", tags=["todos"])
tags_router = APIRouter(prefix="/api/tags", tags=["tags"])
MAX_ATTACHMENT_SIZE = 50 * 1024 * 1024 # 50MB


@tags_router.get("")
async def list_tags(db: "Session" = Depends(get_db_session)):
"""获取所有标签"""
from lifetrace.storage.models import Tag # noqa: PLC0415

tags = db.query(Tag).filter(Tag.deleted_at.is_(None)).order_by(Tag.tag_name).all()
return {"tags": [{"id": t.id, "name": t.tag_name} for t in tags]}


def _sanitize_filename(name: str) -> str:
return FsPath(name).name if name else "attachment"

Expand All @@ -39,10 +51,18 @@ async def list_todos(
limit: int = Query(200, ge=1, le=2000, description="返回数量限制"),
offset: int = Query(0, ge=0, description="偏移量"),
status: str | None = Query(None, description="状态筛选:active/completed/canceled"),
page: int | None = Query(None, ge=1, description="页码(从1开始)"),
page_size: int | None = Query(None, ge=1, le=200, description="每页数量(最大200)"),
service: TodoService = Depends(get_todo_service),
):
"""获取待办列表"""
return service.list_todos(limit, offset, status)
if page is not None and page_size is not None:
actual_limit = page_size
actual_offset = (page - 1) * page_size
else:
actual_limit = limit
actual_offset = offset
return service.list_todos(actual_limit, actual_offset, status)


@router.get("/{todo_id}", response_model=TodoResponse)
Expand Down
7 changes: 6 additions & 1 deletion lifetrace/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ def get_cors_origins() -> list[str]:

app.add_middleware(
CORSMiddleware,
allow_origins=get_cors_origins(),
allow_origins=["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
Expand Down Expand Up @@ -127,6 +127,11 @@ def _register_priority_modules(app: FastAPI) -> None:

registered = register_modules(app, priority_ids, states=states)
app.state.registered_modules = set(registered)

# 注册 tags router
from lifetrace.routers.todo import tags_router
app.include_router(tags_router)
logger.info("Registered /api/tags endpoint")
app.state.deferred_modules = [
mid for mid in deferred_ids if mid not in app.state.registered_modules
]
Expand Down
24 changes: 24 additions & 0 deletions lifetrace/services/todo_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
处理 Todo 相关的业务逻辑,与数据访问层解耦。
"""

import html
from typing import Any

from fastapi import HTTPException
Expand Down Expand Up @@ -62,6 +63,17 @@ def list_todos(self, limit: int, offset: int, status: str | None) -> dict[str, A

def create_todo(self, data: TodoCreate) -> TodoResponse:
"""创建 Todo"""
# Sanitize user input to prevent XSS
data.name = html.escape(data.name)
if data.description is not None:
data.description = html.escape(data.description)

# Validate time range
st = data.start_time or data.dtstart
et = data.end_time or data.dtend
if st and et and st >= et:
raise HTTPException(status_code=422, detail="start_time must be before end_time")

dtstart = data.dtstart or data.start_time or data.deadline or data.due
dtend = data.dtend or data.end_time
due = data.due or data.deadline
Expand Down Expand Up @@ -139,6 +151,18 @@ def create_todo(self, data: TodoCreate) -> TodoResponse:

def update_todo(self, todo_id: int, data: TodoUpdate) -> TodoResponse: # noqa: C901, PLR0912, PLR0915
"""更新 Todo"""
# Sanitize user input to prevent XSS
if data.name is not None:
data.name = html.escape(data.name)
if data.description is not None:
data.description = html.escape(data.description)

# Validate time range
st = getattr(data, "start_time", None) or getattr(data, "dtstart", None)
et = getattr(data, "end_time", None) or getattr(data, "dtend", None)
if st and et and st >= et:
raise HTTPException(status_code=422, detail="start_time must be before end_time")

# 检查是否存在
if not self.repository.get_by_id(todo_id):
raise HTTPException(status_code=404, detail="todo 不存在")
Expand Down
3 changes: 3 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -170,3 +170,6 @@ max-public-methods = 20
[tool.uv]
index-url = "https://pypi.tuna.tsinghua.edu.cn/simple"
extra-index-url = ["https://pypi.org/simple"]

[tool.uv.extra-build-dependencies]
hdbscan = ["setuptools", "cython<3", "numpy"]
Loading