Skip to content
Open
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
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ Launch requirements:
- Return each chat turn as one complete JSON response; do not stream partial output.
- Preserve conversation context across follow-up questions.
- Let administrators choose the pages where the chat widget appears and configure its color scheme, screen position, and welcome message.
- Select OpenAI or Groq for chat generation through one server environment variable while keeping all provider credentials and model settings in environment variables.
- Select OpenAI or Groq for chat generation through encrypted installation configuration stored by the backend.
- Support both native and optional Docker server deployment, with ParadeDB BM25 + pgvector as the normal production retrieval mode only after its package and readiness gates pass.
- Provide an admin Test Chat submenu for exercising the widget UI and backend API integration.
- Power the website first while keeping the backend reusable for a future mobile app.
Expand Down Expand Up @@ -73,7 +73,7 @@ Future-facing requirements:
- [`server/RANKING_AND_CITATION_CONTRACT.md`](server/RANKING_AND_CITATION_CONTRACT.md): versioned relevance-first ranking, review aggregation, promotion disclosures, citations, uncertainty, deduplication, and evaluation gates.
- [`server/CONVERSATION_CONTEXT_CONTRACT.md`](server/CONVERSATION_CONTEXT_CONTRACT.md): exact visitor ownership, bounded history, turn audit records, PostgreSQL checkpoints, history access, deletion, anonymization, and retention.
- [`server/SEMANTIC_SEARCH_ARCHITECTURE_AND_FLOW_GUIDE.md`](server/SEMANTIC_SEARCH_ARCHITECTURE_AND_FLOW_GUIDE.md): Ask Sunny indexing, retrieval, chat, caching, security, and semantic-search flows.
- [`server/DATABASE_SCHEMA.md`](server/DATABASE_SCHEMA.md): PostgreSQL schema for content, embeddings, conversations, user data, analytics, admin sessions, and migrations.
- [`server/DATABASE_SCHEMA.md`](server/DATABASE_SCHEMA.md): PostgreSQL schema for content, embeddings, conversations, user data, analytics, API keys, and migrations.
- [`server/REST_API_CONTRACT.md`](server/REST_API_CONTRACT.md): backend REST endpoints called by WordPress, future mobile clients, and server admins.

### Plugin
Expand Down Expand Up @@ -112,7 +112,7 @@ WordPress remains responsible for collecting site content, configuring and rende
- Ask Sunny is single-tenant. Do not use a multi-tenant `sites` and `site_domains` model as the main architecture.
- Browser JavaScript calls WordPress REST only. Browser code never receives AI-provider, embedding-provider, or backend API keys.
- The backend uses LangGraph for orchestration and short-term workflow state. Application tables store durable conversation, message, tool-call, profile, and usage records.
- The backend uses a provider-neutral abstraction for Responses API calls. `AI_PROVIDER=openai|groq` selects a registered adapter at runtime; provider keys, base URLs, models, and embedding settings remain environment configuration, while database tables store no provider discriminator or provider-specific conversation state. Chat responses are not streamed.
- The backend uses a provider-neutral abstraction for Responses API calls. One singleton global database record selects a registered adapter and supplies its encrypted API key and chat model for every installation; non-secret endpoints, timeouts, and independent embedding settings remain environment configuration. Conversation tables store no provider-specific response or conversation state. Chat responses are not streamed.
- Native or Dockerized ParadeDB uses `pg_search` for BM25 and pgvector for dense similarity. Hybrid retrieval is the verified production default and fuses both candidate sets only after applying the stored data-source allowlist and structured filters. Installation and upgrades begin with hybrid disabled; it must not be enabled until the installed package/image matches the running PostgreSQL major version, execution OS, and architecture and all extension, index, and smoke checks pass.
- WordPress and Directorist remain the content source of truth for launch. Backend content tables are an indexed search/read model.
- Backend content storage is separated by source kind. Directorist listings use a dedicated `listings` table with inline normalized state and vector data; reviews and optional WordPress content use their own content and embedding tables.
Expand Down
8 changes: 4 additions & 4 deletions plugin/ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -247,11 +247,11 @@ The Data Sources submenu should include:
- Index-status counts update with search and tab-specific filters but are calculated before applying the selected index-status value, so the administrator can see how many records exist in every status without clearing the filter.
- An explicit **Delete indexed data** action on each item and a destructive **Delete all indexed data** action for each source tab, both protected by confirmation and `manage_options`.
- Diagnostics.
- Recent usage summary fetched from backend.
- Diagnostics summary fetched from backend.

