Files
2026-07-13 13:30:30 +08:00

185 lines
5.6 KiB
Python

# Copyright 2026 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import os
from fastapi import FastAPI
from fastapi.responses import RedirectResponse
from fastapi.staticfiles import StaticFiles
from google.adk.cli.fast_api import get_fast_api_app
from google.adk.runners import Runner
from google.adk.sessions.database_session_service import DatabaseSessionService
from google.cloud import logging as google_cloud_logging
from pydantic import BaseModel
from app.agent import app as agent_app
from app.app_utils.telemetry import setup_telemetry
from app.app_utils.typing import Feedback
from app.live_onboarding import (
artifact_response,
case_payload,
create_live_case,
get_case,
latest_case_payload,
mark_document_signed,
mark_hardware_delivered,
static_root,
)
from app.resume_handler import OnboardingResumeHandler
setup_telemetry()
logging_client = google_cloud_logging.Client()
logger = logging_client.logger(__name__)
allow_origins = (
os.getenv("ALLOW_ORIGINS", "").split(",") if os.getenv("ALLOW_ORIGINS") else None
)
# Artifact bucket for ADK (created by Terraform, passed via env var)
logs_bucket_name = os.environ.get("LOGS_BUCKET_NAME")
AGENT_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
# Persistent SQLite session configuration
session_service_uri = "sqlite+aiosqlite:///sessions.db"
artifact_service_uri = f"gs://{logs_bucket_name}" if logs_bucket_name else None
app: FastAPI = get_fast_api_app(
agents_dir=AGENT_DIR,
web=True,
artifact_service_uri=artifact_service_uri,
allow_origins=allow_origins,
session_service_uri=session_service_uri,
otel_to_cloud=True,
)
app.title = "new-hire-onboarding"
app.description = "API for interacting with the Agent new-hire-onboarding"
db_session_service = DatabaseSessionService(db_url=session_service_uri)
webhook_runner = Runner(app=agent_app, session_service=db_session_service)
resume_handler = OnboardingResumeHandler(runner=webhook_runner)
app.mount(
"/live-onboarding",
StaticFiles(directory=static_root() / "live-onboarding", html=True),
name="live-onboarding",
)
@app.get("/demo")
def demo_index() -> RedirectResponse:
return RedirectResponse(url="/live-onboarding/")
@app.post("/api/live-onboarding/start")
async def start_live_onboarding() -> dict:
case = await create_live_case(db_session_service)
return {
"active": True,
"case": case_payload(get_case(case.id)),
}
@app.get("/api/live-onboarding/cases/{case_id}")
def get_live_onboarding_case(case_id: str) -> dict:
return {
"active": True,
"case": case_payload(get_case(case_id)),
}
@app.post("/api/live-onboarding/cases/{case_id}/sign")
async def sign_live_onboarding_packet(case_id: str) -> dict:
case = get_case(case_id)
if case.document_signed:
return {"active": True, "case": case_payload(case)}
await mark_document_signed(case, resume_handler)
return {"active": True, "case": case_payload(case)}
@app.post("/api/live-onboarding/cases/{case_id}/deliver-hardware")
async def confirm_live_hardware_delivery(case_id: str) -> dict:
case = get_case(case_id)
if case.hardware_delivered:
return {"active": True, "case": case_payload(case)}
await mark_hardware_delivered(case, resume_handler)
return {"active": True, "case": case_payload(case)}
@app.get("/api/live-onboarding/cases/{case_id}/artifacts/{artifact_id}")
def get_live_onboarding_artifact(case_id: str, artifact_id: str):
return artifact_response(case_id, artifact_id)
@app.get("/api/live-onboarding/current")
def get_current_live_onboarding_case() -> dict:
return latest_case_payload()
class WebhookPayload(BaseModel):
user_id: str
session_id: str
class HardwareWebhookPayload(WebhookPayload):
tracking_id: str
@app.post("/webhooks/document_signed")
async def trigger_document_signed_webhook(payload: WebhookPayload) -> dict[str, str]:
"""Webhook called when employee signs their contract. Wakes up the onboarding agent."""
await resume_handler.receive_signed_documents_callback(
user_id=payload.user_id, session_id=payload.session_id
)
return {
"status": "success",
"message": "Document signature processed, agent resumed.",
}
@app.post("/webhooks/hardware_delivered")
async def trigger_hardware_delivered_webhook(
payload: HardwareWebhookPayload,
) -> dict[str, str]:
"""Webhook called when carrier confirms delivery of the laptop. Wakes up the onboarding agent."""
await resume_handler.receive_hardware_delivery_callback(
user_id=payload.user_id,
session_id=payload.session_id,
tracking_id=payload.tracking_id,
)
return {
"status": "success",
"message": "Hardware delivery processed, agent resumed.",
}
@app.post("/feedback")
def collect_feedback(feedback: Feedback) -> dict[str, str]:
"""Collect and log feedback.
Args:
feedback: The feedback data to log
Returns:
Success message
"""
logger.log_struct(feedback.model_dump(), severity="INFO")
return {"status": "success"}
# Main execution
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="0.0.0.0", port=8000)