Skip to content

Commit c60150e

Browse files
committed
fix: CRUD resource operations
- listing was not working - made all services, routers, models as consistent as possible
1 parent 9d5ebc5 commit c60150e

30 files changed

+765
-924
lines changed

src/askui/chat/api/app.py

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
from contextlib import asynccontextmanager
22
from typing import AsyncGenerator
33

4-
from fastapi import APIRouter, FastAPI
4+
from fastapi import APIRouter, FastAPI, Request, status
55
from fastapi.middleware.cors import CORSMiddleware
6+
from fastapi.responses import JSONResponse
67

78
from askui.chat.api.assistants.dependencies import get_assistant_service
89
from askui.chat.api.assistants.router import router as assistants_router
@@ -12,6 +13,7 @@
1213
from askui.chat.api.messages.router import router as messages_router
1314
from askui.chat.api.runs.router import router as runs_router
1415
from askui.chat.api.threads.router import router as threads_router
16+
from askui.utils.api_utils import ConflictError, LimitReachedError, NotFoundError
1517

1618

1719
@asynccontextmanager
@@ -38,6 +40,37 @@ async def lifespan(app: FastAPI) -> AsyncGenerator[None, None]: # noqa: ARG001
3840
allow_headers=["*"],
3941
)
4042

43+
44+
@app.exception_handler(NotFoundError)
45+
def not_found_error_handler(
46+
request: Request, # noqa: ARG001
47+
exc: NotFoundError,
48+
) -> JSONResponse:
49+
return JSONResponse(
50+
status_code=status.HTTP_404_NOT_FOUND, content={"detail": str(exc)}
51+
)
52+
53+
54+
@app.exception_handler(ConflictError)
55+
def conflict_error_handler(
56+
request: Request, # noqa: ARG001
57+
exc: ConflictError,
58+
) -> JSONResponse:
59+
return JSONResponse(
60+
status_code=status.HTTP_409_CONFLICT, content={"detail": str(exc)}
61+
)
62+
63+
64+
@app.exception_handler(LimitReachedError)
65+
def limit_reached_error_handler(
66+
request: Request, # noqa: ARG001
67+
exc: LimitReachedError,
68+
) -> JSONResponse:
69+
return JSONResponse(
70+
status_code=status.HTTP_400_BAD_REQUEST, content={"detail": str(exc)}
71+
)
72+
73+
4174
# Include routers
4275
v1_router = APIRouter(prefix="/v1")
4376
v1_router.include_router(assistants_router)
Lines changed: 42 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,53 @@
11
from typing import Literal
22

3-
from pydantic import BaseModel, Field
3+
from pydantic import BaseModel
44

5+
from askui.chat.api.models import AssistantId
6+
from askui.utils.api_utils import Resource
57
from askui.utils.datetime_utils import UnixDatetime, now
68
from askui.utils.id_utils import generate_time_ordered_id
9+
from askui.utils.not_given import NOT_GIVEN, BaseModelWithNotGiven, NotGiven
710

811

9-
class Assistant(BaseModel):
10-
"""An assistant that can be used in a thread."""
12+
class AssistantBase(BaseModel):
13+
"""Base assistant model."""
1114

12-
id: str = Field(default_factory=lambda: generate_time_ordered_id("asst"))
13-
created_at: UnixDatetime = Field(default_factory=now)
1415
name: str | None = None
1516
description: str | None = None
17+
avatar: str | None = None
18+
19+
20+
class AssistantCreateParams(AssistantBase):
21+
"""Parameters for creating an assistant."""
22+
23+
24+
class AssistantModifyParams(BaseModelWithNotGiven):
25+
"""Parameters for modifying an assistant."""
26+
27+
name: str | NotGiven = NOT_GIVEN
28+
description: str | NotGiven = NOT_GIVEN
29+
avatar: str | NotGiven = NOT_GIVEN
30+
31+
32+
class Assistant(AssistantBase, Resource):
33+
"""An assistant that can be used in a thread."""
34+
35+
id: AssistantId
1636
object: Literal["assistant"] = "assistant"
17-
avatar: str | None = Field(default=None, description="URL of the avatar image")
37+
created_at: UnixDatetime
38+
39+
@classmethod
40+
def create(cls, params: AssistantCreateParams) -> "Assistant":
41+
return cls(
42+
id=generate_time_ordered_id("asst"),
43+
created_at=now(),
44+
**params.model_dump(),
45+
)
46+
47+
def modify(self, params: AssistantModifyParams) -> "Assistant":
48+
return Assistant.model_validate(
49+
{
50+
**self.model_dump(),
51+
**params.model_dump(),
52+
}
53+
)
Lines changed: 28 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
1-
from fastapi import APIRouter, HTTPException
1+
from fastapi import APIRouter, status
22

