From 7fc744c2730f1a9dfd4720a8530a9f22c3ffa70b Mon Sep 17 00:00:00 2001 From: Alexander Date: Mon, 26 May 2025 17:50:48 +0200 Subject: [PATCH 01/13] feat(db): added missing & --- backend/pkg/db/clickhouse/connector.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/pkg/db/clickhouse/connector.go b/backend/pkg/db/clickhouse/connector.go index 607b197bb..ea99288e7 100644 --- a/backend/pkg/db/clickhouse/connector.go +++ b/backend/pkg/db/clickhouse/connector.go @@ -733,7 +733,7 @@ func (c *connectorImpl) InsertCustom(session *sessions.Session, msg *messages.Cu return fmt.Errorf("can't marshal custom event: %s", err) } var customPayload interface{} - if err := json.Unmarshal([]byte(msg.Payload), customPayload); err != nil { + if err := json.Unmarshal([]byte(msg.Payload), &customPayload); err != nil { log.Printf("can't unmarshal custom event payload into object: %s", err) customPayload = map[string]interface{}{ "payload": msg.Payload, From 59c10cdbea4703d66f5056f6d990f2fc862c3aaf Mon Sep 17 00:00:00 2001 From: jonathan-caleb-griffin <124153003+jonathan-caleb-griffin@users.noreply.github.com> Date: Mon, 26 May 2025 17:58:08 +0200 Subject: [PATCH 02/13] ui: fetch suggestions for kai prompts * update display logic * don't copy array when unnecessary * only update prompt ideas when array size changes --------- Co-authored-by: Jonathan Griffin --- frontend/app/components/Kai/KaiChat.tsx | 2 +- frontend/app/components/Kai/KaiService.ts | 11 ++++++ .../app/components/Kai/components/Ideas.tsx | 35 ++++++++++++++++--- .../Kai/components/IntroSection.tsx | 4 +-- 4 files changed, 45 insertions(+), 7 deletions(-) diff --git a/frontend/app/components/Kai/KaiChat.tsx b/frontend/app/components/Kai/KaiChat.tsx index 35fb8f257..d3952cffa 100644 --- a/frontend/app/components/Kai/KaiChat.tsx +++ b/frontend/app/components/Kai/KaiChat.tsx @@ -119,7 +119,7 @@ function KaiChat() { }} > {section === 'intro' ? ( - + ) : ( => { + const r = await this.client.get(`/kai/${projectId}/prompt-suggestions`); + if (!r.ok) { + throw new Error('Failed to fetch prompt suggestions'); + } + const data = await r.json(); + return data; + }; } diff --git a/frontend/app/components/Kai/components/Ideas.tsx b/frontend/app/components/Kai/components/Ideas.tsx index 1cd2f86e2..2cca2d444 100644 --- a/frontend/app/components/Kai/components/Ideas.tsx +++ b/frontend/app/components/Kai/components/Ideas.tsx @@ -1,16 +1,43 @@ import React from 'react'; import { Lightbulb, MoveRight } from 'lucide-react'; +import { useQuery } from '@tanstack/react-query'; +import { kaiService } from 'App/services'; +import { useTranslation } from 'react-i18next'; -function Ideas({ onClick }: { onClick: (query: string) => void }) { +function Ideas({ onClick, projectId }: { onClick: (query: string) => void, projectId: string }) { + const { t } = useTranslation(); + const { + data: suggestedPromptIdeas = [], + isPending, + } = useQuery({ + queryKey: ['kai', 'prompt-suggestions', projectId], + queryFn: () => kaiService.getPromptSuggestions(projectId), + staleTime: 1000 * 60, + }); + const ideas = React.useMemo(() => { + const defaultPromptIdeas = [ + 'Top user journeys', + 'Where do users drop off', + 'Failed network requests today', + ]; + const result = suggestedPromptIdeas; + const targetSize = 3; + while (result.length < targetSize && defaultPromptIdeas.length) { + result.push(defaultPromptIdeas.pop()) + } + return result; + }, [suggestedPromptIdeas.length]); return ( <>
Ideas:
- - - + { + isPending ? + (
{t('Generating ideas')}...
) : + (
{ideas.map(title => ())}
) + } ); } diff --git a/frontend/app/components/Kai/components/IntroSection.tsx b/frontend/app/components/Kai/components/IntroSection.tsx index 71a69b99c..c6356e9ba 100644 --- a/frontend/app/components/Kai/components/IntroSection.tsx +++ b/frontend/app/components/Kai/components/IntroSection.tsx @@ -2,7 +2,7 @@ import React from 'react'; import ChatInput from './ChatInput'; import Ideas from './Ideas'; -function IntroSection({ onAsk }: { onAsk: (query: string) => void }) { +function IntroSection({ onAsk, projectId }: { onAsk: (query: string) => void, projectId: string }) { const isLoading = false; return ( <> @@ -14,7 +14,7 @@ function IntroSection({ onAsk }: { onAsk: (query: string) => void }) { {/* null} />*/}
- onAsk(query)} /> + onAsk(query)} projectId={projectId} />
From 63b89c816b95e5baff2f564668a08251dfdd166d Mon Sep 17 00:00:00 2001 From: Alexander Date: Tue, 27 May 2025 11:39:39 +0200 Subject: [PATCH 03/13] Custom event's payload (#3439) * feat(db): extra log * feat(db): removed debug log --- backend/pkg/db/clickhouse/connector.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/pkg/db/clickhouse/connector.go b/backend/pkg/db/clickhouse/connector.go index ea99288e7..c35f67fda 100644 --- a/backend/pkg/db/clickhouse/connector.go +++ b/backend/pkg/db/clickhouse/connector.go @@ -732,7 +732,7 @@ func (c *connectorImpl) InsertCustom(session *sessions.Session, msg *messages.Cu if err != nil { return fmt.Errorf("can't marshal custom event: %s", err) } - var customPayload interface{} + customPayload := make(map[string]interface{}) if err := json.Unmarshal([]byte(msg.Payload), &customPayload); err != nil { log.Printf("can't unmarshal custom event payload into object: %s", err) customPayload = map[string]interface{}{ From ed39bbf1d434c4c3a30b4bc2aea25e0bdd55a328 Mon Sep 17 00:00:00 2001 From: Taha Yassine Kraiem Date: Tue, 27 May 2025 12:17:04 +0200 Subject: [PATCH 04/13] fix(chalice): fixed missing timestamp in sessions replay fix(chalice): fixed nested custom events in session replay fix(chalice): fixed issues events in session replay --- api/chalicelib/core/events/events_ch.py | 20 +++++++++----------- api/chalicelib/core/issues/issues_ch.py | 11 +++++++---- api/chalicelib/utils/exp_ch_helper.py | 20 +++++++++++++++++--- api/chalicelib/utils/helper.py | 4 ++-- 4 files changed, 35 insertions(+), 20 deletions(-) diff --git a/api/chalicelib/core/events/events_ch.py b/api/chalicelib/core/events/events_ch.py index f1f28a9e7..384a31d3d 100644 --- a/api/chalicelib/core/events/events_ch.py +++ b/api/chalicelib/core/events/events_ch.py @@ -1,18 +1,13 @@ from chalicelib.utils import ch_client from .events_pg import * - - -def __explode_properties(rows): - for i in range(len(rows)): - rows[i] = {**rows[i], **rows[i]["$properties"]} - rows[i].pop("$properties") - return rows +from chalicelib.utils.exp_ch_helper import explode_dproperties, add_timestamp def get_customs_by_session_id(session_id, project_id): with ch_client.ClickHouseClient() as cur: rows = cur.execute(""" \ SELECT `$properties`, + properties, created_at, 'CUSTOM' AS type FROM product_analytics.events @@ -21,8 +16,10 @@ def get_customs_by_session_id(session_id, project_id): AND `$event_name`!='INCIDENT' ORDER BY created_at;""", {"project_id": project_id, "session_id": session_id}) - rows = __explode_properties(rows) - return helper.list_to_camel_case(rows) + rows = helper.list_to_camel_case(rows, ignore_keys=["properties"]) + rows = explode_dproperties(rows) + rows = add_timestamp(rows) + return rows def __merge_cells(rows, start, count, replacement): @@ -69,12 +66,13 @@ def get_by_session_id(session_id, project_id, group_clickrage=False, event_type: parameters={"project_id": project_id, "session_id": session_id, "select_events": select_events}) rows = cur.execute(query) - rows = __explode_properties(rows) + rows = explode_dproperties(rows) if group_clickrage and 'CLICK' in select_events: rows = __get_grouped_clickrage(rows=rows, session_id=session_id, project_id=project_id) rows = helper.list_to_camel_case(rows) rows = sorted(rows, key=lambda k: k["createdAt"]) + rows = add_timestamp(rows) return rows @@ -91,7 +89,7 @@ def get_incidents_by_session_id(session_id, project_id): ORDER BY created_at;""", parameters={"project_id": project_id, "session_id": session_id}) rows = cur.execute(query) - rows = __explode_properties(rows) + rows = explode_dproperties(rows) rows = helper.list_to_camel_case(rows) rows = sorted(rows, key=lambda k: k["createdAt"]) return rows diff --git a/api/chalicelib/core/issues/issues_ch.py b/api/chalicelib/core/issues/issues_ch.py index b6297f598..3b0421747 100644 --- a/api/chalicelib/core/issues/issues_ch.py +++ b/api/chalicelib/core/issues/issues_ch.py @@ -1,6 +1,6 @@ from chalicelib.utils import ch_client, helper import datetime -from .issues_pg import get_all_types +from chalicelib.utils.exp_ch_helper import explode_dproperties, add_timestamp def get(project_id, issue_id): @@ -21,7 +21,7 @@ def get(project_id, issue_id): def get_by_session_id(session_id, project_id, issue_type=None): with ch_client.ClickHouseClient() as cur: query = cur.format(query=f"""\ - SELECT * + SELECT created_at, `$properties` FROM product_analytics.events WHERE session_id = %(session_id)s AND project_id= %(project_id)s @@ -29,8 +29,11 @@ def get_by_session_id(session_id, project_id, issue_type=None): {"AND issue_type = %(type)s" if issue_type is not None else ""} ORDER BY created_at;""", parameters={"session_id": session_id, "project_id": project_id, "type": issue_type}) - data = cur.execute(query) - return helper.list_to_camel_case(data) + rows = cur.execute(query) + rows = explode_dproperties(rows) + rows = helper.list_to_camel_case(rows) + rows = add_timestamp(rows) + return rows # To reduce the number of issues in the replay; diff --git a/api/chalicelib/utils/exp_ch_helper.py b/api/chalicelib/utils/exp_ch_helper.py index a0d02a524..f1231ec09 100644 --- a/api/chalicelib/utils/exp_ch_helper.py +++ b/api/chalicelib/utils/exp_ch_helper.py @@ -1,13 +1,14 @@ import logging +import math import re +import struct +from decimal import Decimal from typing import Union, Any import schemas from chalicelib.utils import sql_helper as sh +from chalicelib.utils.TimeUTC import TimeUTC from schemas import SearchEventOperator -import math -import struct -from decimal import Decimal logger = logging.getLogger(__name__) @@ -233,3 +234,16 @@ def best_clickhouse_type(value): return "Float64" raise TypeError(f"Unsupported type: {type(value).__name__}") + + +def explode_dproperties(rows): + for i in range(len(rows)): + rows[i] = {**rows[i], **rows[i]["$properties"]} + rows[i].pop("$properties") + return rows + + +def add_timestamp(rows): + for row in rows: + row["timestamp"] = TimeUTC.datetime_to_timestamp(row["createdAt"]) + return rows diff --git a/api/chalicelib/utils/helper.py b/api/chalicelib/utils/helper.py index 0c128d4a1..3d10ddcf3 100644 --- a/api/chalicelib/utils/helper.py +++ b/api/chalicelib/utils/helper.py @@ -15,11 +15,11 @@ def random_string(length=36): return "".join(random.choices(string.hexdigits, k=length)) -def list_to_camel_case(items: list[dict], flatten: bool = False) -> list[dict]: +def list_to_camel_case(items: list[dict], flatten: bool = False, ignore_keys=[]) -> list[dict]: for i in range(len(items)): if flatten: items[i] = flatten_nested_dicts(items[i]) - items[i] = dict_to_camel_case(items[i]) + items[i] = dict_to_camel_case(items[i], ignore_keys=[]) return items From 4f02fd1e9e1002c57fc220ae9363b981815ea5c8 Mon Sep 17 00:00:00 2001 From: rjshrjndrn Date: Mon, 26 May 2025 16:59:24 +0200 Subject: [PATCH 05/13] chore(helmcharts): assist-api Signed-off-by: rjshrjndrn --- .../openreplay/charts/assist-api/.helmignore | 23 ++++ .../openreplay/charts/assist-api/Chart.yaml | 24 ++++ .../charts/assist-api/templates/NOTES.txt | 22 ++++ .../charts/assist-api/templates/_helpers.tpl | 65 ++++++++++ .../assist-api/templates/deployment.yaml | 103 +++++++++++++++ .../charts/assist-api/templates/hpa.yaml | 33 +++++ .../charts/assist-api/templates/ingress.yaml | 36 ++++++ .../charts/assist-api/templates/service.yaml | 18 +++ .../assist-api/templates/serviceMonitor.yaml | 18 +++ .../assist-api/templates/serviceaccount.yaml | 13 ++ .../templates/tests/test-connection.yaml | 15 +++ .../openreplay/charts/assist-api/values.yaml | 119 ++++++++++++++++++ 12 files changed, 489 insertions(+) create mode 100644 scripts/helmcharts/openreplay/charts/assist-api/.helmignore create mode 100644 scripts/helmcharts/openreplay/charts/assist-api/Chart.yaml create mode 100644 scripts/helmcharts/openreplay/charts/assist-api/templates/NOTES.txt create mode 100644 scripts/helmcharts/openreplay/charts/assist-api/templates/_helpers.tpl create mode 100644 scripts/helmcharts/openreplay/charts/assist-api/templates/deployment.yaml create mode 100644 scripts/helmcharts/openreplay/charts/assist-api/templates/hpa.yaml create mode 100644 scripts/helmcharts/openreplay/charts/assist-api/templates/ingress.yaml create mode 100644 scripts/helmcharts/openreplay/charts/assist-api/templates/service.yaml create mode 100644 scripts/helmcharts/openreplay/charts/assist-api/templates/serviceMonitor.yaml create mode 100644 scripts/helmcharts/openreplay/charts/assist-api/templates/serviceaccount.yaml create mode 100644 scripts/helmcharts/openreplay/charts/assist-api/templates/tests/test-connection.yaml create mode 100644 scripts/helmcharts/openreplay/charts/assist-api/values.yaml diff --git a/scripts/helmcharts/openreplay/charts/assist-api/.helmignore b/scripts/helmcharts/openreplay/charts/assist-api/.helmignore new file mode 100644 index 000000000..0e8a0eb36 --- /dev/null +++ b/scripts/helmcharts/openreplay/charts/assist-api/.helmignore @@ -0,0 +1,23 @@ +# Patterns to ignore when building packages. +# This supports shell glob matching, relative path matching, and +# negation (prefixed with !). Only one pattern per line. +.DS_Store +# Common VCS dirs +.git/ +.gitignore +.bzr/ +.bzrignore +.hg/ +.hgignore +.svn/ +# Common backup files +*.swp +*.bak +*.tmp +*.orig +*~ +# Various IDEs +.project +.idea/ +*.tmproj +.vscode/ diff --git a/scripts/helmcharts/openreplay/charts/assist-api/Chart.yaml b/scripts/helmcharts/openreplay/charts/assist-api/Chart.yaml new file mode 100644 index 000000000..498f81dd6 --- /dev/null +++ b/scripts/helmcharts/openreplay/charts/assist-api/Chart.yaml @@ -0,0 +1,24 @@ +apiVersion: v2 +name: assist-api +description: A Helm chart for Kubernetes + +# A chart can be either an 'application' or a 'library' chart. +# +# Application charts are a collection of templates that can be packaged into versioned archives +# to be deployed. +# +# Library charts provide useful utilities or functions for the chart developer. They're included as +# a dependency of application charts to inject those utilities and functions into the rassist-apiing +# pipeline. Library charts do not define any templates and therefore cannot be deployed. +type: application + +# This is the chart version. This version number should be incremented each time you make changes +# to the chart and its templates, including the app version. +# Versions are expected to follow Semantic Versioning (https://semver.org/) +version: 0.1.1 + +# This is the version number of the application being deployed. This version number should be +# incremented each time you make changes to the application. Versions are not expected to +# follow Semantic Versioning. They should reflect the version the application is using. +# It is recommended to use it with quotes. +AppVersion: "v1.22.0" diff --git a/scripts/helmcharts/openreplay/charts/assist-api/templates/NOTES.txt b/scripts/helmcharts/openreplay/charts/assist-api/templates/NOTES.txt new file mode 100644 index 000000000..f5106914d --- /dev/null +++ b/scripts/helmcharts/openreplay/charts/assist-api/templates/NOTES.txt @@ -0,0 +1,22 @@ +1. Get the application URL by running these commands: +{{- if .Values.ingress.enabled }} +{{- range $host := .Values.ingress.hosts }} + {{- range .paths }} + http{{ if $.Values.ingress.tls }}s{{ end }}://{{ $host.host }}{{ .path }} + {{- end }} +{{- end }} +{{- else if contains "NodePort" .Values.service.type }} + export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "assist-api.fullname" . }}) + export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}") + echo http://$NODE_IP:$NODE_PORT +{{- else if contains "LoadBalancer" .Values.service.type }} + NOTE: It may take a few minutes for the LoadBalancer IP to be available. + You can watch the status of by running 'kubectl get --namespace {{ .Release.Namespace }} svc -w {{ include "assist-api.fullname" . }}' + export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ include "assist-api.fullname" . }} --template "{{"{{ range (index .status.loadBalancer.ingress 0) }}{{.}}{{ end }}"}}") + echo http://$SERVICE_IP:{{ .Values.service.port }} +{{- else if contains "ClusterIP" .Values.service.type }} + export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app.kubernetes.io/name={{ include "assist-api.name" . }},app.kubernetes.io/instance={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}") + export CONTAINER_PORT=$(kubectl get pod --namespace {{ .Release.Namespace }} $POD_NAME -o jsonpath="{.spec.containers[0].ports[0].containerPort}") + echo "Visit http://127.0.0.1:8080 to use your application" + kubectl --namespace {{ .Release.Namespace }} port-forward $POD_NAME 8080:$CONTAINER_PORT +{{- end }} diff --git a/scripts/helmcharts/openreplay/charts/assist-api/templates/_helpers.tpl b/scripts/helmcharts/openreplay/charts/assist-api/templates/_helpers.tpl new file mode 100644 index 000000000..568007bc7 --- /dev/null +++ b/scripts/helmcharts/openreplay/charts/assist-api/templates/_helpers.tpl @@ -0,0 +1,65 @@ +{{/* +Expand the name of the chart. +*/}} +{{- define "assist-api.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Create a default fully qualified app name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +If release name contains chart name it will be used as a full name. +*/}} +{{- define "assist-api.fullname" -}} +{{- if .Values.fullnameOverride }} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- $name := default .Chart.Name .Values.nameOverride }} +{{- if contains $name .Release.Name }} +{{- .Release.Name | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} +{{- end }} +{{- end }} +{{- end }} + +{{/* +Create chart name and version as used by the chart label. +*/}} +{{- define "assist-api.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Common labels +*/}} +{{- define "assist-api.labels" -}} +helm.sh/chart: {{ include "assist-api.chart" . }} +{{ include "assist-api.selectorLabels" . }} +{{- if .Chart.AppVersion }} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +{{- end }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- if .Values.global.appLabels }} +{{- .Values.global.appLabels | toYaml | nindent 0}} +{{- end}} +{{- end }} + +{{/* +Selector labels +*/}} +{{- define "assist-api.selectorLabels" -}} +app.kubernetes.io/name: {{ include "assist-api.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end }} + +{{/* +Create the name of the service account to use +*/}} +{{- define "assist-api.serviceAccountName" -}} +{{- if .Values.serviceAccount.create }} +{{- default (include "assist-api.fullname" .) .Values.serviceAccount.name }} +{{- else }} +{{- default "default" .Values.serviceAccount.name }} +{{- end }} +{{- end }} diff --git a/scripts/helmcharts/openreplay/charts/assist-api/templates/deployment.yaml b/scripts/helmcharts/openreplay/charts/assist-api/templates/deployment.yaml new file mode 100644 index 000000000..e8540295b --- /dev/null +++ b/scripts/helmcharts/openreplay/charts/assist-api/templates/deployment.yaml @@ -0,0 +1,103 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "assist-api.fullname" . }} + namespace: {{ .Release.Namespace }} + labels: + {{- include "assist-api.labels" . | nindent 4 }} +spec: + {{- if not .Values.autoscaling.enabled }} + replicas: {{ .Values.replicaCount }} + {{- end }} + selector: + matchLabels: + {{- include "assist-api.selectorLabels" . | nindent 6 }} + template: + metadata: + {{- with .Values.podAnnotations }} + annotations: + {{- toYaml . | nindent 8 }} + {{- end }} + labels: + {{- include "assist-api.selectorLabels" . | nindent 8 }} + spec: + {{- with .Values.imagePullSecrets }} + imagePullSecrets: + {{- toYaml . | nindent 8 }} + {{- end }} + serviceAccountName: {{ include "assist-api.serviceAccountName" . }} + securityContext: + {{- toYaml .Values.podSecurityContext | nindent 8 }} + shareProcessNamespace: true + containers: + - name: {{ .Chart.Name }} + securityContext: + {{- toYaml .Values.securityContext | nindent 12 }} + {{- if .Values.global.enterpriseEditionLicense }} + image: "{{ tpl .Values.image.repository . }}:{{ .Values.image.tag | default .Chart.AppVersion }}-ee" + {{- else }} + image: "{{ tpl .Values.image.repository . }}:{{ .Values.image.tag | default .Chart.AppVersion }}" + {{- end }} + imagePullPolicy: {{ .Values.image.pullPolicy }} + {{- if .Values.healthCheck}} + {{- .Values.healthCheck | toYaml | nindent 10}} + {{- end}} + env: + - name: LICENSE_KEY + value: '{{ .Values.global.enterpriseEditionLicense }}' + - name: KAFKA_SERVERS + value: '{{ .Values.global.kafka.kafkaHost }}:{{ .Values.global.kafka.kafkaPort }}' + - name: KAFKA_USE_SSL + value: '{{ .Values.global.kafka.kafkaUseSsl }}' + - name: ASSIST_KEY + value: {{ .Values.global.assistKey }} + - name: pg_password + {{- if .Values.global.postgresql.existingSecret }} + valueFrom: + secretKeyRef: + name: {{ .Values.global.postgresql.existingSecret }} + key: postgresql-postgres-password + {{- else }} + value: '{{ .Values.global.postgresql.postgresqlPassword }}' + {{- end}} + - name: POSTGRES_STRING + value: 'postgres://{{ .Values.global.postgresql.postgresqlUser }}:$(pg_password)@{{ .Values.global.postgresql.postgresqlHost }}:{{ .Values.global.postgresql.postgresqlPort }}/{{ .Values.global.postgresql.postgresqlDatabase }}' + {{- include "openreplay.env.redis_string" .Values.global.redis | nindent 12 }} + {{- range $key, $val := .Values.global.env }} + - name: {{ $key }} + value: '{{ $val }}' + {{- end }} + {{- range $key, $val := .Values.env }} + - name: {{ $key }} + value: '{{ $val }}' + {{- end}} + ports: + {{- range $key, $val := .Values.service.ports }} + - name: {{ $key }} + containerPort: {{ $val }} + protocol: TCP + {{- end }} + volumeMounts: + {{- include "openreplay.volume.redis_ca_certificate.mount" .Values.global.redis | nindent 12 }} + {{- with .Values.persistence.mounts }} + {{- toYaml . | nindent 12 }} + {{- end }} + resources: + {{- toYaml .Values.resources | nindent 12 }} + volumes: + {{- with .Values.persistence.volumes }} + {{- toYaml . | nindent 8 }} + {{- end }} + {{- include "openreplay.volume.redis_ca_certificate" .Values.global.redis | nindent 8 }} + {{- with .Values.nodeSelector }} + nodeSelector: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.affinity }} + affinity: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.tolerations }} + tolerations: + {{- toYaml . | nindent 8 }} + {{- end }} diff --git a/scripts/helmcharts/openreplay/charts/assist-api/templates/hpa.yaml b/scripts/helmcharts/openreplay/charts/assist-api/templates/hpa.yaml new file mode 100644 index 000000000..04f9e7961 --- /dev/null +++ b/scripts/helmcharts/openreplay/charts/assist-api/templates/hpa.yaml @@ -0,0 +1,33 @@ +{{- if .Values.autoscaling.enabled }} +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: {{ include "assist-api.fullname" . }} + namespace: {{ .Release.Namespace }} + labels: + {{- include "assist-api.labels" . | nindent 4 }} +spec: + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: {{ include "assist-api.fullname" . }} + minReplicas: {{ .Values.autoscaling.minReplicas }} + maxReplicas: {{ .Values.autoscaling.maxReplicas }} + metrics: + {{- if .Values.autoscaling.targetCPUUtilizationPercentage }} + - type: Resource + resource: + name: cpu + target: + type: Utilization + averageUtilization: {{ .Values.autoscaling.targetCPUUtilizationPercentage }} + {{- end }} + {{- if .Values.autoscaling.targetMemoryUtilizationPercentage }} + - type: Resource + resource: + name: memory + target: + type: Utilization + averageUtilization: {{ .Values.autoscaling.targetMemoryUtilizationPercentage }} + {{- end }} +{{- end }} diff --git a/scripts/helmcharts/openreplay/charts/assist-api/templates/ingress.yaml b/scripts/helmcharts/openreplay/charts/assist-api/templates/ingress.yaml new file mode 100644 index 000000000..6341db4f2 --- /dev/null +++ b/scripts/helmcharts/openreplay/charts/assist-api/templates/ingress.yaml @@ -0,0 +1,36 @@ +{{- if .Values.ingress.enabled -}} +{{- $fullName := include "assist-api.fullname" . -}} +{{- $svcPort := .Values.service.ports.http -}} +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: {{ $fullName }} + namespace: {{ .Release.Namespace }} + labels: + {{- include "assist-api.labels" . | nindent 4 }} + annotations: + nginx.ingress.kubernetes.io/rewrite-target: /$1 + nginx.ingress.kubernetes.io/upstream-hash-by: $http_x_forwarded_for + {{- with .Values.ingress.annotations }} + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + ingressClassName: "{{ tpl .Values.ingress.className . }}" + tls: + - hosts: + - {{ .Values.global.domainName }} + {{- if .Values.ingress.tls.secretName}} + secretName: {{ .Values.ingress.tls.secretName }} + {{- end }} + rules: + - host: {{ .Values.global.domainName }} + http: + paths: + - path: /api-assist/(.*) + pathType: Prefix + backend: + service: + name: {{ $fullName }} + port: + number: {{ $svcPort }} +{{- end }} diff --git a/scripts/helmcharts/openreplay/charts/assist-api/templates/service.yaml b/scripts/helmcharts/openreplay/charts/assist-api/templates/service.yaml new file mode 100644 index 000000000..6838d1702 --- /dev/null +++ b/scripts/helmcharts/openreplay/charts/assist-api/templates/service.yaml @@ -0,0 +1,18 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ include "assist-api.fullname" . }} + namespace: {{ .Release.Namespace }} + labels: + {{- include "assist-api.labels" . | nindent 4 }} +spec: + type: {{ .Values.service.type }} + ports: + {{- range $key, $val := .Values.service.ports }} + - port: {{ $val }} + targetPort: {{ $key }} + protocol: TCP + name: {{ $key }} + {{- end}} + selector: + {{- include "assist-api.selectorLabels" . | nindent 4 }} diff --git a/scripts/helmcharts/openreplay/charts/assist-api/templates/serviceMonitor.yaml b/scripts/helmcharts/openreplay/charts/assist-api/templates/serviceMonitor.yaml new file mode 100644 index 000000000..c57c21606 --- /dev/null +++ b/scripts/helmcharts/openreplay/charts/assist-api/templates/serviceMonitor.yaml @@ -0,0 +1,18 @@ +{{- if and ( .Capabilities.APIVersions.Has "monitoring.coreos.com/v1" ) ( .Values.serviceMonitor.enabled ) }} +apiVersion: monitoring.coreos.com/v1 +kind: ServiceMonitor +metadata: + name: {{ include "assist-api.fullname" . }} + namespace: {{ .Release.Namespace }} + labels: + {{- include "assist-api.labels" . | nindent 4 }} + {{- if .Values.serviceMonitor.additionalLabels }} + {{- toYaml .Values.serviceMonitor.additionalLabels | nindent 4 }} + {{- end }} +spec: + endpoints: + {{- .Values.serviceMonitor.scrapeConfigs | toYaml | nindent 4 }} + selector: + matchLabels: + {{- include "assist-api.selectorLabels" . | nindent 6 }} +{{- end }} diff --git a/scripts/helmcharts/openreplay/charts/assist-api/templates/serviceaccount.yaml b/scripts/helmcharts/openreplay/charts/assist-api/templates/serviceaccount.yaml new file mode 100644 index 000000000..06db808ac --- /dev/null +++ b/scripts/helmcharts/openreplay/charts/assist-api/templates/serviceaccount.yaml @@ -0,0 +1,13 @@ +{{- if .Values.serviceAccount.create -}} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ include "assist-api.serviceAccountName" . }} + namespace: {{ .Release.Namespace }} + labels: + {{- include "assist-api.labels" . | nindent 4 }} + {{- with .Values.serviceAccount.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +{{- end }} diff --git a/scripts/helmcharts/openreplay/charts/assist-api/templates/tests/test-connection.yaml b/scripts/helmcharts/openreplay/charts/assist-api/templates/tests/test-connection.yaml new file mode 100644 index 000000000..57e38f294 --- /dev/null +++ b/scripts/helmcharts/openreplay/charts/assist-api/templates/tests/test-connection.yaml @@ -0,0 +1,15 @@ +apiVersion: v1 +kind: Pod +metadata: + name: "{{ include "assist-api.fullname" . }}-test-connection" + labels: + {{- include "assist-api.labels" . | nindent 4 }} + annotations: + "helm.sh/hook": test +spec: + containers: + - name: wget + image: busybox + command: ['wget'] + args: ['{{ include "assist-api.fullname" . }}:{{ .Values.service.port }}'] + restartPolicy: Never diff --git a/scripts/helmcharts/openreplay/charts/assist-api/values.yaml b/scripts/helmcharts/openreplay/charts/assist-api/values.yaml new file mode 100644 index 000000000..f5bf928e0 --- /dev/null +++ b/scripts/helmcharts/openreplay/charts/assist-api/values.yaml @@ -0,0 +1,119 @@ +# Default values for openreplay. +# This is a YAML-formatted file. +# Declare variables to be passed into your templates. + +replicaCount: 1 + +image: + repository: "{{ .Values.global.openReplayContainerRegistry }}/assist-api" + pullPolicy: IfNotPresent + # Overrides the image tag whose default is the chart appVersion. + tag: "" + +imagePullSecrets: [] +nameOverride: "assist-api" +fullnameOverride: "assist-api-openreplay" + +serviceAccount: + # Specifies whether a service account should be created + create: true + # Annotations to add to the service account + annotations: {} + # The name of the service account to use. + # If not set and create is true, a name is generated using the fullname template + name: "" + +podAnnotations: {} + +securityContext: + runAsUser: 1001 + runAsGroup: 1001 +podSecurityContext: + runAsUser: 1001 + runAsGroup: 1001 + fsGroup: 1001 + fsGroupChangePolicy: "OnRootMismatch" + # podSecurityContext: {} + # fsGroup: 2000 + + # securityContext: {} + # capabilities: + # drop: + # - ALL + # readOnlyRootFilesystem: true + # runAsNonRoot: true + # runAsUser: 1000 + +#service: +# type: ClusterIP +# port: 9000 + +serviceMonitor: + enabled: false + additionalLabels: + release: observability + scrapeConfigs: + - port: metrics + honorLabels: true + interval: 15s + path: /metrics + scheme: http + scrapeTimeout: 10s + +service: + type: ClusterIP + ports: + http: 8080 + metrics: 8888 + +ingress: + enabled: false + className: "{{ .Values.global.ingress.controller.ingressClassResource.name }}" + annotations: + nginx.ingress.kubernetes.io/proxy-read-timeout: "3600" + nginx.ingress.kubernetes.io/proxy-send-timeout: "3600" + # kubernetes.io/ingress.class: nginx + # kubernetes.io/tls-acme: "true" + tls: + secretName: openreplay-ssl + +resources: {} + # We usually recommend not to specify default resources and to leave this as a conscious + # choice for the user. This also increases chances charts run on environments with little + # resources, such as Minikube. If you do want to specify resources, uncomment the following + # lines, adjust them as necessary, and remove the curly braces after 'resources:'. + # limits: + # cpu: 100m + # memory: 128Mi + # requests: + # cpu: 100m +# memory: 128Mi + +autoscaling: + enabled: false + minReplicas: 1 + maxReplicas: 5 + targetCPUUtilizationPercentage: 80 + # targetMemoryUtilizationPercentage: 80 + +env: + CLEAR_SOCKET_TIME: 720 + + +nodeSelector: {} + +tolerations: [] + +affinity: {} + + +persistence: {} + # # Spec of spec.template.spec.containers[*].volumeMounts + # mounts: + # - name: kafka-ssl + # mountPath: /opt/kafka/ssl + # # Spec of spec.template.spec.volumes + # volumes: + # - name: kafka-ssl + # secret: +# secretName: kafka-ssl From 2c975c768eaa1d253c606740e8c2e3db8f0cf69b Mon Sep 17 00:00:00 2001 From: rjshrjndrn Date: Tue, 27 May 2025 12:55:28 +0200 Subject: [PATCH 06/13] feat: refactor assist-api chart and remove ingress routing - Add assist-api as new chart dependency with conditional enablement - Remove Kafka environment variables from assist-api deployment - Add REDIS_CACHE_ENABLED with enterprise license conditional logic - Delete assist-api ingress.yaml for direct service routing - Remove S3 configuration from assist deployment - Add openreplay.assist_url helper for service discovery - Configure assist-api as disabled by default in values BREAKING CHANGE: assist-api no longer uses ingress routing and requires direct service access. S3 configuration removed from assist service. Signed-off-by: rjshrjndrn --- scripts/helmcharts/openreplay/Chart.yaml | 4 +++ .../assist-api/templates/deployment.yaml | 6 ++-- .../charts/assist-api/templates/ingress.yaml | 36 ------------------- .../charts/assist/templates/deployment.yaml | 24 ------------- .../openreplay/templates/_helpers.tpl | 8 +++++ scripts/helmcharts/openreplay/values.yaml | 2 ++ 6 files changed, 16 insertions(+), 64 deletions(-) delete mode 100644 scripts/helmcharts/openreplay/charts/assist-api/templates/ingress.yaml diff --git a/scripts/helmcharts/openreplay/Chart.yaml b/scripts/helmcharts/openreplay/Chart.yaml index 71f89e366..664a32226 100644 --- a/scripts/helmcharts/openreplay/Chart.yaml +++ b/scripts/helmcharts/openreplay/Chart.yaml @@ -37,3 +37,7 @@ dependencies: repository: file://charts/connector version: 0.1.1 condition: connector.enabled + - name: assist-api + repository: file://charts/assist-api + version: 0.1.1 + condition: assist-api.enabled diff --git a/scripts/helmcharts/openreplay/charts/assist-api/templates/deployment.yaml b/scripts/helmcharts/openreplay/charts/assist-api/templates/deployment.yaml index e8540295b..b1b540590 100644 --- a/scripts/helmcharts/openreplay/charts/assist-api/templates/deployment.yaml +++ b/scripts/helmcharts/openreplay/charts/assist-api/templates/deployment.yaml @@ -45,10 +45,6 @@ spec: env: - name: LICENSE_KEY value: '{{ .Values.global.enterpriseEditionLicense }}' - - name: KAFKA_SERVERS - value: '{{ .Values.global.kafka.kafkaHost }}:{{ .Values.global.kafka.kafkaPort }}' - - name: KAFKA_USE_SSL - value: '{{ .Values.global.kafka.kafkaUseSsl }}' - name: ASSIST_KEY value: {{ .Values.global.assistKey }} - name: pg_password @@ -62,6 +58,8 @@ spec: {{- end}} - name: POSTGRES_STRING value: 'postgres://{{ .Values.global.postgresql.postgresqlUser }}:$(pg_password)@{{ .Values.global.postgresql.postgresqlHost }}:{{ .Values.global.postgresql.postgresqlPort }}/{{ .Values.global.postgresql.postgresqlDatabase }}' + - name: REDIS_CACHE_ENABLED + value: {{- if .Values.global.enterpriseEditionLicense }}true{{- else }}false{{- end }} {{- include "openreplay.env.redis_string" .Values.global.redis | nindent 12 }} {{- range $key, $val := .Values.global.env }} - name: {{ $key }} diff --git a/scripts/helmcharts/openreplay/charts/assist-api/templates/ingress.yaml b/scripts/helmcharts/openreplay/charts/assist-api/templates/ingress.yaml deleted file mode 100644 index 6341db4f2..000000000 --- a/scripts/helmcharts/openreplay/charts/assist-api/templates/ingress.yaml +++ /dev/null @@ -1,36 +0,0 @@ -{{- if .Values.ingress.enabled -}} -{{- $fullName := include "assist-api.fullname" . -}} -{{- $svcPort := .Values.service.ports.http -}} -apiVersion: networking.k8s.io/v1 -kind: Ingress -metadata: - name: {{ $fullName }} - namespace: {{ .Release.Namespace }} - labels: - {{- include "assist-api.labels" . | nindent 4 }} - annotations: - nginx.ingress.kubernetes.io/rewrite-target: /$1 - nginx.ingress.kubernetes.io/upstream-hash-by: $http_x_forwarded_for - {{- with .Values.ingress.annotations }} - {{- toYaml . | nindent 4 }} - {{- end }} -spec: - ingressClassName: "{{ tpl .Values.ingress.className . }}" - tls: - - hosts: - - {{ .Values.global.domainName }} - {{- if .Values.ingress.tls.secretName}} - secretName: {{ .Values.ingress.tls.secretName }} - {{- end }} - rules: - - host: {{ .Values.global.domainName }} - http: - paths: - - path: /api-assist/(.*) - pathType: Prefix - backend: - service: - name: {{ $fullName }} - port: - number: {{ $svcPort }} -{{- end }} diff --git a/scripts/helmcharts/openreplay/charts/assist/templates/deployment.yaml b/scripts/helmcharts/openreplay/charts/assist/templates/deployment.yaml index a4af9a9f8..bb2c75577 100644 --- a/scripts/helmcharts/openreplay/charts/assist/templates/deployment.yaml +++ b/scripts/helmcharts/openreplay/charts/assist/templates/deployment.yaml @@ -49,30 +49,6 @@ spec: value: {{ .Values.global.assistKey }} - name: AWS_DEFAULT_REGION value: "{{ .Values.global.s3.region }}" - - name: S3_HOST - {{- if contains "minio" .Values.global.s3.endpoint }} - value: '{{ ternary "https" "http" .Values.global.ORSecureAccess}}://{{ .Values.global.domainName }}:{{ ternary .Values.global.ingress.controller.service.ports.https .Values.global.ingress.controller.service.ports.http .Values.global.ORSecureAccess }}' - {{- else}} - value: '{{ .Values.global.s3.endpoint }}' - {{- end}} - - name: S3_KEY - {{- if .Values.global.s3.existingSecret }} - valueFrom: - secretKeyRef: - name: {{ .Values.global.s3.existingSecret }} - key: access-key - {{- else }} - value: {{ .Values.global.s3.accessKey }} - {{- end }} - - name: S3_SECRET - {{- if .Values.global.s3.existingSecret }} - valueFrom: - secretKeyRef: - name: {{ .Values.global.s3.existingSecret }} - key: secret-key - {{- else }} - value: {{ .Values.global.s3.secretKey }} - {{- end }} - name: REDIS_URL value: {{ .Values.global.redis.redisHost }} {{- range $key, $val := .Values.global.env }} diff --git a/scripts/helmcharts/openreplay/templates/_helpers.tpl b/scripts/helmcharts/openreplay/templates/_helpers.tpl index e239d2f8f..ddbcb764d 100644 --- a/scripts/helmcharts/openreplay/templates/_helpers.tpl +++ b/scripts/helmcharts/openreplay/templates/_helpers.tpl @@ -161,3 +161,11 @@ Create the volume mount config for redis TLS certificates {{- printf "postgres://%s:$(pg_password)@%s:%s/%s" .Values.global.postgresql.postgresqlUser .Values.global.postgresql.postgresqlHost .Values.global.postgresql.postgresqlPort .Values.global.postgresql.postgresqlDatabase -}} {{- end -}} {{- end}} + +{{- define "openreplay.assist_url"}} +{{- if .Values.global.enterpriseEditionLicense }} +assist-api-openreplay:8080 +{{- else}} +assist-openreplay:8080 +{{- end}} +{{- end}} diff --git a/scripts/helmcharts/openreplay/values.yaml b/scripts/helmcharts/openreplay/values.yaml index 8be97a2dc..627db73c6 100644 --- a/scripts/helmcharts/openreplay/values.yaml +++ b/scripts/helmcharts/openreplay/values.yaml @@ -47,6 +47,8 @@ vault: &vault {{- with secret "database/creds/db-app" -}} POSTGRES_STRING=postgres://{{.Data.username}}:{{.Data.password}}@postgresql.db.svc.cluster.local:5432/postgres {{- end -}} +assist-api: + enabled: false minio: # Force initialize minio, even if the instance is not provisioned by OR From bace9735df32fbe4415937e6031a4afac55ef250 Mon Sep 17 00:00:00 2001 From: rjshrjndrn Date: Tue, 27 May 2025 13:00:06 +0200 Subject: [PATCH 07/13] feat: add enterprise edition config to assist chart - Add COMPRESSION, port, CACHE_REFRESH_INTERVAL_SECONDS, and debug environment variables for enterprise edition deployments - Use conditional logic based on enterpriseEditionLicense flag - Update REDIS_URL to support both global and local redis config with fallback using default template function This enables enterprise-specific features and improves redis configuration flexibility for the assist service. Signed-off-by: rjshrjndrn --- .../charts/assist/templates/deployment.yaml | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/scripts/helmcharts/openreplay/charts/assist/templates/deployment.yaml b/scripts/helmcharts/openreplay/charts/assist/templates/deployment.yaml index bb2c75577..99552f88c 100644 --- a/scripts/helmcharts/openreplay/charts/assist/templates/deployment.yaml +++ b/scripts/helmcharts/openreplay/charts/assist/templates/deployment.yaml @@ -50,7 +50,17 @@ spec: - name: AWS_DEFAULT_REGION value: "{{ .Values.global.s3.region }}" - name: REDIS_URL - value: {{ .Values.global.redis.redisHost }} + value: {{ default .Values.global.redis.redisHost .Values.redis.redisHost }} + {{- if .Values.global.enterpriseEditionLicense }} + - name: COMPRESSION + value: "true" + - name: port + value: "9000" + - name: CACHE_REFRESH_INTERVAL_SECONDS + value: '5' + - name: debug + value: '0' + {{- end}} {{- range $key, $val := .Values.global.env }} - name: {{ $key }} value: '{{ $val }}' From 70dda4032a363038b9c0503f2f5003f664158e9b Mon Sep 17 00:00:00 2001 From: rjshrjndrn Date: Tue, 27 May 2025 13:06:56 +0200 Subject: [PATCH 08/13] feat: add FQDN and path template to assist URL helper - Replace simple hostnames with fully qualified domain names - Include namespace and cluster domain in assist service URLs - Add /assist/%s path pattern for both enterprise and standard editions - Ensures proper service discovery across Kubernetes namespaces Signed-off-by: rjshrjndrn --- .../charts/assist/templates/ingress.yaml | 19 ------------------- .../charts/chalice/templates/deployment.yaml | 2 +- .../openreplay/templates/_helpers.tpl | 4 ++-- 3 files changed, 3 insertions(+), 22 deletions(-) diff --git a/scripts/helmcharts/openreplay/charts/assist/templates/ingress.yaml b/scripts/helmcharts/openreplay/charts/assist/templates/ingress.yaml index 41436c679..df355cefe 100644 --- a/scripts/helmcharts/openreplay/charts/assist/templates/ingress.yaml +++ b/scripts/helmcharts/openreplay/charts/assist/templates/ingress.yaml @@ -10,25 +10,6 @@ metadata: {{- include "assist.labels" . | nindent 4 }} annotations: nginx.ingress.kubernetes.io/rewrite-target: /$1 - nginx.ingress.kubernetes.io/configuration-snippet: | - #set $sticky_used "no"; - #if ($sessionid != "") { - # set $sticky_used "yes"; - #} - - #add_header X-Debug-Session-ID $sessionid; - #add_header X-Debug-Session-Type "wss"; - #add_header X-Sticky-Session-Used $sticky_used; - #add_header X-Upstream-Server $upstream_addr; - - proxy_hide_header access-control-allow-headers; - proxy_hide_header Access-Control-Allow-Origin; - add_header 'Access-Control-Allow-Origin' $http_origin always; - add_header 'Access-Control-Allow-Methods' 'GET, OPTIONS' always; - add_header 'Access-Control-Allow-Headers' 'sessionid, Content-Type, Authorization' always; - add_header 'Access-Control-Max-Age' 1728000; - add_header 'Content-Type' 'text/plain charset=UTF-8'; - nginx.ingress.kubernetes.io/upstream-hash-by: $sessionid {{- with .Values.ingress.annotations }} diff --git a/scripts/helmcharts/openreplay/charts/chalice/templates/deployment.yaml b/scripts/helmcharts/openreplay/charts/chalice/templates/deployment.yaml index 3d86796a7..f690fcb33 100644 --- a/scripts/helmcharts/openreplay/charts/chalice/templates/deployment.yaml +++ b/scripts/helmcharts/openreplay/charts/chalice/templates/deployment.yaml @@ -59,7 +59,7 @@ spec: - name: sourcemaps_reader value: "http://sourcemapreader-openreplay.{{.Release.Namespace}}.{{.Values.global.clusterDomain}}:9000/%s/sourcemaps" - name: ASSIST_URL - value: "http://assist-openreplay.{{.Release.Namespace}}.{{.Values.global.clusterDomain}}:9001/assist/%s" + value: {{- include "openreplay.assist_url" . }} - name: ASSIST_JWT_SECRET value: {{ .Values.global.assistJWTSecret }} - name: JWT_SECRET diff --git a/scripts/helmcharts/openreplay/templates/_helpers.tpl b/scripts/helmcharts/openreplay/templates/_helpers.tpl index ddbcb764d..9a99eadf3 100644 --- a/scripts/helmcharts/openreplay/templates/_helpers.tpl +++ b/scripts/helmcharts/openreplay/templates/_helpers.tpl @@ -164,8 +164,8 @@ Create the volume mount config for redis TLS certificates {{- define "openreplay.assist_url"}} {{- if .Values.global.enterpriseEditionLicense }} -assist-api-openreplay:8080 +"http://assist-api-openreplay.{{.Release.Namespace}}.{{.Values.global.clusterDomain}}:9001/assist/%s" {{- else}} -assist-openreplay:8080 +"http://assist-openreplay.{{.Release.Namespace}}.{{.Values.global.clusterDomain}}:9001/assist/%s" {{- end}} {{- end}} From ceabceb18468229ec2e0ed4f90a9f61b452d2f68 Mon Sep 17 00:00:00 2001 From: rjshrjndrn Date: Tue, 27 May 2025 15:13:35 +0200 Subject: [PATCH 09/13] fix(helm): standardize template syntax and fix variable references - Add quotes around boolean string values in REDIS_CACHE_ENABLED - Fix variable reference from .Values.redis.redisHost to .Values.redisHost - Remove extra hyphens in template expressions for cleaner syntax - Update assist_url helper to use printf for better string formatting These changes ensure consistent Helm template syntax and fix broken variable references that could cause deployment issues. Signed-off-by: rjshrjndrn --- .../openreplay/charts/assist-api/templates/deployment.yaml | 2 +- .../openreplay/charts/assist/templates/deployment.yaml | 2 +- .../openreplay/charts/chalice/templates/deployment.yaml | 2 +- scripts/helmcharts/openreplay/templates/_helpers.tpl | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/scripts/helmcharts/openreplay/charts/assist-api/templates/deployment.yaml b/scripts/helmcharts/openreplay/charts/assist-api/templates/deployment.yaml index b1b540590..232b9528f 100644 --- a/scripts/helmcharts/openreplay/charts/assist-api/templates/deployment.yaml +++ b/scripts/helmcharts/openreplay/charts/assist-api/templates/deployment.yaml @@ -59,7 +59,7 @@ spec: - name: POSTGRES_STRING value: 'postgres://{{ .Values.global.postgresql.postgresqlUser }}:$(pg_password)@{{ .Values.global.postgresql.postgresqlHost }}:{{ .Values.global.postgresql.postgresqlPort }}/{{ .Values.global.postgresql.postgresqlDatabase }}' - name: REDIS_CACHE_ENABLED - value: {{- if .Values.global.enterpriseEditionLicense }}true{{- else }}false{{- end }} + value: {{ if .Values.global.enterpriseEditionLicense }}"true"{{ else }}"false"{{ end }} {{- include "openreplay.env.redis_string" .Values.global.redis | nindent 12 }} {{- range $key, $val := .Values.global.env }} - name: {{ $key }} diff --git a/scripts/helmcharts/openreplay/charts/assist/templates/deployment.yaml b/scripts/helmcharts/openreplay/charts/assist/templates/deployment.yaml index 99552f88c..d12498255 100644 --- a/scripts/helmcharts/openreplay/charts/assist/templates/deployment.yaml +++ b/scripts/helmcharts/openreplay/charts/assist/templates/deployment.yaml @@ -50,7 +50,7 @@ spec: - name: AWS_DEFAULT_REGION value: "{{ .Values.global.s3.region }}" - name: REDIS_URL - value: {{ default .Values.global.redis.redisHost .Values.redis.redisHost }} + value: {{ default .Values.global.redis.redisHost .Values.redisHost }} {{- if .Values.global.enterpriseEditionLicense }} - name: COMPRESSION value: "true" diff --git a/scripts/helmcharts/openreplay/charts/chalice/templates/deployment.yaml b/scripts/helmcharts/openreplay/charts/chalice/templates/deployment.yaml index f690fcb33..a18256e30 100644 --- a/scripts/helmcharts/openreplay/charts/chalice/templates/deployment.yaml +++ b/scripts/helmcharts/openreplay/charts/chalice/templates/deployment.yaml @@ -59,7 +59,7 @@ spec: - name: sourcemaps_reader value: "http://sourcemapreader-openreplay.{{.Release.Namespace}}.{{.Values.global.clusterDomain}}:9000/%s/sourcemaps" - name: ASSIST_URL - value: {{- include "openreplay.assist_url" . }} + value: {{ include "openreplay.assist_url" . }} - name: ASSIST_JWT_SECRET value: {{ .Values.global.assistJWTSecret }} - name: JWT_SECRET diff --git a/scripts/helmcharts/openreplay/templates/_helpers.tpl b/scripts/helmcharts/openreplay/templates/_helpers.tpl index 9a99eadf3..e25379b03 100644 --- a/scripts/helmcharts/openreplay/templates/_helpers.tpl +++ b/scripts/helmcharts/openreplay/templates/_helpers.tpl @@ -164,8 +164,8 @@ Create the volume mount config for redis TLS certificates {{- define "openreplay.assist_url"}} {{- if .Values.global.enterpriseEditionLicense }} -"http://assist-api-openreplay.{{.Release.Namespace}}.{{.Values.global.clusterDomain}}:9001/assist/%s" +{{- printf "http://assist-api-openreplay.%s.%s:9001/assist/%s" .Release.Namespace .Values.global.clusterDomain }} {{- else}} -"http://assist-openreplay.{{.Release.Namespace}}.{{.Values.global.clusterDomain}}:9001/assist/%s" +{{- printf "http://assist-openreplay.%s.%s:9001/assist/%s" .Release.Namespace .Values.global.clusterDomain }} {{- end}} {{- end}} From 3bd2578c9c872de1204e6612a9aad5de3c5f8ae4 Mon Sep 17 00:00:00 2001 From: rjshrjndrn Date: Tue, 27 May 2025 15:30:12 +0200 Subject: [PATCH 10/13] fix(helmcharts): clickhouse fix the configmap mount Signed-off-by: rjshrjndrn --- .../clickhouse/templates/configmap.yaml | 2 + .../databases/charts/clickhouse/values.yaml | 37 +++++++++---------- 2 files changed, 20 insertions(+), 19 deletions(-) diff --git a/scripts/helmcharts/databases/charts/clickhouse/templates/configmap.yaml b/scripts/helmcharts/databases/charts/clickhouse/templates/configmap.yaml index 709e0929e..4fdd5709d 100644 --- a/scripts/helmcharts/databases/charts/clickhouse/templates/configmap.yaml +++ b/scripts/helmcharts/databases/charts/clickhouse/templates/configmap.yaml @@ -1,3 +1,4 @@ +{{- if .Values.configOverride.serverConfig }} --- apiVersion: v1 kind: ConfigMap @@ -8,6 +9,7 @@ data: {{ $filename }}: |- {{ $content | indent 4 }} {{- end }} +{{- end }} --- apiVersion: v1 kind: ConfigMap diff --git a/scripts/helmcharts/databases/charts/clickhouse/values.yaml b/scripts/helmcharts/databases/charts/clickhouse/values.yaml index 641d80bab..68a42f494 100644 --- a/scripts/helmcharts/databases/charts/clickhouse/values.yaml +++ b/scripts/helmcharts/databases/charts/clickhouse/values.yaml @@ -87,27 +87,26 @@ storageSize: 100Gi configOverride: serverConfig: zz-server-override.xml: |- - # - # - # information - # true - # - # - # - # 0.0.0.0 - # 100 - # 64 - # 2 - # fair_round_robin - # 102400000000 - # 10000 - # 0.8 - # - # 26214 - # + + + information + true + + + + 0.0.0.0 + 100 + # another-config.xml: |- # - # value + # 64 + # 2 + # fair_round_robin + # 102400000000 + # 10000 + # 0.8 + # + # 26214 # userConfig: zz-user-override.xml: |- From 7217517a116df97efd07842077f5f2b2334a8d11 Mon Sep 17 00:00:00 2001 From: rjshrjndrn Date: Tue, 27 May 2025 16:05:29 +0200 Subject: [PATCH 11/13] fix(assist): url templating Signed-off-by: rjshrjndrn --- scripts/helmcharts/openreplay/templates/_helpers.tpl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/helmcharts/openreplay/templates/_helpers.tpl b/scripts/helmcharts/openreplay/templates/_helpers.tpl index e25379b03..82419631f 100644 --- a/scripts/helmcharts/openreplay/templates/_helpers.tpl +++ b/scripts/helmcharts/openreplay/templates/_helpers.tpl @@ -164,8 +164,8 @@ Create the volume mount config for redis TLS certificates {{- define "openreplay.assist_url"}} {{- if .Values.global.enterpriseEditionLicense }} -{{- printf "http://assist-api-openreplay.%s.%s:9001/assist/%s" .Release.Namespace .Values.global.clusterDomain }} +{{- printf "http://assist-api-openreplay.%s.%s:9001/assist/%%s" .Release.Namespace .Values.global.clusterDomain }} {{- else}} -{{- printf "http://assist-openreplay.%s.%s:9001/assist/%s" .Release.Namespace .Values.global.clusterDomain }} +{{- printf "http://assist-openreplay.%s.%s:9001/assist/%%s" .Release.Namespace .Values.global.clusterDomain }} {{- end}} {{- end}} From 517fe6c99e0b72dc144f4960cfdd9c4c923decdb Mon Sep 17 00:00:00 2001 From: jonathan-caleb-griffin <124153003+jonathan-caleb-griffin@users.noreply.github.com> Date: Tue, 27 May 2025 17:11:46 +0200 Subject: [PATCH 12/13] allow thread prompt suggestions (#3442) Co-authored-by: Jonathan Griffin --- frontend/app/components/Kai/KaiService.ts | 4 +++- frontend/app/components/Kai/components/ChatLog.tsx | 2 ++ frontend/app/components/Kai/components/Ideas.tsx | 6 +++--- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/frontend/app/components/Kai/KaiService.ts b/frontend/app/components/Kai/KaiService.ts index 1843af9cd..48ab8eafc 100644 --- a/frontend/app/components/Kai/KaiService.ts +++ b/frontend/app/components/Kai/KaiService.ts @@ -126,8 +126,10 @@ export default class KaiService extends AiService { getPromptSuggestions = async ( projectId: string, + threadId?: string | null, ): Promise => { - const r = await this.client.get(`/kai/${projectId}/prompt-suggestions`); + const endpoint = (threadId) ? `/kai/${projectId}/chats/${threadId}/prompt-suggestions` : `/kai/${projectId}/prompt-suggestions`; + const r = await this.client.get(endpoint); if (!r.ok) { throw new Error('Failed to fetch prompt suggestions'); } diff --git a/frontend/app/components/Kai/components/ChatLog.tsx b/frontend/app/components/Kai/components/ChatLog.tsx index ecb4405b4..5c3dca6c5 100644 --- a/frontend/app/components/Kai/components/ChatLog.tsx +++ b/frontend/app/components/Kai/components/ChatLog.tsx @@ -1,6 +1,7 @@ import React from 'react'; import ChatInput from './ChatInput'; import ChatMsg, { ChatNotice } from './ChatMsg'; +import Ideas from "./Ideas"; import { Loader } from 'UI'; import { kaiStore } from '../KaiStore'; import { observer } from 'mobx-react-lite'; @@ -81,6 +82,7 @@ function ChatLog({ duration={processingStage.duration} /> ) : null} + {(!processingStage && lastHumanMsgInd && messages.length == lastHumanMsgInd + 2) ? onSubmit(query)} projectId={projectId} threadId={threadId}/> : null}
diff --git a/frontend/app/components/Kai/components/Ideas.tsx b/frontend/app/components/Kai/components/Ideas.tsx index 2cca2d444..f1104756e 100644 --- a/frontend/app/components/Kai/components/Ideas.tsx +++ b/frontend/app/components/Kai/components/Ideas.tsx @@ -4,14 +4,14 @@ import { useQuery } from '@tanstack/react-query'; import { kaiService } from 'App/services'; import { useTranslation } from 'react-i18next'; -function Ideas({ onClick, projectId }: { onClick: (query: string) => void, projectId: string }) { +function Ideas({ onClick, projectId, threadId = null }: { onClick: (query: string) => void, projectId: string, threadId?: string | null }) { const { t } = useTranslation(); const { data: suggestedPromptIdeas = [], isPending, } = useQuery({ - queryKey: ['kai', 'prompt-suggestions', projectId], - queryFn: () => kaiService.getPromptSuggestions(projectId), + queryKey: ['kai', projectId, 'chats', threadId, 'prompt-suggestions'], + queryFn: () => kaiService.getPromptSuggestions(projectId, threadId), staleTime: 1000 * 60, }); const ideas = React.useMemo(() => { From 4e2158ab641971b827dbe15886ee925ff9ba4280 Mon Sep 17 00:00:00 2001 From: Delirium Date: Tue, 27 May 2025 17:46:44 +0200 Subject: [PATCH 13/13] new Kai design (#3443) * redesign is inevitable * ui: change kai design, add sessions to list, swap to new icon * ui: some changes for suggestions in thread --- frontend/app/components/Kai/KaiChat.tsx | 84 ++++++++----- frontend/app/components/Kai/KaiService.ts | 5 +- frontend/app/components/Kai/KaiStore.ts | 11 +- frontend/app/components/Kai/SocketManager.ts | 8 +- .../components/Kai/components/ChatHeader.tsx | 82 +++++++------ .../components/Kai/components/ChatInput.tsx | 111 +++++++++++++----- .../app/components/Kai/components/ChatLog.tsx | 25 ++-- .../app/components/Kai/components/ChatMsg.tsx | 67 +++++------ .../components/Kai/components/ChatsModal.tsx | 7 +- .../app/components/Kai/components/Ideas.tsx | 77 +++++++----- .../Kai/components/IntroSection.tsx | 32 +++-- frontend/app/components/Modal/index.tsx | 2 + .../shared/SessionItem/SessionItem.tsx | 10 +- frontend/app/components/ui/Icons/index.ts | 1 + frontend/app/components/ui/Icons/kai_mono.tsx | 18 +++ frontend/app/components/ui/SVG.tsx | 6 +- frontend/app/svg/icons/kai-mono.svg | 11 ++ 17 files changed, 365 insertions(+), 192 deletions(-) create mode 100644 frontend/app/components/ui/Icons/kai_mono.tsx create mode 100644 frontend/app/svg/icons/kai-mono.svg diff --git a/frontend/app/components/Kai/KaiChat.tsx b/frontend/app/components/Kai/KaiChat.tsx index d3952cffa..4d3d13586 100644 --- a/frontend/app/components/Kai/KaiChat.tsx +++ b/frontend/app/components/Kai/KaiChat.tsx @@ -18,7 +18,7 @@ function KaiChat() { const chatTitle = kaiStore.chatTitle; const setTitle = kaiStore.setTitle; const userId = userStore.account.id; - const userLetter = userStore.account.name[0].toUpperCase(); + const userName = userStore.account.name; const { activeSiteId } = projectsStore; const [section, setSection] = React.useState<'intro' | 'chat'>('intro'); const [threadId, setThreadId] = React.useState(null); @@ -44,7 +44,11 @@ function KaiChat() { hideModal(); }} />, - { right: true, width: 300 }, + { + right: true, + width: 320, + className: 'bg-none flex items-center h-screen', + }, ); }; @@ -93,44 +97,70 @@ function KaiChat() { const newThread = await kaiService.createKaiChat(activeSiteId); if (newThread) { setThreadId(newThread.toString()); + kaiStore.setTitle(null); setSection('chat'); } else { toast.error("Something wen't wrong. Please try again later."); } }; + + const onCancel = () => { + if (!threadId) return; + void kaiStore.cancelGeneration({ + projectId: activeSiteId, + threadId, + }); + }; + return ( -
+
-
- {section === 'intro' ? ( - - ) : ( - - )} -
+ {section === 'intro' ? ( + <> +
+ +
+
+ OpenReplay AI can make mistakes. Verify its outputs. +
+ + ) : ( + + )}
); diff --git a/frontend/app/components/Kai/KaiService.ts b/frontend/app/components/Kai/KaiService.ts index 48ab8eafc..22cade0f8 100644 --- a/frontend/app/components/Kai/KaiService.ts +++ b/frontend/app/components/Kai/KaiService.ts @@ -36,6 +36,7 @@ export default class KaiService extends AiService { supports_visualization: boolean; chart: string; chart_data: string; + sessions?: Record[]; }[]; title: string; }> => { @@ -128,7 +129,9 @@ export default class KaiService extends AiService { projectId: string, threadId?: string | null, ): Promise => { - const endpoint = (threadId) ? `/kai/${projectId}/chats/${threadId}/prompt-suggestions` : `/kai/${projectId}/prompt-suggestions`; + const endpoint = threadId + ? `/kai/${projectId}/chats/${threadId}/prompt-suggestions` + : `/kai/${projectId}/prompt-suggestions`; const r = await this.client.get(endpoint); if (!r.ok) { throw new Error('Failed to fetch prompt suggestions'); diff --git a/frontend/app/components/Kai/KaiStore.ts b/frontend/app/components/Kai/KaiStore.ts index 7369b25b9..e7f496176 100644 --- a/frontend/app/components/Kai/KaiStore.ts +++ b/frontend/app/components/Kai/KaiStore.ts @@ -3,6 +3,7 @@ import { BotChunk, ChatManager } from './SocketManager'; import { kaiService as aiService, kaiService } from 'App/services'; import { toast } from 'react-toastify'; import Widget from 'App/mstore/types/widget'; +import Session, { ISession } from '@/types/session/session'; export interface Message { text: string; @@ -15,6 +16,7 @@ export interface Message { supports_visualization: boolean; feedback: boolean | null; duration: number; + sessions?: Session[]; } export interface SentMessage extends Omit< @@ -161,6 +163,9 @@ class KaiStore { chart: m.chart, supports_visualization: m.supports_visualization, chart_data: m.chart_data, + sessions: m.sessions + ? m.sessions.map((s) => new Session(s)) + : undefined, }; }), ); @@ -220,6 +225,9 @@ class KaiStore { chart: '', supports_visualization: msg.supports_visualization, chart_data: '', + sessions: msg.sessions + ? msg.sessions.map((s) => new Session(s)) + : undefined, }; this.bumpUsage(); this.addMessage(msgObj); @@ -268,7 +276,7 @@ class KaiStore { deleting.push(this.lastKaiMessage.index); } this.deleteAtIndex(deleting); - this.setReplacing(false); + this.setReplacing(null); } this.addMessage({ text: message, @@ -309,7 +317,6 @@ class KaiStore { cancelGeneration = async (settings: { projectId: string; - userId: string; threadId: string; }) => { try { diff --git a/frontend/app/components/Kai/SocketManager.ts b/frontend/app/components/Kai/SocketManager.ts index 7fcda3171..5906890c3 100644 --- a/frontend/app/components/Kai/SocketManager.ts +++ b/frontend/app/components/Kai/SocketManager.ts @@ -1,5 +1,6 @@ import io from 'socket.io-client'; import { toast } from 'react-toastify'; +import { ISession } from '@/types/session/session'; export class ChatManager { socket: ReturnType; @@ -77,9 +78,7 @@ export class ChatManager { msgCallback, titleCallback, }: { - msgCallback: ( - msg: StateEvent | BotChunk, - ) => void; + msgCallback: (msg: StateEvent | BotChunk) => void; titleCallback: (title: string) => void; }) => { this.socket.on('chunk', (msg: BotChunk) => { @@ -111,7 +110,8 @@ export interface BotChunk { messageId: string; duration: number; supports_visualization: boolean; - type: 'chunk' + sessions?: ISession[]; + type: 'chunk'; } interface StateEvent { diff --git a/frontend/app/components/Kai/components/ChatHeader.tsx b/frontend/app/components/Kai/components/ChatHeader.tsx index ae3cb4068..29aaa4365 100644 --- a/frontend/app/components/Kai/components/ChatHeader.tsx +++ b/frontend/app/components/Kai/components/ChatHeader.tsx @@ -1,56 +1,68 @@ import React from 'react'; import { Icon } from 'UI'; -import { MessagesSquare, ArrowLeft } from 'lucide-react'; +import { MessagesSquare, ArrowLeft, SquarePen } from 'lucide-react'; import { useTranslation } from 'react-i18next'; function ChatHeader({ openChats = () => {}, goBack, chatTitle, + onCreate, }: { goBack?: () => void; openChats?: () => void; chatTitle: string | null; + onCreate: () => void; }) { const { t } = useTranslation(); + //absolute top-0 left-0 right-0 z-10 return ( -
-
- {goBack ? ( +
+
+
+ {goBack ? ( +
+ +
{t('Back')}
+
+ ) : ( +
+ +
Kai
+
+ )} +
+
+ {chatTitle ? ( +
+ {chatTitle} +
+ ) : null} +
+
+ {goBack ? ( +
+ +
{t('New Chat')}
+
+ ) : null}
- -
{t('Back')}
+ +
{t('Chats')}
- ) : null} -
-
- {chatTitle ? ( -
- {chatTitle} -
- ) : ( - <> - -
Kai
- - )} -
-
-
- -
{t('Chats')}
diff --git a/frontend/app/components/Kai/components/ChatInput.tsx b/frontend/app/components/Kai/components/ChatInput.tsx index 237b79d6c..b860c4c32 100644 --- a/frontend/app/components/Kai/components/ChatInput.tsx +++ b/frontend/app/components/Kai/components/ChatInput.tsx @@ -1,6 +1,6 @@ import React from 'react'; import { Button, Input, Tooltip } from 'antd'; -import { SendHorizonal, OctagonX } from 'lucide-react'; +import { X, ArrowUp } from 'lucide-react'; import { kaiStore } from '../KaiStore'; import { observer } from 'mobx-react-lite'; import Usage from './Usage'; @@ -8,11 +8,13 @@ import Usage from './Usage'; function ChatInput({ isLoading, onSubmit, - threadId, + isArea, + onCancel, }: { isLoading?: boolean; onSubmit: (str: string) => void; - threadId: string; + onCancel: () => void; + isArea?: boolean; }) { const inputRef = React.useRef(null); const usage = kaiStore.usage; @@ -28,8 +30,7 @@ function ChatInput({ return; } if (isProcessing) { - const settings = { projectId: '2325', userId: '0', threadId }; - void kaiStore.cancelGeneration(settings); + onCancel(); } else { if (inputValue.length > 0) { onSubmit(inputValue); @@ -50,7 +51,34 @@ function ChatInput({ }, [inputValue]); const isReplacing = kaiStore.replacing !== null; - + const placeholder = limited + ? `You've reached the daily limit for queries, come again tomorrow!` + : 'Ask anything about your product and users...'; + if (isArea) { + return ( +
+ setInputValue(e.target.value)} + /> +
+ +
+
+ ); + } return (
setInputValue(e.target.value)} @@ -76,31 +101,19 @@ function ChatInput({ + + ); +} + export default observer(ChatInput); diff --git a/frontend/app/components/Kai/components/ChatLog.tsx b/frontend/app/components/Kai/components/ChatLog.tsx index 5c3dca6c5..d06e9df4b 100644 --- a/frontend/app/components/Kai/components/ChatLog.tsx +++ b/frontend/app/components/Kai/components/ChatLog.tsx @@ -1,7 +1,7 @@ import React from 'react'; import ChatInput from './ChatInput'; import ChatMsg, { ChatNotice } from './ChatMsg'; -import Ideas from "./Ideas"; +import Ideas from './Ideas'; import { Loader } from 'UI'; import { kaiStore } from '../KaiStore'; import { observer } from 'mobx-react-lite'; @@ -9,17 +9,17 @@ import { observer } from 'mobx-react-lite'; function ChatLog({ projectId, threadId, - userLetter, initialMsg, chatTitle, setInitialMsg, + onCancel, }: { projectId: string; threadId: any; - userLetter: string; initialMsg: string | null; setInitialMsg: (msg: string | null) => void; chatTitle: string | null; + onCancel: () => void; }) { const messages = kaiStore.messages; const loading = kaiStore.loadingChat; @@ -51,11 +51,15 @@ function ChatLog({ }); }, [messages.length, processingStage]); - const lastHumanMsgInd: null | number = kaiStore.lastHumanMessage.index; + const lastKaiMessageInd: null | number = kaiStore.lastKaiMessage.index; + const lastHumanMsgInd: number | null = kaiStore.lastHumanMessage.index; + const showIdeas = + !processingStage && lastKaiMessageInd === messages.length - 1; return (
( ) : null} - {(!processingStage && lastHumanMsgInd && messages.length == lastHumanMsgInd + 2) ? onSubmit(query)} projectId={projectId} threadId={threadId}/> : null} + {showIdeas ? ( + onSubmit(query)} + projectId={projectId} + threadId={threadId} + /> + ) : null}
-
- +
+
diff --git a/frontend/app/components/Kai/components/ChatMsg.tsx b/frontend/app/components/Kai/components/ChatMsg.tsx index d335952ae..5239f9745 100644 --- a/frontend/app/components/Kai/components/ChatMsg.tsx +++ b/frontend/app/components/Kai/components/ChatMsg.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import { Icon, CopyButton } from 'UI'; +import { CopyButton } from 'UI'; import { observer } from 'mobx-react-lite'; import cn from 'classnames'; import Markdown from 'react-markdown'; @@ -8,7 +8,7 @@ import { Loader, ThumbsUp, ThumbsDown, - ListRestart, + SquarePen, FileDown, Clock, ChartLine, @@ -20,6 +20,7 @@ import { durationFormatted } from 'App/date'; import WidgetChart from '@/components/Dashboard/components/WidgetChart'; import Widget from 'App/mstore/types/widget'; import { useTranslation } from 'react-i18next'; +import SessionItem from 'Shared/SessionItem'; function ChatMsg({ userName, @@ -168,28 +169,12 @@ function ChatMsg({ }, [metricData, chart_data]); return (
- {isUser ? ( -
- {userName} -
- ) : ( -
- -
- )} -
+
) : null} + {message.sessions ? ( +
+ {message.sessions.map((session) => ( +
+ +
+ ))} +
+ ) : null} {isUser ? ( - <> -
- -
{t('Edit')}
-
+
+ +
+ +
+
{t('Cancel')}
- +
) : (
{duration ? : null} diff --git a/frontend/app/components/Kai/components/ChatsModal.tsx b/frontend/app/components/Kai/components/ChatsModal.tsx index 16f374d15..8fb279aea 100644 --- a/frontend/app/components/Kai/components/ChatsModal.tsx +++ b/frontend/app/components/Kai/components/ChatsModal.tsx @@ -44,10 +44,13 @@ function ChatsModal({ refetch(); }; return ( -
+
- {t('Chats')} + {t('Previous Chats')}
{usage.percent > 80 ? (
diff --git a/frontend/app/components/Kai/components/Ideas.tsx b/frontend/app/components/Kai/components/Ideas.tsx index f1104756e..bade87acb 100644 --- a/frontend/app/components/Kai/components/Ideas.tsx +++ b/frontend/app/components/Kai/components/Ideas.tsx @@ -4,54 +4,69 @@ import { useQuery } from '@tanstack/react-query'; import { kaiService } from 'App/services'; import { useTranslation } from 'react-i18next'; -function Ideas({ onClick, projectId, threadId = null }: { onClick: (query: string) => void, projectId: string, threadId?: string | null }) { +function Ideas({ + onClick, + projectId, + threadId = null, +}: { + onClick: (query: string) => void; + projectId: string; + threadId?: string | null; +}) { const { t } = useTranslation(); - const { - data: suggestedPromptIdeas = [], - isPending, - } = useQuery({ - queryKey: ['kai', projectId, 'chats', threadId, 'prompt-suggestions'], - queryFn: () => kaiService.getPromptSuggestions(projectId, threadId), - staleTime: 1000 * 60, + const { data: suggestedPromptIdeas = [], isPending } = useQuery({ + queryKey: ['kai', projectId, 'chats', threadId, 'prompt-suggestions'], + queryFn: () => kaiService.getPromptSuggestions(projectId, threadId), + staleTime: 1000 * 60, }); const ideas = React.useMemo(() => { - const defaultPromptIdeas = [ - 'Top user journeys', - 'Where do users drop off', - 'Failed network requests today', - ]; - const result = suggestedPromptIdeas; - const targetSize = 3; - while (result.length < targetSize && defaultPromptIdeas.length) { - result.push(defaultPromptIdeas.pop()) - } + const defaultPromptIdeas = [ + 'Top user journeys', + 'Where do users drop off', + 'Failed network requests today', + ]; + const result = suggestedPromptIdeas; + const targetSize = 3; + while (result.length < targetSize && defaultPromptIdeas.length) { + result.push(defaultPromptIdeas.pop()); + } return result; }, [suggestedPromptIdeas.length]); return ( - <> +
- - Ideas: + Suggested Ideas:
- { - isPending ? - (
{t('Generating ideas')}...
) : - (
{ideas.map(title => ())}
) - } - + {isPending ? ( +
+ {t('Generating ideas')}... +
+ ) : ( +
+ {ideas.map((title) => ( + + ))} +
+ )} +
); } -function IdeaItem({ title, onClick }: { title: string, onClick: (query: string) => void }) { +function IdeaItem({ + title, + onClick, +}: { + title: string; + onClick: (query: string) => void; +}) { return (
onClick(title)} className={ - 'flex items-center gap-2 cursor-pointer text-gray-dark hover:text-black' + 'cursor-pointer text-gray-dark hover:text-black rounded-full px-4 py-2 shadow border' } > - - {title} + {title}
); } diff --git a/frontend/app/components/Kai/components/IntroSection.tsx b/frontend/app/components/Kai/components/IntroSection.tsx index c6356e9ba..dd28f667a 100644 --- a/frontend/app/components/Kai/components/IntroSection.tsx +++ b/frontend/app/components/Kai/components/IntroSection.tsx @@ -2,24 +2,34 @@ import React from 'react'; import ChatInput from './ChatInput'; import Ideas from './Ideas'; -function IntroSection({ onAsk, projectId }: { onAsk: (query: string) => void, projectId: string }) { +function IntroSection({ + onAsk, + onCancel, + userName, + projectId, +}: { + onAsk: (query: string) => void; + projectId: string; + onCancel: () => void; + userName: string; +}) { const isLoading = false; return ( <> -
- Kai is your AI assistant, delivering smart insights in response to your - queries. -
-
- {/* null} />*/} - +
+
+ Hey {userName}, how can I help you? +
+
onAsk(query)} projectId={projectId} />
-
- OpenReplay AI can make mistakes. Verify its outputs. -
); } diff --git a/frontend/app/components/Modal/index.tsx b/frontend/app/components/Modal/index.tsx index 3b84beeb8..22a531656 100644 --- a/frontend/app/components/Modal/index.tsx +++ b/frontend/app/components/Modal/index.tsx @@ -1,6 +1,7 @@ // @ts-nocheck import React, { Component, createContext } from 'react'; import Modal from './Modal'; +import { className } from '@medv/finder'; const ModalContext = createContext({ component: null, @@ -29,6 +30,7 @@ export class ModalProvider extends Component { this.setState({ component, props, + className: props.className || undefined, }); document.addEventListener('keydown', this.handleKeyDown); document.querySelector('body').style.overflow = 'hidden'; diff --git a/frontend/app/components/shared/SessionItem/SessionItem.tsx b/frontend/app/components/shared/SessionItem/SessionItem.tsx index af42caf1d..23128a84f 100644 --- a/frontend/app/components/shared/SessionItem/SessionItem.tsx +++ b/frontend/app/components/shared/SessionItem/SessionItem.tsx @@ -71,6 +71,7 @@ interface Props { bookmarked?: boolean; toggleFavorite?: (sessionId: string) => void; query?: string; + slim?: boolean; } const PREFETCH_STATE = { @@ -99,6 +100,7 @@ function SessionItem(props: RouteComponentProps & Props) { isDisabled, live: propsLive, isAdd, + slim, } = props; const { @@ -261,7 +263,7 @@ function SessionItem(props: RouteComponentProps & Props) { } >
e.stopPropagation()} onMouseEnter={handleHover} @@ -343,7 +345,7 @@ function SessionItem(props: RouteComponentProps & Props) { : 'Event'}
- + )}
@@ -373,7 +375,7 @@ function SessionItem(props: RouteComponentProps & Props) { )} {userOs && userBrowser && ( - + )} {userOs && ( )} {userOs && ( - + )} + ); +} + +export default Kai_mono; diff --git a/frontend/app/components/ui/SVG.tsx b/frontend/app/components/ui/SVG.tsx index 36dedf09d..e10f6830c 100644 --- a/frontend/app/components/ui/SVG.tsx +++ b/frontend/app/components/ui/SVG.tsx @@ -355,6 +355,7 @@ import { Integrations_vuejs, Integrations_zustand, Journal_code, + Kai_mono, Kai, Kai_colored, Key, @@ -487,7 +488,7 @@ import { Zoom_in } from './Icons' -export type IconNames = 'activity' | 'analytics' | 'anchor' | 'arrow-bar-left' | 'arrow-clockwise' | 'arrow-counterclockwise' | 'arrow-down-short' | 'arrow-down-up' | 'arrow-down' | 'arrow-repeat' | 'arrow-right-short' | 'arrow-up-short' | 'arrow-up' | 'avatar/icn_avatar1' | 'avatar/icn_avatar10' | 'avatar/icn_avatar11' | 'avatar/icn_avatar12' | 'avatar/icn_avatar13' | 'avatar/icn_avatar14' | 'avatar/icn_avatar15' | 'avatar/icn_avatar16' | 'avatar/icn_avatar17' | 'avatar/icn_avatar18' | 'avatar/icn_avatar19' | 'avatar/icn_avatar2' | 'avatar/icn_avatar20' | 'avatar/icn_avatar21' | 'avatar/icn_avatar22' | 'avatar/icn_avatar23' | 'avatar/icn_avatar3' | 'avatar/icn_avatar4' | 'avatar/icn_avatar5' | 'avatar/icn_avatar6' | 'avatar/icn_avatar7' | 'avatar/icn_avatar8' | 'avatar/icn_avatar9' | 'ban' | 'bar-chart-line' | 'bar-pencil' | 'battery-charging' | 'battery' | 'bell-plus' | 'bell-slash' | 'bell' | 'binoculars' | 'book' | 'bookmark' | 'broadcast' | 'browser/browser' | 'browser/chrome' | 'browser/edge' | 'browser/electron' | 'browser/facebook' | 'browser/firefox' | 'browser/ie' | 'browser/opera' | 'browser/safari' | 'buildings' | 'bullhorn' | 'calendar' | 'call' | 'camera-video-off' | 'camera-video' | 'camera' | 'card-list' | 'card-text' | 'caret-down-fill' | 'caret-right-fill' | 'chat-dots' | 'chat-left-text' | 'chat-square-quote' | 'check-circle-fill' | 'check-circle' | 'check' | 'chevron-down' | 'chevron-left' | 'chevron-right' | 'chevron-up' | 'circle-fill' | 'circle' | 'click-hesitation' | 'click-rage' | 'clipboard-check' | 'clock-history' | 'clock' | 'close' | 'code' | 'cog' | 'cogs' | 'collection-play' | 'collection' | 'color/apple' | 'color/browser/chrome' | 'color/browser/edge' | 'color/browser/facebook' | 'color/browser/firefox' | 'color/browser/google' | 'color/browser/opera' | 'color/browser/safari' | 'color/browser/unknown' | 'color/browser/whale' | 'color/chrome' | 'color/country/de' | 'color/country/fr' | 'color/country/gb' | 'color/country/in' | 'color/country/us' | 'color/de' | 'color/device/desktop' | 'color/device/mobile' | 'color/device/tablet' | 'color/device/unkown' | 'color/edge' | 'color/fedora' | 'color/firefox' | 'color/fr' | 'color/gb' | 'color/in' | 'color/issues/bad_request' | 'color/issues/click_rage' | 'color/issues/cpu' | 'color/issues/crash' | 'color/issues/custom' | 'color/issues/dead_click' | 'color/issues/errors' | 'color/issues/excessive_scrolling' | 'color/issues/js_exception' | 'color/issues/memory' | 'color/issues/missing_resource' | 'color/issues/mouse_thrashing' | 'color/issues/slow_page_load' | 'color/microsoft' | 'color/opera' | 'color/os/android' | 'color/os/apple' | 'color/os/elementary' | 'color/os/fedora' | 'color/os/ios' | 'color/os/linux' | 'color/os/macos' | 'color/os/microsoft' | 'color/os/ubuntu' | 'color/os/unkown' | 'color/safari' | 'color/ubuntu' | 'color/us' | 'columns-gap' | 'console/error' | 'console/exception' | 'console/info' | 'console/warning' | 'console' | 'controller' | 'cookies' | 'copy' | 'credit-card-2-back' | 'cross' | 'cubes' | 'cursor-trash' | 'cypress' | 'dash' | 'dashboard-icn' | 'dashboards/circle-alert' | 'dashboards/cohort-chart' | 'dashboards/heatmap-2' | 'dashboards/user-journey' | 'db-icons/icn-card-clickMap' | 'db-icons/icn-card-errors' | 'db-icons/icn-card-funnel' | 'db-icons/icn-card-funnels' | 'db-icons/icn-card-insights' | 'db-icons/icn-card-library' | 'db-icons/icn-card-mapchart' | 'db-icons/icn-card-pathAnalysis' | 'db-icons/icn-card-performance' | 'db-icons/icn-card-resources' | 'db-icons/icn-card-table' | 'db-icons/icn-card-timeseries' | 'db-icons/icn-card-webVitals' | 'desktop' | 'device' | 'diagram-3' | 'dizzy' | 'door-closed' | 'download' | 'drag' | 'edit' | 'ellipsis-v' | 'emoji-dizzy' | 'enter' | 'envelope-check' | 'envelope-paper' | 'envelope-x' | 'envelope' | 'errors-icon' | 'event/click' | 'event/click_hesitation' | 'event/clickrage' | 'event/code' | 'event/i-cursor' | 'event/input' | 'event/input_hesitation' | 'event/link' | 'event/location' | 'event/mouse_thrashing' | 'event/resize' | 'event/view' | 'exclamation-circle-fill' | 'exclamation-circle' | 'exclamation-triangle' | 'explosion' | 'external-link-alt' | 'eye-slash-fill' | 'eye-slash' | 'eye' | 'fetch-request' | 'fetch' | 'fflag-multi' | 'fflag-single' | 'file-bar-graph' | 'file-code' | 'file-medical-alt' | 'file-pdf' | 'file' | 'files' | 'filetype-js' | 'filetype-pdf' | 'filter' | 'filters/arrow-return-right' | 'filters/browser' | 'filters/chevrons-up-down' | 'filters/click' | 'filters/clickrage' | 'filters/code' | 'filters/console' | 'filters/country' | 'filters/cpu-load' | 'filters/custom' | 'filters/device' | 'filters/dom-complete' | 'filters/duration' | 'filters/error' | 'filters/fetch-failed' | 'filters/fetch' | 'filters/file-code' | 'filters/graphql' | 'filters/i-cursor' | 'filters/input' | 'filters/lcpt' | 'filters/link' | 'filters/location' | 'filters/memory-load' | 'filters/metadata' | 'filters/os' | 'filters/perfromance-network-request' | 'filters/platform' | 'filters/referrer' | 'filters/resize' | 'filters/rev-id' | 'filters/screen' | 'filters/state-action' | 'filters/tag-element' | 'filters/ttfb' | 'filters/user-alt' | 'filters/userid' | 'filters/view' | 'flag-na' | 'folder-plus' | 'folder2' | 'fullscreen' | 'funnel/cpu-fill' | 'funnel/cpu' | 'funnel/dizzy' | 'funnel/emoji-angry-fill' | 'funnel/emoji-angry' | 'funnel/emoji-dizzy-fill' | 'funnel/exclamation-circle-fill' | 'funnel/exclamation-circle' | 'funnel/file-earmark-break-fill' | 'funnel/file-earmark-break' | 'funnel/file-earmark-minus-fill' | 'funnel/file-earmark-minus' | 'funnel/file-medical-alt' | 'funnel/file-x' | 'funnel/hdd-fill' | 'funnel/hourglass-top' | 'funnel/image-fill' | 'funnel/image' | 'funnel/microchip' | 'funnel/mouse' | 'funnel/patch-exclamation-fill' | 'funnel/sd-card' | 'funnel-fill' | 'funnel' | 'gear' | 'github' | 'graph-up' | 'grid-3x3' | 'grid-check' | 'grid' | 'hash' | 'headset' | 'history' | 'ic-errors' | 'ic-network' | 'ic-rage' | 'ic-resources' | 'icn_fetch-request' | 'icn_referrer' | 'icn_url' | 'id-card' | 'image' | 'info-circle-fill' | 'info-circle' | 'info-square' | 'info' | 'input-hesitation' | 'inspect' | 'integrations/assist' | 'integrations/bugsnag-text' | 'integrations/bugsnag' | 'integrations/cloudwatch-text' | 'integrations/cloudwatch' | 'integrations/datadog' | 'integrations/dynatrace' | 'integrations/elasticsearch-text' | 'integrations/elasticsearch' | 'integrations/github' | 'integrations/graphql' | 'integrations/jira-text' | 'integrations/jira' | 'integrations/mobx' | 'integrations/newrelic-text' | 'integrations/newrelic' | 'integrations/ngrx' | 'integrations/openreplay-text' | 'integrations/openreplay' | 'integrations/redux' | 'integrations/rollbar-text' | 'integrations/rollbar' | 'integrations/segment' | 'integrations/sentry-text' | 'integrations/sentry' | 'integrations/slack-bw' | 'integrations/slack' | 'integrations/stackdriver' | 'integrations/sumologic-text' | 'integrations/sumologic' | 'integrations/teams-white' | 'integrations/teams' | 'integrations/vuejs' | 'integrations/zustand' | 'journal-code' | 'kai' | 'kai_colored' | 'key' | 'keyboard' | 'layers-half' | 'lightbulb-on' | 'lightbulb' | 'link-45deg' | 'list-alt' | 'list-ul' | 'list' | 'low-disc-space' | 'magic' | 'map-marker-alt' | 'memory-ios' | 'memory' | 'metadata-more' | 'mic-mute' | 'mic' | 'minus' | 'mobile' | 'mouse-alt' | 'mouse-pointer-click' | 'network' | 'next1' | 'no-dashboard' | 'no-metrics-chart' | 'no-metrics' | 'no-recordings' | 'orIcn' | 'orSpot' | 'orspotOutline' | 'os/android' | 'os/chrome_os' | 'os/fedora' | 'os/ios' | 'os/linux' | 'os/mac_os_x' | 'os/other' | 'os/ubuntu' | 'os/windows' | 'os' | 'pause-circle-fill' | 'pause-fill' | 'pause' | 'pdf-download' | 'pencil-stop' | 'pencil' | 'people' | 'percent' | 'performance-icon' | 'person-border' | 'person-fill' | 'person' | 'pie-chart-fill' | 'pin-fill' | 'play-circle-bold' | 'play-circle-light' | 'play-circle' | 'play-fill-new' | 'play-fill' | 'play-hover' | 'play' | 'plug' | 'plus-circle' | 'plus' | 'prev1' | 'pulse' | 'puppeteer' | 'puzzle-piece' | 'puzzle' | 'pwright' | 'question-circle' | 'question-lg' | 'quotes' | 'record-circle-fill' | 'record-circle' | 'record2' | 'redo' | 'redux' | 'referrer' | 'remote-control' | 'resources-icon' | 'safe' | 'sandglass' | 'search' | 'server' | 'share-alt' | 'shield-lock' | 'side_menu_closed' | 'side_menu_open' | 'signpost-split' | 'signup' | 'slack' | 'slash-circle' | 'sleep' | 'sliders' | 'social/slack' | 'social/trello' | 'sparkles' | 'speedometer2' | 'spinner' | 'square-mouse-pointer' | 'star' | 'step-forward' | 'stickies' | 'stop-record-circle' | 'stopwatch' | 'store' | 'sync-alt' | 'table' | 'tags' | 'terminal' | 'thermometer-sun' | 'toggles' | 'tools' | 'trash' | 'turtle' | 'user-alt' | 'user-circle' | 'user-friends' | 'user-journey' | 'user-switch' | 'users' | 'vendors/graphql' | 'web-vitals' | 'wifi' | 'window-x' | 'window' | 'zoom-in'; +export type IconNames = 'activity' | 'analytics' | 'anchor' | 'arrow-bar-left' | 'arrow-clockwise' | 'arrow-counterclockwise' | 'arrow-down-short' | 'arrow-down-up' | 'arrow-down' | 'arrow-repeat' | 'arrow-right-short' | 'arrow-up-short' | 'arrow-up' | 'avatar/icn_avatar1' | 'avatar/icn_avatar10' | 'avatar/icn_avatar11' | 'avatar/icn_avatar12' | 'avatar/icn_avatar13' | 'avatar/icn_avatar14' | 'avatar/icn_avatar15' | 'avatar/icn_avatar16' | 'avatar/icn_avatar17' | 'avatar/icn_avatar18' | 'avatar/icn_avatar19' | 'avatar/icn_avatar2' | 'avatar/icn_avatar20' | 'avatar/icn_avatar21' | 'avatar/icn_avatar22' | 'avatar/icn_avatar23' | 'avatar/icn_avatar3' | 'avatar/icn_avatar4' | 'avatar/icn_avatar5' | 'avatar/icn_avatar6' | 'avatar/icn_avatar7' | 'avatar/icn_avatar8' | 'avatar/icn_avatar9' | 'ban' | 'bar-chart-line' | 'bar-pencil' | 'battery-charging' | 'battery' | 'bell-plus' | 'bell-slash' | 'bell' | 'binoculars' | 'book' | 'bookmark' | 'broadcast' | 'browser/browser' | 'browser/chrome' | 'browser/edge' | 'browser/electron' | 'browser/facebook' | 'browser/firefox' | 'browser/ie' | 'browser/opera' | 'browser/safari' | 'buildings' | 'bullhorn' | 'calendar' | 'call' | 'camera-video-off' | 'camera-video' | 'camera' | 'card-list' | 'card-text' | 'caret-down-fill' | 'caret-right-fill' | 'chat-dots' | 'chat-left-text' | 'chat-square-quote' | 'check-circle-fill' | 'check-circle' | 'check' | 'chevron-down' | 'chevron-left' | 'chevron-right' | 'chevron-up' | 'circle-fill' | 'circle' | 'click-hesitation' | 'click-rage' | 'clipboard-check' | 'clock-history' | 'clock' | 'close' | 'code' | 'cog' | 'cogs' | 'collection-play' | 'collection' | 'color/apple' | 'color/browser/chrome' | 'color/browser/edge' | 'color/browser/facebook' | 'color/browser/firefox' | 'color/browser/google' | 'color/browser/opera' | 'color/browser/safari' | 'color/browser/unknown' | 'color/browser/whale' | 'color/chrome' | 'color/country/de' | 'color/country/fr' | 'color/country/gb' | 'color/country/in' | 'color/country/us' | 'color/de' | 'color/device/desktop' | 'color/device/mobile' | 'color/device/tablet' | 'color/device/unkown' | 'color/edge' | 'color/fedora' | 'color/firefox' | 'color/fr' | 'color/gb' | 'color/in' | 'color/issues/bad_request' | 'color/issues/click_rage' | 'color/issues/cpu' | 'color/issues/crash' | 'color/issues/custom' | 'color/issues/dead_click' | 'color/issues/errors' | 'color/issues/excessive_scrolling' | 'color/issues/js_exception' | 'color/issues/memory' | 'color/issues/missing_resource' | 'color/issues/mouse_thrashing' | 'color/issues/slow_page_load' | 'color/microsoft' | 'color/opera' | 'color/os/android' | 'color/os/apple' | 'color/os/elementary' | 'color/os/fedora' | 'color/os/ios' | 'color/os/linux' | 'color/os/macos' | 'color/os/microsoft' | 'color/os/ubuntu' | 'color/os/unkown' | 'color/safari' | 'color/ubuntu' | 'color/us' | 'columns-gap' | 'console/error' | 'console/exception' | 'console/info' | 'console/warning' | 'console' | 'controller' | 'cookies' | 'copy' | 'credit-card-2-back' | 'cross' | 'cubes' | 'cursor-trash' | 'cypress' | 'dash' | 'dashboard-icn' | 'dashboards/circle-alert' | 'dashboards/cohort-chart' | 'dashboards/heatmap-2' | 'dashboards/user-journey' | 'db-icons/icn-card-clickMap' | 'db-icons/icn-card-errors' | 'db-icons/icn-card-funnel' | 'db-icons/icn-card-funnels' | 'db-icons/icn-card-insights' | 'db-icons/icn-card-library' | 'db-icons/icn-card-mapchart' | 'db-icons/icn-card-pathAnalysis' | 'db-icons/icn-card-performance' | 'db-icons/icn-card-resources' | 'db-icons/icn-card-table' | 'db-icons/icn-card-timeseries' | 'db-icons/icn-card-webVitals' | 'desktop' | 'device' | 'diagram-3' | 'dizzy' | 'door-closed' | 'download' | 'drag' | 'edit' | 'ellipsis-v' | 'emoji-dizzy' | 'enter' | 'envelope-check' | 'envelope-paper' | 'envelope-x' | 'envelope' | 'errors-icon' | 'event/click' | 'event/click_hesitation' | 'event/clickrage' | 'event/code' | 'event/i-cursor' | 'event/input' | 'event/input_hesitation' | 'event/link' | 'event/location' | 'event/mouse_thrashing' | 'event/resize' | 'event/view' | 'exclamation-circle-fill' | 'exclamation-circle' | 'exclamation-triangle' | 'explosion' | 'external-link-alt' | 'eye-slash-fill' | 'eye-slash' | 'eye' | 'fetch-request' | 'fetch' | 'fflag-multi' | 'fflag-single' | 'file-bar-graph' | 'file-code' | 'file-medical-alt' | 'file-pdf' | 'file' | 'files' | 'filetype-js' | 'filetype-pdf' | 'filter' | 'filters/arrow-return-right' | 'filters/browser' | 'filters/chevrons-up-down' | 'filters/click' | 'filters/clickrage' | 'filters/code' | 'filters/console' | 'filters/country' | 'filters/cpu-load' | 'filters/custom' | 'filters/device' | 'filters/dom-complete' | 'filters/duration' | 'filters/error' | 'filters/fetch-failed' | 'filters/fetch' | 'filters/file-code' | 'filters/graphql' | 'filters/i-cursor' | 'filters/input' | 'filters/lcpt' | 'filters/link' | 'filters/location' | 'filters/memory-load' | 'filters/metadata' | 'filters/os' | 'filters/perfromance-network-request' | 'filters/platform' | 'filters/referrer' | 'filters/resize' | 'filters/rev-id' | 'filters/screen' | 'filters/state-action' | 'filters/tag-element' | 'filters/ttfb' | 'filters/user-alt' | 'filters/userid' | 'filters/view' | 'flag-na' | 'folder-plus' | 'folder2' | 'fullscreen' | 'funnel/cpu-fill' | 'funnel/cpu' | 'funnel/dizzy' | 'funnel/emoji-angry-fill' | 'funnel/emoji-angry' | 'funnel/emoji-dizzy-fill' | 'funnel/exclamation-circle-fill' | 'funnel/exclamation-circle' | 'funnel/file-earmark-break-fill' | 'funnel/file-earmark-break' | 'funnel/file-earmark-minus-fill' | 'funnel/file-earmark-minus' | 'funnel/file-medical-alt' | 'funnel/file-x' | 'funnel/hdd-fill' | 'funnel/hourglass-top' | 'funnel/image-fill' | 'funnel/image' | 'funnel/microchip' | 'funnel/mouse' | 'funnel/patch-exclamation-fill' | 'funnel/sd-card' | 'funnel-fill' | 'funnel' | 'gear' | 'github' | 'graph-up' | 'grid-3x3' | 'grid-check' | 'grid' | 'hash' | 'headset' | 'history' | 'ic-errors' | 'ic-network' | 'ic-rage' | 'ic-resources' | 'icn_fetch-request' | 'icn_referrer' | 'icn_url' | 'id-card' | 'image' | 'info-circle-fill' | 'info-circle' | 'info-square' | 'info' | 'input-hesitation' | 'inspect' | 'integrations/assist' | 'integrations/bugsnag-text' | 'integrations/bugsnag' | 'integrations/cloudwatch-text' | 'integrations/cloudwatch' | 'integrations/datadog' | 'integrations/dynatrace' | 'integrations/elasticsearch-text' | 'integrations/elasticsearch' | 'integrations/github' | 'integrations/graphql' | 'integrations/jira-text' | 'integrations/jira' | 'integrations/mobx' | 'integrations/newrelic-text' | 'integrations/newrelic' | 'integrations/ngrx' | 'integrations/openreplay-text' | 'integrations/openreplay' | 'integrations/redux' | 'integrations/rollbar-text' | 'integrations/rollbar' | 'integrations/segment' | 'integrations/sentry-text' | 'integrations/sentry' | 'integrations/slack-bw' | 'integrations/slack' | 'integrations/stackdriver' | 'integrations/sumologic-text' | 'integrations/sumologic' | 'integrations/teams-white' | 'integrations/teams' | 'integrations/vuejs' | 'integrations/zustand' | 'journal-code' | 'kai-mono' | 'kai' | 'kai_colored' | 'key' | 'keyboard' | 'layers-half' | 'lightbulb-on' | 'lightbulb' | 'link-45deg' | 'list-alt' | 'list-ul' | 'list' | 'low-disc-space' | 'magic' | 'map-marker-alt' | 'memory-ios' | 'memory' | 'metadata-more' | 'mic-mute' | 'mic' | 'minus' | 'mobile' | 'mouse-alt' | 'mouse-pointer-click' | 'network' | 'next1' | 'no-dashboard' | 'no-metrics-chart' | 'no-metrics' | 'no-recordings' | 'orIcn' | 'orSpot' | 'orspotOutline' | 'os/android' | 'os/chrome_os' | 'os/fedora' | 'os/ios' | 'os/linux' | 'os/mac_os_x' | 'os/other' | 'os/ubuntu' | 'os/windows' | 'os' | 'pause-circle-fill' | 'pause-fill' | 'pause' | 'pdf-download' | 'pencil-stop' | 'pencil' | 'people' | 'percent' | 'performance-icon' | 'person-border' | 'person-fill' | 'person' | 'pie-chart-fill' | 'pin-fill' | 'play-circle-bold' | 'play-circle-light' | 'play-circle' | 'play-fill-new' | 'play-fill' | 'play-hover' | 'play' | 'plug' | 'plus-circle' | 'plus' | 'prev1' | 'pulse' | 'puppeteer' | 'puzzle-piece' | 'puzzle' | 'pwright' | 'question-circle' | 'question-lg' | 'quotes' | 'record-circle-fill' | 'record-circle' | 'record2' | 'redo' | 'redux' | 'referrer' | 'remote-control' | 'resources-icon' | 'safe' | 'sandglass' | 'search' | 'server' | 'share-alt' | 'shield-lock' | 'side_menu_closed' | 'side_menu_open' | 'signpost-split' | 'signup' | 'slack' | 'slash-circle' | 'sleep' | 'sliders' | 'social/slack' | 'social/trello' | 'sparkles' | 'speedometer2' | 'spinner' | 'square-mouse-pointer' | 'star' | 'step-forward' | 'stickies' | 'stop-record-circle' | 'stopwatch' | 'store' | 'sync-alt' | 'table' | 'tags' | 'terminal' | 'thermometer-sun' | 'toggles' | 'tools' | 'trash' | 'turtle' | 'user-alt' | 'user-circle' | 'user-friends' | 'user-journey' | 'user-switch' | 'users' | 'vendors/graphql' | 'web-vitals' | 'wifi' | 'window-x' | 'window' | 'zoom-in'; interface Props { name: IconNames; @@ -1561,6 +1562,9 @@ const SVG = (props: Props) => { // case 'journal-code': case 'journal-code': return ; + // case 'kai-mono': + case 'kai-mono': return ; + case 'kai': return ; diff --git a/frontend/app/svg/icons/kai-mono.svg b/frontend/app/svg/icons/kai-mono.svg new file mode 100644 index 000000000..97738455d --- /dev/null +++ b/frontend/app/svg/icons/kai-mono.svg @@ -0,0 +1,11 @@ + + + + + + + + + + +