goji-search 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +70 -0
- package/dist/goji-search/components/elements/action-buttons.d.ts +12 -0
- package/dist/goji-search/components/elements/action-buttons.js +135 -0
- package/dist/goji-search/components/elements/calendar-integration.d.ts +5 -0
- package/dist/goji-search/components/elements/calendar-integration.js +49 -0
- package/dist/goji-search/components/elements/inspiration-menu.d.ts +7 -0
- package/dist/goji-search/components/elements/inspiration-menu.js +79 -0
- package/dist/goji-search/components/elements/message-list.d.ts +24 -0
- package/dist/goji-search/components/elements/message-list.js +293 -0
- package/dist/goji-search/components/elements/search-input.d.ts +15 -0
- package/dist/goji-search/components/elements/search-input.js +90 -0
- package/dist/goji-search/components/elements/suggested-questions.d.ts +6 -0
- package/dist/goji-search/components/elements/suggested-questions.js +54 -0
- package/dist/goji-search/components/goji-search-component.d.ts +2 -0
- package/dist/goji-search/components/goji-search-component.js +505 -0
- package/dist/goji-search/config/company.d.ts +29 -0
- package/dist/goji-search/config/company.js +68 -0
- package/dist/goji-search/lib/calendar-config.d.ts +9 -0
- package/dist/goji-search/lib/calendar-config.js +10 -0
- package/dist/goji-search/lib/goji-client.d.ts +81 -0
- package/dist/goji-search/lib/goji-client.js +176 -0
- package/dist/goji-search.css +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +2 -0
- package/package.json +47 -0
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GojiSearch API Client
|
|
3
|
+
* Handles both HTTP and WebSocket connections to the GojiSearch backend
|
|
4
|
+
*/
|
|
5
|
+
export interface ChatMessage {
|
|
6
|
+
role: "user" | "assistant";
|
|
7
|
+
content: string;
|
|
8
|
+
timestamp: number;
|
|
9
|
+
}
|
|
10
|
+
export interface ChatSource {
|
|
11
|
+
index: number;
|
|
12
|
+
url: string;
|
|
13
|
+
title: string;
|
|
14
|
+
domain: string;
|
|
15
|
+
page_type: string;
|
|
16
|
+
score: number;
|
|
17
|
+
}
|
|
18
|
+
export interface ChatResponse {
|
|
19
|
+
session_id: string;
|
|
20
|
+
answer: string;
|
|
21
|
+
sources: ChatSource[];
|
|
22
|
+
}
|
|
23
|
+
export interface StreamDeltaMessage {
|
|
24
|
+
type: "chat_delta";
|
|
25
|
+
delta: string;
|
|
26
|
+
}
|
|
27
|
+
export interface StreamDoneMessage {
|
|
28
|
+
type: "chat_done";
|
|
29
|
+
session_id: string;
|
|
30
|
+
answer: string;
|
|
31
|
+
sources: ChatSource[];
|
|
32
|
+
}
|
|
33
|
+
export type StreamMessage = StreamDeltaMessage | StreamDoneMessage;
|
|
34
|
+
export declare class GojiSearchClient {
|
|
35
|
+
private baseUrl;
|
|
36
|
+
private wsUrl;
|
|
37
|
+
private ws;
|
|
38
|
+
private currentHandler;
|
|
39
|
+
private reconnectAttempts;
|
|
40
|
+
private maxReconnectAttempts;
|
|
41
|
+
constructor(baseUrl?: string);
|
|
42
|
+
private ensureWebSocketConnection;
|
|
43
|
+
closeWebSocket(): void;
|
|
44
|
+
/**
|
|
45
|
+
* Send a chat message (non-streaming)
|
|
46
|
+
*/
|
|
47
|
+
chat(params: {
|
|
48
|
+
message: string;
|
|
49
|
+
sessionId?: string;
|
|
50
|
+
limit?: number;
|
|
51
|
+
language?: string;
|
|
52
|
+
}): Promise<ChatResponse>;
|
|
53
|
+
/**
|
|
54
|
+
* Send a chat message with streaming (WebSocket)
|
|
55
|
+
* Reuses existing WebSocket connection
|
|
56
|
+
*/
|
|
57
|
+
streamChat(params: {
|
|
58
|
+
message: string;
|
|
59
|
+
sessionId?: string;
|
|
60
|
+
limit?: number;
|
|
61
|
+
language?: string;
|
|
62
|
+
onDelta: (delta: string) => void;
|
|
63
|
+
onDone: (response: ChatResponse) => void;
|
|
64
|
+
onError: (error: Error) => void;
|
|
65
|
+
}): Promise<() => void>;
|
|
66
|
+
/**
|
|
67
|
+
* Get chat history for a session
|
|
68
|
+
*/
|
|
69
|
+
getHistory(sessionId: string): Promise<{
|
|
70
|
+
history: ChatMessage[];
|
|
71
|
+
}>;
|
|
72
|
+
/**
|
|
73
|
+
* Clear a chat session
|
|
74
|
+
*/
|
|
75
|
+
clearSession(sessionId: string): Promise<void>;
|
|
76
|
+
/**
|
|
77
|
+
* Health check
|
|
78
|
+
*/
|
|
79
|
+
health(): Promise<any>;
|
|
80
|
+
}
|
|
81
|
+
export declare const gojiClient: GojiSearchClient;
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GojiSearch API Client
|
|
3
|
+
* Handles both HTTP and WebSocket connections to the GojiSearch backend
|
|
4
|
+
*/
|
|
5
|
+
export class GojiSearchClient {
|
|
6
|
+
baseUrl;
|
|
7
|
+
wsUrl;
|
|
8
|
+
ws = null;
|
|
9
|
+
currentHandler = null;
|
|
10
|
+
reconnectAttempts = 0;
|
|
11
|
+
maxReconnectAttempts = 3;
|
|
12
|
+
constructor(baseUrl = "http://localhost:8000") {
|
|
13
|
+
this.baseUrl = baseUrl;
|
|
14
|
+
this.wsUrl = baseUrl.replace(/^http/, "ws");
|
|
15
|
+
}
|
|
16
|
+
ensureWebSocketConnection() {
|
|
17
|
+
if (this.ws && this.ws.readyState === WebSocket.OPEN) {
|
|
18
|
+
return Promise.resolve();
|
|
19
|
+
}
|
|
20
|
+
return new Promise((resolve, reject) => {
|
|
21
|
+
this.ws = new WebSocket(`${this.wsUrl}/ws/chat`);
|
|
22
|
+
this.ws.onopen = () => {
|
|
23
|
+
console.log("[GojiSearch] WebSocket connected");
|
|
24
|
+
this.reconnectAttempts = 0;
|
|
25
|
+
resolve();
|
|
26
|
+
};
|
|
27
|
+
this.ws.onmessage = (event) => {
|
|
28
|
+
try {
|
|
29
|
+
const data = JSON.parse(event.data);
|
|
30
|
+
console.log("[GojiSearch] WebSocket message:", data.type, data);
|
|
31
|
+
if (data.type === "ready") {
|
|
32
|
+
console.log("[GojiSearch] WebSocket ready");
|
|
33
|
+
}
|
|
34
|
+
else if (data.type === "chat_delta") {
|
|
35
|
+
if (this.currentHandler) {
|
|
36
|
+
console.log("[GojiSearch] Delta:", data.delta);
|
|
37
|
+
this.currentHandler.onDelta(data.delta);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
else if (data.type === "chat_done") {
|
|
41
|
+
console.log("[GojiSearch] Chat done, answer length:", data.answer?.length, "sources:", data.sources?.length);
|
|
42
|
+
if (this.currentHandler) {
|
|
43
|
+
this.currentHandler.onDone({
|
|
44
|
+
session_id: data.session_id,
|
|
45
|
+
answer: data.answer,
|
|
46
|
+
sources: data.sources,
|
|
47
|
+
});
|
|
48
|
+
// Clear handler after completion
|
|
49
|
+
this.currentHandler = null;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
else if (data.type === "error") {
|
|
53
|
+
if (this.currentHandler) {
|
|
54
|
+
this.currentHandler.onError(new Error(data.error || "Unknown error"));
|
|
55
|
+
this.currentHandler = null;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
catch (error) {
|
|
60
|
+
console.error("[GojiSearch] Parse error:", error);
|
|
61
|
+
if (this.currentHandler) {
|
|
62
|
+
this.currentHandler.onError(error);
|
|
63
|
+
this.currentHandler = null;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
this.ws.onerror = (event) => {
|
|
68
|
+
console.error("[GojiSearch] WebSocket error");
|
|
69
|
+
reject(new Error("WebSocket connection failed"));
|
|
70
|
+
};
|
|
71
|
+
this.ws.onclose = (event) => {
|
|
72
|
+
console.log("[GojiSearch] WebSocket closed");
|
|
73
|
+
this.ws = null;
|
|
74
|
+
// Attempt reconnect if not a clean close
|
|
75
|
+
if (!event.wasClean && this.reconnectAttempts < this.maxReconnectAttempts) {
|
|
76
|
+
this.reconnectAttempts++;
|
|
77
|
+
console.log(`[GojiSearch] Reconnecting (${this.reconnectAttempts}/${this.maxReconnectAttempts})...`);
|
|
78
|
+
setTimeout(() => this.ensureWebSocketConnection(), 1000 * this.reconnectAttempts);
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
closeWebSocket() {
|
|
84
|
+
if (this.ws) {
|
|
85
|
+
this.ws.close();
|
|
86
|
+
this.ws = null;
|
|
87
|
+
}
|
|
88
|
+
this.currentHandler = null;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Send a chat message (non-streaming)
|
|
92
|
+
*/
|
|
93
|
+
async chat(params) {
|
|
94
|
+
const response = await fetch(`${this.baseUrl}/chat`, {
|
|
95
|
+
method: "POST",
|
|
96
|
+
headers: {
|
|
97
|
+
"Content-Type": "application/json",
|
|
98
|
+
},
|
|
99
|
+
body: JSON.stringify({
|
|
100
|
+
message: params.message,
|
|
101
|
+
session_id: params.sessionId,
|
|
102
|
+
limit: params.limit || 5,
|
|
103
|
+
language: params.language,
|
|
104
|
+
}),
|
|
105
|
+
});
|
|
106
|
+
if (!response.ok) {
|
|
107
|
+
throw new Error(`Chat request failed: ${response.statusText}`);
|
|
108
|
+
}
|
|
109
|
+
return response.json();
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Send a chat message with streaming (WebSocket)
|
|
113
|
+
* Reuses existing WebSocket connection
|
|
114
|
+
*/
|
|
115
|
+
async streamChat(params) {
|
|
116
|
+
try {
|
|
117
|
+
// Ensure WebSocket is connected
|
|
118
|
+
await this.ensureWebSocketConnection();
|
|
119
|
+
// Set current handler (only one message at a time)
|
|
120
|
+
this.currentHandler = {
|
|
121
|
+
onDelta: params.onDelta,
|
|
122
|
+
onDone: params.onDone,
|
|
123
|
+
onError: params.onError,
|
|
124
|
+
};
|
|
125
|
+
console.log("[GojiSearch] Sending message:", params.message);
|
|
126
|
+
// Send message
|
|
127
|
+
this.ws.send(JSON.stringify({
|
|
128
|
+
message: params.message,
|
|
129
|
+
session_id: params.sessionId,
|
|
130
|
+
limit: params.limit || 5,
|
|
131
|
+
stream: true,
|
|
132
|
+
language: params.language,
|
|
133
|
+
}));
|
|
134
|
+
// Return cleanup function
|
|
135
|
+
return () => {
|
|
136
|
+
if (this.currentHandler === params) {
|
|
137
|
+
this.currentHandler = null;
|
|
138
|
+
}
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
catch (error) {
|
|
142
|
+
params.onError(error);
|
|
143
|
+
return () => { }; // No-op cleanup
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Get chat history for a session
|
|
148
|
+
*/
|
|
149
|
+
async getHistory(sessionId) {
|
|
150
|
+
const response = await fetch(`${this.baseUrl}/chat/history/${sessionId}`);
|
|
151
|
+
if (!response.ok) {
|
|
152
|
+
throw new Error(`Failed to get history: ${response.statusText}`);
|
|
153
|
+
}
|
|
154
|
+
return response.json();
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Clear a chat session
|
|
158
|
+
*/
|
|
159
|
+
async clearSession(sessionId) {
|
|
160
|
+
const response = await fetch(`${this.baseUrl}/chat/clear/${sessionId}`, {
|
|
161
|
+
method: "DELETE",
|
|
162
|
+
});
|
|
163
|
+
if (!response.ok) {
|
|
164
|
+
throw new Error(`Failed to clear session: ${response.statusText}`);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Health check
|
|
169
|
+
*/
|
|
170
|
+
async health() {
|
|
171
|
+
const response = await fetch(`${this.baseUrl}/health`);
|
|
172
|
+
return response.json();
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
// Global instance
|
|
176
|
+
export const gojiClient = new GojiSearchClient(process.env.NEXT_PUBLIC_GOJI_API_URL || "http://localhost:8000");
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
@property --a{syntax: "<angle>"; inherits: true; initial-value: 0deg;}@property --l{syntax: "<number>"; inherits: true; initial-value: 0;}@property --x{syntax: "<length>"; inherits: false; initial-value: 0;}@property --y{syntax: "<length>"; inherits: false; initial-value: 0;}@property --o{syntax: "<number>"; inherits: false; initial-value: 0;}@property --value{syntax: "<angle>"; inherits: true; initial-value: 0deg;}@property --width-ratio{syntax: "<number>"; inherits: true; initial-value: 0;}@property --scale{syntax: "<number>"; inherits: true; initial-value: 0;}:root{--count: 4;--radius: .75rem;--width: .125rem;--duration: 8s}.ai{--s: 3.3rem;--p: calc(var(--s) / 4);width:var(--s);aspect-ratio:1;--bg-color: color-mix(in srgb, #7b7bf4, transparent 90%);background:radial-gradient(60% 75% at center,var(--bg-color) 50%,transparent 50%),radial-gradient(75% 60% at center,var(--bg-color) 50%,transparent 50%);padding:var(--p);display:grid;place-items:center;position:relative;border-radius:50%}@keyframes ai{0%{--a: 360deg;--l: .35;--o: 1}30%{--l: 1.5}70%{--o: .4;--l: .05}98%{--o: .7}to{--a: 0deg;--l: .35;--o: 1}}.c{opacity:.9;position:absolute;width:1.25rem;aspect-ratio:1;border-radius:50%;--offset-per-item: calc(360deg / var(--count));--current-angle-offset: calc(var(--offset-per-item) * var(--i) + var(--a));translate:calc(cos(var(--current-angle-offset)) * var(--radius) + var(--x, 0)) calc(sin(var(--current-angle-offset)) * var(--radius) * -1);scale:calc(.6 + var(--l));animation:ai 5.5s cubic-bezier(.45,-.35,.68,.24) infinite;transition:opacity .3s linear;opacity:var(--o, 1)}.c:nth-child(1){--i: 0}.c:nth-child(2){--i: 1}.c:nth-child(3){--i: 2}.c:nth-child(4){--i: 3}.c1{background:radial-gradient(50% 50% at center,#c979ee,#74bcd6);--x: .125rem;width:2rem;animation-timing-function:cubic-bezier(.12,.32,.68,.24)}.c2{background:radial-gradient(50% 50% at center,#ef788c,#e7e7fb);width:1.875rem}.c3{background:radial-gradient(50% 50% at center,#eb7fc6,transparent);width:.625rem;opacity:.6;--x: -.125rem}.c4{background:#6d67c8;animation-timing-function:cubic-bezier(.39,-.03,.75,.47)}.container-ai{overflow:hidden;background:#b6a9f8;width:100%;border-radius:50%;aspect-ratio:1;position:relative;display:grid;place-items:center}.glass-ai{overflow:hidden;position:absolute;--w: .0625rem;inset:calc(var(--p) - var(--w));border-radius:50%;-webkit-backdrop-filter:blur(.1625rem);backdrop-filter:blur(.1625rem);box-shadow:0 0 1rem color-mix(in srgb,black,transparent 70%);background:radial-gradient(1.25rem at 70% 30%,rgba(255,255,255,.7),transparent)}.glass-ai:after{content:"";position:absolute;inset:0;--c: rgba(255, 255, 255, .03);--w: .0625rem;--g: .1875rem;background:repeating-linear-gradient(var(--c),var(--c),var(--w),transparent var(--w),transparent calc(var(--w) + var(--g)));border-radius:inherit;border:.125rem rgba(255,255,255,.1) solid}.rings-ai{aspect-ratio:1;border-radius:50%;position:absolute;inset:0;perspective:11rem;opacity:.9}.rings-ai:before,.rings-ai:after{content:"";position:absolute;inset:0;background:red;border-radius:50%;--width-ratio: 1;border:calc(var(--width) * var(--width-ratio)) solid transparent;mask:linear-gradient(#fff 0 0) padding-box,linear-gradient(#fff 0 0);background:linear-gradient(#fff,#00f,#f0f,violet,#ffffe0) border-box;mask-composite:exclude;-webkit-mask-composite:xor;animation:ring-ai var(--duration) ease-in-out infinite;--start: 180deg;--value: var(--start);--scale: 1;transform:rotateY(var(--value)) rotateX(var(--value)) rotate(var(--value)) scale(var(--scale))}.rings-ai:before{--start: 180deg}.rings-ai:after{--start: 90deg}.rings-ai>.rings-ai:before{--start: 360deg}.rings-ai>.rings-ai:after{--start: 270deg}@keyframes ring-ai{0%{--value: var(--start);--scale: 1}50%{--scale: 1.2;--width-ratio: 1.5}70%{--scale: 1;--value: calc(var(--start) + 180deg);--width-ratio: 1}80%{--scale: 1.2;--width-ratio: 1.5}to{--value: calc(var(--start) + 360deg);--scale: 1;--width-ratio: 1}}@keyframes fadeIn{0%{opacity:0;transform:translateY(8px)}to{opacity:1;transform:translateY(0)}}@keyframes pulse{0%,to{opacity:.4}50%{opacity:1}}@keyframes slideUp{0%{opacity:0;transform:translateY(8px)}to{opacity:1;transform:translateY(0)}}@keyframes slideUpMenu{0%{opacity:0;transform:translateY(12px) scale(.95)}to{opacity:1;transform:translateY(0) scale(1)}}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { GojiSearchComponent } from './goji-search/components/goji-search-component';
|
package/dist/index.js
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "goji-search",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Embeddable GojiSearch components for both React and vanilla JS",
|
|
5
|
+
"private": false,
|
|
6
|
+
"type": "module",
|
|
7
|
+
"main": "dist/index.js",
|
|
8
|
+
"module": "dist/index.js",
|
|
9
|
+
"types": "./dist/index.d.ts",
|
|
10
|
+
"files": [
|
|
11
|
+
"dist"
|
|
12
|
+
],
|
|
13
|
+
"exports": {
|
|
14
|
+
".": {
|
|
15
|
+
"types": "./dist/index.d.ts",
|
|
16
|
+
"import": "./dist/index.js"
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
"scripts": {
|
|
20
|
+
"dev": "vite",
|
|
21
|
+
"build": "tsc && vite build && npx tsc",
|
|
22
|
+
"dev-pack": "tsc && vite build && npx tsc && cp dist/goji-search.css dist/goji-search/components/goji-search.css && cp -r src/goji-search/assets dist/goji-search/ && npm pack",
|
|
23
|
+
"lint": "eslint ."
|
|
24
|
+
},
|
|
25
|
+
"peerDependencies": {
|
|
26
|
+
"react": "^18.0.0 || ^19.0.0",
|
|
27
|
+
"react-dom": "^18.0.0 || ^19.0.0"
|
|
28
|
+
},
|
|
29
|
+
"devDependencies": {
|
|
30
|
+
"@eslint/js": "^8.57.0",
|
|
31
|
+
"@types/node": "^20.12.0",
|
|
32
|
+
"@types/react": "^18.2.56",
|
|
33
|
+
"@types/react-dom": "^18.2.19",
|
|
34
|
+
"@vitejs/plugin-react": "^4.2.1",
|
|
35
|
+
"eslint": "^8.57.0",
|
|
36
|
+
"eslint-plugin-react-hooks": "^4.6.0",
|
|
37
|
+
"eslint-plugin-react-refresh": "^0.4.5",
|
|
38
|
+
"typescript": "^5.4.5",
|
|
39
|
+
"vite": "^7.1.9"
|
|
40
|
+
},
|
|
41
|
+
"dependencies": {
|
|
42
|
+
"@calcom/embed-react": "^1.5.3",
|
|
43
|
+
"lucide-react": "^0.545.0",
|
|
44
|
+
"react-markdown": "^10.1.0",
|
|
45
|
+
"remark-gfm": "^4.0.1"
|
|
46
|
+
}
|
|
47
|
+
}
|