Skip to content

Commit

Permalink
Merge branch 'master' into dw-editor-visualization
Browse files Browse the repository at this point in the history
  • Loading branch information
EDsCODE authored Nov 26, 2024
2 parents 2570268 + 2196052 commit 02f4268
Show file tree
Hide file tree
Showing 30 changed files with 876 additions and 162 deletions.
1 change: 1 addition & 0 deletions ee/clickhouse/views/experiments.py
Original file line number Diff line number Diff line change
Expand Up @@ -339,6 +339,7 @@ def update(self, instance: Experiment, validated_data: dict, *args: Any, **kwarg
# if (
# not instance.filters.get("events")
# and not instance.filters.get("actions")
# and not instance.filters.get("data_warehouse")
# and validated_data.get("start_date")
# and not validated_data.get("filters")
# ):
Expand Down
9 changes: 7 additions & 2 deletions ee/hogai/schema_generator/nodes.py
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,10 @@ def _construct_messages(
human_messages, visualization_messages = filter_visualization_conversation(messages)
first_ai_message = True

for human_message, ai_message in itertools.zip_longest(human_messages, visualization_messages):
for idx, (human_message, ai_message) in enumerate(
itertools.zip_longest(human_messages, visualization_messages)
):
# Plans go first
if ai_message:
conversation.append(
HumanMessagePromptTemplate.from_template(
Expand All @@ -161,14 +164,16 @@ def _construct_messages(
).format(plan=generated_plan)
)

# Then questions
if human_message:
conversation.append(
HumanMessagePromptTemplate.from_template(QUESTION_PROMPT, template_format="mustache").format(
question=human_message.content
)
)

if ai_message:
# Then schemas, but include only last generated schema because it doesn't need more context.
if ai_message and idx + 1 == len(visualization_messages):
conversation.append(
LangchainAssistantMessage(content=ai_message.answer.model_dump_json() if ai_message.answer else "")
)
Expand Down
67 changes: 67 additions & 0 deletions ee/hogai/schema_generator/test/test_nodes.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,11 @@
from ee.hogai.schema_generator.nodes import SchemaGeneratorNode, SchemaGeneratorToolsNode
from ee.hogai.schema_generator.utils import SchemaGeneratorOutput
from posthog.schema import (
AssistantMessage,
AssistantTrendsQuery,
FailureMessage,
HumanMessage,
RouterMessage,
VisualizationMessage,
)
from posthog.test.base import APIBaseTest, ClickhouseTestMixin
Expand Down Expand Up @@ -169,6 +171,71 @@ def test_agent_reconstructs_conversation_and_merges_messages(self):
self.assertNotIn("{{question}}", history[5].content)
self.assertIn("Follow\nUp", history[5].content)

def test_agent_reconstructs_typical_conversation(self):
node = DummyGeneratorNode(self.team)
history = node._construct_messages(
{
"messages": [
HumanMessage(content="Question 1"),
RouterMessage(content="trends"),
VisualizationMessage(answer=AssistantTrendsQuery(series=[]), plan="Plan 1"),
AssistantMessage(content="Summary 1"),
HumanMessage(content="Question 2"),
RouterMessage(content="funnel"),
VisualizationMessage(answer=AssistantTrendsQuery(series=[]), plan="Plan 2"),
AssistantMessage(content="Summary 2"),
HumanMessage(content="Question 3"),
RouterMessage(content="funnel"),
],
"plan": "Plan 3",
}
)
self.assertEqual(len(history), 8)
self.assertEqual(history[0].type, "human")
self.assertIn("mapping", history[0].content)
self.assertEqual(history[1].type, "human")
self.assertIn("Plan 1", history[1].content)
self.assertEqual(history[2].type, "human")
self.assertIn("Question 1", history[2].content)
self.assertEqual(history[3].type, "human")
self.assertIn("Plan 2", history[3].content)
self.assertEqual(history[4].type, "human")
self.assertIn("Question 2", history[4].content)
self.assertEqual(history[5].type, "ai")
self.assertEqual(history[6].type, "human")
self.assertIn("Plan 3", history[6].content)
self.assertEqual(history[7].type, "human")
self.assertIn("Question 3", history[7].content)

def test_prompt(self):
node = DummyGeneratorNode(self.team)
state = {
"messages": [
HumanMessage(content="Question 1"),
RouterMessage(content="trends"),
VisualizationMessage(answer=AssistantTrendsQuery(series=[]), plan="Plan 1"),
AssistantMessage(content="Summary 1"),
HumanMessage(content="Question 2"),
RouterMessage(content="funnel"),
VisualizationMessage(answer=AssistantTrendsQuery(series=[]), plan="Plan 2"),
AssistantMessage(content="Summary 2"),
HumanMessage(content="Question 3"),
RouterMessage(content="funnel"),
],
"plan": "Plan 3",
}
with patch.object(DummyGeneratorNode, "_model") as generator_model_mock:

def assert_prompt(prompt):
self.assertEqual(len(prompt), 4)
self.assertEqual(prompt[0].type, "system")
self.assertEqual(prompt[1].type, "human")
self.assertEqual(prompt[2].type, "ai")
self.assertEqual(prompt[3].type, "human")

generator_model_mock.return_value = RunnableLambda(assert_prompt)
node.run(state, {})

def test_failover_with_incorrect_schema(self):
node = DummyGeneratorNode(self.team)
with patch.object(DummyGeneratorNode, "_model") as generator_model_mock:
Expand Down
31 changes: 31 additions & 0 deletions ee/hogai/taxonomy_agent/test/test_nodes.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
AssistantTrendsQuery,
FailureMessage,
HumanMessage,
RouterMessage,
VisualizationMessage,
)
from posthog.test.base import APIBaseTest, ClickhouseTestMixin, _create_event, _create_person
Expand Down Expand Up @@ -116,6 +117,36 @@ def test_agent_reconstructs_conversation_with_failures(self):
self.assertIn("Text", history[0].content)
self.assertNotIn("{{question}}", history[0].content)

def test_agent_reconstructs_typical_conversation(self):
node = self._get_node()
history = node._construct_messages(
{
"messages": [
HumanMessage(content="Question 1"),
RouterMessage(content="trends"),
VisualizationMessage(answer=AssistantTrendsQuery(series=[]), plan="Plan 1"),
AssistantMessage(content="Summary 1"),
HumanMessage(content="Question 2"),
RouterMessage(content="funnel"),
VisualizationMessage(answer=AssistantTrendsQuery(series=[]), plan="Plan 2"),
AssistantMessage(content="Summary 2"),
HumanMessage(content="Question 3"),
RouterMessage(content="funnel"),
]
}
)
self.assertEqual(len(history), 5)
self.assertEqual(history[0].type, "human")
self.assertIn("Question 1", history[0].content)
self.assertEqual(history[1].type, "ai")
self.assertEqual(history[1].content, "Plan 1")
self.assertEqual(history[2].type, "human")
self.assertIn("Question 2", history[2].content)
self.assertEqual(history[3].type, "ai")
self.assertEqual(history[3].content, "Plan 2")
self.assertEqual(history[4].type, "human")
self.assertIn("Question 3", history[4].content)

def test_agent_filters_out_low_count_events(self):
_create_person(distinct_ids=["test"], team=self.team)
for i in range(26):
Expand Down
35 changes: 34 additions & 1 deletion ee/hogai/test/test_utils.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,14 @@
from langchain_core.messages import HumanMessage as LangchainHumanMessage

from ee.hogai.utils import filter_visualization_conversation, merge_human_messages
from posthog.schema import AssistantTrendsQuery, FailureMessage, HumanMessage, VisualizationMessage
from posthog.schema import (
AssistantMessage,
AssistantTrendsQuery,
FailureMessage,
HumanMessage,
RouterMessage,
VisualizationMessage,
)
from posthog.test.base import BaseTest


Expand Down Expand Up @@ -37,3 +44,29 @@ def test_filter_trends_conversation(self):
self.assertEqual(
visualization_messages, [VisualizationMessage(answer=AssistantTrendsQuery(series=[]), plan="plan")]
)

def test_filters_typical_conversation(self):
human_messages, visualization_messages = filter_visualization_conversation(
[
HumanMessage(content="Question 1"),
RouterMessage(content="trends"),
VisualizationMessage(answer=AssistantTrendsQuery(series=[]), plan="Plan 1"),
AssistantMessage(content="Summary 1"),
HumanMessage(content="Question 2"),
RouterMessage(content="funnel"),
VisualizationMessage(answer=AssistantTrendsQuery(series=[]), plan="Plan 2"),
AssistantMessage(content="Summary 2"),
]
)
self.assertEqual(len(human_messages), 2)
self.assertEqual(len(visualization_messages), 2)
self.assertEqual(
human_messages, [LangchainHumanMessage(content="Question 1"), LangchainHumanMessage(content="Question 2")]
)
self.assertEqual(
visualization_messages,
[
VisualizationMessage(answer=AssistantTrendsQuery(series=[]), plan="Plan 1"),
VisualizationMessage(answer=AssistantTrendsQuery(series=[]), plan="Plan 2"),
],
)
25 changes: 0 additions & 25 deletions frontend/src/globals.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,30 +22,5 @@ declare global {
}
IMPERSONATED_SESSION?: boolean
POSTHOG_JS_UUID_VERSION?: string

EventSourcePolyfill: typeof EventSource
}
}

