Agentic Browser

Home Projects Agentic Browser Python Backend Api Service Layer Architecture

Service Layer Architecture

Purpose and Scope

This document explains the Service Layer pattern used throughout the Agentic Browser backend. Services encapsulate business logic, coordinate between API routers and tool modules, and provide consistent error handling and logging. This layer sits between the FastAPI routers (documented in 3.3) and the reusable tool modules (documented in 3.5).

For information about agent-specific service orchestration, see the React Agent Architecture (4.1).

Service Layer Role

The service layer implements a three-tier architecture pattern:

Architecture Diagram

Sources: services/github_service.py1-40 services/website_service.py1-53 services/youtube_service.py1-35 services/google_search_service.py1-31 services/gmail_service.py1-56 services/calendar_service.py1-38 services/pyjiit_service.py1-125 services/react_agent_service.py1-92

Separation of Concerns

Layer Responsibility Example Code Entity
Router HTTP request/response handling, input validation, authentication checks @router.post("/answer")
Service Business logic orchestration, error handling, logging, data transformation GitHubService.generate_answer()
Tool Reusable operations, external API calls, data processing convert_github_repo_to_markdown()

This separation enables:

  • Testability: Services can be unit tested independently of FastAPI infrastructure
  • Reusability: Tools can be shared across services and agent tools
  • Maintainability: Changes to business logic don't affect HTTP handling
  • Composability: Services coordinate multiple tools for complex workflows

Service Class Structure

Common Service Pattern

All services follow a consistent structure:

Architecture Diagram

Sources: services/github_service.py10-40 services/website_service.py8-53 services/youtube_service.py7-35 services/google_search_service.py7-31 services/gmail_service.py10-56 services/calendar_service.py8-38 services/pyjiit_service.py13-125 services/react_agent_service.py14-92

Service Categories

Services can be categorized by their interaction patterns:

Category Services Tool Integration LLM Integration
LLM-Driven Q&A GitHubService, WebsiteService, YouTubeService Content extraction tools Prompt chains with get_chain() or similar
Direct API Wrapper GmailService, CalendarService, GoogleSearchService Tool functions that wrap external APIs None (agent tools use LLM separately)
Session Management PyjiitService Custom API client with encryption None
Agent Orchestration ReactAgentService Agent tools via GraphBuilder LLM called by LangGraph

Error Handling and Logging

Structured Logging Pattern

All services obtain a logger using the get_logger(__name__) pattern from the core module:

Architecture Diagram

Example: GitHubService logging

Example: ReactAgentService logging

Sources: services/github_service.py3-38 services/website_service.py1-51 services/youtube_service.py1-33 services/google_search_service.py1-29 services/gmail_service.py1-54 services/calendar_service.py1-36 services/pyjiit_service.py4-123 services/react_agent_service.py8-86

Exception Handling Pattern

Services implement a consistent try/except pattern:

Architecture Diagram

Q&A Services (GitHub, Website, YouTube) return graceful error messages:

API Wrapper Services (Gmail, Calendar, GoogleSearch, PyJIIT) propagate exceptions to routers:

Sources: services/github_service.py37-39 services/website_service.py50-52 services/youtube_service.py32-34 services/react_agent_service.py85-91 services/gmail_service.py17-20 services/calendar_service.py15-17 services/google_search_service.py28-30 services/pyjiit_service.py20-22

Integration with Tool Layer

Services delegate specific operations to tool modules, maintaining a clear dependency hierarchy:

Architecture Diagram

Sources: services/github_service.py5-19 services/website_service.py3-22 services/youtube_service.py2-17 services/google_search_service.py2-15 services/gmail_service.py2-50 services/calendar_service.py2-34 services/pyjiit_service.py8-60

Service-to-Tool Call Patterns

Service Tool Import Tool Function Call Line Reference
GitHubService from tools.github_crawler import convert_github_repo_to_markdown await convert_github_repo_to_markdown(url) services/github_service.py5-19
WebsiteService from tools.website_context import markdown_fetcher markdown_fetcher(url) services/website_service.py3-22
YouTubeService from prompts.youtube import youtube_chain youtube_chain.invoke({...}) services/youtube_service.py2-17
GoogleSearchService from tools.google_search.seach_agent import web_search_pipeline web_search_pipeline(query, max_results=max_results) services/google_search_service.py2-15
GmailService from tools.gmail.list_unread_emails import list_unread list_unread(access_token, max_results=max_results) services/gmail_service.py3-13
CalendarService from tools.calendar.get_calender_events import get_calendar_events get_calendar_events(access_token, max_results=max_results) services/calendar_service.py3-11
PyjiitService from tools.pyjiit.wrapper import Webportal, WebportalSession Webportal().student_login(...) services/pyjiit_service.py8-16

Integration with Prompt and LLM Layer

Q&A-oriented services integrate with prompt chains to generate responses:

Architecture Diagram

Sources: services/github_service.py4-30 services/website_service.py2-43 services/youtube_service.py2-23

