Skip to content

Commit ecdbcb4

Browse files
committed
update aliases in the background
1 parent 81e89a3 commit ecdbcb4

File tree

4 files changed

+100
-107
lines changed

4 files changed

+100
-107
lines changed

core/config/config.exs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,9 @@ config :canary, Oban,
7070
{Oban.Plugins.Lifeline, rescue_after: :timer.minutes(5)},
7171
{Oban.Plugins.Cron,
7272
crontab: [
73-
{"0 0 * * *", Canary.Workers.SourceProcessor}
73+
{"0 0 * * *", Canary.Workers.SourceProcessor},
74+
{"0 0 */2 * *", Canary.Workers.Pruner},
75+
{"0 0 * * *", Canary.Workers.QueryAliases}
7476
]}
7577
]
7678

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
defmodule Canary.Workers.QueryAliases do
2+
use Oban.Worker, queue: :default, max_attempts: 1
3+
4+
alias Canary.Accounts.Project
5+
6+
@impl true
7+
def perform(%Oban.Job{args: %{"project_id" => project_id}}) do
8+
{:ok, %{insights_config: config}} = Ash.get(Project, project_id, load: [:insights_config])
9+
10+
{:ok, analytics_result} =
11+
Canary.Analytics.query(:search_breakdown, %{project_id: project_id, n: 100, days: 120})
12+
13+
{:ok, completion_result} =
14+
Canary.AI.chat(%{
15+
model: Application.fetch_env!(:canary, :general_model),
16+
messages: [
17+
%{role: "system", content: "Only output JSON that strictly follows the schema."},
18+
%{
19+
role: "user",
20+
content: """
21+
---
22+
#{Jason.encode!(analytics_result, pretty: true)}
23+
---
24+
25+
Above are analytics on quries user typed in the search bar.
26+
Some of them are just partial queries performed while typing, and some of them are full queries but similar.
27+
Write down aliases if they can be combined. Take extra care on partial queries.
28+
29+
For example, if we have "examp", "example" and "exam" in the quries list, we can combine them into:
30+
name: "example",
31+
members: ["example", "examp", "exam"]
32+
33+
Each item in "members" must be picked from the above queries, without any extra text.
34+
Length of "members" should be more than 1, otherwise, there's no point to create an alias.
35+
36+
This is existing aliases. You can remove, add, or edit it, but should mostly follow the intention & keep it.
37+
---
38+
#{Jason.encode!(config, pretty: true)}
39+
---
40+
"""
41+
}
42+
],
43+
temperature: 0,
44+
max_tokens: 3000,
45+
response_format: %{
46+
type: "json_schema",
47+
json_schema: %{
48+
name: "Aliases",
49+
strict: true,
50+
schema: %{
51+
type: "object",
52+
properties: %{
53+
aliases: %{
54+
type: "array",
55+
items: %{
56+
type: "object",
57+
properties: %{
58+
name: %{type: "string"},
59+
members: %{
60+
type: "array",
61+
items: %{type: "string"}
62+
}
63+
},
64+
required: ["name", "members"],
65+
additionalProperties: false
66+
}
67+
}
68+
},
69+
required: ["aliases"],
70+
additionalProperties: false
71+
}
72+
}
73+
}
74+
})
75+
76+
%{"aliases" => aliases} = Jason.decode!(completion_result)
77+
78+
if is_nil(config) do
79+
Canary.Insights.Config
80+
|> Ash.Changeset.for_create(:create, %{aliases: aliases, project_id: project_id})
81+
|> Ash.create()
82+
else
83+
config
84+
|> Ash.Changeset.for_update(:update, %{aliases: aliases})
85+
|> Ash.update()
86+
end
87+
88+
:ok
89+
end
90+
end

core/lib/canary_web/live/insights_live/config.ex

