Homer is a platform-agnostic, multi-agent text service written in Go.
CI status:
- GitHub Actions workflow:
.github/workflows/go-ci.yml(runsgo vetandgo teston push/PR)
summarize: summarizes one or more documentsrewrite: rewrites text in a selected mode- Uses explicit agent orchestration: Planner -> Executor -> Critic (optional)
- Exposes HTTP API endpoints:
GET /api/healthGET /metricsGET /api/capabilitiesGET /api/connectors/google_docs/auth/startGET /api/connectors/google_docs/auth/callbackPOST /api/connectors/importPOST /api/connectors/exportPOST /api/task
Client (web/CLI/Docs/etc.)
|
v
POST /api/task
|
v
Planner -> Executor -> (Optional) Critic
|
v
Provider (mock, OpenAI, or Gemini)
Connector implementations live in backend/internal/connectors:
Connectorinterface defines import/export operationsNoopConnectorkeeps core API independent from integrationsGoogleDocsConnectorperforms Google Docs import/export through Google Docs API
Current status:
- Core
/api/taskflow does not require any connector and works independently - Connector routes use structured errors when connector credentials are missing or invalid
- Google Docs connector supports OAuth authorization-code flow with in-memory session token storage
POST /api/task
{
"task": "summarize",
"documents": [{ "id": "d1", "title": "Doc", "content": "Text" }],
"text": "",
"mode": "professional",
"instructions": "Focus on action items",
"style": "paragraph",
"enableCritic": false
}Notes:
taskmust besummarizeorrewritedocumentsrequired forsummarizetextrequired forrewrite
Validation and runtime errors return:
{
"error": {
"code": "missing_text",
"message": "text is required for rewrite",
"requestId": "4e11fe43-e81c-40e8-b5cf-f9d4f0a65fe6"
}
}Connector routes may additionally return:
403 connector_forbidden404 connector_document_not_found429 connector_rate_limited502 connector_upstream_unauthorized503 connector_service_unavailable
OAuth callback routes may additionally return:
400 oauth_access_denied400 invalid_oauth_state502 oauth_exchange_failed
- OpenAPI:
backend/openapi.yaml
- Prometheus-compatible metrics endpoint:
GET /metrics - Provider metrics:
homer_provider_requests_totalhomer_provider_request_duration_seconds
- Connector metrics:
homer_connector_requests_totalhomer_connector_request_duration_seconds
- Provider and connector operation logs include
request_idfor request correlation.
Capabilities:
curl -sS http://localhost:8080/api/capabilitiesMetrics:
curl -sS http://localhost:8080/metricsSummarize:
curl -sS -X POST http://localhost:8080/api/task \
-H "Content-Type: application/json" \
-d '{
"task":"summarize",
"documents":[{"id":"d1","title":"Doc","content":"Launch is planned for Q1."}],
"style":"paragraph",
"instructions":"Focus on milestones"
}'Rewrite:
curl -sS -X POST http://localhost:8080/api/task \
-H "Content-Type: application/json" \
-d '{
"task":"rewrite",
"documents":[],
"text":"We will utilize the system to optimize efficiency.",
"mode":"simplify",
"instructions":"Keep it short"
}'Connector import (requires CONNECTOR_PROVIDER=google_docs and credentials):
curl -sS -X POST http://localhost:8080/api/connectors/import \
-H "Content-Type: application/json" \
-H "X-Connector-Key: ${CONNECTOR_API_KEY}" \
-H "X-Connector-Session: ${CONNECTOR_SESSION_KEY}" \
-d '{
"documentId":"doc-123"
}'Google Docs OAuth start:
curl -sS http://localhost:8080/api/connectors/google_docs/auth/startGoogle Docs OAuth callback (state and code from provider redirect):
curl -sS "http://localhost:8080/api/connectors/google_docs/auth/callback?state=${STATE}&code=${CODE}"Copy .env.example values into your shell/session:
PORT(default8080)LLM_PROVIDER(mock,openai, orgemini)LLM_TIMEOUT_MS(outbound LLM call timeout in ms; default15000)LLM_MAX_RETRIES(bounded retry count per outbound LLM call; default2, max5)OPENAI_API_KEY(required when provider isopenai)OPENAI_MODEL(defaultgpt-4o-mini)GEMINI_API_KEYorGOOGLE_API_KEY(required when provider isgemini)GEMINI_MODEL(defaultgemini-2.5-flash)CONNECTOR_PROVIDER(noneorgoogle_docs; defaultnone)CONNECTOR_API_KEY(optional; when set, required for connector import/export routes)CONNECTOR_RATE_LIMIT_PER_MINUTE(connector route request cap per minute; default60, set0to disable)GOOGLE_DOCS_ACCESS_TOKEN(recommended for local dev connector calls)GOOGLE_APPLICATION_CREDENTIALS(alternative service account credentials file path)GOOGLE_OAUTH_CLIENT_ID(required for OAuth authorization-code flow)GOOGLE_OAUTH_CLIENT_SECRET(required for OAuth authorization-code flow)GOOGLE_OAUTH_REDIRECT_URL(required for OAuth authorization-code flow callback)GOOGLE_OAUTH_SCOPES(optional comma/space separated scopes; defaults to Google Docs scope)GOOGLE_OAUTH_STATE_TTL(optional OAuth state lifetime, default10m)
Example Gemini setup:
export LLM_PROVIDER=gemini
export GEMINI_API_KEY=your_api_key
export GEMINI_MODEL=gemini-2.5-flashcd backend
go run ./cmd/serverRun the platform-agnostic CLI against a local or remote Homer backend:
cd backend
go run ./cmd/cli --base-url http://localhost:8080 healthCommon commands:
# Runtime info
go run ./cmd/cli --base-url http://localhost:8080 capabilities
# Summarize
go run ./cmd/cli --base-url http://localhost:8080 summarize \
-id doc-1 \
-title "Meeting Notes" \
-content "Launch is planned for Q1 and hiring starts next week." \
-style bullet
# Rewrite
go run ./cmd/cli --base-url http://localhost:8080 rewrite \
-text "We will utilize the platform to optimize efficiency." \
-mode simplify
# Connector import/export (when connector routes are enabled)
go run ./cmd/cli --base-url http://localhost:8080 \
--connector-key "$CONNECTOR_API_KEY" \
--connector-session "$CONNECTOR_SESSION_KEY" \
connector-import -document-id doc-123
go run ./cmd/cli --base-url http://localhost:8080 \
--connector-key "$CONNECTOR_API_KEY" \
--connector-session "$CONNECTOR_SESSION_KEY" \
connector-export -document-id doc-123 -content "Updated content"CLI environment variables:
HOMER_BASE_URL(default API base URL for CLI)HOMER_AUTH_TOKEN(Bearer token forAuthorization)HOMER_CONNECTOR_KEY(defaultX-Connector-Key)HOMER_CONNECTOR_SESSION(defaultX-Connector-Session)
Build:
docker build -f backend/Dockerfile -t homer-backend:local .Run with compose:
docker compose up --buildDeployment script:
scripts/gcp/deploy_cloud_run.sh
Runtime env template:
deploy/cloudrun.env.template
IAM and API prerequisites:
- Enable APIs: Cloud Run, Artifact Registry, IAM.
- Deploy identity roles:
roles/run.adminroles/artifactregistry.writerroles/iam.serviceAccountUser(for the runtime service account)
- Runtime service account should have access to any secrets/APIs required by your selected provider mode.
One-command deploy:
GCP_PROJECT_ID=my-project \
GCP_REGION=us-central1 \
CLOUD_RUN_SERVICE=homer-backend \
IMAGE_REPO=homer \
CLOUD_RUN_ENV_FILE=deploy/cloudrun.env.template \
./scripts/gcp/deploy_cloud_run.shPost-deploy smoke checks:
SERVICE_URL="$(gcloud run services describe homer-backend --project my-project --region us-central1 --format='value(status.url)')"
curl -fsS "${SERVICE_URL}/api/health"
curl -fsS "${SERVICE_URL}/api/capabilities"Runtime config matrix:
| Mode | Required runtime env vars | Notes |
|---|---|---|
| Mock only | LLM_PROVIDER=mock, CONNECTOR_PROVIDER=none |
Fastest smoke-test mode |
| OpenAI no connector | LLM_PROVIDER=openai, OPENAI_API_KEY, CONNECTOR_PROVIDER=none |
Set optional OPENAI_MODEL |
| Gemini no connector | LLM_PROVIDER=gemini, GEMINI_API_KEY (or GOOGLE_API_KEY), CONNECTOR_PROVIDER=none |
Set optional GEMINI_MODEL |
| Google Docs via env token | CONNECTOR_PROVIDER=google_docs, GOOGLE_DOCS_ACCESS_TOKEN |
Good for quick non-user OAuth testing |
| Google Docs via OAuth | CONNECTOR_PROVIDER=google_docs, GOOGLE_OAUTH_CLIENT_ID, GOOGLE_OAUTH_CLIENT_SECRET, GOOGLE_OAUTH_REDIRECT_URL |
Use /api/connectors/google_docs/auth/start and callback flow |
cd backend
go test ./...backend/
cmd/cli/main.go
cmd/server/main.go
internal/api/
internal/agents/
internal/cli/
internal/connectors/
internal/domain/
internal/llm/
internal/middleware/
deploy/
cloudrun.env.template
scripts/gcp/
deploy_cloud_run.sh