diff --git a/.icons/perplexica.svg b/.icons/perplexica.svg new file mode 100644 index 000000000..0b3a85b5e --- /dev/null +++ b/.icons/perplexica.svg @@ -0,0 +1,8 @@ + + + + + diff --git a/registry/coder-labs/.images/perplexica.png b/registry/coder-labs/.images/perplexica.png new file mode 100644 index 000000000..840849377 Binary files /dev/null and b/registry/coder-labs/.images/perplexica.png differ diff --git a/registry/coder-labs/modules/perplexica/README.md b/registry/coder-labs/modules/perplexica/README.md new file mode 100644 index 000000000..d1e37299d --- /dev/null +++ b/registry/coder-labs/modules/perplexica/README.md @@ -0,0 +1,55 @@ +--- +display_name: Perplexica +description: Run Perplexica AI search engine in your workspace via Docker +icon: ../../../../.icons/perplexica.svg +verified: false +tags: [ai, search, docker] +--- + +# Perplexica + +Run [Perplexica](https://github.com/ItzCrazyKns/Perplexica), a privacy-focused AI search engine, in your Coder workspace. Supports cloud providers (OpenAI, Anthropic Claude) and local LLMs via Ollama. + +```tf +module "perplexica" { + count = data.coder_workspace.me.start_count + source = "registry.coder.com/coder-labs/perplexica/coder" + version = "1.0.0" + agent_id = coder_agent.main.id +} +``` + +This module uses the full Perplexica image with embedded SearXNG for simpler setup with no external dependencies. + +![Perplexica](../../.images/perplexica.png) + +## Prerequisites + +This module requires Docker to be available on the host. + +## Examples + +### With API Keys + +```tf +module "perplexica" { + count = data.coder_workspace.me.start_count + source = "registry.coder.com/coder-labs/perplexica/coder" + version = "1.0.0" + agent_id = coder_agent.main.id + openai_api_key = var.openai_api_key + anthropic_api_key = var.anthropic_api_key +} +``` + +### With Local Ollama + +```tf +module "perplexica" { + count = data.coder_workspace.me.start_count + source = "registry.coder.com/coder-labs/perplexica/coder" + version = "1.0.0" + agent_id = coder_agent.main.id + ollama_api_url = "http://ollama-external-endpoint:11434" +} +``` diff --git a/registry/coder-labs/modules/perplexica/main.tf b/registry/coder-labs/modules/perplexica/main.tf new file mode 100644 index 000000000..4a7aa5c81 --- /dev/null +++ b/registry/coder-labs/modules/perplexica/main.tf @@ -0,0 +1,108 @@ +terraform { + required_version = ">= 1.0" + + required_providers { + coder = { + source = "coder/coder" + version = ">= 2.5" + } + } +} + +variable "agent_id" { + type = string + description = "The ID of a Coder agent." +} + +variable "docker_socket" { + type = string + description = "(Optional) Docker socket URI" + default = "" +} + +variable "port" { + type = number + description = "The port to run Perplexica on." + default = 3000 +} + +variable "data_path" { + type = string + description = "Host path to mount for Perplexica data persistence." + default = "./perplexica-data" +} + +variable "uploads_path" { + type = string + description = "Host path to mount for Perplexica file uploads." + default = "./perplexica-uploads" +} + +variable "openai_api_key" { + type = string + description = "OpenAI API key." + default = "" + sensitive = true +} + +variable "anthropic_api_key" { + type = string + description = "Anthropic API key for Claude models." + default = "" + sensitive = true +} + +variable "ollama_api_url" { + type = string + description = "Ollama API URL for local LLM support." + default = "" +} + +variable "share" { + type = string + default = "owner" + validation { + condition = var.share == "owner" || var.share == "authenticated" || var.share == "public" + error_message = "Incorrect value. Please set either 'owner', 'authenticated', or 'public'." + } +} + +variable "order" { + type = number + description = "The order determines the position of app in the UI presentation. The lowest order is shown first and apps with equal order are sorted by name (ascending order)." + default = null +} + +variable "group" { + type = string + description = "The name of a group that this app belongs to." + default = null +} + +resource "coder_script" "perplexica" { + agent_id = var.agent_id + display_name = "Perplexica" + icon = "/icon/perplexica.svg" + script = templatefile("${path.module}/run.sh", { + DOCKER_HOST : var.docker_socket, + PORT : var.port, + DATA_PATH : var.data_path, + UPLOADS_PATH : var.uploads_path, + OPENAI_API_KEY : var.openai_api_key, + ANTHROPIC_API_KEY : var.anthropic_api_key, + OLLAMA_API_URL : var.ollama_api_url, + }) + run_on_start = true +} + +resource "coder_app" "perplexica" { + agent_id = var.agent_id + slug = "perplexica" + display_name = "Perplexica" + url = "http://localhost:${var.port}" + icon = "/icon/perplexica.svg" + subdomain = true + share = var.share + order = var.order + group = var.group +} diff --git a/registry/coder-labs/modules/perplexica/perplexica.tftest.hcl b/registry/coder-labs/modules/perplexica/perplexica.tftest.hcl new file mode 100644 index 000000000..3e572cdf0 --- /dev/null +++ b/registry/coder-labs/modules/perplexica/perplexica.tftest.hcl @@ -0,0 +1,26 @@ +run "plan_basic" { + command = plan + + variables { + agent_id = "test-agent" + } + + assert { + condition = resource.coder_app.perplexica.url == "http://localhost:3000" + error_message = "Default port should be 3000" + } +} + +run "plan_custom_port" { + command = plan + + variables { + agent_id = "test-agent" + port = 8080 + } + + assert { + condition = resource.coder_app.perplexica.url == "http://localhost:8080" + error_message = "Should use custom port" + } +} diff --git a/registry/coder-labs/modules/perplexica/run.sh b/registry/coder-labs/modules/perplexica/run.sh new file mode 100755 index 000000000..ba12bbd1b --- /dev/null +++ b/registry/coder-labs/modules/perplexica/run.sh @@ -0,0 +1,67 @@ +#!/usr/bin/env sh + +set -eu + +BOLD='\033[0;1m' +RESET='\033[0m' + +printf "$${BOLD}Starting Perplexica...$${RESET}\n" + +# Set Docker host if provided +if [ -n "${DOCKER_HOST}" ]; then + export DOCKER_HOST="${DOCKER_HOST}" +fi + +# Wait for docker to become ready +max_attempts=10 +delay=2 +attempt=1 + +while ! docker ps; do + if [ $attempt -ge $max_attempts ]; then + echo "Failed to list containers after $${max_attempts} attempts." + exit 1 + fi + echo "Attempt $${attempt} failed, retrying in $${delay}s..." + sleep $delay + attempt=$(expr "$attempt" + 1) + delay=$(expr "$delay" \* 2) +done + +# Pull the image +IMAGE="itzcrazykns1337/perplexica:latest" +docker pull "$${IMAGE}" + +# Build docker run command +DOCKER_ARGS="-d --rm --name perplexica -p ${PORT}:3000" + +# Add mounts - convert relative paths to absolute +DATA_PATH="${DATA_PATH}" +UPLOADS_PATH="${UPLOADS_PATH}" + +mkdir -p "$${DATA_PATH}" +mkdir -p "$${UPLOADS_PATH}" + +DATA_PATH_ABS=$(cd "$${DATA_PATH}" && pwd) +UPLOADS_PATH_ABS=$(cd "$${UPLOADS_PATH}" && pwd) + +DOCKER_ARGS="$${DOCKER_ARGS} -v $${DATA_PATH_ABS}:/home/perplexica/data" +DOCKER_ARGS="$${DOCKER_ARGS} -v $${UPLOADS_PATH_ABS}:/home/perplexica/uploads" + +# Add environment variables if provided +if [ -n "${OPENAI_API_KEY}" ]; then + DOCKER_ARGS="$${DOCKER_ARGS} -e OPENAI_API_KEY=${OPENAI_API_KEY}" +fi + +if [ -n "${ANTHROPIC_API_KEY}" ]; then + DOCKER_ARGS="$${DOCKER_ARGS} -e ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY}" +fi + +if [ -n "${OLLAMA_API_URL}" ]; then + DOCKER_ARGS="$${DOCKER_ARGS} -e OLLAMA_API_URL=${OLLAMA_API_URL}" +fi + +# Run container +docker run $${DOCKER_ARGS} "$${IMAGE}" + +printf "\n$${BOLD}Perplexica is running on port ${PORT}$${RESET}\n"