Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
54 commits
Select commit Hold shift + click to select a range
4bb053f
feat(dashboards): fold export contract and chat rollout primitives
pratzrao Mar 19, 2026
62c16a3
chore(ai-chat): resolve backend runtime dependencies for chroma and l…
pratzrao Mar 19, 2026
5a7f395
feat(ai-chat): add dashboard chat and ai context schema foundation
pratzrao Mar 19, 2026
a90f6cb
feat(ai-chat): add consent status and context management APIs
pratzrao Mar 19, 2026
4061101
feat(ai-chat): add chroma sidecar and vector store primitives
pratzrao Mar 19, 2026
b96e063
feat(ai-chat): add dbt docs generation and context build pipeline
pratzrao Mar 19, 2026
dc6a70d
feat(ai-chat): schedule per-org dashboard chat context builds
pratzrao Mar 19, 2026
b1caa90
feat(ai-chat): add langgraph dashboard chat runtime support
pratzrao Mar 19, 2026
58b817f
feat(ai-chat): add dashboard chat websocket transport
pratzrao Mar 20, 2026
44b00ea
fix(ai-chat): allow dashboard chat websocket auth via access token co…
pratzrao Mar 20, 2026
99bec80
fix(ai-chat): align runtime safety and gating with approved plan
pratzrao Mar 20, 2026
ce64b35
fix(ai-chat): address dashboard chat review findings
pratzrao Mar 20, 2026
5f35ee8
fix(ai-chat): close remaining backend review gaps
pratzrao Mar 20, 2026
3a4dfb2
fix(ai-chat): align dashboard chat runtime and prompts
pratzrao Mar 24, 2026
0a7be4a
fix(ai-chat): improve answer formatting contract
pratzrao Mar 25, 2026
65c5fca
refactor(ai-chat): modularize dashboard chat backend
pratzrao Mar 25, 2026
137942c
refactor(ai-chat): align dashboard chat backend
pratzrao Mar 26, 2026
2787574
first refactor
Ishankoradia Mar 26, 2026
ad2a70e
refactoring tool calling and use our logger class
Ishankoradia Mar 26, 2026
8943bea
updates
Ishankoradia Mar 26, 2026
132bb56
imports global
Ishankoradia Mar 26, 2026
a045439
updates vecto store refactor
Ishankoradia Mar 26, 2026
b3f585b
updates
Ishankoradia Mar 26, 2026
77c81ff
updates
Ishankoradia Mar 27, 2026
17d3668
updates
Ishankoradia Mar 27, 2026
292f73e
fix(ai-chat): harden chroma collection creation flow
pratzrao Mar 27, 2026
39d9e54
fix(ai-chat): use chroma native missing collection errors
pratzrao Mar 27, 2026
e3a1105
Merge pull request #1282 from DalgoT4D/codex/pratiksha-ishan-followup…
Ishankoradia Mar 27, 2026
af593db
chroma docker file
Ishankoradia Mar 27, 2026
c940f64
Merge pull request #1281 from DalgoT4D/refactor-langraph
Ishankoradia Mar 27, 2026
34cf321
refactor(ai-chat): add langgraph checkpointed runtime
pratzrao Mar 27, 2026
856cdfa
updates
Ishankoradia Apr 1, 2026
f26f3ee
updates
Ishankoradia Apr 1, 2026
b7ee7ea
cleanup
Ishankoradia Apr 1, 2026
c73c912
updates
Ishankoradia Apr 1, 2026
a9cf71e
using pydantic contracts and validation
Ishankoradia Apr 1, 2026
4b3e2b2
not needed
Ishankoradia Apr 1, 2026
e4d05fb
updates
Ishankoradia Apr 1, 2026
898d198
revert
Ishankoradia Apr 1, 2026
da79f86
Merge pull request #1285 from DalgoT4D/codex/pratiksha-langgraph-upgrade
Ishankoradia Apr 1, 2026
6e10004
merge: sync impl with main
pratzrao Apr 6, 2026
7951bd4
feat(ai-chat): add stop progress and explore sources
pratzrao Apr 6, 2026
b73d102
refactor(ai-chat): use explicit dashboard chat contracts
pratzrao Apr 6, 2026
49737f6
feat(ai-chat): add prompts feedback and source polish
pratzrao Apr 6, 2026
2a209b7
refactor(ai-chat): simplify suggested prompt generation
pratzrao Apr 6, 2026
7b6e441
fix: remove dashboard chat first-name greeting
pratzrao Apr 7, 2026
8875ada
refactor: simplify dashboard chat freshness contract
pratzrao Apr 7, 2026
6de58ba
Merge pull request #1294 from DalgoT4D/codex/pratiksha-chat-with-dash…
Ishankoradia Apr 7, 2026
f1e442f
squashed migrations, added a seeder for initial prompts
Ishankoradia Apr 7, 2026
b717535
build: upgrade chromadb to 1.5.6
pratzrao Apr 8, 2026
c97519c
docs: clarify chromadb 1.5.6 missing collection handling
pratzrao Apr 8, 2026
2b06140
build: upgrade chroma compose image
pratzrao Apr 8, 2026
0491689
Trim whitespace from login usernames
pratzrao Apr 8, 2026
33b941a
Move login username trimming into API handlers
pratzrao Apr 8, 2026
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
13 changes: 12 additions & 1 deletion .env.template
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,17 @@ ESTIMATE_TIME_FOR_QUEUE_RUNS="false"
LLM_SERVICE_API_URL="http://127.0.0.1:7001"
LLM_SERVICE_API_KEY="<api key>"
LLM_SERVICE_API_VER="" # can be empty or v1
OPENAI_API_KEY=""

