fix(backend): resolve ty check errors across api, mcp, and lifespan typing
This commit is contained in:
parent
679e4e81f4
commit
389ca5ffdc
6 changed files with 36 additions and 28 deletions
|
|
@ -6,7 +6,7 @@ from uuid import UUID, uuid4
|
||||||
from fastapi import APIRouter, Depends, HTTPException, Query
|
from fastapi import APIRouter, Depends, HTTPException, Query
|
||||||
from google.genai import types as genai_types
|
from google.genai import types as genai_types
|
||||||
from pydantic import ValidationError
|
from pydantic import ValidationError
|
||||||
from sqlmodel import Session, SQLModel, select
|
from sqlmodel import Session, SQLModel, col, select
|
||||||
|
|
||||||
from db import get_session
|
from db import get_session
|
||||||
from innercontext.api.utils import get_or_404
|
from innercontext.api.utils import get_or_404
|
||||||
|
|
@ -218,7 +218,7 @@ def list_products(
|
||||||
product_ids = [p.id for p in products]
|
product_ids = [p.id for p in products]
|
||||||
inventory_rows = (
|
inventory_rows = (
|
||||||
session.exec(
|
session.exec(
|
||||||
select(ProductInventory).where(ProductInventory.product_id.in_(product_ids))
|
select(ProductInventory).where(col(ProductInventory.product_id).in_(product_ids))
|
||||||
).all()
|
).all()
|
||||||
if product_ids
|
if product_ids
|
||||||
else []
|
else []
|
||||||
|
|
|
||||||
|
|
@ -187,11 +187,12 @@ def _is_minoxidil_product(product: Product) -> bool:
|
||||||
|
|
||||||
|
|
||||||
def _ev(v: object) -> str:
|
def _ev(v: object) -> str:
|
||||||
return (
|
if v is None:
|
||||||
v.value
|
return ""
|
||||||
if v is not None and hasattr(v, "value")
|
value = getattr(v, "value", None)
|
||||||
else str(v) if v is not None else ""
|
if isinstance(value, str):
|
||||||
)
|
return value
|
||||||
|
return str(v)
|
||||||
|
|
||||||
|
|
||||||
def _build_skin_context(session: Session) -> str:
|
def _build_skin_context(session: Session) -> str:
|
||||||
|
|
@ -218,7 +219,7 @@ def _build_grooming_context(
|
||||||
session: Session, weekdays: Optional[list[int]] = None
|
session: Session, weekdays: Optional[list[int]] = None
|
||||||
) -> str:
|
) -> str:
|
||||||
entries = session.exec(
|
entries = session.exec(
|
||||||
select(GroomingSchedule).order_by(GroomingSchedule.day_of_week)
|
select(GroomingSchedule).order_by(col(GroomingSchedule.day_of_week))
|
||||||
).all()
|
).all()
|
||||||
if not entries:
|
if not entries:
|
||||||
return "HARMONOGRAM PIELĘGNACJI: brak\n"
|
return "HARMONOGRAM PIELĘGNACJI: brak\n"
|
||||||
|
|
@ -251,7 +252,7 @@ def _build_recent_history(session: Session) -> str:
|
||||||
steps = session.exec(
|
steps = session.exec(
|
||||||
select(RoutineStep)
|
select(RoutineStep)
|
||||||
.where(RoutineStep.routine_id == r.id)
|
.where(RoutineStep.routine_id == r.id)
|
||||||
.order_by(RoutineStep.order_index)
|
.order_by(col(RoutineStep.order_index))
|
||||||
).all()
|
).all()
|
||||||
step_names = []
|
step_names = []
|
||||||
for s in steps:
|
for s in steps:
|
||||||
|
|
@ -267,12 +268,12 @@ def _build_recent_history(session: Session) -> str:
|
||||||
|
|
||||||
|
|
||||||
def _build_products_context(session: Session, time_filter: Optional[str] = None) -> str:
|
def _build_products_context(session: Session, time_filter: Optional[str] = None) -> str:
|
||||||
stmt = select(Product).where(Product.is_tool.is_(False))
|
stmt = select(Product).where(col(Product.is_tool).is_(False))
|
||||||
products = session.exec(stmt).all()
|
products = session.exec(stmt).all()
|
||||||
product_ids = [p.id for p in products]
|
product_ids = [p.id for p in products]
|
||||||
inventory_rows = (
|
inventory_rows = (
|
||||||
session.exec(
|
session.exec(
|
||||||
select(ProductInventory).where(ProductInventory.product_id.in_(product_ids))
|
select(ProductInventory).where(col(ProductInventory.product_id).in_(product_ids))
|
||||||
).all()
|
).all()
|
||||||
if product_ids
|
if product_ids
|
||||||
else []
|
else []
|
||||||
|
|
@ -433,7 +434,7 @@ def list_routines(
|
||||||
steps_by_routine: dict = {}
|
steps_by_routine: dict = {}
|
||||||
if routine_ids:
|
if routine_ids:
|
||||||
all_steps = session.exec(
|
all_steps = session.exec(
|
||||||
select(RoutineStep).where(RoutineStep.routine_id.in_(routine_ids))
|
select(RoutineStep).where(col(RoutineStep.routine_id).in_(routine_ids))
|
||||||
).all()
|
).all()
|
||||||
for step in all_steps:
|
for step in all_steps:
|
||||||
steps_by_routine.setdefault(step.routine_id, []).append(step)
|
steps_by_routine.setdefault(step.routine_id, []).append(step)
|
||||||
|
|
|
||||||
|
|
@ -168,7 +168,7 @@ async def analyze_skin_photos(
|
||||||
)
|
)
|
||||||
|
|
||||||
image_summary = (
|
image_summary = (
|
||||||
f"{len(photos)} image(s): {', '.join(p.content_type for p in photos)}"
|
f"{len(photos)} image(s): {', '.join((p.content_type or 'unknown') for p in photos)}"
|
||||||
)
|
)
|
||||||
response = call_gemini(
|
response = call_gemini(
|
||||||
endpoint="skincare/analyze-photos",
|
endpoint="skincare/analyze-photos",
|
||||||
|
|
|
||||||
|
|
@ -64,8 +64,13 @@ def call_gemini(
|
||||||
response = client.models.generate_content(
|
response = client.models.generate_content(
|
||||||
model=model, contents=contents, config=config
|
model=model, contents=contents, config=config
|
||||||
)
|
)
|
||||||
with suppress(Exception):
|
candidates = getattr(response, "candidates", None)
|
||||||
finish_reason = response.candidates[0].finish_reason.name
|
if candidates:
|
||||||
|
first_candidate = candidates[0]
|
||||||
|
reason = getattr(first_candidate, "finish_reason", None)
|
||||||
|
reason_name = getattr(reason, "name", None)
|
||||||
|
if isinstance(reason_name, str):
|
||||||
|
finish_reason = reason_name
|
||||||
if finish_reason and finish_reason != "STOP":
|
if finish_reason and finish_reason != "STOP":
|
||||||
success = False
|
success = False
|
||||||
error_detail = f"finish_reason: {finish_reason}"
|
error_detail = f"finish_reason: {finish_reason}"
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from datetime import date, timedelta
|
from datetime import date, datetime, timedelta
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
from uuid import UUID
|
from uuid import UUID
|
||||||
|
|
||||||
|
|
@ -79,9 +79,9 @@ def get_open_inventory() -> list[dict]:
|
||||||
with Session(engine) as session:
|
with Session(engine) as session:
|
||||||
stmt = (
|
stmt = (
|
||||||
select(ProductInventory, Product)
|
select(ProductInventory, Product)
|
||||||
.join(Product, ProductInventory.product_id == Product.id)
|
.join(Product, col(ProductInventory.product_id) == col(Product.id))
|
||||||
.where(ProductInventory.is_opened == True) # noqa: E712
|
.where(col(ProductInventory.is_opened).is_(True))
|
||||||
.where(ProductInventory.finished_at == None) # noqa: E711
|
.where(col(ProductInventory.finished_at).is_(None))
|
||||||
)
|
)
|
||||||
rows = session.exec(stmt).all()
|
rows = session.exec(stmt).all()
|
||||||
return [
|
return [
|
||||||
|
|
@ -118,7 +118,7 @@ def get_recent_routines(days: int = 14) -> list[dict]:
|
||||||
steps = session.exec(
|
steps = session.exec(
|
||||||
select(RoutineStep)
|
select(RoutineStep)
|
||||||
.where(RoutineStep.routine_id == routine.id)
|
.where(RoutineStep.routine_id == routine.id)
|
||||||
.order_by(RoutineStep.order_index)
|
.order_by(col(RoutineStep.order_index))
|
||||||
).all()
|
).all()
|
||||||
|
|
||||||
steps_data = []
|
steps_data = []
|
||||||
|
|
@ -254,14 +254,14 @@ def get_medications() -> list[dict]:
|
||||||
(valid_to IS NULL or >= today)."""
|
(valid_to IS NULL or >= today)."""
|
||||||
with Session(engine) as session:
|
with Session(engine) as session:
|
||||||
medications = session.exec(select(MedicationEntry)).all()
|
medications = session.exec(select(MedicationEntry)).all()
|
||||||
today = date.today()
|
today = datetime.combine(date.today(), datetime.min.time())
|
||||||
result = []
|
result = []
|
||||||
for med in medications:
|
for med in medications:
|
||||||
usages = session.exec(
|
usages = session.exec(
|
||||||
select(MedicationUsage)
|
select(MedicationUsage)
|
||||||
.where(MedicationUsage.medication_record_id == med.record_id)
|
.where(MedicationUsage.medication_record_id == med.record_id)
|
||||||
.where(
|
.where(
|
||||||
(MedicationUsage.valid_to == None) # noqa: E711
|
col(MedicationUsage.valid_to).is_(None)
|
||||||
| (col(MedicationUsage.valid_to) >= today)
|
| (col(MedicationUsage.valid_to) >= today)
|
||||||
)
|
)
|
||||||
).all()
|
).all()
|
||||||
|
|
@ -310,10 +310,10 @@ def get_expiring_inventory(days: int = 30) -> list[dict]:
|
||||||
cutoff = date.today() + timedelta(days=days)
|
cutoff = date.today() + timedelta(days=days)
|
||||||
stmt = (
|
stmt = (
|
||||||
select(ProductInventory, Product)
|
select(ProductInventory, Product)
|
||||||
.join(Product, ProductInventory.product_id == Product.id)
|
.join(Product, col(ProductInventory.product_id) == col(Product.id))
|
||||||
.where(ProductInventory.is_opened == True) # noqa: E712
|
.where(col(ProductInventory.is_opened).is_(True))
|
||||||
.where(ProductInventory.finished_at == None) # noqa: E711
|
.where(col(ProductInventory.finished_at).is_(None))
|
||||||
.where(ProductInventory.expiry_date != None) # noqa: E711
|
.where(col(ProductInventory.expiry_date).is_not(None))
|
||||||
.where(col(ProductInventory.expiry_date) <= cutoff)
|
.where(col(ProductInventory.expiry_date) <= cutoff)
|
||||||
)
|
)
|
||||||
rows = session.exec(stmt).all()
|
rows = session.exec(stmt).all()
|
||||||
|
|
@ -341,7 +341,7 @@ def get_grooming_schedule() -> list[dict]:
|
||||||
"""Get the full grooming schedule sorted by day of week (0=Monday, 6=Sunday)."""
|
"""Get the full grooming schedule sorted by day of week (0=Monday, 6=Sunday)."""
|
||||||
with Session(engine) as session:
|
with Session(engine) as session:
|
||||||
entries = session.exec(
|
entries = session.exec(
|
||||||
select(GroomingSchedule).order_by(GroomingSchedule.day_of_week)
|
select(GroomingSchedule).order_by(col(GroomingSchedule.day_of_week))
|
||||||
).all()
|
).all()
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
from contextlib import asynccontextmanager
|
from contextlib import asynccontextmanager
|
||||||
|
from typing import Any, AsyncIterator, Mapping
|
||||||
|
|
||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv
|
||||||
|
|
||||||
|
|
@ -7,6 +8,7 @@ load_dotenv() # load .env before db.py reads DATABASE_URL
|
||||||
from fastapi import FastAPI # noqa: E402
|
from fastapi import FastAPI # noqa: E402
|
||||||
from fastapi.middleware.cors import CORSMiddleware # noqa: E402
|
from fastapi.middleware.cors import CORSMiddleware # noqa: E402
|
||||||
from fastmcp.utilities.lifespan import combine_lifespans # noqa: E402
|
from fastmcp.utilities.lifespan import combine_lifespans # noqa: E402
|
||||||
|
from starlette.applications import Starlette # noqa: E402
|
||||||
|
|
||||||
from db import create_db_and_tables # noqa: E402
|
from db import create_db_and_tables # noqa: E402
|
||||||
from innercontext.api import ( # noqa: E402
|
from innercontext.api import ( # noqa: E402
|
||||||
|
|
@ -23,7 +25,7 @@ mcp_app = mcp.http_app(path="/mcp")
|
||||||
|
|
||||||
|
|
||||||
@asynccontextmanager
|
@asynccontextmanager
|
||||||
async def lifespan(app: FastAPI):
|
async def lifespan(app: Starlette) -> AsyncIterator[Mapping[str, Any] | None]:
|
||||||
create_db_and_tables()
|
create_db_and_tables()
|
||||||
yield
|
yield
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue