Skip to content

Commit 9db0527

Browse files
committed
refactor(api): updated the new apis for hifi-kub-adk
1 parent d1ce4d8 commit 9db0527

5 files changed

Lines changed: 825 additions & 2 deletions

File tree

app/api/v1/hifi_adk_api.py

Lines changed: 320 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,320 @@
1+
from fastapi import APIRouter, HTTPException, Depends, status
2+
from typing import Optional, List
3+
import logging
4+
from app.schemas.auth_schema import CurrentUser
5+
from app.schemas.message_schema import UserMessageSchema
6+
from app.middlewares.firebase_middleware import get_current_user_typed
7+
from app.core.hifi_adk import HiFiADK, HiFiADKError, HiFiADKHTTPError
8+
from app.core.env import Env
9+
from pydantic import BaseModel
10+
11+
# Initialize logger
12+
logging.basicConfig(level=logging.INFO)
13+
logger = logging.getLogger(__name__)
14+
15+
# Initialize router
16+
hifi_adk_router = APIRouter()
17+
18+
# Get HiFiADK instance
19+
def get_hifi_adk_client() -> HiFiADK:
20+
"""Get or create HiFiADK client instance"""
21+
api_url = Env.hifi_adk_api_url
22+
if not api_url:
23+
raise HTTPException(status_code=500, detail="HiFi ADK API URL is not configured")
24+
return HiFiADK.get_instance(api=api_url)
25+
26+
27+
# Request/Response Schemas
28+
class CreateSessionRequest(BaseModel):
29+
session_id: str
30+
31+
32+
class RunAgentRequest(BaseModel):
33+
session_id: str
34+
message: str
35+
role: Optional[str] = 'user'
36+
streaming: Optional[bool] = False
37+
state_delta: Optional[dict] = None
38+
raw_files: Optional[List[dict]] = None
39+
40+
41+
class RunAgentInlineDataRequest(BaseModel):
42+
session_id: str
43+
message: str
44+
role: Optional[str] = 'user'
45+
streaming: Optional[bool] = False
46+
state_delta: Optional[dict] = None
47+
inline_files: Optional[List[dict]] = None
48+
49+
50+
class DeleteSessionRequest(BaseModel):
51+
session_id: str
52+
53+
54+
# API Endpoints
55+
@hifi_adk_router.post("/create-session")
56+
async def create_session(user: CurrentUser = Depends(get_current_user_typed)) -> dict:
57+
"""Creates a new session for the specified user."""
58+
logging.info("Creating session for user_id: %s", user.uid)
59+
60+
if not user.uid:
61+
raise HTTPException(status_code=400, detail="User ID is required")
62+
63+
try:
64+
client = get_hifi_adk_client()
65+
66+
# Generate a unique session ID (you might want to use UUID or similar)
67+
import uuid
68+
session_id = str(uuid.uuid4())
69+
70+
session = client.create_session(
71+
user_id=user.uid,
72+
session_id=session_id
73+
)
74+
75+
logging.info("Created session for user_id: %s", user.uid)
76+
77+
return {
78+
"message": "successfully created session",
79+
"data": session.dict()
80+
}
81+
except HiFiADKHTTPError as e:
82+
logger.error(f"HTTP error creating session: {e.message}")
83+
raise HTTPException(status_code=e.status_code, detail=e.message)
84+
except HiFiADKError as e:
85+
logger.error(f"Error creating session: {str(e)}")
86+
raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(e))
87+
except Exception as e:
88+
logger.error(f"Unexpected error creating session: {str(e)}")
89+
raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail="Internal server error")
90+
91+
92+
@hifi_adk_router.get("/list-sessions")
93+
async def list_sessions(user: CurrentUser = Depends(get_current_user_typed)) -> dict:
94+
"""Lists all sessions for the specified user."""
95+
try:
96+
hifi_client = get_hifi_adk_client()
97+
sessions_response = hifi_client.list_sessions(user_id=user.uid)
98+
99+
sessions = sessions_response.sessions
100+
101+
if not sessions:
102+
logging.info("No sessions found for user_id: %s", user.uid)
103+
return {
104+
"message": "no sessions found",
105+
"data": []
106+
}
107+
108+
# Debug logging to understand the data structure
109+
logging.info("Sessions type: %s", type(sessions))
110+
logging.info("Sessions content: %s", sessions)
111+
112+
sessions_data = []
113+
114+
# Handle different possible structures of sessions
115+
if isinstance(sessions, dict):
116+
# If sessions is a dict, iterate over its values
117+
session_items = sessions.values() if sessions else []
118+
elif isinstance(sessions, list):
119+
# If sessions is a list, use it directly
120+
session_items = sessions
121+
else:
122+
session_items = [sessions] if sessions else []
123+
124+
for session in session_items:
125+
# Handle case where session is a dictionary directly
126+
if isinstance(session, dict):
127+
session_dict = session
128+
session_id = session_dict.get("id", session_dict.get("sessionId", ""))
129+
130+
# Safely get title from Firebase
131+
title = "Chat Session"
132+
try:
133+
if session_id:
134+
from app.core.firebase_client import client as firebase_client
135+
chat_doc = firebase_client.collection("chats").document(session_id).get()
136+
if chat_doc.exists:
137+
doc_data = chat_doc.to_dict()
138+
if doc_data and "title" in doc_data:
139+
title = doc_data["title"]
140+
except Exception as e:
141+
logging.warning("Failed to get title for session %s: %s", session_id, e)
142+
143+
sessions_data.append({
144+
"appName": session_dict.get("app_name", session_dict.get("appName", "")),
145+
"session_name": title,
146+
"events": session_dict.get("events", []),
147+
"state": session_dict.get("state", {}),
148+
"id": session_id,
149+
"userId": session_dict.get("user_id", session_dict.get("userId", user.uid)),
150+
"lastUpdateTime": session_dict.get("last_update_time", session_dict.get("lastUpdateTime", session_dict.get("updatedAt", "")))
151+
})
152+
# Handle case where session is a list containing dictionaries
153+
elif isinstance(session, list) and len(session) > 0 and isinstance(session[0], dict):
154+
session_dict = session[0]
155+
session_id = session_dict.get("id", session_dict.get("sessionId", ""))
156+
157+
# Safely get title from Firebase
158+
title = "Chat Session"
159+
try:
160+
if session_id:
161+
from app.core.firebase_client import client as firebase_client
162+
chat_doc = firebase_client.collection("chats").document(session_id).get()
163+
if chat_doc.exists:
164+
doc_data = chat_doc.to_dict()
165+
if doc_data and "title" in doc_data:
166+
title = doc_data["title"]
167+
except Exception as e:
168+
logging.warning("Failed to get title for session %s: %s", session_id, e)
169+
170+
sessions_data.append({
171+
"appName": session_dict.get("app_name", session_dict.get("appName", "")),
172+
"session_name": title,
173+
"events": session_dict.get("events", []),
174+
"state": session_dict.get("state", {}),
175+
"id": session_id,
176+
"userId": session_dict.get("user_id", session_dict.get("userId", user.uid)),
177+
"lastUpdateTime": session_dict.get("last_update_time", session_dict.get("lastUpdateTime", session_dict.get("updatedAt", "")))
178+
})
179+
else:
180+
# Skip sessions that don't match expected structure
181+
logging.warning("Skipping session with unexpected structure. Type: %s, Content: %s", type(session), session)
182+
183+
return {
184+
"message": "successfully fetched sessions",
185+
"data": sessions_data
186+
}
187+
except HiFiADKHTTPError as e:
188+
logger.error(f"HTTP error listing sessions: {e.message}")
189+
raise HTTPException(status_code=e.status_code, detail=e.message)
190+
except HiFiADKError as e:
191+
logger.error(f"Error listing sessions: {str(e)}")
192+
raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(e))
193+
except Exception as e:
194+
logger.error(f"Unexpected error listing sessions: {str(e)}")
195+
raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail="Internal server error")
196+
197+
198+
@hifi_adk_router.delete("/delete-session")
199+
async def delete_session(session_id: str, user: CurrentUser = Depends(get_current_user_typed)) -> dict:
200+
"""Deletes a session for the specified user."""
201+
try:
202+
hifi_client = get_hifi_adk_client()
203+
hifi_client.delete_session(user_id=user.uid, session_id=session_id)
204+
205+
# Delete from Firebase
206+
from app.core.firebase_client import client as firebase_client
207+
firebase_client.collection("chats").document(session_id).delete()
208+
209+
return {
210+
"message": "successfully deleted session",
211+
"data": session_id
212+
}
213+
except HiFiADKHTTPError as e:
214+
logger.error(f"HTTP error deleting session: {e.message}")
215+
raise HTTPException(status_code=e.status_code, detail=e.message)
216+
except HiFiADKError as e:
217+
logger.error(f"Error deleting session: {str(e)}")
218+
raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(e))
219+
except Exception as e:
220+
logger.error(f"Unexpected error deleting session: {str(e)}")
221+
raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail="Internal server error")
222+
223+
224+
@hifi_adk_router.get("/get-session")
225+
async def get_session(session_id: str, user: CurrentUser = Depends(get_current_user_typed)) -> dict:
226+
"""Gets a specific session."""
227+
try:
228+
hifi_client = get_hifi_adk_client()
229+
session_response = hifi_client.get_session(user_id=user.uid, session_id=session_id)
230+
231+
if not session_response:
232+
raise HTTPException(status_code=400, detail="Session not found")
233+
234+
session = session_response.dict()
235+
236+
# Safely get title from Firebase
237+
title = "Chat Session"
238+
try:
239+
from app.core.firebase_client import client as firebase_client
240+
chat_doc = firebase_client.collection("chats").document(session_id).get()
241+
if chat_doc.exists:
242+
doc_data = chat_doc.to_dict()
243+
if doc_data and "title" in doc_data:
244+
title = doc_data["title"]
245+
except Exception as e:
246+
logging.warning("Failed to get title for session %s: %s", session_id, e)
247+
248+
return {
249+
"message": "successfully fetched session",
250+
"data": {
251+
"appName": session.get("appName", ""),
252+
"session_name": title,
253+
"events": session.get("events", []),
254+
"state": session.get("state", {}),
255+
"id": session.get("sessionId", ""),
256+
"userId": session.get("userId", ""),
257+
"lastUpdateTime": session.get("updatedAt", session.get("lastUpdateTime", ""))
258+
}
259+
}
260+
except HiFiADKHTTPError as e:
261+
logger.error(f"HTTP error getting session: {e.message}")
262+
raise HTTPException(status_code=e.status_code, detail=e.message)
263+
except HiFiADKError as e:
264+
logger.error(f"Error getting session: {str(e)}")
265+
raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(e))
266+
except Exception as e:
267+
logger.error(f"Unexpected error getting session: {str(e)}")
268+
raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail="Internal server error")
269+
270+
271+
@hifi_adk_router.post("/send-message")
272+
async def send_message(session_id: str, user_message: UserMessageSchema, user: CurrentUser = Depends(get_current_user_typed)):
273+
"""Sends a message to the deployed agent."""
274+
try:
275+
from app.core.firebase_client import client as firebase_client
276+
from app.services.gemini_service import gemini
277+
278+
hifi_client = get_hifi_adk_client()
279+
280+
# Get or create chat title
281+
chat_doc = firebase_client.collection("chats").document(session_id).get()
282+
if chat_doc.exists:
283+
chat_data = chat_doc.to_dict()
284+
title = chat_data["title"] if chat_data else "Chat Session"
285+
else:
286+
title = gemini.generate_title(user_message.message)
287+
288+
if not title:
289+
title = "Chat Session"
290+
291+
firebase_client.collection("chats").document(session_id).set({
292+
"title": title,
293+
"user_id": user.uid
294+
})
295+
296+
print(f"Sending message to session {session_id}:")
297+
print(f"Message: {user_message.message}")
298+
print("\nResponse:")
299+
300+
# Use run_agent to send message
301+
response = hifi_client.run_agent(
302+
user_id=user.uid,
303+
session_id=session_id,
304+
message=user_message.message,
305+
role="user",
306+
streaming=False
307+
)
308+
309+
return response.dict()
310+
311+
except HiFiADKHTTPError as e:
312+
logger.error(f"HTTP error sending message: {e.message}")
313+
raise HTTPException(status_code=e.status_code, detail=e.message)
314+
except HiFiADKError as e:
315+
logger.error(f"Error sending message: {str(e)}")
316+
raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(e))
317+
except Exception as e:
318+
logger.error(f"Unexpected error sending message: {str(e)}")
319+
raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail="Internal server error")
320+

app/api/v1/main.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,11 @@
22
from .dev_api import dev_router
33
from .kite_api import kite_router
44
from .adk_api import adk_router
5+
from .hifi_adk_api import hifi_adk_router
56

67
v1_router = APIRouter()
78

89
v1_router.include_router(dev_router, prefix="/dev", tags=["dev"])
910
v1_router.include_router(kite_router, prefix="/kite", tags=["kite"])
10-
v1_router.include_router(adk_router, prefix="/adk", tags=["adk"])
11+
v1_router.include_router(adk_router, prefix="/hfi-adk", tags=["hifi-adk"])
12+
v1_router.include_router(hifi_adk_router, prefix="/adk", tags=["adk"])

app/core/env.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,8 @@
2323
google_cloud_location=os.getenv("GOOGLE_CLOUD_LOCATION", ""),
2424
google_cloud_staging_bucket=os.getenv("GOOGLE_CLOUD_STAGING_BUCKET", ""),
2525
google_agent_resource_id=os.getenv("GOOGLE_AGENT_RESOURCE_ID", ""),
26-
gemini_api_key=os.getenv("GEMINI_API_KEY", "")
26+
gemini_api_key=os.getenv("GEMINI_API_KEY", ""),
27+
hifi_adk_api_url=os.getenv("HIFI_ADK_API_URL", "")
2728
)
2829

2930
# For backward compatibility, also create Env as an alias

0 commit comments

Comments
 (0)