####################################################################################################
# AI DASHBOARD CHAT VECTOR STORE
####################################################################################################

AI_DASHBOARD_CHAT_CHROMA_COLLECTION_PREFIX=org_
AI_DASHBOARD_CHAT_CHROMA_EMBEDDING_MODEL=text-embedding-3-small
AI_DASHBOARD_CHAT_CHROMA_HOST=localhost
AI_DASHBOARD_CHAT_CHROMA_PORT=8003
AI_DASHBOARD_CHAT_CHROMA_SSL=False

####################################################################################################
# MONITORING & LOGGING
Expand Down Expand Up @@ -183,4 +194,4 @@ LOGS_MOUNT=

SCHEMA_CHANGE_DETECTION_INTER_ORG_DELAY=60
SCHEMA_CHANGE_DETECTION_SCHEDULE_HOUR=18
SCHEMA_CHANGE_DETECTION_SCHEDULE_MINUTE=30
SCHEMA_CHANGE_DETECTION_SCHEDULE_MINUTE=30
20 changes: 10 additions & 10 deletions Docker/docker-compose.dev.yml
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,15 @@ services:
celery_worker:
image: dalgo_backend:0.1
command: celery
depends_on:
backend:
condition: service_started
redis_server:
condition: service_started
initdb:
condition: service_completed_successfully
networks:
- dalgo-network
environment:
- REDIS_HOST=${REDIS_HOST}
- REDIS_PORT=${REDIS_PORT}
Expand All @@ -112,15 +121,6 @@ services:
- DBPASSWORD=${DBPASSWORD}
- DBADMINUSER=${DBADMINUSER}
- DBADMINPASSWORD=${DBADMINPASSWORD}
depends_on:
backend:
condition: service_started
redis_server:
condition: service_started
initdb:
condition: service_completed_successfully
networks:
- dalgo-network
celery_beat:
image: dalgo_backend:0.1
command: beat
Expand Down Expand Up @@ -165,4 +165,4 @@ volumes:

networks:
dalgo-network:
driver: bridge
driver: bridge
1 change: 1 addition & 0 deletions Docker/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ services:
- redis_data:/data
networks:
- dalgo-network

backend:
image: dalgo_backend:latest
command: backend
Expand Down
28 changes: 23 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,25 @@ PREFECT_PROXY_API_URL=
### Step 6: Create secrets directory
- Set `DEV_SECRETS_DIR` in `.env` unless you want to use Amazon's Secrets Manager

### Step 7: Install DBT
### Step 7: Install Chroma for dashboard chat

Dashboard chat retrieval uses a local Chroma server in development. One simple way to run it is:

```bash
docker run --rm \
-p 8003:8000 \
-v "$PWD/.local/chroma-data:/data" \
chromadb/chroma:0.5.23
```

Then point the backend env to it:

```bash
AI_DASHBOARD_CHAT_CHROMA_HOST=localhost
AI_DASHBOARD_CHAT_CHROMA_PORT=8003
```

### Step 8: Install DBT

The platform now supports multiple DBT versions using `uv` and `pyproject.toml` for better dependency management.

Expand Down Expand Up @@ -178,11 +196,11 @@ $DBT_VENV/

