cevheri commited on
Commit
f031ca8
·
1 Parent(s): 2651354

feat: initial conversation api implementation

Browse files
app/api/chat_api.py CHANGED
@@ -9,7 +9,7 @@ from app.schema.chat_schema import (
9
  MessageResponse,
10
  PlotResponse,
11
  )
12
- from app.schema.conversation import (
13
  ConversationResponse,
14
  ConversationItemResponse,
15
  )
@@ -57,7 +57,7 @@ async def create_chat_completion(
57
 
58
 
59
  # get all chat completions
60
- @router.get("/chat/completions", response_model=List[ChatCompletionResponse])
61
  async def list_chat_completions(
62
  request: Request,
63
  username: str = Depends(auth_service.verify_credentials),
@@ -80,10 +80,7 @@ async def list_chat_completions(
80
 
81
 
82
  # get a chat completion by id
83
- @router.get(
84
- "/chat/completions/{completion_id}",
85
- response_model=ChatCompletionResponse,
86
- )
87
  @api_response()
88
  async def retrieve_chat_completion(
89
  completion_id: str,
@@ -101,10 +98,7 @@ async def retrieve_chat_completion(
101
 
102
 
103
  # get all messages for a chat completion
104
- @router.get(
105
- "/chat/completions/{completion_id}/messages",
106
- response_model=List[MessageResponse],
107
- )
108
  @api_response()
109
  async def list_messages(
110
  completion_id: str,
@@ -125,10 +119,7 @@ async def list_messages(
125
  # plot api list
126
  ################
127
  # get a plot for a message
128
- @router.get(
129
- "/chat/completions/{completion_id}/messages/{message_id}/plot",
130
- response_model=PlotResponse,
131
- )
132
  @api_response()
133
  async def retrieve_plot(
134
  completion_id: str,
@@ -154,8 +145,7 @@ async def retrieve_plot(
154
 
155
 
156
  # get all conversations
157
- @router.get("/conversations", response_model=ConversationResponse)
158
- @api_response()
159
  async def list_conversations(
160
  request: Request,
161
  username: str = Depends(auth_service.verify_credentials),
@@ -165,8 +155,7 @@ async def list_conversations(
165
  """
166
  logger.debug(f"Listing conversations for username: {username}")
167
  try:
168
- # return await service.find_all_conversations(username)
169
- return {"items": [], "total": 0, "limit": 10, "offset": 0}
170
  except Exception as e:
171
  logger.error(f"Error in list_conversations: {str(e)}")
172
  raise HTTPException(status_code=500, detail=str(e))
@@ -175,11 +164,7 @@ async def list_conversations(
175
  # get a conversation by id
176
 
177
 
178
- @router.get(
179
- "/conversations/{completion_id}",
180
- response_model=ConversationItemResponse,
181
- )
182
- @api_response()
183
  async def retrieve_conversation(
184
  completion_id: str,
185
  request: Request,
 
9
  MessageResponse,
10
  PlotResponse,
11
  )
12
+ from app.schema.conversation_schema import (
13
  ConversationResponse,
14
  ConversationItemResponse,
15
  )
 
57
 
58
 
59
  # get all chat completions
60
+ @router.get("/chat/completions", response_model=List[ChatCompletionResponse], deprecated=True)
61
  async def list_chat_completions(
62
  request: Request,
63
  username: str = Depends(auth_service.verify_credentials),
 
80
 
81
 
82
  # get a chat completion by id
83
+ @router.get("/chat/completions/{completion_id}", response_model=ChatCompletionResponse)
 
 
 
84
  @api_response()
85
  async def retrieve_chat_completion(
86
  completion_id: str,
 
98
 
99
 
100
  # get all messages for a chat completion
101
+ @router.get("/chat/completions/{completion_id}/messages", response_model=List[MessageResponse], deprecated=True)
 
 
 
102
  @api_response()
103
  async def list_messages(
104
  completion_id: str,
 
119
  # plot api list
120
  ################
121
  # get a plot for a message
122
+ @router.get("/chat/completions/{completion_id}/messages/{message_id}/plot", response_model=PlotResponse)
 
 
 
123
  @api_response()
124
  async def retrieve_plot(
125
  completion_id: str,
 
145
 
146
 
147
  # get all conversations
148
+ @router.get("/conversations", response_model=ConversationResponse, response_model_exclude_none=True)
 
149
  async def list_conversations(
150
  request: Request,
151
  username: str = Depends(auth_service.verify_credentials),
 
155
  """
156
  logger.debug(f"Listing conversations for username: {username}")
157
  try:
158
+ return await service.find_all_conversations(username)
 
159
  except Exception as e:
160
  logger.error(f"Error in list_conversations: {str(e)}")
161
  raise HTTPException(status_code=500, detail=str(e))
 
164
  # get a conversation by id
165
 
166
 
167
+ @router.get("/conversations/{completion_id}", response_model=ConversationItemResponse, response_model_exclude_none=True)
 
 
 
 
168
  async def retrieve_conversation(
169
  completion_id: str,
170
  request: Request,
app/mapper/conversation_mapper.py ADDED
@@ -0,0 +1,33 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # ChatCompletion to ConversationItem
2
+
3
+ from app.mapper.base_mapper import BaseMapper
4
+ from app.model.chat_model import ChatCompletion
5
+ from app.schema.conversation_schema import ConversationItemResponse
6
+
7
+
8
+ class ConversationMapper(BaseMapper[ChatCompletion, ConversationItemResponse]):
9
+ """Mapper for converting between ChatCompletion model and ConversationItem schema."""
10
+
11
+ def to_schema(self, model: ChatCompletion) -> ConversationItemResponse:
12
+ """Convert ChatCompletion model to ConversationItem schema."""
13
+ # Convert datetime to Unix timestamp
14
+ created_timestamp = int(model.created_date.timestamp()) if model.created_date else None
15
+ last_updated_timestamp = int(model.last_updated_date.timestamp()) if model.last_updated_date else None
16
+
17
+ # Get the first message content as title if title is not set
18
+ title = model.title
19
+ if not title and model.messages:
20
+ first_message = model.messages[0]
21
+ title = first_message.content[:20] + "..." if len(first_message.content) > 20 else first_message.content
22
+
23
+ return ConversationItemResponse(
24
+ completion_id=model.completion_id,
25
+ title=title,
26
+ create_time=created_timestamp,
27
+ update_time=last_updated_timestamp,
28
+ is_archived=model.is_archived,
29
+ is_starred=model.is_starred,
30
+ )
31
+
32
+ def to_model(self, schema: ConversationItemResponse) -> ChatCompletion:
33
+ raise NotImplementedError("ConversationMapper.to_model is not implemented")
app/schema/{conversation.py → conversation_schema.py} RENAMED
@@ -37,7 +37,10 @@ class ConversationItemResponse(BaseModel):
37
  default=None,
38
  description="Indicates whether the conversation is excluded from memory or history, if set.",
39
  )
40
- memory_scope: str = Field(description="Scope of the conversation's memory, e.g., 'global_enabled' for global memory access.")
 
 
 
41
  workspace_id: Optional[str] = Field(
42
  default=None,
43
  description="Identifier for the workspace the conversation belongs to, if applicable.",
@@ -46,8 +49,14 @@ class ConversationItemResponse(BaseModel):
46
  default=None,
47
  description="Status of any asynchronous operations related to the conversation, if applicable.",
48
  )
49
- safe_urls: List[str] = Field(description="List of URLs deemed safe for the conversation context.")
50
- blocked_urls: List[str] = Field(description="List of URLs blocked for the conversation context.")
 
 
 
 
 
 
51
  conversation_origin: Optional[str] = Field(
52
  default=None,
53
  description="Origin or source of the conversation, if specified.",
 
37
  default=None,
38
  description="Indicates whether the conversation is excluded from memory or history, if set.",
39
  )
40
+ memory_scope: Optional[str] = Field(
41
+ default=None,
42
+ description="Scope of the conversation's memory, e.g., 'global_enabled' for global memory access.",
43
+ )
44
  workspace_id: Optional[str] = Field(
45
  default=None,
46
  description="Identifier for the workspace the conversation belongs to, if applicable.",
 
49
  default=None,
50
  description="Status of any asynchronous operations related to the conversation, if applicable.",
51
  )
52
+ safe_urls: Optional[List[str]] = Field(
53
+ default=None,
54
+ description="List of URLs deemed safe for the conversation context.",
55
+ )
56
+ blocked_urls: Optional[List[str]] = Field(
57
+ default=None,
58
+ description="List of URLs blocked for the conversation context.",
59
+ )
60
  conversation_origin: Optional[str] = Field(
61
  default=None,
62
  description="Origin or source of the conversation, if specified.",
app/service/chat_service.py CHANGED
@@ -9,19 +9,21 @@ from app.schema.chat_schema import (
9
  )
10
  from app.model.chat_model import ChatCompletion, ChatMessage
11
  from app.mapper.chat_mapper import ChatMapper
 
12
  import uuid
13
  from loguru import logger
14
- from app.schema.conversation import ConversationResponse
15
 
16
 
17
  class ChatService:
18
  def __init__(self):
19
  self.chat_repository = ChatRepository()
20
  self.chat_mapper = ChatMapper()
 
21
 
22
  async def handle_chat_completion(self, request: ChatCompletionRequest) -> ChatCompletionResponse:
23
  last_user_message = request.messages[-1].content
24
- response_content = f"TODO implement ai-agent response for this message: {last_user_message}"
25
  username = "admin"
26
 
27
  # Convert request to model
@@ -55,7 +57,18 @@ class ChatService:
55
 
56
  # conversation service
57
  async def find_all_conversations(self, username: str) -> List[ConversationResponse]:
58
- raise NotImplementedError("Not implemented")
 
 
 
 
 
 
 
 
59
 
60
  async def find_conversation_by_id(self, completion_id: str) -> ConversationResponse:
61
- raise NotImplementedError("Not implemented")
 
 
 
 
9
  )
10
  from app.model.chat_model import ChatCompletion, ChatMessage
11
  from app.mapper.chat_mapper import ChatMapper
12
+ from app.mapper.conversation_mapper import ConversationMapper
13
  import uuid
14
  from loguru import logger
15
+ from app.schema.conversation_schema import ConversationResponse
16
 
17
 
18
  class ChatService:
19
  def __init__(self):
20
  self.chat_repository = ChatRepository()
21
  self.chat_mapper = ChatMapper()
22
+ self.conversation_mapper = ConversationMapper()
23
 
24
  async def handle_chat_completion(self, request: ChatCompletionRequest) -> ChatCompletionResponse:
25
  last_user_message = request.messages[-1].content
26
+ logger.debug(f"TODO implement ai-agent response for this message: {last_user_message}")
27
  username = "admin"
28
 
29
  # Convert request to model
 
57
 
58
  # conversation service
59
  async def find_all_conversations(self, username: str) -> List[ConversationResponse]:
60
+ """Find all conversations for a given username."""
61
+ query = {"created_by": username}
62
+ sort = {"last_updated_date": -1} # Sort by last updated date in descending order
63
+
64
+ entities = await self.chat_repository.find(query, page=1, limit=100, sort=sort)
65
+ result = self.conversation_mapper.to_schema_list(entities)
66
+ return ConversationResponse(items=result, total=len(result), limit=100, offset=0)
67
+
68
+
69
 
70
  async def find_conversation_by_id(self, completion_id: str) -> ConversationResponse:
71
+ """Find a conversation by its completion ID."""
72
+ entity = await self.chat_repository.find_by_id(completion_id)
73
+ result = self.conversation_mapper.to_schema(entity) if entity else None
74
+ return ConversationResponse(items=[result], total=1, limit=1, offset=0)