/** Copied from https://github.com/DefinitelyTyped/DefinitelyTyped/blob/aaf94a0a/types/event-source-polyfill/index.d.ts#L43 */
export declare class EventSource {
static readonly CLOSED: number
static readonly CONNECTING: number
static readonly OPEN: number

constructor(url: string, eventSourceInitDict?: EventSource.EventSourceInitDict)

readonly CLOSED: number
readonly CONNECTING: number
readonly OPEN: number
readonly url: string
readonly readyState: number
readonly withCredentials: boolean
onopen: (evt: MessageEvent) => any
onmessage: (evt: MessageEvent) => any
onerror: (evt: MessageEvent) => any
addEventListener(type: string, listener: (evt: MessageEvent) => void): void
dispatchEvent(evt: Event): boolean
removeEventListener(type: string, listener: (evt: MessageEvent) => void): void
close(): void
}
18 changes: 16 additions & 2 deletions frontend/src/lib/components/Alerts/alertsLogic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,5 +33,19 @@ export const alertsLogic = kea<alertsLogicType>([
afterMount(({ actions }) => actions.loadAlerts()),
])

const alertComparatorKey = (alert: AlertType): number =>
!alert.enabled ? 3 : alert.state === AlertState.NOT_FIRING ? 2 : 1
const alertComparatorKey = (alert: AlertType): number => {
if (!alert.enabled) {
return 100
}

switch (alert.state) {
case AlertState.FIRING:
return 1
case AlertState.ERRORED:
return 2
case AlertState.SNOOZED:
return 3
case AlertState.NOT_FIRING:
return 4
}
}
14 changes: 10 additions & 4 deletions frontend/src/lib/components/Alerts/views/Alerts.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { TZLabel } from '@posthog/apps-common'
import { IconCheck } from '@posthog/icons'
import { Tooltip } from '@posthog/lemon-ui'
import { LemonTag, Tooltip } from '@posthog/lemon-ui'
import { useActions, useValues } from 'kea'
import { router } from 'kea-router'
import { FeedbackNotice } from 'lib/components/FeedbackNotice'
Expand Down Expand Up @@ -49,7 +48,6 @@ export function Alerts({ alertId }: AlertsProps): JSX.Element {
className={alert.enabled ? '' : 'text-muted'}
title={
<div className="flex flex-row gap-3 items-center">
{alert.enabled ? <AlertStateIndicator alert={alert} /> : null}
<div>{name}</div>
</div>
}
Expand All @@ -58,6 +56,13 @@ export function Alerts({ alertId }: AlertsProps): JSX.Element {
)
},
},
{
title: 'Status',
dataIndex: 'state',
render: function renderStateIndicator(_, alert: AlertType) {
return alert.enabled ? <AlertStateIndicator alert={alert} /> : null
},
},
{
title: 'Last checked',
sorter: true,
Expand Down Expand Up @@ -98,7 +103,8 @@ export function Alerts({ alertId }: AlertsProps): JSX.Element {
title: 'Enabled',
dataIndex: 'enabled',
key: 'enabled',
render: (enabled: any) => (enabled ? <IconCheck /> : null),
render: (enabled: any) =>
enabled ? <LemonTag type="success">ENABLED</LemonTag> : <LemonTag type="danger">DISABLED</LemonTag>,
},
]

