Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
92 changes: 90 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ jobs:
chmod 600 chatwoot.env
cp evolution.env.example evolution.env
chmod 600 evolution.env
cp tata.env.example tata.env
chmod 600 tata.env

# -----------------------------------------------------------------------
# 2. Pull images
Expand All @@ -61,7 +63,7 @@ jobs:
# unchanged layers on subsequent runs.
# -----------------------------------------------------------------------
- name: Pull images
run: docker compose pull postgres redis rails evolution-api
run: docker compose pull postgres redis rails evolution-api tata-agent

# -----------------------------------------------------------------------
# 3. Start stack (CI subset)
Expand All @@ -71,7 +73,7 @@ jobs:
# with rails causes a UniqueViolation crash loop on pg_stat_statements.
# -----------------------------------------------------------------------
- name: Start stack
run: docker compose up -d postgres redis rails evolution-api
run: docker compose up -d postgres redis rails evolution-api tata-agent

# -----------------------------------------------------------------------
# 4. Wait for Chatwoot to be ready
Expand Down Expand Up @@ -192,6 +194,91 @@ jobs:
puts "✔ Evolution API instance 'testco' created successfully"
RUBY

# -----------------------------------------------------------------------
# 8. Wait for Tata agent to be ready
#
# TCP check on port 8000 inside the Docker network.
# -----------------------------------------------------------------------
- name: Wait for Tata agent
timeout-minutes: 5
run: |
until docker exec chatwoot_rails ruby -e \
"require 'socket'; Socket.tcp('tata-agent', 8000, connect_timeout: 2) { }" \
2>/dev/null; do
echo "Waiting for Tata agent..."
sleep 5
done
echo "✔ Tata agent is ready"

# -----------------------------------------------------------------------
# 9. Test Chatwoot agent-bot API – create bot and reset access token
#
# Uses the Chatwoot Account API to:
# 1. Create an agent bot pointing to the tata-agent webhook endpoint.
# 2. Reset the bot access token and verify the response contains
# an "access_token" field.
# This proves the Chatwoot ↔ tata-agent integration is wired correctly
# without requiring a real OpenAI key or triggering an actual LLM call.
# -----------------------------------------------------------------------
- name: Test agent-bot API – create bot and reset token
run: |
docker exec -i chatwoot_rails bundle exec rails runner - <<'RUBY'
require 'net/http'
require 'json'

# Retrieve the first account (created in step 5)
account = Account.first
account_id = account.id

# Create a user token for account API access
user = account.users.first || begin
u = User.create!(
name: 'Bot Admin',
email: 'botadmin@example.org',
password: 'Password123!'
)
AccountUser.create!(account: account, user: u, role: :administrator)
u
end
api_token = user.access_token.token

# 1. Create an agent bot
uri = URI("http://localhost:3000/api/v1/accounts/#{account_id}/agent_bots")
req = Net::HTTP::Post.new(uri)
req['Content-Type'] = 'application/json'
req['api_access_token'] = api_token
req.body = {
name: 'Tata CI Bot',
description: 'CI test bot',
outgoing_url: 'http://tata-agent:8000/webhook',
bot_type: 'webhook',
bot_config: {}
}.to_json

res = Net::HTTP.start(uri.host, uri.port) { |h| h.request(req) }
abort("Create bot failed: HTTP #{res.code} — #{res.body}") \
unless res.code == '200'
bot = JSON.parse(res.body)
bot_id = bot['id']
puts "✔ Agent bot created (id=#{bot_id}, name=#{bot['name']})"

# 2. Reset access token
uri2 = URI("http://localhost:3000/api/v1/accounts/#{account_id}/agent_bots/#{bot_id}/reset_access_token")
req2 = Net::HTTP::Post.new(uri2)
req2['Content-Type'] = 'application/json'
req2['api_access_token'] = api_token

res2 = Net::HTTP.start(uri2.host, uri2.port) { |h| h.request(req2) }
abort("Reset token failed: HTTP #{res2.code} — #{res2.body}") \
unless res2.code == '200'
token_data = JSON.parse(res2.body)
abort('Response missing access_token field') \
unless token_data.key?('access_token') && !token_data['access_token'].to_s.empty?
puts "✔ Agent bot access token reset (token=#{token_data['access_token'][0..7]}...)"

puts "✔ All agent-bot API assertions passed"
RUBY

# -----------------------------------------------------------------------
# Debug: dump container logs on any failure
# -----------------------------------------------------------------------
Expand All @@ -202,6 +289,7 @@ jobs:
echo "=== chatwoot_postgres ===" && docker logs chatwoot_postgres 2>&1 | tail -20 || true
echo "=== chatwoot_redis ===" && docker logs chatwoot_redis 2>&1 | tail -20 || true
echo "=== evolution_api ===" && docker logs evolution_api 2>&1 | tail -50 || true
echo "=== tata_agent ===" && docker logs tata_agent 2>&1 | tail -50 || true

# -----------------------------------------------------------------------
# Tear down
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
.env
chatwoot.env
evolution.env
tata.env
acme.json

# ---------------------------------------------------------------------------
Expand Down
37 changes: 37 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
# • redis — cache and job queues
# • rails — Chatwoot Rails web server (runs DB migrations on start)
# • sidekiq — Chatwoot Sidekiq background worker
# • tata-agent — Tata AI agent bot (webhook receiver), exposed at bot.<DOMAIN>
# • evolution-api — Evolution API (WhatsApp gateway), exposed at evo.<DOMAIN>
#
# Prerequisites / first-time setup: see README.md
Expand Down Expand Up @@ -163,6 +164,42 @@ services:
command:
- "bundle exec rails db:chatwoot_prepare && exec bundle exec sidekiq -C config/sidekiq.yml"