Backend diagnostics and usage use `GET /installation/diagnostics` and
`GET /installation/usage` with the installation credential. The plugin must not call `/admin/*`,
store an admin key/session, or expose the installation key to browser code.
Backend diagnostics use `GET /system/diagnostics` with the website credential. The backend exposes
no website usage route. The plugin must not call `/admin/*`, store an admin key, or expose the
website key to browser code.

The Test Chat submenu should render the production widget component in an isolated admin preview and send messages through an admin-only WordPress REST route. It displays connection/provider/hybrid-search diagnostics, request correlation ID, latency, answer, citations, recommendations, and sanitized errors. Test conversations use the backend `admin_test` channel and must not bypass the same response validation or source allowlist used by public chat.

Expand Down
13 changes: 6 additions & 7 deletions plugin/REST_API_CONTRACT.md
Original file line number Diff line number Diff line change
Expand Up @@ -305,7 +305,7 @@ Explicitly deletes all indexed records for a source after admin confirmation. Th

### `POST /provision`

Calls backend `/auth/provision-installation` using a server-side provisioning key.
Calls backend `/auth/provision` using a server-side provisioning key.

Response:

Expand Down Expand Up @@ -387,9 +387,8 @@ Returns local indexing status.
### `GET /diagnostics`

Checks WordPress-side state and the backend's installation-scoped diagnostics. WordPress calls
backend `GET /installation/diagnostics` with the installation credential; it never calls a backend
`/admin/*` route or stores a backend administrator credential. Recent usage is fetched separately
from backend `GET /installation/usage` and reduced to the same safe aggregates for the dashboard.
backend `GET /system/diagnostics` with the website credential; it never calls a backend `/admin/*`
route or stores a backend administrator credential. The backend exposes no website usage route.

```json
{
Expand All @@ -398,9 +397,9 @@ from backend `GET /installation/usage` and reduced to the same safe aggregates f
"backend": {
"ok": true,
"database": "ok",
"ai_provider": "groq",
"ai_provider_configured": true,
"embedding_provider": "openai",
"chat_ai_router": "groq",
"chat_ai_configured": true,
"embedding_ai_router": "openai",
"hybrid_search": "enabled",
"paradedb": "ok",
"allowed_data_sources_version": 6,
Expand Down
42 changes: 23 additions & 19 deletions server/ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ The server is responsible for:
- Language: JavaScript, following the backend service's Bun/Hono runtime pattern.
- HTTP framework: Hono.
- Agent framework: LangGraph.js.
- Model API: provider-neutral generation interface with adapters registered by name and selected at runtime from `AI_PROVIDER`.
- Embeddings: independently configured embedding provider; OpenAI is the launch default.
- Model API: router-neutral generation interface selected by database-backed chat router/model options.
- Embeddings: independently selected OpenAI or Gemini router/model options with fixed dimensions.
- Database: ParadeDB's PostgreSQL distribution with `pg_search` and pgvector.
- Search: hybrid BM25 keyword matching plus dense vector similarity.
- Cache: Redis optional.
Expand All @@ -38,27 +38,14 @@ LOG_LEVEL=info
REQUEST_BODY_LIMIT=2mb

ASK_SUNNY_INSTALLATION_PROVISIONING_KEY=replace-with-long-random-secret
ASK_SUNNY_ADMIN_EMAIL=admin@example.com
ASK_SUNNY_ADMIN_USERNAME=admin
ASK_SUNNY_ADMIN_PASSWORD=replace-with-strong-password
ASK_SUNNY_ADMIN_SESSION_TTL_SECONDS=86400

DATABASE_URL=postgres://ask_sunny:strong-password@127.0.0.1:5432/ask_sunny
PG_POOL_MAX=10

AI_PROVIDER=openai
AI_REQUEST_TIMEOUT_MS=45000

OPENAI_API_KEY=replace-with-openai-api-key
OPENAI_BASE_URL=https://api.openai.com/v1
OPENAI_CHAT_MODEL=replace-with-supported-openai-model

GROQ_API_KEY=replace-with-groq-api-key
GROQ_BASE_URL=https://api.groq.com/openai/v1
GROQ_CHAT_MODEL=replace-with-supported-groq-model

EMBEDDING_PROVIDER=openai
OPENAI_EMBEDDINGS_URL=https://api.openai.com/v1/embeddings
EMBEDDING_MODEL=text-embedding-3-small
EMBEDDING_DIMENSIONS=1536
EMBEDDING_REQUEST_TIMEOUT_MS=15000
EMBEDDING_MAX_RETRIES=2
Expand Down Expand Up @@ -92,7 +79,20 @@ MAX_TOOL_ITERATIONS=6
DEFAULT_TIMEZONE=UTC
```