Expand Down
26 changes: 16 additions & 10 deletions frontend/src/lib/components/Alerts/views/EditAlertModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,13 @@ export function AlertStateTable({ alert }: { alert: AlertType }): JSX.Element |

return (
<div className="bg-bg-3000 p-4 mt-10 rounded-lg">
<h3>
Current status - {alert.state}
{alert.snoozed_until && ` until ${formatDate(dayjs(alert?.snoozed_until), 'MMM D, HH:mm')}`}{' '}
<div className="flex flex-row gap-2 items-center mb-2">
<h3 className="m-0">Current status: </h3>
<AlertStateIndicator alert={alert} />
</h3>
<h3 className="m-0">
{alert.snoozed_until && ` until ${formatDate(dayjs(alert?.snoozed_until), 'MMM D, HH:mm')}`}
</h3>
</div>
<table className="w-full table-auto border-spacing-2 border-collapse">
<thead>
<tr className="text-left">
Expand Down Expand Up @@ -92,7 +94,7 @@ export function EditAlertModal({
const { setAlertFormValue } = useActions(formLogic)

const trendsLogic = trendsDataLogic({ dashboardItemId: insightShortId })
const { alertSeries, isNonTimeSeriesDisplay, isBreakdownValid } = useValues(trendsLogic)
const { alertSeries, isNonTimeSeriesDisplay, isBreakdownValid, formula } = useValues(trendsLogic)

const creatingNewAlert = alertForm.id === undefined

Expand Down Expand Up @@ -161,13 +163,17 @@ export function EditAlertModal({
options={alertSeries?.map(({ event }, index) => ({
label: isBreakdownValid
? 'any breakdown value'
: formula
? `Formula (${formula})`
: `${alphabet[index]} - ${event}`,
value: index,
value: isBreakdownValid || formula ? 0 : index,
}))}
disabledReason={
isBreakdownValid &&
`For trends with breakdown, the alert will fire if any of the breakdown
values breaches the threshold.`
(isBreakdownValid &&
`For trends with breakdown, the alert will fire if any of the breakdown
values breaches the threshold.`) ||
(formula &&
`When using formula mode, can only alert on formula value`)
}
/>
</LemonField>
Expand Down Expand Up @@ -273,7 +279,7 @@ export function EditAlertModal({
{
value: InsightThresholdType.PERCENTAGE,
label: '%',
tooltip: 'Percentage',
tooltip: 'Percent',
},
{
value: InsightThresholdType.ABSOLUTE,
Expand Down
Loading

0 comments on commit 02f4268

Please sign in to comment.