Skip to content

Commit fce9247

Browse files
authored
revert to inkeep (#1538)
1 parent fb5199e commit fce9247

File tree

2 files changed

+314
-2
lines changed

2 files changed

+314
-2
lines changed
Lines changed: 312 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,312 @@
1+
"""UI and logic inkeep chat component."""
2+
3+
import reflex as rx
4+
from reflex.utils.imports import ImportVar
5+
from reflex.vars import Var
6+
7+
8+
class InkeepSearchBar(rx.NoSSRComponent):
9+
tag = "InkeepSearchBar"
10+
library = "@inkeep/cxkit-react@0.5.55"
11+
12+
13+
class Search(rx.el.Div):
14+
def add_imports(self):
15+
"""Add the imports for the component."""
16+
return {
17+
"react": {ImportVar(tag="useContext")},
18+
"$/utils/context": {ImportVar(tag="ColorModeContext")},
19+
}
20+
21+
def add_hooks(self):
22+
"""Add the hooks for the component."""
23+
return [
24+
"const { resolvedColorMode } = useContext(ColorModeContext)",
25+
"""
26+
const escalationParams = {
27+
type: "object",
28+
properties: {
29+
explanation: {
30+
type: "string",
31+
description: "A brief few word justification of why a specific confidence level was chosen.",
32+
},
33+
answerConfidence: {
34+
anyOf: [
35+
{
36+
type: "string",
37+
const: "very_confident",
38+
description: `\n The AI Assistant provided a complete and direct answer to all parts of the User Question.\n The answer fully resolved the issue without requiring any further action from the User.\n Every part of the answer was cited from the information sources.\n The assistant did not ask for more information or provide options requiring User action.\n This is the highest Answer Confidence level and should be used sparingly.\n `,
39+
},
40+
{
41+
type: "string",
42+
const: "somewhat_confident",
43+
description: `\n The AI Assistant provided a complete and direct answer to the User Question, but the answer contained minor caveats or uncertainties. \n \n Examples:\n • The AI Assistant asked follow-up questions to the User\n • The AI Assistant requested additional information from the User\n • The AI Assistant suggested uncertainty in the answer\n • The AI Assistant answered the question but mentioned potential exceptions\n `,
44+
},
45+
{
46+
type: "string",
47+
const: "not_confident",
48+
description: `\n The AI Assistant tried to answer the User Question but did not fully resolve it.\n The assistant provided options requiring further action from the User, asked for more information, showed uncertainty,\n suggested the user contact support or provided contact information, or provided an indirect or incomplete answer.\n This is the most common Answer Confidence level.\n \n Examples:\n • The AI Assistant provided a general answer not directly related to the User Question\n • The AI Assistant said to reach out to support or provided an email address or contact information\n • The AI Assistant provided options that require further action from the User to resolve the issue\n `,
49+
},
50+
{
51+
type: "string",
52+
const: "no_sources",
53+
description: `\n The AI Assistant did not use or cite any sources from the information sources to answer the User Question.\n `,
54+
},
55+
{
56+
type: "string",
57+
const: "other",
58+
description: `\n The User Question is unclear or unrelated to the subject matter.\n `,
59+
},
60+
],
61+
description: "A measure of how confidently the AI Assistant completely and directly answered the User Question.",
62+
},
63+
},
64+
required: ["explanation", "answerConfidence"],
65+
additionalProperties: false,
66+
};
67+
const searchBarProps = {
68+
baseSettings: {
69+
apiKey: '6299820854cd95d0a6e55a502d5bae06549e62360e7805a6',
70+
customIcons: {search: {custom: "/icons/search.svg"}},
71+
organizationDisplayName: 'Reflex',
72+
primaryBrandColor: '#6E56CF',
73+
transformSource: (source) => {
74+
const urlPatterns = {
75+
blog: 'reflex.dev/blog',
76+
library: 'reflex.dev/docs/library',
77+
apiRef: 'reflex.dev/docs/api-reference',
78+
docs: 'reflex.dev/docs',
79+
}
80+
81+
function matchUrl(pattern) {
82+
return source.url.includes(pattern)
83+
}
84+
85+
function getBreadcrumbs() {
86+
if (matchUrl(urlPatterns.blog)) {
87+
return ['Blogs', ...source.breadcrumbs.slice(1)]
88+
}
89+
if (matchUrl(urlPatterns.library)) {
90+
return ['Components', ...source.breadcrumbs.slice(1)]
91+
}
92+
if (matchUrl(urlPatterns.apiRef)) {
93+
return ['API Reference']
94+
}
95+
if (matchUrl(urlPatterns.docs)) {
96+
return ['Docs', ...source.breadcrumbs.slice(1)]
97+
}
98+
return source.breadcrumbs
99+
}
100+
101+
const breadcrumbs = getBreadcrumbs()
102+
103+
function getTabs() {
104+
const tabMap = {
105+
[urlPatterns.blog]: 'Blogs',
106+
[urlPatterns.library]: 'Components',
107+
[urlPatterns.apiRef]: 'API Reference',
108+
[urlPatterns.docs]: 'Docs',
109+
}
110+
111+
for (const [pattern, tab] of Object.entries(tabMap)) {
112+
if (matchUrl(pattern)) {
113+
return [
114+
...(source.tabs ?? []),
115+
// If the first breadcrumb is the same as the tab, use the remaining breadcrumbs
116+
// This is only if you don't want breadcrumbs to include current tab, e.g. just "Blog Post" instead of "Blogs > Blog Post" in the Blogs tab
117+
// The tab type accepts a string or an object with a breadcrumbs property i.e. breadcrumbs shown for this source in that tab
118+
[
119+
tab,
120+
{ breadcrumbs: breadcrumbs[0] === tab ? breadcrumbs.slice(1) : breadcrumbs },
121+
],
122+
]
123+
}
124+
}
125+
return source.tabs
126+
}
127+
128+
return {
129+
...source,
130+
tabs: getTabs(),
131+
breadcrumbs,
132+
}
133+
},
134+
colorMode: {
135+
forcedColorMode: resolvedColorMode, // options: 'light' or dark'
136+
},
137+
theme: {
138+
// Add inline styles using the recommended approach from the docs
139+
styles: [
140+
{
141+
key: "custom-theme",
142+
type: "style",
143+
value: `
144+
[data-theme='light'] .ikp-search-bar__button,
145+
[data-theme='dark'] .ikp-search-bar__button {
146+
display: flex;
147+
max-height: 32px;
148+
min-height: 32px;
149+
padding: 6px;
150+
min-width: 256px;
151+
justify-content: space-between;
152+
align-items: center;
153+
border-radius: 10px;
154+
border: 1px solid var(--c-slate-5, #E0E1E6);
155+
background: var(--c-slate-1);
156+
/* Small */
157+
font-family: "Instrument Sans";
158+
font-size: 14px;
159+
font-style: normal;
160+
font-weight: 500;
161+
line-height: 20px;
162+
/* 142.857% */
163+
letter-spacing: -0.0125em;
164+
color: var(--c-slate-9, #8B8D98);
165+
/* Shadow/Large */
166+
box-shadow: 0px 24px 12px 0px rgba(28, 32, 36, 0.02), 0px 8px 8px 0px rgba(28, 32, 36, 0.02), 0px 2px 6px 0px rgba(28, 32, 36, 0.02);
167+
transition: background-color 0.1s linear, width 0s;
168+
}
169+
170+
[data-theme='light'] .ikp-search-bar__container,
171+
[data-theme='dark'] .ikp-search-bar__container {
172+
display: flex;
173+
justify-content: center;
174+
align-items: center;
175+
}
176+
177+
[data-theme='light'] .ikp-search-bar__button:hover,
178+
[data-theme='dark'] .ikp-search-bar__button:hover {
179+
background-color: var(--c-slate-3, #F0F0F3);
180+
}
181+
182+
[data-theme='dark'] .ikp-modal__overlay {
183+
background: rgba(18, 17, 19, 0.50);
184+
backdrop-filter: blur(20px);
185+
}
186+
187+
@media (max-width: 80em) {
188+
[data-theme='light'] .ikp-search-bar__button,
189+
[data-theme='dark'] .ikp-search-bar__button {
190+
padding: 2px 12px;
191+
display: block;
192+
height: 32px;
193+
min-height: 32px;
194+
width: 32px;
195+
max-width: 6em;
196+
min-width: 0px;
197+
}
198+
199+
.ikp-search-bar__button {
200+
align-items: center;
201+
justify-content: center;
202+
}
203+
204+
.ikp-search-bar__kbd-wrapper,
205+
.ikp-search-bar__text {
206+
display: none;
207+
}
208+
209+
.ikp-search-bar__icon {
210+
padding: 0;
211+
margin-right: 2px;
212+
}
213+
214+
.ikp-search-bar__content-wrapper {
215+
justify-content: center;
216+
}
217+
}
218+
219+
.ikp-search-bar__icon {
220+
display: flex;
221+
}
222+
223+
.ikp-search-bar__icon svg {
224+
width: auto;
225+
}
226+
227+
.ikp-search-bar__kbd-wrapper {
228+
padding: 0px 8px;
229+
justify-content: center;
230+
align-items: center;
231+
border-radius: 4px;
232+
box-shadow: none;
233+
color: var(--c-slate-9, #8B8D98);
234+
font-family: "Instrument Sans";
235+
--ikp-colors-transparent: var(--c-slate-3, #FCFCFD);
236+
background: var(--c-slate-3, #F0F0F3) !important;
237+
font-size: 12px;
238+
font-style: normal;
239+
font-weight: 500;
240+
line-height: 20px;
241+
/* 166.667% */
242+
letter-spacing: -0.09px;
243+
}
244+
245+
.ikp-search-bar__text,
246+
.ikp-search-bar__icon {
247+
color: var(--c-slate-9, #8B8D98);
248+
font-weight: 500;
249+
}
250+
`,
251+
},
252+
{
253+
key: "google-fonts-instrument",
254+
type: "link",
255+
value: "https://fonts.googleapis.com/css2?family=Instrument+Sans:wght@400;500;700&display=swap",
256+
},
257+
],
258+
}
259+
},
260+
searchSettings: { // optional InkeepSearchSettings
261+
tabs: ['All', 'Docs', 'Components', 'API Reference', 'Blogs', 'GitHub', 'Forums'].map((t) => [
262+
t,
263+
{ isAlwaysVisible: true },
264+
]),
265+
placeholder: 'Search',
266+
},
267+
aiChatSettings: { // optional typeof InkeepAIChatSettings
268+
exampleQuestions: [
269+
'How does Reflex work?',
270+
'What types of apps can I build with Reflex?',
271+
'Where can I deploy my apps?',
272+
],
273+
getTools: () => [
274+
{
275+
type: "function",
276+
function: {
277+
name: "provideAnswerConfidence",
278+
description: "Determine how confident the AI assistant was and whether or not to escalate to humans.",
279+
parameters: escalationParams,
280+
},
281+
renderMessageButtons: ({ args }) => {
282+
const confidence = args.answerConfidence;
283+
if (["not_confident", "no_sources", "other"].includes(confidence)) {
284+
return [
285+
{
286+
label: "Contact Support",
287+
action: {
288+
'type': 'open_form',
289+
},
290+
}
291+
];
292+
}
293+
return [];
294+
},
295+
},
296+
],
297+
298+
},
299+
};""",
300+
]
301+
302+
@classmethod
303+
def create(cls):
304+
"""Create the search component."""
305+
return super().create(
306+
InkeepSearchBar.create(
307+
special_props=[Var("{...searchBarProps}")],
308+
)
309+
)
310+
311+
312+
inkeep = Search.create
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
"""Search bar component for the navbar."""
22

33
import reflex as rx
4-
from .typesense import typesense_search
4+
from .inkeep import inkeep
55

66

77
@rx.memo
88
def search_bar() -> rx.Component:
9-
return typesense_search()
9+
return inkeep()

0 commit comments

Comments
 (0)