3-
# from fastapi import status
43
from askui.chat.api.assistants.dependencies import AssistantServiceDep
5-
from askui.chat.api.assistants.models import Assistant
6-
from askui.chat.api.assistants.service import (
7-
AssistantService, # AssistantModifyRequest, CreateAssistantRequest,
4+
from askui.chat.api.assistants.models import (
5+
Assistant,
6+
AssistantCreateParams,
7+
AssistantModifyParams,
88
)
9-
from askui.chat.api.models import ListQueryDep
9+
from askui.chat.api.assistants.service import AssistantService
10+
from askui.chat.api.models import AssistantId, ListQueryDep
1011
from askui.utils.api_utils import ListQuery, ListResponse
1112

1213
router = APIRouter(prefix="/assistants", tags=["assistants"])
@@ -17,51 +18,37 @@ def list_assistants(
1718
query: ListQuery = ListQueryDep,
1819
assistant_service: AssistantService = AssistantServiceDep,
1920
) -> ListResponse[Assistant]:
20-
"""List all assistants."""
2121
return assistant_service.list_(query=query)
2222

2323

24-
# @router.post("", status_code=status.HTTP_201_CREATED)
25-
# def create_assistant(
26-
# request: CreateAssistantRequest,
27-
# assistant_service: AssistantService = AssistantServiceDep,
28-
# ) -> Assistant:
29-
# """Create a new assistant."""
30-
# return assistant_service.create(request)
24+
@router.post("", status_code=status.HTTP_201_CREATED)
25+
def create_assistant(
26+
params: AssistantCreateParams,
27+
assistant_service: AssistantService = AssistantServiceDep,
28+
) -> Assistant:
29+
return assistant_service.create(params)
3130

3231

3332
@router.get("/{assistant_id}")
3433
def retrieve_assistant(
35-
assistant_id: str,
34+
assistant_id: AssistantId,
3635
assistant_service: AssistantService = AssistantServiceDep,
3736
) -> Assistant:
38-
"""Get an assistant by ID."""
39-
try:
40-
return assistant_service.retrieve(assistant_id)
41-
except FileNotFoundError as e:
42-
raise HTTPException(status_code=404, detail=str(e)) from e
37+
return assistant_service.retrieve(assistant_id)
4338

4439

45-
# @router.post("/{assistant_id}")
46-
# def modify_assistant(
47-
# assistant_id: str,
48-
# request: AssistantModifyRequest,
49-
# assistant_service: AssistantService = AssistantServiceDep,
50-
# ) -> Assistant:
51-
# """Update an assistant."""
52-
# try:
53-
# return assistant_service.modify(assistant_id, request)
54-
# except FileNotFoundError as e:
55-
# raise HTTPException(status_code=404, detail=str(e)) from e
40+
@router.post("/{assistant_id}")
41+
def modify_assistant(
42+
assistant_id: AssistantId,
43+
params: AssistantModifyParams,
44+
assistant_service: AssistantService = AssistantServiceDep,
45+
) -> Assistant:
46+
return assistant_service.modify(assistant_id, params)
5647

5748

58-
# @router.delete("/{assistant_id}", status_code=status.HTTP_204_NO_CONTENT)
59-
# def delete_assistant(
60-
# assistant_id: str,
61-
# assistant_service: AssistantService = AssistantServiceDep,
62-
# ) -> None:
63-
# """Delete an assistant."""
64-
# try:
65-
# assistant_service.delete(assistant_id)
66-
# except FileNotFoundError as e:
67-
# raise HTTPException(status_code=404, detail=str(e)) from e
49+
@router.delete("/{assistant_id}", status_code=status.HTTP_204_NO_CONTENT)
50+
def delete_assistant(
51+
assistant_id: AssistantId,
52+
assistant_service: AssistantService = AssistantServiceDep,
53+
) -> None:
54+
assistant_service.delete(assistant_id)

0 commit comments

Comments
 (0)