Lines changed: 1 addition & 99 deletions
Original file line numberDiff line numberDiff line change
@@ -9,18 +9,6 @@ defmodule CanaryWeb.InsightLive.Config do
99
<div class="text-md font-semibold">Aliases</div>
1010
<div class="mb-4">
1111
<div>Set this if you want to combine duplicated queries.</div>
12-
<div>
13-
Click
14-
<span
15-
class="hero-sparkles w-5 h-5 cursor-pointer"
16-
phx-click="generate"
17-
phx-target={@myself}
18-
phx-value-item={@form.name}
19-
>
20-
</span>
21-
to generate from scratch.
22-
</div>
23-
<div :if={@generating?}>Generating... (may takes ~10 seconds)</div>
2412
</div>
2513
2614
<.form
@@ -44,7 +32,7 @@ defmodule CanaryWeb.InsightLive.Config do
4432
form_control={%{label: "Name"}}
4533
/>
4634
<Primer.octicon
47-
class="ml-auto"
35+
class="ml-auto cursor-pointer"
4836
name="x-16"
4937
phx-target={@myself}
5038
phx-click="remove_alias"
@@ -114,7 +102,6 @@ defmodule CanaryWeb.InsightLive.Config do
114102
socket
115103
|> assign(assigns)
116104
|> assign_form()
117-
|> assign(:generating?, false)
118105

119106
{:ok, socket}
120107
end
@@ -269,89 +256,4 @@ defmodule CanaryWeb.InsightLive.Config do
269256

270257
{:noreply, assign(socket, form: form)}
271258
end
272-
273-
def handle_event("generate", %{"item" => path}, socket) do
274-
quries = socket.assigns.quries |> Enum.slice(0, 20)
275-
276-
socket =
277-
socket
278-
|> assign(:generating?, true)
279-
|> start_async(:generate, fn ->
280-
{:ok, completion} =
281-
Canary.AI.chat(%{
282-
model: Application.fetch_env!(:canary, :general_model),
283-
messages: [
284-
%{
285-
role: "user",
286-
content: """
287-
#{quries}
288-
---
289-
290-
Above are quries user typed in the search bar.
291-
Some of them are just partial queries performed while typing, and some of them are full queries but similar.
292-
Write down aliases if they can be combined. Take extra care on partial queries.
293-
294-
For example, if we have "examp", "example" and "exam" in the quries list, we can combine them into:
295-
name: "example",
296-
members: ["example", "examp", "exam"]
297-
298-
Each item in "members" must be picked from the above queries, without any extra text.
299-
"""
300-
}
301-
],
302-
temperature: 0,
303-
max_tokens: 2000,
304-
response_format: %{
305-
type: "json_schema",
306-
json_schema: %{
307-
name: "Aliases",
308-
strict: true,
309-
schema: %{
310-
type: "object",
311-
properties: %{
312-
aliases: %{
313-
type: "array",
314-
items: %{
315-
type: "object",
316-
properties: %{
317-
name: %{type: "string"},
318-
members: %{
319-
type: "array",
320-
items: %{type: "string"}
321-
}
322-
},
323-
required: ["name", "members"],
324-
additionalProperties: false
325-
}
326-
}
327-
},
328-
required: ["aliases"],
329-
additionalProperties: false
330-
}
331-
}
332-
}
333-
})
334-
335-
{path, Jason.decode!(completion)}
336-
end)
337-
338-
{:noreply, socket}
339-
end
340-
341-
@impl true
342-
def handle_async(:generate, {:ok, {path, data}}, socket) do
343-
form =
344-
socket.assigns.form
345-
|> AshPhoenix.Form.update_form(path, fn nested_form ->
346-
params = Map.merge(nested_form.params, data)
347-
AshPhoenix.Form.validate(nested_form, params)
348-
end)
349-
350-
socket =
351-
socket
352-
|> assign(:generating?, false)
353-
|> assign(:form, form)
354-
355-
{:noreply, socket}
356-
end
357259
end

core/lib/canary_web/live/insights_live/index.ex

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -23,20 +23,19 @@ defmodule CanaryWeb.InsightLive.Index do
2323
<div class="flex flex-col gap-4">
2424
<div class="flex flex-row justify-between items-center mb-2">
2525
<h2>Insights</h2>
26-
<div>
27-
<%!-- <.button is_primary phx-click={show_modal("insights-config-dialog")}>
26+
<%!-- <div>
27+
<.button is_primary phx-click={show_modal("insights-config-dialog")}>
2828
Config
29-
</.button> --%>
29+
</.button>
3030
31-
<%!-- <.modal id="insights-config-dialog">
31+
<.modal id="insights-config-dialog">
3232
<.live_component
3333
id="insights-config"
3434
module={CanaryWeb.InsightLive.Config}
3535
current_project={@current_project}
36-
quries={if @search_breakdown.result, do: @search_breakdown.result.labels, else: []}
3736
/>
38-
</.modal> --%>
39-
</div>
37+
</.modal>
38+
</div> --%>
4039
</div>
4140
4241
<div class="flex flex-col gap-4">

0 commit comments

Comments
 (0)