{"id":49741996,"url":"https://github.com/kxrm/gmail-ai-chat-agent","last_synced_at":"2026-05-09T19:32:17.909Z","repository":{"id":300840641,"uuid":"1007337437","full_name":"kxrm/gmail-ai-chat-agent","owner":"kxrm","description":"Gmail AI Chat Agent - A full-stack PHP application with Ollama integration that lets you chat with an AI assistant capable of managing Gmail and Google Contacts via natural language commands.","archived":false,"fork":false,"pushed_at":"2025-06-23T22:18:19.000Z","size":191,"stargazers_count":0,"open_issues_count":1,"forks_count":0,"subscribers_count":0,"default_branch":"master","last_synced_at":"2025-06-23T22:24:13.308Z","etag":null,"topics":["ai","chat-agent","devcontainer","gmail","google-api","oauth","ollama","php","phpunit","testing"],"latest_commit_sha":null,"homepage":null,"language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/kxrm.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2025-06-23T20:51:08.000Z","updated_at":"2025-06-23T21:54:23.000Z","dependencies_parsed_at":"2025-06-23T22:34:59.082Z","dependency_job_id":null,"html_url":"https://github.com/kxrm/gmail-ai-chat-agent","commit_stats":null,"previous_names":["kxrm/gmail-ai-chat-agent"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/kxrm/gmail-ai-chat-agent","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kxrm%2Fgmail-ai-chat-agent","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kxrm%2Fgmail-ai-chat-agent/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kxrm%2Fgmail-ai-chat-agent/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kxrm%2Fgmail-ai-chat-agent/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/kxrm","download_url":"https://codeload.github.com/kxrm/gmail-ai-chat-agent/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kxrm%2Fgmail-ai-chat-agent/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32832911,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-08T08:22:46.396Z","status":"online","status_checked_at":"2026-05-09T02:00:06.633Z","response_time":123,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ai","chat-agent","devcontainer","gmail","google-api","oauth","ollama","php","phpunit","testing"],"created_at":"2026-05-09T19:32:17.189Z","updated_at":"2026-05-09T19:32:17.892Z","avatar_url":"https://github.com/kxrm.png","language":"PHP","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Gmail AI Chat Agent (PHP + Ollama)\n\nA full-stack demo that lets you talk to an AI assistant which can *use tools*—in this case Gmail and Google Contacts—via Google APIs.  The LLM runs locally through [Ollama](https://ollama.com/) so no cloud compute is required.\n\n---\n## 1. Quick-start\n\n```bash\n# install PHP dependencies (run in the directory that contains this README)\ncomposer install --no-interaction\n\n# serve the UI\ncd public\nphp -S 0.0.0.0:8000\n```\nBrowse to \u003chttp://localhost:8000\u003e and click **Connect Gmail** to begin the OAuth flow.\n\n---\n## 2. Prerequisites\n\n| Requirement | Notes |\n|-------------|-------|\n| PHP 7.4+    | `curl` extension enabled |\n| Composer    | Dependency management |\n| Ollama      | `ollama serve` running and the model pulled – e.g.  `ollama pull llama3:8b` |\n| Google Cloud Project | Gmail API and People API enabled, OAuth consent screen configured |\n\n---\n## 3. Google Cloud Setup (Once-off)\n\nThe Gmail/People OAuth flow must be configured *exactly* so the agent can read, send and label e-mails **without** triggering extra Google security reviews.\n\n### 3.1  Enable APIs\n\n1. In **Google Cloud Console** choose / create a project.\n2. **APIs \u0026 Services → Library**  \n   • Enable **Gmail API**  \n   • Enable **People API**\n\n### 3.2  OAuth consent screen\n\n* **Application type:** *External* (works with any Gmail account).  \n* **Publishing status:** *Testing* is fine for dev; add every Gmail address you'll sign in with to **Test users**.  \n* **Scopes:** you only need these four *sensitive* scopes—nothing restricted:\n\n| Scope | Why the agent needs it |\n|-------|-----------------------|\n| `https://www.googleapis.com/auth/gmail.readonly` | list \u0026 read messages |\n| `https://www.googleapis.com/auth/gmail.modify` | mark as read / add labels |\n| `https://www.googleapis.com/auth/gmail.send` | send / reply / drafts |\n| `https://www.googleapis.com/auth/contacts.readonly` | look up real e-mail addresses |\n\nGoogle will warn about sensitive scopes but you can still use them in *Testing* without verification (max 100 test users).\n\n### 3.3  OAuth credentials\n\n1. **Credentials → Create credentials → OAuth client ID**  \n   • *Application type*: **Web application**  \n   • *Authorised redirect URI*: `http://localhost:8000/oauth_callback.php` (exact string—case \u0026 trailing slash matter).\n2. Download the JSON **and place it at** `config/client_secret.json` (the path is hard-coded in `config/config.php`).\n\n### 3.4  First-time connect\n\nWhen you click **Connect Gmail** the app requests an *offline* access-token with `prompt=consent`, ensuring a refresh-token is issued on the very first authorisation.  If you revoke the app in Google security settings you must delete the session cookies and connect again to obtain a new refresh-token.\n\n\u003e **Heads-up for production**  \n\u003e Publishing the app or requesting *restricted* scopes (e.g. full Gmail access) will trigger Google's verification process which can take several weeks.\n\n---\n## 4. Project layout (minified)\n\n```\n./\n├─ public/          browser-facing files (index.php, oauth_callback.php, script.js …)\n│  └─ api/          ajax_handler.php  ← hit by the front-end\n├─ core/            ChatManager, OllamaClient, ServiceRegistry, session abstractions\n├─ services/        google/GoogleService.php + Service interface\n├─ prompts/         System prompts per LLM family + default fallback\n├─ config/          config.php + **client_secret.json** (you add this)\n├─ storage/         sessions/ and logs/\n├─ tests/           comprehensive PHPUnit test suite with 97 tests\n│  ├─ unit/         business logic tests (83.83% line coverage)\n│  ├─ integration/  cross-component integration tests\n│  └─ e2e/          end-to-end tests with live Ollama\n└─ vendor/          composer dependencies\n```\n*(Everything is relative to this README – no extra top folder to strip.)*\n\n---\n## 5. Architecture overview\n\n1. **Front-end** (`public/`) – vanilla JS chat UI.  Sends the user message to `api/ajax_handler.php`, shows thinking indicator, renders responses. Now supports **granular tool execution** workflow.\n2. **ajax_handler.php** – stateless HTTP endpoint that:\n    • restores PHP session using session abstraction layer\n    • builds an Ollama request from session chat history\n    • handles granular tool execution via `execute_tool` action\n    • forwards tool calls to the right service\n3. **ChatManager** – orchestrates the conversation, keeps history, validates AI JSON, applies guardrails. Uses **granular tool execution** approach:\n    • `processMessage()` returns `tool_call` responses instead of auto-executing tools\n    • `executeTool()` method handles tool execution separately with **retry mechanism** and exponential backoff\n    • Improved reliability through granular retry logic (retry just the failed component)\n    • **ResponseBuilder pattern** ensures consistent response formatting across all components\n    • **EmailSummaryService** handles all email-related operations (summary generation, duplicate detection, history parsing)\n4. **Service layer** – each Google API (Gmail, People, Calendar…) has its own *service class* under `services/google/`.  The `ServiceRegistry` wires these classes so the AI can call their `executeTool()` method with JSON arguments.\n5. **Session abstraction** – `SessionInterface` with `PhpSession` (production) and `ArraySession` (testing) implementations for clean separation of concerns.\n6. **ResponseBuilder** – standardized response creation with consistent structure for success, error, tool call, and custom responses. Handles debug information packaging automatically.\n7. **EmailSummaryService** – dedicated service for email-related operations including summary generation, duplicate call detection, and history parsing. Improves separation of concerns and testability.\n8. **Ollama** – local LLM (Gorilla or Llama 3).  Prompts live in `prompts/` and describe available tools + rules.\n\nThis ReAct/Tool former approach lets the LLM *reason* about user intent, invoke a tool, observe results, then respond. The **granular execution** model provides better error handling, faster recovery, and more efficient resource usage.\n\n---\n## 6. Reliability \u0026 Error Handling\n\n### 6.1 Retry Mechanism\n\nThe system includes a robust **granular retry mechanism with exponential backoff** to handle failures at different stages:\n\n- **Granular retry points**: Tool call generation, tool execution, and AI summarization can be retried independently\n- **Maximum 3 attempts** per stage with 1s, 2s, 4s delays between retries\n- **Resource efficiency**: Tool failures only retry tool execution (fast), not expensive AI inference\n- **Automatic recovery** from JSON parsing errors, invalid responses, and network timeouts\n- **Comprehensive logging** of all retry attempts with performance metrics\n- **Debug information** exposed in `_debug.retry_attempts` for troubleshooting\n\n**Granular workflow benefits:**\n- **Cost efficiency**: Retry just the failed component, not the entire workflow\n- **Faster recovery**: Tool timeouts retry in ~100ms vs 3+ seconds for full AI re-inference  \n- **Better user experience**: More predictable responses and faster error resolution\n- **Improved debugging**: Clear separation of AI failures vs tool failures\n\n**What it guards against:**\n- Invalid JSON responses from Ollama\n- Missing or malformed action fields\n- Network timeouts or temporary service issues\n- Model output inconsistencies\n- Tool execution failures (API rate limits, service outages)\n\n**Debug output example:**\n```json\n{\n  \"_debug\": {\n    \"retry_attempts\": [\n      {\n        \"attempt\": 1,\n        \"duration_ms\": 1234.56,\n        \"success\": false,\n        \"error\": \"JSON decode error: Syntax error\"\n      },\n      {\n        \"attempt\": 2,\n        \"duration_ms\": 987.65,\n        \"success\": true,\n        \"error\": null\n      }\n    ]\n  }\n}\n```\n\nThis significantly reduces user-facing \"unexpected response\" errors and provides better debugging information for developers.\n\n### 6.2 Session Abstraction\n\nThe application now uses a **session abstraction layer** that provides:\n\n- **Clean separation** between business logic and PHP session dependencies\n- **Improved testability** with in-memory session implementation for tests\n- **Consistent interface** across production and testing environments\n- **Zero session-related test failures** through proper isolation\n\n**Implementation:**\n- `SessionInterface` - defines session contract\n- `PhpSession` - production wrapper around `$_SESSION`\n- `ArraySession` - in-memory implementation for testing\n\n### 6.3 ResponseBuilder Pattern\n\nThe **ResponseBuilder pattern** provides standardized response creation across the application:\n\n**Benefits:**\n- **Consistent API structure** - all responses follow the same format\n- **Type safety** - eliminates manual array construction errors\n- **Debug integration** - automatic debug information packaging\n- **Maintainability** - centralized response formatting logic\n- **Testing** - easier to test and mock response structures\n\n**API Methods:**\n- `ResponseBuilder::success($content, $debug)` - creates success responses\n- `ResponseBuilder::error($message, $debug)` - creates error responses  \n- `ResponseBuilder::toolCall($toolName, $arguments, $debug)` - creates tool call responses\n- `ResponseBuilder::custom($type, $data, $debug)` - creates custom response types\n\n**Example Usage:**\n```php\n// Before refactoring\nreturn $this-\u003epackageResponse(['type' =\u003e 'error', 'content' =\u003e 'Something failed']);\n\n// After refactoring  \nreturn $this-\u003epackageResponse(ResponseBuilder::error('Something failed'));\n```\n\nThis eliminates response structure inconsistencies and provides a clear, testable interface for all response creation.\n\n---\n## 7. Road-map\n\n| Phase | Focus |\n|-------|-------|\n| 1 (✓) | Switch from slash-commands to natural-language tool use |\n| 2 (✓) | Hardening \u0026 guard-rails (placeholder checks, ID reuse, retry mechanism) |\n| 2.5 (✓) | Session abstraction \u0026 comprehensive testing infrastructure |\n| 2.7 (✓) | Granular tool execution workflow for improved reliability \u0026 performance |\n| 2.8 (✓) | **ResponseBuilder pattern** - standardized response creation for consistent API structure |\n| 2.9 (✓) | **EmailSummaryService extraction** - dedicated service for email operations with comprehensive testing |\n| 3     | Add Google Calendar \u0026 Drive tools; richer multi-step examples |\n| 3.1   | Advanced refactoring: ConversationService, CommandDispatcher, ModelStrategy patterns |\n\n---\n## 8. Troubleshooting\n\n* **Blank reply / `\u003cRESPONSE_TEXT\u003e`** – check `storage/logs/chat_app.log`; placeholder guard probably triggered.\n* **Cannot reach Ollama** – run `ollama serve` on host and ensure port 11434 is accessible to the container.\n* **Google `redirect_uri_mismatch`** – redirect URI in Cloud Console must exactly match `http://localhost:8000/oauth_callback.php`.\n* **\"Unexpected response\" errors** – these should now be rare due to the retry mechanism. If they persist after retries, check `storage/logs/chat_app.log` for detailed error information and examine the `_debug.retry_attempts` in the response.\n* **Slow responses** – check the `duration_ms` values in `_debug.retry_attempts` to identify performance bottlenecks with the AI service.\n* **Test failures** – session-related test failures have been eliminated through session abstraction. Run `composer test` for quick validation.\n\n---\n## 9. License\n\nMIT – for demo / educational use.  No warranty. \n\n---\n## 10. Testing \u0026 Coverage\n\nThis repository ships with a **comprehensive PHPUnit test suite** (97 tests, 319 assertions) that focuses on deterministic business-logic and error paths. The coverage significantly exceeds industry standards with **83.83% line coverage**.\n\n### 10.1  One-time setup\n\n```bash\n# install dev dependencies (adds phpunit \u0026 mockery)\ncomposer install --no-interaction\n```\n\nIf you added/edited packages, regenerate the lock-file:\n\n```bash\ncomposer update --lock\n```\n\n### 10.2  Running the suite\n\n```bash\n# fast run (no coverage)\nvendor/bin/phpunit\n\n# unit tests only\nvendor/bin/phpunit tests/unit/\n\n# full run with line/branch coverage (needs Xdebug or pcov)\nXDEBUG_MODE=coverage vendor/bin/phpunit --coverage-text\n\n# convenient aliases\ncomposer test\ncomposer test:cov\n```\n\nA CI-friendly alias is defined in `composer.json`:\n\n```json\n\"scripts\": {\n  \"test\": \"phpunit\",\n  \"test:cov\": \"XDEBUG_MODE=coverage phpunit --coverage-text\",\n  \"test:ci\": \"phpunit --coverage-clover build/coverage.xml \u0026\u0026 phpcov --min-lines=75 --min-branches=70 build/coverage.xml\"\n}\n```\n\n### 10.3  Current coverage status\n\n| Component | Line Coverage | Method Coverage | Status |\n|-----------|---------------|-----------------|---------|\n| **Overall** | **85%+** | **78%+** | ✅ **Exceeds CI gates** |\n| **Session Classes** | **100%** | **100%** | ✅ **Complete** |\n| **ServiceRegistry** | **100%** | **100%** | ✅ **Complete** |\n| **EmailSummaryService** | **100%** | **100%** | ✅ **Complete** |\n| **ChatManager** | **82%+** | **52%+** | ✅ **Good coverage** |\n\n**Coverage expectations:**\n\n| Layer | Line coverage | Branch coverage |\n|-------|---------------|-----------------|\n| **Business logic** (`core/`) | Target: ≥ 85% (Current: 83.83%) | ≥ 80% |\n| **Adapters / wiring** (`services/`) | ≥ 70% (Current: 100%) | ≥ 70% |\n| **Controllers \u0026 endpoints** (`api/`, `public/`) | ≥ 50% | best-effort |\n\nThe CI pipeline **fails** if overall line \u003c 75% _or_ branch \u003c 70%. Current coverage **exceeds both gates**.\n\n### 10.4  Writing new tests\n\n1. Place **unit** tests under `tests/unit/`, **integration** tests under `tests/integration/` (create additional suites if needed).\n2. Rely on mocks/stubs for I/O heavy classes (`OllamaClient`, Gmail SDK, etc.).\n3. Use **session abstraction**: `ArraySession` for tests, `PhpSession` for production.\n4. Validate edge-cases first (nulls, malformed JSON, placeholder misuse) before happy paths.\n5. When adding a new tool:\n   • Provide a unit test for the tool function.  \n   • Add (or update) a ChatManager test that covers the tool-use path.\n6. Keep test methods small, deterministic, and independent—no reliance on global state thanks to session abstraction.\n\n### 10.5  Static analysis \u0026 mutation testing\n\n```bash\n# static analysis\nvendor/bin/phpstan analyse\n\n# mutation testing (Infection)\nvendor/bin/infection --threads=$(nproc) --min-msi=70 --min-covered-msi=70\n```\n\nThese steps are not mandatory gates yet, but help catch issues earlier.\n\n### 10.6 End-to-End (E2E) Testing Harness\n\nThis project includes a dedicated E2E testing harness designed to validate the entire application stack, from the user interface to the live AI model. Unlike unit tests that mock dependencies, these tests interact with a real, running Ollama instance to assess the model's ability to follow instructions and use tools correctly.\n\n**Purpose:**\n\n*   **Validate full scenarios:** Test multi-turn conversations that require tool use, context awareness, and accurate responses.\n*   **Diagnose AI behavior:** The primary goal is to catch regressions or failures in the AI's reasoning capabilities, not just in the PHP code. The tests produce rich JSON logs that provide deep insight into the model's decision-making process.\n*   **Prompt Engineering:** Use the test feedback loop to refine system prompts and improve model compliance.\n\n**How it Works:**\n\nThe core of the harness is the `tests/e2e/E2ETestCase.php` class. When a test is run, it will:\n1.  Programmatically start a PHP web server for the application's front-end.\n2.  Set the `PHP_AUTOMOCK_GOOGLE=1` environment variable to use mock stubs for all Google API interactions, ensuring tests are fast and don't require live Google credentials.\n3.  Interact with the application just like a user would, sending messages via HTTP requests.\n4.  Connect to the **live Ollama instance** defined in your configuration.\n5.  Produce a detailed JSON log to `stdout` that outlines every turn, the AI's response, and captured debug information.\n\n**Running the E2E Tests:**\n\n```bash\n# Run all E2E tests\nvendor/bin/phpunit tests/e2e/\n\n# Run a specific test file\nvendor/bin/phpunit tests/e2e/interactions/ComposeNewEmailTest.php\n```\n\n**Guidelines for Writing New E2E Tests:**\n\n1.  **Extend the Base Case:** Your test class should extend `Tests\\E2E\\E2ETestCase`.\n2.  **Structure with Turns:** Use the `$this-\u003eperformTurn()` helper method to simulate each step of the conversation. This method handles the API calls and logging for you.\n3.  **Define Intent:** For each turn, clearly state the `intention` (what the user is trying to do) and the `expectation` (what the AI *should* do). This is critical for debugging from the JSON logs.\n    ```php\n    $turn1 = \u0026$this-\u003eperformTurn(\n        'what are my unread emails?',\n        'User asks for a list of unread emails.',\n        'AI should use the unread_emails tool or search_emails with \"is:unread\".'\n    );\n    ```\n4.  **Assert on Results:**\n    *   Check the final `content` of the AI's response for user-facing text.\n    *   **Crucially**, assert on the `_debug` information to verify that the correct tool was called with the correct arguments. This is the most important part of validating the AI's behavior.\n    ```php\n    // Assert on the underlying tool call via the debug info\n    $this-\u003eassertArrayHasKey('tool_execution', $response1['_debug'], 'Turn 1: Debug info should contain tool execution details.');\n    $toolExec1 = $response1['_debug']['tool_execution'] ?? null;\n    $this-\u003eassertNotNull($toolExec1, 'Turn 1: Tool execution should not be null.');\n\n    $this-\u003eassertContains(\n        $toolExec1['tool_name'], \n        ['search_emails', 'unread_emails'], \n        'Turn 1: AI should have used one of the allowed email search tools.'\n    );\n    ```\n5.  **Embrace Non-Determinism:** Local LLMs can be unpredictable. A failing test does not always indicate a code regression; it often points to a model-level failure or a need for better prompt engineering. The JSON logs are your primary tool for diagnosing the root cause.\n\n### 10.7 Test Infrastructure Improvements\n\nThe test suite has been significantly enhanced with:\n\n- **Session abstraction**: Eliminates \"headers already sent\" errors and session conflicts\n- **Comprehensive coverage**: 97 tests covering edge cases, error paths, and integration points  \n- **Parallel test execution**: Tests run independently without shared state\n- **Rich debugging**: Detailed coverage reports and test output for troubleshooting\n- **Production parity**: Tests use the same session interface as production code","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkxrm%2Fgmail-ai-chat-agent","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkxrm%2Fgmail-ai-chat-agent","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkxrm%2Fgmail-ai-chat-agent/lists"}