`AI_PROVIDER` is the only switch for chat generation. A provider registry resolves that value to an adapter implementing the provider-neutral generation interface; orchestration, persistence, routes, and domain services must not branch on provider names. The selected adapter's API key, base URL, and model must be valid at startup; credentials for an inactive provider may be omitted. Embeddings are configured independently because generation and embedding providers do not have identical capabilities. Changing `AI_PROVIDER` does not change stored vector dimensions or trigger re-embedding, and provider identity is not stored in application database tables.
The global `options` key/value rows store the selected generation provider, encrypted API key, and chat
model globally. A provider registry resolves that database record to an adapter implementing the
provider-neutral generation interface; orchestration, routes, and domain services must not branch
on provider names. Missing or incomplete stored provider configuration
fails the related request before work begins and never falls back to process environment settings.
Non-secret provider base URLs and the shared request timeout remain deployment controls. Embeddings
remain independently configured because generation and embedding providers do not have identical
capabilities.

The option repository is the single persistence boundary for the `options` table. It exposes only
generic insert, get, update, delete, `getByKeys`, and `updateMany` operations. Provider key names,
mapping, validation, and summary projection remain in the application layer; the repository has no
provider-specific helpers. `updateMany` accepts only option items and always assigns row
`updated_at` values from the database clock.

Embedding requests use independent timeout and retry controls. `EMBEDDING_REQUEST_TIMEOUT_MS`
defaults to 15000 and accepts 1000 through 60000. `EMBEDDING_MAX_RETRIES` defaults to 2 and accepts
Expand All @@ -102,7 +102,7 @@ milliseconds, and may be raised by a valid `Retry-After` value up to that same c

Because chat is returned as one complete response, the WordPress proxy timeout must be greater than `AI_REQUEST_TIMEOUT_MS`; a 60-second WordPress timeout provides application overhead around the 45-second provider timeout.

Model names are deployment configuration, not hardcoded constants. Verify the selected provider's current model, Responses API, structured-output, and tool-use support before production launch. The provider adapter must not send parameters unsupported by the active provider.
Model names are global database configuration, not hardcoded constants. Verify the selected provider's current model, Responses API, structured-output, and tool-use support before production launch. The provider adapter must not send parameters unsupported by the active provider.

The example connection URLs target native services. Docker Compose overrides their hosts with Compose service names such as `paradedb` and `redis`; application code and all other configuration remain identical.

Expand Down Expand Up @@ -181,7 +181,11 @@ Use the configured provider's Responses API for:
- Complete structured response generation for the widget.
- Multi-turn continuity through server-side conversation context.

The launch adapter registry includes `openai` and `groq`. `AI_PROVIDER=openai` resolves the OpenAI adapter and `AI_PROVIDER=groq` resolves the Groq adapter. Adding a future provider requires registering another implementation, not editing orchestration or persistence code. Each adapter owns request construction, supported parameters, structured-output validation, tool-call normalization, usage normalization, timeout handling, and error mapping.
The launch adapter registry includes `openai` and `groq`; the global `options` key/value store
provider type resolves the matching adapter. Adding a future provider requires registering another
implementation, not editing orchestration or conversation persistence code. Each adapter owns
request construction, supported parameters, structured-output validation, tool-call normalization,
usage normalization, timeout handling, and error mapping.

Do not depend on provider-hosted conversation state. The application loads and persists provider-neutral conversation history and LangGraph state, then supplies the required context on every turn. Database tables do not store the active provider or provider-specific conversation IDs. This keeps provider switching deterministic and avoids coupling to provider-specific response-storage features.

Expand Down
Loading