# ---------------------------------------------------------------------------
# Tata Customer Agent – AI agent bot (webhook receiver for Chatwoot)
#
# Exposes the webhook endpoint at https://bot.<DOMAIN>/webhook via Traefik.
# Shares the existing Chatwoot postgres for vector + conversation storage.
# Copy tata.env.example → tata.env and fill in all values, then:
# 1. Create an agent bot in Chatwoot (see README – Bot setup)
# 2. Reset the bot access token and copy it to CHATWOOT_API_TOKEN in tata.env
# 3. docker compose restart tata-agent
# ---------------------------------------------------------------------------
tata-agent:
image: ghcr.io/santzit/tata-customer-agent:v0.1.1
container_name: tata_agent
restart: unless-stopped
networks:
- chatwoot-net
depends_on:
postgres:
condition: service_healthy
rails:
condition: service_started
env_file: tata.env
labels:
- "traefik.enable=true"
# HTTP → HTTPS redirect
- "traefik.http.routers.tata-http.rule=Host(`bot.${DOMAIN}`)"
- "traefik.http.routers.tata-http.entrypoints=web"
- "traefik.http.routers.tata-http.middlewares=redirect-https"
# HTTPS with Let's Encrypt TLS
- "traefik.http.routers.tata.rule=Host(`bot.${DOMAIN}`)"
- "traefik.http.routers.tata.entrypoints=websecure"
- "traefik.http.routers.tata.tls=true"
- "traefik.http.routers.tata.tls.certresolver=letsencrypt"
- "traefik.http.routers.tata.service=tata"
- "traefik.http.services.tata.loadbalancer.server.port=8000"

# ---------------------------------------------------------------------------
# Evolution API – WhatsApp gateway / messaging integration
#
Expand Down
79 changes: 79 additions & 0 deletions tata.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
# =============================================================================
# tata.env.example – Tata Customer Agent configuration (v0.2x)
#
# Copy to tata.env and fill in every value.
# Do NOT commit tata.env to version control.
#
# Reference: https://github.com/santzit/tata-customer-agent
#
# Workflow:
# 1. Start the stack (docker compose up -d)
# 2. Create a Chatwoot account and an agent bot (see README – Bot setup)
# 3. Reset the bot access token via the Chatwoot API
# 4. Fill in CHATWOOT_API_TOKEN and CHATWOOT_ACCOUNT_ID below
# 5. Restart the tata-agent container (docker compose restart tata-agent)
# =============================================================================

# ---------------------------------------------------------------------------
# OpenAI credentials
#
# Replace with your real key before going to production.
# OPENAI_API_KEY: https://platform.openai.com/api-keys
# ---------------------------------------------------------------------------
OPENAI_API_KEY=sk-placeholder-replace-with-real-key

# Chat model used for reply generation.
LLM_MODEL=gpt-4.1

# LLM provider: "openai" for openai.com, "azure" for Azure OpenAI.
LLM_PROVIDER=openai

# Azure OpenAI endpoint — leave blank when LLM_PROVIDER=openai.
OPENAI_API_ENDPOINT=

# Embedding model for the pgvector knowledge base.
EMBEDDING_MODEL_SMALL=text-embedding-3-small

# ---------------------------------------------------------------------------
# Database (PostgreSQL with pgvector extension)
#
# Shares the existing Chatwoot postgres container.
# The tata tables (PG_VECTOR_TABLE, PG_MEMORY_TABLE) are created automatically
# in the chatwoot_production database and do not conflict with Chatwoot tables.
#
# The password must match POSTGRES_PASSWORD in .env.
# ---------------------------------------------------------------------------
POSTGRES_DSN=postgresql://chatwoot:testpass@postgres:5432/chatwoot_production

# Table names — change only if they conflict with existing tables.
PG_VECTOR_TABLE=tata_knowledge
PG_MEMORY_TABLE=tata_conversations

# ---------------------------------------------------------------------------
# Chatwoot integration
#
# CHATWOOT_BASE_URL: Internal Docker service URL for the Rails container.
# Keep as "http://rails:3000" — do not use the public URL.
# CHATWOOT_API_TOKEN: Agent-bot access token obtained by resetting the bot
# token via POST /api/v1/accounts/:id/agent_bots/:id/reset_access_token
# CHATWOOT_ACCOUNT_ID: Numeric Chatwoot account ID (visible in the Chatwoot URL).
# ---------------------------------------------------------------------------
CHATWOOT_BASE_URL=http://rails:3000
CHATWOOT_API_TOKEN=changeme-agent-bot-access-token
CHATWOOT_ACCOUNT_ID=1

# ---------------------------------------------------------------------------
# Webhook security (optional)
#
# Set to the same value configured in the Chatwoot agent-bot outgoing URL
# to validate incoming webhook signatures. Leave blank to disable.
# ---------------------------------------------------------------------------
WEBHOOK_TOKEN=

# ---------------------------------------------------------------------------
# Bot behaviour
#
# RESPONSE_DELAY_SECONDS: seconds of conversation silence before the agent
# sends its reply (message-buffer debounce window). Default: 120.
# ---------------------------------------------------------------------------
RESPONSE_DELAY_SECONDS=120
Loading