Skip to content

Commit bb9fca0

Browse files
authored
Add LuftBnB recipe (#108)
* add luftbnb recipe * auto-generate mock results with LLM * add summary ui
1 parent 537c50d commit bb9fca0

File tree

5 files changed

+341
-61
lines changed

5 files changed

+341
-61
lines changed

typescript/packages/lookslike-high-level/src/components/window-manager.ts

Lines changed: 51 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { LitElement, html, css } from "lit";
22
import { customElement, property } from "lit/decorators.js";
3-
import { view, tags, render, style } from "@commontools/common-ui";
3+
import { tags, render, style } from "@commontools/common-ui";
44
import { isGem, Gem, ID } from "../recipe.js";
55
const { include } = tags;
66

@@ -9,56 +9,56 @@ export class CommonWindowManager extends LitElement {
99
static override styles = [
1010
style.baseStyles,
1111
css`
12-
:host {
13-
display: flex;
14-
overflow-x: auto;
15-
overflow-y: visible;
16-
width: 100%;
17-
height: 95vh;
18-
padding: 20px 0; /* Add vertical padding */
19-
}
20-
.window {
21-
flex: 0 0 auto;
22-
width: 25%;
23-
min-width: 300px;
24-
height: 95%; /* Make the window full height */
25-
margin-left: 20px;
26-
margin-bottom: 20px;
27-
border: 1px solid #e0e0e0;
28-
border-radius: var(--radius);
29-
background-color: rgba(255, 255, 255, 0.8);
30-
backdrop-filter: blur(10px);
31-
box-shadow:
32-
0 10px 20px rgba(0, 0, 0, 0.1),
33-
0 6px 6px rgba(0, 0, 0, 0.1),
34-
0 0 0 1px rgba(0, 0, 0, 0.05);
35-
transition: all 0.3s ease;
36-
overflow: hidden;
37-
}
38-
.close-button {
39-
z-index: 1;
40-
position: absolute;
41-
top: 8px;
42-
right: 8px;
43-
width: 16px;
44-
height: 16px;
45-
border-radius: 50%;
46-
background-color: rgba(0, 0, 0, 0.1);
47-
border: none;
48-
cursor: pointer;
49-
display: flex;
50-
align-items: center;
51-
justify-content: center;
52-
font-size: 12px;
53-
color: rgba(0, 0, 0, 0.4);
54-
font-weight: bold;
55-
transition: all 0.2s ease;
56-
}
57-
.close-button:hover {
58-
background-color: rgba(0, 0, 0, 0.15);
59-
color: rgba(0, 0, 0, 0.6);
60-
}
61-
`
12+
:host {
13+
display: flex;
14+
overflow-x: auto;
15+
overflow-y: visible;
16+
width: 100%;
17+
height: 95vh;
18+
padding: 20px 0; /* Add vertical padding */
19+
}
20+
.window {
21+
flex: 0 0 auto;
22+
width: 25%;
23+
min-width: 300px;
24+
height: 95%; /* Make the window full height */
25+
margin-left: 20px;
26+
margin-bottom: 20px;
27+
border: 1px solid #e0e0e0;
28+
border-radius: var(--radius);
29+
background-color: rgba(255, 255, 255, 0.8);
30+
backdrop-filter: blur(10px);
31+
box-shadow:
32+
0 10px 20px rgba(0, 0, 0, 0.1),
33+
0 6px 6px rgba(0, 0, 0, 0.1),
34+
0 0 0 1px rgba(0, 0, 0, 0.05);
35+
transition: all 0.3s ease;
36+
overflow: hidden;
37+
}
38+
.close-button {
39+
z-index: 1;
40+
position: absolute;
41+
top: 8px;
42+
right: 8px;
43+
width: 16px;
44+
height: 16px;
45+
border-radius: 50%;
46+
background-color: rgba(0, 0, 0, 0.1);
47+
border: none;
48+
cursor: pointer;
49+
display: flex;
50+
align-items: center;
51+
justify-content: center;
52+
font-size: 12px;
53+
color: rgba(0, 0, 0, 0.4);
54+
font-weight: bold;
55+
transition: all 0.2s ease;
56+
}
57+
.close-button:hover {
58+
background-color: rgba(0, 0, 0, 0.15);
59+
color: rgba(0, 0, 0, 0.6);
60+
}
61+
`,
6262
];
6363

6464
@property({ type: Array })

typescript/packages/lookslike-high-level/src/data.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ const { state } = signal;
55

66
import { todoList, makeTodoItem } from "./recipes/todo-list.js";
77
import { localSearch } from "./recipes/local-search.js";
8+
import { luftBnBSearch } from "./recipes/luft-bnb-search.js";
89

910
import "./recipes/todo-list-as-task.js"; // Necessary, so that suggestions are indexed.
1011

@@ -44,4 +45,31 @@ export const recipes: RecipeManifest[] = [
4445
recipe: localSearch,
4546
inputs: { query: "", location: "" },
4647
},
48+
{
49+
name: "Find a LuftBnB place to stay",
50+
recipe: luftBnBSearch,
51+
inputs: { ...getFridayAndMondayDateStrings(), location: "" },
52+
},
4753
];
54+
55+
// Helper for mock data
56+
function getFridayAndMondayDateStrings() {
57+
const today = new Date();
58+
const daysUntilFriday = (5 - today.getDay() + 7) % 7;
59+
60+
const nextFriday = new Date(
61+
today.getTime() + daysUntilFriday * 24 * 60 * 60 * 1000
62+
);
63+
const followingMonday = new Date(
64+
nextFriday.getTime() + 3 * 24 * 60 * 60 * 1000
65+
);
66+
67+
const formatDate = (date: Date): string => {
68+
return date.toISOString().split("T")[0];
69+
};
70+
71+
return {
72+
startDate: formatDate(nextFriday),
73+
endDate: formatDate(followingMonday),
74+
};
75+
}

typescript/packages/lookslike-high-level/src/llm-client.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,9 @@ export const suggestionClient = new LLMClient({
88
"You are an assistant that helps match user queries to relevant data gems based on their names and types.",
99
tools: [],
1010
});
11+
12+
export const mockResultClient = new LLMClient({
13+
serverUrl: LLM_SERVER_URL,
14+
system: `Generate dummy data as JSON as per the provided spec. Use the input to imagine what an API response would look like for a request.`,
15+
tools: [],
16+
});

typescript/packages/lookslike-high-level/src/recipes/local-search.ts

Lines changed: 66 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,50 @@
11
import { view, tags } from "@commontools/common-ui";
22
import { signal, stream } from "@commontools/common-frp";
3+
import { generateData } from "@commontools/llm-client";
34
import { recipe, NAME } from "../recipe.js";
5+
import { mockResultClient } from "../llm-client.js";
46
const { binding, repeat } = view;
57
const { vstack, hstack, div, commonInput, button } = tags;
68
const { state, computed } = signal;
79
const { subject } = stream;
810

11+
export interface Place {
12+
name: string;
13+
description: string;
14+
address: string;
15+
city: string;
16+
state: string;
17+
zip: string;
18+
latitude: number;
19+
longitude: number;
20+
rating: number;
21+
}
22+
923
export const localSearch = recipe("local search", ({ query, location }) => {
1024
// Initial search
11-
const results = state(performLocalSearch(query.get(), location.get()));
25+
const places = state<Place[]>([]);
26+
performLocalSearch(query.get(), location.get()).then((results) =>
27+
places.send(results)
28+
);
1229

1330
const search = subject<any>();
14-
1531
search.sink({
16-
send: () => results.send(performLocalSearch(query.get(), location.get())),
32+
send: () =>
33+
performLocalSearch(query.get(), location.get()).then((results) =>
34+
places.send(results)
35+
),
1736
});
1837

38+
const summaries = computed([places], (places: Place[]) =>
39+
places.map((place) => ({
40+
name: place.name,
41+
description: place.description,
42+
address: place.address,
43+
city: place.city + ", " + place.state + " " + place.zip,
44+
rating: "*****".slice(0, place.rating),
45+
}))
46+
);
47+
1948
return {
2049
UI: vstack({}, [
2150
hstack({}, [
@@ -40,17 +69,21 @@ export const localSearch = recipe("local search", ({ query, location }) => {
4069
vstack(
4170
{},
4271
repeat(
43-
results,
72+
summaries,
4473
vstack({}, [
4574
div({}, binding("name")),
4675
div({}, binding("description")),
76+
div({}, binding("imageUrl")),
77+
div({}, binding("address")),
78+
div({}, binding("city")),
79+
div({}, binding("rating")),
4780
])
4881
)
4982
),
5083
]),
5184
query,
5285
location,
53-
results,
86+
places,
5487
[NAME]: computed(
5588
[query, location],
5689
(query: string, location: string) =>
@@ -59,9 +92,32 @@ export const localSearch = recipe("local search", ({ query, location }) => {
5992
};
6093
});
6194

62-
function performLocalSearch(query: string, location: string) {
63-
return [
64-
{ name: "Result 1", description: "Description 1" },
65-
{ name: "Result 2", description: "Description 2" },
66-
];
95+
async function performLocalSearch(
96+
query: string,
97+
location: string
98+
): Promise<Place[]> {
99+
if (!query || !location) return [];
100+
const result = await generateData(
101+
mockResultClient,
102+
`generate 10 places that match they query: ${query} in ${location}`,
103+
[],
104+
{
105+
type: "array",
106+
items: {
107+
type: "object",
108+
properties: {
109+
name: { type: "string" },
110+
description: { type: "string" },
111+
address: { type: "string" },
112+
city: { type: "string" },
113+
state: { type: "string" },
114+
zip: { type: "string" },
115+
latitude: { type: "number" },
116+
longitude: { type: "number" },
117+
rating: { type: "number", minimum: 0, maximum: 5 },
118+
},
119+
},
120+
}
121+
);
122+
return result as Place[];
67123
}

0 commit comments

Comments
 (0)