Chain Construction and Initialization

GitHubService: Obtains chain on-demand per request

WebsiteService: Constructs chain during initialization

YouTubeService: Uses pre-built chain module

Response Handling Pattern

All LLM-driven services handle responses consistently:

# Pattern: Check if response is string or has .content attribute
if isinstance(response, str):
    return response
return response.content

Sources: services/github_service.py32-35 services/website_service.py45-48 services/youtube_service.py27-30

Context Management and Authentication

Services handle authentication context differently based on their requirements:

Architecture Diagram

Sources: services/react_agent_service.py18-34 services/gmail_service.py11-47 services/calendar_service.py9-27 services/pyjiit_service.py14-124

ReactAgentService Context Building

ReactAgentService builds a context dictionary for conditional tool availability:

Architecture Diagram

Implementation:

The GraphBuilder uses this context to determine which tools to make available in the agent's tool list. For example, Gmail and Calendar tools are only available when google_access_token is present.

Sources: services/react_agent_service.py23-36

API Wrapper Services Authentication

Services that wrap external APIs accept authentication tokens as method parameters:

GmailService:

CalendarService:

PyjiitService Session Management:

Sources: services/gmail_service.py11-44 services/calendar_service.py9-19 services/pyjiit_service.py14-60

Common Service Patterns

Asynchronous vs Synchronous Methods

Services use async methods when calling async tools:

Service Method Type Reason
GitHubService.generate_answer() async Calls await convert_github_repo_to_markdown()
WebsiteService.generate_answer() async Chain invocation may be async
YouTubeService.generate_answer() async Chain invocation may be async
ReactAgentService.generate_answer() async Calls await graph.ainvoke()
GmailService.* Synchronous Wraps synchronous tool functions
CalendarService.* Synchronous Wraps synchronous tool functions
GoogleSearchService.search() Synchronous Wraps synchronous tool functions
PyjiitService.* Synchronous Wraps synchronous tool functions

Sources: services/github_service.py11-16 services/website_service.py12-17 services/youtube_service.py8-13 services/react_agent_service.py15-21 services/gmail_service.py11-44 services/calendar_service.py9-19 services/google_search_service.py8 services/pyjiit_service.py14-46

Parameter Consistency

Services maintain consistent parameter naming:

Common Parameters:

  • url: String or HttpUrl for web resources
  • question: User query string
  • chat_history: List of previous messages (format varies by service)
  • access_token: OAuth token for Google services
  • max_results: Result count limit (default varies)

Chat History Formats:

Data Transformation Patterns

Chat History Conversion in ReactAgentService:

services/react_agent_service.py38-56 converts dict-based chat history to LangChain message objects:

messages_list: list = []
if chat_history:
    for entry in chat_history:
        if isinstance(entry, dict):
            role = (entry.get("role") or "").lower()
            content = entry.get("content", "")

            if role == "user":
                messages_list.append(HumanMessage(content=content))
            elif role in {"assistant", "bot", "ai"}:
                messages_list.append(AIMessage(content=content))

Chat History String Building in WebsiteService:

services/website_service.py28-36 concatenates chat history into a string:

chat_history_str = ""
if chat_history:
    for entry in chat_history:
        if isinstance(entry, dict):
            role = entry.get("role", "")
            content = entry.get("content", "")
            chat_history_str += f"{role}: {content}\n"

PyjiitService Data Processing:

services/pyjiit_service.py104-118 extracts and cleans attendance data using regex:

for item in raw_list:
    subj = item.get("subjectcode", "") or ""
    # Extract code in parentheses
    m = re.search(r"\(([^)]+)\)\s*$", subj)
    code = m.group(1) if m else ""
    # Strip trailing bracketed code
    subject_no_bracket = re.sub(r"\s*\([^)]*\)\s*$", "", subj).strip()

Sources: services/react_agent_service.py38-56 services/website_service.py28-36 services/pyjiit_service.py104-118

Return Value Patterns

Services return different data structures based on their purpose:

Service Return Type Example Value
Q&A Services str Answer text or error message
GoogleSearchService List of search results [{...}] from Tavily
GmailService.list_unread_messages() List of email dicts [{"id": ..., "subject": ...}]
CalendarService.list_events() List of event dicts [{"summary": ..., "start": ...}]
PyjiitService.login() Dict[str, Any] Serialized WebportalSession
PyjiitService.get_attendance() List[Dict[str, Any]] Attendance records

Service Instantiation and Dependency Injection

Services are instantiated by routers, typically as singletons or per-request instances:

Architecture Diagram

Most services are stateless and can be instantiated without parameters:

service = GitHubService()
service = YouTubeService()
service = GoogleSearchService()
# etc.

Exception: WebsiteService

services/website_service.py9-10 initializes a chain during construction:

def __init__(self):
    self.chain = get_chain()

This allows reusing the same chain instance across multiple requests, improving performance.

Sources: All service files show parameter-free constructors except services/website_service.py9-10