OpenPoke Documentation

repository·main·Indexed 19 days ago

https://github.com/shlokkhemani/openpoke

OpenPoke is an open-source, multi-agent assistant featuring a FastAPI backend and a Next.js frontend. It provides email triage, reminders, and persistent agents by integrating with OpenRouter for orchestration and Composio for Gmail tooling, including drafting, replying, and forwarding.

Tokens
3.3K
Snippets
10
Records
15
Agent score
67%

What's inside OpenPoke

  1. Understand the OpenPoke project structure

    main

    The project is split into two main directories:

    • server/: Contains the FastAPI application, agent logic, and backend services.
    • web/: Contains the Next.js web application.
    • server/data/: A directory used for runtime data (this is ignored by git and is not part of the source code).
  2. Install and run OpenPoke

    main

    OpenPoke is a multi-agent assistant featuring a FastAPI backend and a Next.js web UI. To run it locally, you must set up both the Python backend and the Node.js frontend, ensuring they both use the same .env configuration.

    Prerequisites

    • Python 3.10+
    • Node.js 18+
    • npm 9+

    Setup Steps

    1. Clone the repository:

      git clone https://github.com/shlokkhemani/OpenPoke
      cd OpenPoke
    2. Configure Environment Variables: Copy the template to .env and populate it with your API keys:

      cp .env.example .env

      Required keys in .env:

      • your_openrouter_api_key_here: From openrouter.ai
      • your_composio_api_key_here: From composio.dev
      • your_gmail_auth_config_id_here: Your Composio Gmail auth config ID
    3. Set up Python Backend:

      python3.10 -m venv .venv
      source .venv/bin/activate
      pip install -r server/requirements.txt
      python -m server.server --reload
    4. Set up Next.js Frontend (in a new terminal):

      npm install --prefix web
      npm run dev --prefix web
    5. Connect Gmail: Navigate to http://localhost:3000, go to Settings → Gmail, and complete the Composio OAuth flow to enable email workflows.

    # Clone and enter
    git clone https://github.com/shlokkhemani/OpenPoke
    cd OpenPoke
    
    # Setup env
    cp .env.example .env
    
    # Backend setup
    python3.10 -m venv .venv
    source .venv/bin/activate
    pip install -r server/requirements.txt
    python -m server.server --reload
    
    # Frontend setup (new terminal)
    npm install --prefix web
    npm run dev --prefix web
  3. Configure OpenRouter and Composio in .env

    main

    OpenPoke requires two primary API integrations to function. These must be configured in the .env file located in the root directory.

    KeyProviderPurpose
    your_openrouter_api_key_hereOpenRouterPowers the multi-agent orchestration stack.
    your_composio_api_key_hereComposioEnables Gmail tooling (drafting, replying, forwarding).
    your_gmail_auth_config_id_hereComposioSpecific ID for your Gmail integration setup.
  4. Initialize and shutdown background services

    main

    The OpenPoke server manages two primary background services that are tied to the FastAPI application lifecycle via startup and shutdown events:

    1. Trigger Scheduler: Managed via get_trigger_scheduler().
    2. Important Email Watcher: Managed via get_important_email_watcher().

    When the application starts, these services are initialized using their .start() methods. When the application shuts down, they are stopped using their .stop() methods to ensure a graceful exit.

  5. How Gmail profile caching works

    main

    To optimize performance, the Gmail service implements a thread-safe in-memory cache for user profiles.

    1. Fetching: When fetch_status is called, the system first checks _get_cached_profile. If a profile is not found in the cache, it calls _fetch_profile_from_composio using the GMAIL_GET_PROFILE tool.
    2. Caching: Once a profile is successfully fetched from Composio, it is stored via _cache_profile with a timestamp (cached_at).
    3. Invalidation: Profiles are cleared from the cache in two scenarios:
      • When _clear_cached_profile(user_id) is called for a specific user.
      • When disconnect_account is called, which clears all profiles for the affected user IDs.
      • When a new connection is initiated for a user, the existing cache for that user is cleared.
  6. Initiate Gmail OAuth connection

    main

    To start the Gmail OAuth connection process, use initiate_connect. This function requires a GmailConnectPayload containing the user_id and an auth_config_id. If auth_config_id is not provided in the payload, it will attempt to use the composio_gmail_auth_config_id from the application settings.

    It returns a JSONResponse containing a redirect_url for the user to complete authentication, a connection_request_id, and the user_id.

    # Example usage (conceptual)
    from server.services.gmail.client import initiate_connect
    from server.models import GmailConnectPayload
    
    payload = GmailConnectPayload(
        user_id="user_123",
        auth_config_id="your_composio_auth_config_id"
    )
    response = initiate_connect(payload, settings)
    # response contains {'ok': True, 'redirect_url': '...', 'connection_request_id': '...', 'user_id': 'user_123'}
  7. Register global exception handlers with register_exception_handlers()

    main

    The register_exception_handlers function configures a FastAPI application to return consistent JSON error responses for common error types. It handles:

    • RequestValidationError: Returns a 422 Unprocessable Entity status with a JSON body containing {"ok": false, "error": "Invalid request", "detail": <errors>}.
    • HTTPException: Returns the specified status code with a JSON body {"ok": false, "error": <detail>}. If the detail is not a string, it is JSON-serialized.
    • Exception (Unhandled): Returns a 500 Internal Server Error status with a JSON body {"ok": false, "error": "Internal server error"}.

    This ensures that all API errors follow a predictable schema for clients.

    from server.app import register_exception_handlers
    from fastapi import FastAPI
    
    app = FastAPI()
    register_exception_handlers(app)
  8. Request a chat completion with request_chat_completion()

    main

    Use request_chat_completion to send a request to the OpenRouter API for a chat completion. It returns the raw JSON payload from the API.

    Parameters:

    • model (str): The ID of the model to use.
    • messages (List[Dict[str, str]]): A list of message dictionaries containing role and content.
    • system (Optional[str]): An optional system prompt to prepend to the messages.
    • api_key (Optional[str]): An optional API key. If not provided, it attempts to use the key from the project settings.
    • tools (Optional[List[Dict[str, Any]]]): An optional list of tool definitions for function calling.
    • base_url (str): The base URL for the OpenRouter API (defaults to OpenRouterBaseURL).

    Returns:

    • Dict[str, Any]: The raw JSON response from the OpenRouter API.
    from server.openrouter_client.client import request_chat_completion
    
    messages = [{"role": "user", "content": "Hello!"}]
    response = await request_chat_completion(
        model="openai/gpt-3.5-turbo",
        messages=messages,
        system="You are a helpful assistant."
    )
    print(response)
  9. Disconnect a Gmail account

    main

    Use disconnect_account to remove a Gmail connection. It accepts a GmailDisconnectPayload.

    If a connection_id is provided, it attempts to delete that specific connection. If only a user_id is provided, it lists all Gmail connections for that user and attempts to delete them all. The function also clears any cached profiles associated with the affected user IDs.

    Returns a JSONResponse containing:

    • ok: boolean
    • disconnected: boolean (true if connections were removed)
    • removed_connection_ids: list of IDs that were successfully deleted
    • warnings: (optional) list of error messages encountered during the process.
    # Example usage (conceptual)
    from server.services.gmail.client import disconnect_account
    from server.models import GmailDisconnectPayload
    
    payload = GmailDisconnectPayload(user_id="user_123")
    response = disconnect_account(payload)
  10. Execute Gmail tools via Composio

    main

    The execute_gmail_tool function allows you to run specific Gmail operations (tools) through the Composio SDK.

    Parameters:

    • tool_name (str): The name of the Gmail tool to execute (e.g., GMAIL_GET_PROFILE).
    • composio_user_id (str): The ID of the user in Composio.
    • arguments (dict, optional): A dictionary of arguments for the tool. Note that user_id is automatically defaulted to "me" if not provided in the arguments.

    Returns:

    • A dictionary containing the normalized tool response.

    Errors:

    • Raises a RuntimeError if the tool invocation fails.
    # Example usage (conceptual)
    from server.services.gmail.client import execute_gmail_tool
    
    result = execute_gmail_tool(
        tool_name="GMAIL_GET_PROFILE",
        composio_user_id="user_123",
        arguments={"user_id": "me"}
    )
  11. Check Gmail connection status and profile

    main

    Use fetch_status to verify if a Gmail account is connected and to retrieve user information. It accepts a GmailStatusPayload which should include either a connection_request_id or a user_id.

    The function returns a JSONResponse with the following structure:

    • ok: boolean
    • connected: boolean (true if status is CONNECTED, SUCCESS, SUCCESSFUL, ACTIVE, or COMPLETED)
    • status: the connection status string
    • email: the extracted user email address
    • user_id: the identified user ID
    • profile: the user's profile data (fetched from Composio or retrieved from cache)
    • profile_source: indicates if the profile came from cache, fetched, or none.
    # Example usage (conceptual)
    from server.services.gmail.client import fetch_status
    from server.models import GmailStatusPayload
    
    payload = GmailStatusPayload(user_id="user_123")
    response = fetch_status(payload)
    # response contains connection status, email, and profile data