Organizations use either `venv` or `venv-1.9.8` in their `dbt_venv` database field.

### Step 8: Add SIGNUPCODE and FRONTEND_URL
### Step 9: Add SIGNUPCODE and FRONTEND_URL

- The `SIGNUPCODE` in `.env` is for signing up using the frontend. If you are running the frontend, set its URL in `FRONTEND_URL`

### Step 9: Start Backend
### Step 10: Start Backend

```
DJANGOSECRET=
Expand All @@ -199,11 +217,11 @@ DJANGOSECRET=

- Start the server `uvicorn ddpui.asgi:application --port <PORT_TO_LISTEN_ON>`

### Step 10: Create first org and user
### Step 11: Create first org and user
- Run `python manage.py createorganduser <Org Name> <Email address> --role super-admin`
- The above command creates a user with super admin role. If we don't provide any role, the default role is of account manager.

### Step11: Running celery
### Step 12: Running celery

We use two separate Celery workers for better task isolation:

Expand Down
182 changes: 182 additions & 0 deletions ddpui/api/dashboard_native_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,16 @@
DashboardLock,
DashboardFilterType,
)
from ddpui.models.dashboard_chat import (
DashboardAIContext,
DashboardChatMessage,
DashboardChatMessageRole,
)
from ddpui.models.org_preferences import OrgPreferences
from ddpui.models.org_user import OrgUser
from ddpui.auth import has_permission
from ddpui.utils.custom_logger import CustomLogger
from ddpui.utils.feature_flags import get_all_feature_flags_for_org
from ddpui.services.dashboard_service import (
DashboardService,
DashboardData,
Expand All @@ -35,6 +42,7 @@
DashboardCreate,
DashboardUpdate,
DashboardResponse,
DashboardExportResponse,
DashboardFilterResponse,
FilterCreate,
FilterUpdate,
Expand All @@ -46,13 +54,60 @@
DashboardShareStatus,
LandingPageResponse,
LandingPageResolveResponse,
DashboardAIContextResponse,
DashboardChatBootstrapResponse,
DashboardChatMessageFeedbackRequest,
DashboardChatMessageFeedbackResponse,
UpdateDashboardAIContextSchema,
)
from ddpui.core.dashboard_chat.suggested_prompts import build_dashboard_suggested_prompts

logger = CustomLogger("ddpui")

dashboard_native_router = Router()


def _get_or_create_dashboard_ai_context(dashboard: Dashboard):
context, _ = DashboardAIContext.objects.get_or_create(dashboard=dashboard)
return context


def _serialize_dashboard_ai_context(dashboard: Dashboard, context: DashboardAIContext):
org_dbt = dashboard.org.dbt
return DashboardAIContextResponse(
dashboard_id=dashboard.id,
dashboard_title=dashboard.title,
dashboard_context_markdown=context.markdown,
dashboard_context_updated_by=context.updated_by.user.email if context.updated_by else None,
dashboard_context_updated_at=context.updated_at,
ai_context_refreshed_at=org_dbt.vector_last_ingested_at if org_dbt else None,
)


def _serialize_dashboard_chat_bootstrap(dashboard: Dashboard) -> DashboardChatBootstrapResponse:
dashboard_export = DashboardService.export_dashboard_context_for_dashboard(dashboard, dashboard.org)

return DashboardChatBootstrapResponse(
dashboard_id=dashboard.id,
suggested_prompts=build_dashboard_suggested_prompts(
dashboard_export=dashboard_export,
),
)


def _ensure_dashboard_chat_feature_enabled(org) -> None:
"""Hide dashboard chat settings endpoints unless the feature flag is enabled."""
if not get_all_feature_flags_for_org(org).get("AI_DASHBOARD_CHAT", False):
raise HttpError(404, "Chat with dashboards is not enabled for this organization")


def _ensure_dashboard_chat_consent_enabled(org) -> None:
"""Require consent before writing dashboard-specific AI context."""
org_preferences = OrgPreferences.objects.filter(org=org).first()
if org_preferences is None or not org_preferences.ai_data_sharing_enabled:
raise HttpError(409, "Enable AI data sharing before updating dashboard AI context")


# Endpoints
@dashboard_native_router.get("/", response=List[DashboardResponse])
@has_permission(["can_view_dashboards"])
Expand Down Expand Up @@ -89,6 +144,133 @@ def get_dashboard(request, dashboard_id: int):
return DashboardResponse(**DashboardService.get_dashboard_response(dashboard))


@dashboard_native_router.get("/{dashboard_id}/export/", response=DashboardExportResponse)
@has_permission(["can_view_dashboards"])
def export_dashboard(request, dashboard_id: int):
"""Export dashboard JSON along with the chart configs it references."""
orguser: OrgUser = request.orguser

try:
return DashboardService.export_dashboard_context(dashboard_id, orguser.org)
except DashboardNotFoundError as err:
raise HttpError(404, "Dashboard not found") from err


@dashboard_native_router.get(
"/{dashboard_id}/ai-context/",
response=DashboardAIContextResponse,
)
@has_permission(["can_manage_org_settings"])
def get_dashboard_ai_context(request, dashboard_id: int):
"""Load dashboard-level AI context settings for settings management."""
orguser: OrgUser = request.orguser
_ensure_dashboard_chat_feature_enabled(orguser.org)

try:
dashboard = DashboardService.get_dashboard(dashboard_id, orguser.org)
except DashboardNotFoundError as err:
raise HttpError(404, "Dashboard not found") from err

context = _get_or_create_dashboard_ai_context(dashboard)

return _serialize_dashboard_ai_context(dashboard, context)


@dashboard_native_router.get(
"/{dashboard_id}/chat-bootstrap/",
response=DashboardChatBootstrapResponse,
)
@has_permission(["can_view_dashboards"])
def get_dashboard_chat_bootstrap(request, dashboard_id: int):
"""Return deterministic UI bootstrap data for dashboard chat."""
orguser: OrgUser = request.orguser
_ensure_dashboard_chat_feature_enabled(orguser.org)

try:
dashboard = DashboardService.get_dashboard(dashboard_id, orguser.org)
except DashboardNotFoundError as err:
raise HttpError(404, "Dashboard not found") from err

return _serialize_dashboard_chat_bootstrap(dashboard)


@dashboard_native_router.post(
"/{dashboard_id}/chat/messages/{message_id}/feedback/",
response=DashboardChatMessageFeedbackResponse,
)
@has_permission(["can_view_dashboards"])
@transaction.atomic
def set_dashboard_chat_message_feedback(
request,
dashboard_id: int,
message_id: int,
payload: DashboardChatMessageFeedbackRequest,
):
"""Persist one locked thumbs-up/thumbs-down selection for an assistant answer."""
orguser: OrgUser = request.orguser
_ensure_dashboard_chat_feature_enabled(orguser.org)

message = (
DashboardChatMessage.objects.select_related("session")
.filter(
id=message_id,
session__org=orguser.org,
session__orguser=orguser,
session__dashboard_id=dashboard_id,
role=DashboardChatMessageRole.ASSISTANT.value,
)
.first()
)
if message is None:
raise HttpError(404, "Assistant message not found")

if message.feedback is not None:
if message.feedback == payload.feedback:
return DashboardChatMessageFeedbackResponse(
message_id=message.id,
feedback=message.feedback,
)
raise HttpError(409, "Feedback has already been recorded for this message")

message.feedback = payload.feedback
message.save(update_fields=["feedback"])

return DashboardChatMessageFeedbackResponse(
message_id=message.id,
feedback=message.feedback,
)


@dashboard_native_router.put(
"/{dashboard_id}/ai-context/",
response=DashboardAIContextResponse,
)
@has_permission(["can_manage_org_settings"])
@transaction.atomic
def update_dashboard_ai_context(
request,
dashboard_id: int,
payload: UpdateDashboardAIContextSchema,
):
"""Update dashboard-level AI context markdown for settings management."""
orguser: OrgUser = request.orguser
_ensure_dashboard_chat_feature_enabled(orguser.org)
_ensure_dashboard_chat_consent_enabled(orguser.org)

try:
dashboard = DashboardService.get_dashboard(dashboard_id, orguser.org)
except DashboardNotFoundError as err:
raise HttpError(404, "Dashboard not found") from err

context = _get_or_create_dashboard_ai_context(dashboard)
context.markdown = payload.dashboard_context_markdown
context.updated_by = orguser
context.updated_at = timezone.now()
context.save()

return _serialize_dashboard_ai_context(dashboard, context)


@dashboard_native_router.post("/", response=DashboardResponse)
@has_permission(["can_create_dashboards"])
def create_dashboard(request, payload: DashboardCreate):
Expand Down
Loading
Loading