@doderasoftware/restify-ai 0.1.0-beta.4 โ 0.1.0-beta.6
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 +354 -413
- package/package.json +21 -9
package/README.md
CHANGED
|
@@ -1,514 +1,455 @@
|
|
|
1
1
|
# @doderasoftware/restify-ai
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
A production-ready AI chatbot component for Vue 3 with real-time SSE streaming, file attachments, @mentions, and seamless Laravel Restify integration.
|
|
4
4
|
|
|
5
|
-
[](https://www.npmjs.com/package/@doderasoftware/restify-ai)
|
|
6
|
-
[](https://vuejs.org/)
|
|
5
|
+
[](https://www.npmjs.com/package/@doderasoftware/restify-ai)
|
|
6
|
+
[](https://opensource.org/licenses/MIT)
|
|
7
|
+
[](https://vuejs.org/)
|
|
8
|
+
[](https://www.typescriptlang.org/)
|
|
9
|
+
[](https://tailwindcss.com/)
|
|
8
10
|
|
|
9
|
-
|
|
11
|
+
**๐ [Laravel Restify](https://laravel-restify.com) | ๐ฆ [npm](https://www.npmjs.com/package/@doderasoftware/restify-ai) | ๐ข [BinarCode](https://binarcode.com)**
|
|
10
12
|
|
|
11
13
|
## โจ Features
|
|
12
14
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
15
|
+
- ๐ **Real-time SSE Streaming** - Smooth character-by-character response streaming
|
|
16
|
+
- ๐ **File Attachments** - Upload and process documents, images, and more
|
|
17
|
+
- ๐ฅ **@Mentions System** - Reference entities from your application (users, documents, etc.)
|
|
18
|
+
- ๐ก **Context-Aware Suggestions** - Smart prompts based on current page/route
|
|
19
|
+
- ๐ฌ **Chat History** - Persistent conversation memory with configurable limits
|
|
20
|
+
- ๐ **Markdown Rendering** - Beautiful formatting with syntax highlighting
|
|
21
|
+
- ๐ **Quota Management** - Track and display API usage limits
|
|
22
|
+
- ๐จ **Fully Customizable** - Override any style with Tailwind CSS classes
|
|
23
|
+
- ๐ **Dark Mode Support** - Automatic dark/light theme detection
|
|
24
|
+
- ๐ฑ **Responsive Design** - Works on desktop, tablet, and mobile
|
|
25
|
+
- โจ๏ธ **Keyboard Shortcuts** - Quick access with configurable shortcuts (Cmd/Ctrl+G)
|
|
26
|
+
- ๐ณ **Fullscreen Mode** - Expandable chat interface
|
|
27
|
+
- ๐ฏ **TypeScript First** - Full type definitions included
|
|
28
|
+
- ๐๏ธ **Pinia Integration** - State management built-in
|
|
29
|
+
- ๐ง **Slot-Based Customization** - Override any component section
|
|
30
|
+
- ๐ **i18n Ready** - Full internationalization support
|
|
31
|
+
- ๐ **Support Mode** - Route conversations to human agents
|
|
32
|
+
- ๐ **Retry Logic** - Automatic retry with configurable backoff
|
|
33
|
+
- โ ๏ธ **Error Handling** - User-friendly error messages
|
|
29
34
|
|
|
30
35
|
## ๐ฆ Installation
|
|
31
36
|
|
|
32
|
-
|
|
37
|
+
```bash
|
|
33
38
|
npm install @doderasoftware/restify-ai
|
|
34
|
-
|
|
35
|
-
pnpm add @doderasoftware/restify-ai
|
|
36
|
-
\`\`\`
|
|
39
|
+
```
|
|
37
40
|
|
|
38
|
-
|
|
41
|
+
### Peer Dependencies
|
|
39
42
|
|
|
40
|
-
|
|
43
|
+
```bash
|
|
44
|
+
npm install vue@^3.3.0 pinia@^2.1.0 tailwindcss@^3.3.0
|
|
45
|
+
```
|
|
41
46
|
|
|
42
47
|
## ๐ Quick Start
|
|
43
48
|
|
|
44
|
-
|
|
49
|
+
### 1. Configure Tailwind CSS
|
|
50
|
+
|
|
51
|
+
```javascript
|
|
52
|
+
// tailwind.config.js
|
|
53
|
+
export default {
|
|
54
|
+
content: [
|
|
55
|
+
"./src/**/*.{vue,js,ts,jsx,tsx}",
|
|
56
|
+
"./node_modules/@doderasoftware/restify-ai/dist/**/*.{js,vue}",
|
|
57
|
+
],
|
|
58
|
+
presets: [
|
|
59
|
+
require("@doderasoftware/restify-ai/tailwind"),
|
|
60
|
+
],
|
|
61
|
+
}
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### 2. Import Styles
|
|
65
|
+
|
|
66
|
+
```typescript
|
|
67
|
+
// main.ts
|
|
68
|
+
import "@doderasoftware/restify-ai/styles"
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### 3. Register the Plugin
|
|
72
|
+
|
|
73
|
+
```typescript
|
|
74
|
+
// plugins/restifyAi.ts
|
|
75
|
+
import { RestifyAiPlugin } from "@doderasoftware/restify-ai"
|
|
76
|
+
import type { App } from "vue"
|
|
77
|
+
|
|
78
|
+
export function setupRestifyAi(app: App) {
|
|
79
|
+
app.use(RestifyAiPlugin, {
|
|
80
|
+
endpoints: {
|
|
81
|
+
ask: "/api/ai/ask",
|
|
82
|
+
uploadFile: "/api/ai/upload",
|
|
83
|
+
quota: "/api/ai/quota",
|
|
84
|
+
},
|
|
85
|
+
getAuthToken: () => localStorage.getItem("token"),
|
|
86
|
+
baseUrl: import.meta.env.VITE_API_URL,
|
|
87
|
+
})
|
|
88
|
+
}
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
```typescript
|
|
45
92
|
// main.ts
|
|
46
|
-
import { createApp } from
|
|
47
|
-
import { createPinia } from
|
|
48
|
-
import
|
|
49
|
-
import
|
|
93
|
+
import { createApp } from "vue"
|
|
94
|
+
import { createPinia } from "pinia"
|
|
95
|
+
import App from "./App.vue"
|
|
96
|
+
import { setupRestifyAi } from "./plugins/restifyAi"
|
|
97
|
+
import "@doderasoftware/restify-ai/styles"
|
|
50
98
|
|
|
51
99
|
const app = createApp(App)
|
|
52
100
|
app.use(createPinia())
|
|
101
|
+
setupRestifyAi(app)
|
|
102
|
+
app.mount("#app")
|
|
103
|
+
```
|
|
53
104
|
|
|
54
|
-
|
|
55
|
-
endpoints: {
|
|
56
|
-
ask: '/api/ai/ask', // Required - SSE streaming endpoint
|
|
57
|
-
quota: '/api/ai/quota', // Optional - quota endpoint
|
|
58
|
-
uploadFile: '/api/ai/upload', // Optional - file upload
|
|
59
|
-
},
|
|
60
|
-
getAuthToken: () => localStorage.getItem('token'),
|
|
61
|
-
})
|
|
105
|
+
### 4. Add the Component
|
|
62
106
|
|
|
63
|
-
|
|
64
|
-
\`\`\`
|
|
65
|
-
|
|
66
|
-
\`\`\`vue
|
|
67
|
-
<!-- App.vue -->
|
|
107
|
+
```vue
|
|
68
108
|
<template>
|
|
69
|
-
<
|
|
70
|
-
|
|
109
|
+
<div>
|
|
110
|
+
<button @click="showChat = true" class="fixed bottom-4 right-4 p-3 bg-blue-600 rounded-full">
|
|
111
|
+
<SparklesIcon class="w-6 h-6 text-white" />
|
|
112
|
+
</button>
|
|
113
|
+
<AiChatDrawer v-model="showChat" />
|
|
114
|
+
</div>
|
|
71
115
|
</template>
|
|
72
116
|
|
|
73
|
-
<script setup>
|
|
74
|
-
import { ref } from
|
|
75
|
-
import { AiChatDrawer } from
|
|
117
|
+
<script setup lang="ts">
|
|
118
|
+
import { ref } from "vue"
|
|
119
|
+
import { AiChatDrawer } from "@doderasoftware/restify-ai"
|
|
76
120
|
|
|
77
121
|
const showChat = ref(false)
|
|
78
122
|
</script>
|
|
79
|
-
|
|
123
|
+
```
|
|
80
124
|
|
|
81
|
-
|
|
125
|
+
### 5. Enable Keyboard Shortcut
|
|
82
126
|
|
|
83
|
-
|
|
127
|
+
```typescript
|
|
128
|
+
import { useAiDrawerShortcut } from "@doderasoftware/restify-ai"
|
|
84
129
|
|
|
85
|
-
|
|
130
|
+
// Enable Cmd/Ctrl+G to toggle drawer
|
|
131
|
+
useAiDrawerShortcut()
|
|
132
|
+
```
|
|
86
133
|
|
|
87
|
-
|
|
88
|
-
|--------|------|-------------|
|
|
89
|
-
| \`endpoints.ask\` | \`string\` | SSE streaming endpoint |
|
|
90
|
-
| \`getAuthToken\` | \`() => string \| Promise<string>\` | Auth token getter |
|
|
134
|
+
## โ๏ธ Configuration
|
|
91
135
|
|
|
92
|
-
###
|
|
136
|
+
### Full Configuration Options
|
|
93
137
|
|
|
94
|
-
|
|
95
|
-
{
|
|
96
|
-
|
|
138
|
+
```typescript
|
|
139
|
+
app.use(RestifyAiPlugin, {
|
|
140
|
+
// REQUIRED
|
|
97
141
|
endpoints: {
|
|
98
|
-
ask:
|
|
99
|
-
|
|
100
|
-
|
|
142
|
+
ask: "/api/ai/ask",
|
|
143
|
+
uploadFile: "/api/ai/upload",
|
|
144
|
+
quota: "/api/ai/quota",
|
|
101
145
|
},
|
|
102
|
-
getAuthToken: () =>
|
|
103
|
-
getCustomHeaders: () => ({
|
|
104
|
-
'X-CSRF-TOKEN': csrfToken,
|
|
105
|
-
'X-App-Version': '1.0.0',
|
|
106
|
-
}),
|
|
107
|
-
}
|
|
108
|
-
\`\`\`
|
|
109
|
-
|
|
110
|
-
### Request Customization
|
|
146
|
+
getAuthToken: () => localStorage.getItem("auth_token"),
|
|
111
147
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
buildRequest: (payload) => ({
|
|
116
|
-
|
|
117
|
-
context: { page: 'dashboard' },
|
|
118
|
-
}),
|
|
119
|
-
|
|
120
|
-
// Custom stream parser (default: OpenAI format)
|
|
121
|
-
parseStreamContent: (data) => JSON.parse(data).content,
|
|
122
|
-
|
|
123
|
-
// Request/response interceptors
|
|
124
|
-
requestInterceptor: (url, options) => options,
|
|
125
|
-
responseInterceptor: (response) => response,
|
|
126
|
-
}
|
|
127
|
-
\`\`\`
|
|
148
|
+
// API CONFIGURATION
|
|
149
|
+
baseUrl: "https://api.example.com",
|
|
150
|
+
getCustomHeaders: () => ({ "X-Tenant-ID": getTenantId() }),
|
|
151
|
+
buildRequest: (payload) => ({ ...payload, custom: "value" }),
|
|
152
|
+
parseStreamContent: (data) => JSON.parse(data).choices?.[0]?.delta?.content,
|
|
128
153
|
|
|
129
|
-
|
|
154
|
+
// RETRY
|
|
155
|
+
retry: { maxRetries: 3, retryDelay: 1000 },
|
|
130
156
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
157
|
+
// INTERNATIONALIZATION
|
|
158
|
+
translate: (key, params) => i18n.t("ai." + key, params),
|
|
159
|
+
labels: {
|
|
160
|
+
title: "AI Assistant",
|
|
161
|
+
placeholder: "Ask me anything...",
|
|
162
|
+
loadingText: "Thinking...",
|
|
137
163
|
},
|
|
138
|
-
}
|
|
139
|
-
\`\`\`
|
|
140
|
-
|
|
141
|
-
### Providers
|
|
142
|
-
|
|
143
|
-
\`\`\`typescript
|
|
144
|
-
{
|
|
145
|
-
// @Mention providers
|
|
146
|
-
mentionProviders: [
|
|
147
|
-
{
|
|
148
|
-
type: 'employee',
|
|
149
|
-
label: 'Employees',
|
|
150
|
-
priority: 10,
|
|
151
|
-
search: async (query) => api.searchEmployees(query),
|
|
152
|
-
},
|
|
153
|
-
],
|
|
154
|
-
|
|
155
|
-
// Route-aware suggestions
|
|
156
|
-
suggestionProviders: [
|
|
157
|
-
{
|
|
158
|
-
id: 'dashboard',
|
|
159
|
-
routes: ['/dashboard', '/analytics'],
|
|
160
|
-
getSuggestions: (context) => [...],
|
|
161
|
-
},
|
|
162
|
-
],
|
|
163
|
-
|
|
164
|
-
// Default suggestions for empty state
|
|
165
|
-
defaultSuggestions: [
|
|
166
|
-
{ id: '1', title: 'How can you help?', prompt: 'What can you do?' },
|
|
167
|
-
],
|
|
168
|
-
}
|
|
169
|
-
\`\`\`
|
|
170
164
|
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
### Custom Components
|
|
194
|
-
|
|
195
|
-
\`\`\`typescript
|
|
196
|
-
{
|
|
197
|
-
assistantAvatar: MyAvatarComponent,
|
|
198
|
-
userAvatar: UserAvatarComponent,
|
|
199
|
-
}
|
|
200
|
-
\`\`\`
|
|
165
|
+
// MENTION PROVIDERS
|
|
166
|
+
mentionProviders: [{
|
|
167
|
+
type: "user",
|
|
168
|
+
label: "Users",
|
|
169
|
+
search: async (query) => api.searchUsers(query),
|
|
170
|
+
}],
|
|
171
|
+
|
|
172
|
+
// SUGGESTION PROVIDERS
|
|
173
|
+
suggestionProviders: [{
|
|
174
|
+
id: "invoices",
|
|
175
|
+
routes: ["/invoices/*"],
|
|
176
|
+
getSuggestions: (ctx) => [
|
|
177
|
+
{ id: "1", title: "Create Invoice", prompt: "Help me create an invoice" }
|
|
178
|
+
],
|
|
179
|
+
}],
|
|
180
|
+
|
|
181
|
+
// THEMING
|
|
182
|
+
theme: {
|
|
183
|
+
primaryColor: "#3b82f6",
|
|
184
|
+
userBubbleColor: "#3b82f6",
|
|
185
|
+
drawerWidth: "500px",
|
|
186
|
+
},
|
|
201
187
|
|
|
202
|
-
|
|
188
|
+
// LIMITS
|
|
189
|
+
chatHistoryLimit: 50,
|
|
190
|
+
maxAttachments: 5,
|
|
191
|
+
maxFileSize: 10 * 1024 * 1024,
|
|
203
192
|
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
onQuotaFetched: (quota) => console.log(quota),
|
|
208
|
-
onMessageSent: (message) => analytics.track('message_sent'),
|
|
209
|
-
onResponseReceived: (message) => console.log(message),
|
|
210
|
-
onDrawerToggle: (isOpen) => console.log(isOpen),
|
|
211
|
-
onNewChat: () => console.log('New chat started'),
|
|
212
|
-
|
|
213
|
-
// Stream hooks
|
|
214
|
-
onStreamStart: () => console.log('Streaming...'),
|
|
215
|
-
onStreamEnd: (fullMessage) => console.log('Done'),
|
|
216
|
-
onStreamChunk: (chunk) => console.log(chunk.content),
|
|
217
|
-
beforeSend: (payload) => payload,
|
|
218
|
-
afterResponse: (message) => saveToHistory(message),
|
|
219
|
-
|
|
220
|
-
// File upload hooks
|
|
221
|
-
onFileUploadStart: (file) => console.log('Uploading', file.name),
|
|
222
|
-
onFileUploadProgress: (file, progress) => console.log(progress),
|
|
223
|
-
onFileUploadComplete: (file) => console.log('Done'),
|
|
224
|
-
onFileUploadError: (file, error) => console.error(error),
|
|
225
|
-
}
|
|
226
|
-
\`\`\`
|
|
193
|
+
// FEATURES
|
|
194
|
+
keyboardShortcut: "cmd+g",
|
|
195
|
+
enableSupportMode: true,
|
|
227
196
|
|
|
228
|
-
|
|
197
|
+
// CUSTOM AVATARS
|
|
198
|
+
assistantAvatar: CustomAvatarComponent,
|
|
199
|
+
userAvatar: () => currentUser.value?.avatarUrl,
|
|
229
200
|
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
},
|
|
238
|
-
translate: (key, params) => i18n.t(key, params),
|
|
239
|
-
can: (permission) => user.hasPermission(permission),
|
|
240
|
-
}
|
|
241
|
-
\`\`\`
|
|
242
|
-
|
|
243
|
-
---
|
|
244
|
-
|
|
245
|
-
## ๐งฉ Components
|
|
201
|
+
// CALLBACKS
|
|
202
|
+
onMessageSent: (msg) => analytics.track("ai_sent"),
|
|
203
|
+
onError: (err) => Sentry.captureException(err),
|
|
204
|
+
onStreamStart: () => console.log("Stream started"),
|
|
205
|
+
beforeSend: (payload) => ({ ...payload, timestamp: Date.now() }),
|
|
206
|
+
})
|
|
207
|
+
```
|
|
246
208
|
|
|
247
|
-
|
|
209
|
+
## ๐จ UI Customization
|
|
248
210
|
|
|
249
|
-
|
|
211
|
+
The `:ui` prop allows complete control over every element's styling:
|
|
250
212
|
|
|
251
|
-
|
|
213
|
+
```vue
|
|
214
|
+
<AiChatDrawer
|
|
215
|
+
v-model="isOpen"
|
|
216
|
+
:ui="{
|
|
217
|
+
backdrop: 'bg-black/50 backdrop-blur-sm',
|
|
218
|
+
drawer: 'shadow-2xl',
|
|
219
|
+
panel: 'bg-gray-50 dark:bg-gray-900',
|
|
220
|
+
header: 'border-b-2 border-blue-500',
|
|
221
|
+
newChatButton: 'bg-gradient-to-r from-blue-500 to-purple-500',
|
|
222
|
+
}"
|
|
223
|
+
/>
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
### Available UI Keys - AiChatDrawer
|
|
227
|
+
|
|
228
|
+
| Key | Description |
|
|
229
|
+
|-----|-------------|
|
|
230
|
+
| `backdrop` | Backdrop overlay |
|
|
231
|
+
| `drawer` | Drawer container |
|
|
232
|
+
| `panel` | Inner panel |
|
|
233
|
+
| `header` | Header container |
|
|
234
|
+
| `headerTitle` | Header title text |
|
|
235
|
+
| `body` | Chat body area |
|
|
236
|
+
| `footer` | Footer container |
|
|
237
|
+
| `newChatButton` | New chat button |
|
|
238
|
+
| `errorContainer` | Error message container |
|
|
239
|
+
| `retryButton` | Retry button |
|
|
240
|
+
|
|
241
|
+
### Available UI Keys - ChatInput
|
|
242
|
+
|
|
243
|
+
| Key | Description |
|
|
244
|
+
|-----|-------------|
|
|
245
|
+
| `root` | Input root container |
|
|
246
|
+
| `textarea` | Textarea element |
|
|
247
|
+
| `sendButton` | Send button |
|
|
248
|
+
| `attachButton` | Attachment button |
|
|
249
|
+
| `suggestionsDropdown` | Suggestions dropdown |
|
|
250
|
+
|
|
251
|
+
### Available UI Keys - ChatMessage
|
|
252
|
+
|
|
253
|
+
| Key | Description |
|
|
254
|
+
|-----|-------------|
|
|
255
|
+
| `root` | Message root container |
|
|
256
|
+
| `userBubble` | User message bubble |
|
|
257
|
+
| `assistantBubble` | Assistant message bubble |
|
|
258
|
+
| `content` | Message content |
|
|
259
|
+
| `loadingDots` | Loading animation dots |
|
|
260
|
+
|
|
261
|
+
## ๐ Props
|
|
252
262
|
|
|
253
263
|
| Prop | Type | Default | Description |
|
|
254
264
|
|------|------|---------|-------------|
|
|
255
|
-
|
|
|
256
|
-
|
|
|
257
|
-
|
|
|
258
|
-
|
|
|
259
|
-
|
|
|
260
|
-
|
|
|
261
|
-
|
|
|
262
|
-
|
|
|
263
|
-
|
|
|
264
|
-
|
|
|
265
|
-
|
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
| \`ui\` | \`AiChatDrawerUI\` | - | Custom CSS classes |
|
|
269
|
-
| \`texts\` | \`AiChatDrawerTexts\` | - | Custom text labels |
|
|
270
|
-
| \`historyLimit\` | \`HistoryLimitConfig\` | - | History limit config |
|
|
271
|
-
| \`loadingText\` | \`LoadingTextConfig\` | - | Dynamic loading text |
|
|
272
|
-
|
|
273
|
-
#### Events
|
|
265
|
+
| `modelValue` | `boolean` | required | Controls drawer visibility (v-model) |
|
|
266
|
+
| `width` | `string` | `"600px"` | Drawer width |
|
|
267
|
+
| `fullscreenWidth` | `string` | `"90vw"` | Width when fullscreen |
|
|
268
|
+
| `topOffset` | `string` | `"0"` | Top offset (for fixed headers) |
|
|
269
|
+
| `position` | `"left" \| "right"` | `"right"` | Drawer position |
|
|
270
|
+
| `showBackdrop` | `boolean` | `false` | Show backdrop overlay |
|
|
271
|
+
| `closeOnEscape` | `boolean` | `true` | Close on Escape key |
|
|
272
|
+
| `showQuota` | `boolean` | `true` | Show quota display |
|
|
273
|
+
| `confirmClose` | `boolean` | `true` | Confirm before clearing |
|
|
274
|
+
| `ui` | `AiChatDrawerUI` | `{}` | Custom CSS classes |
|
|
275
|
+
| `texts` | `AiChatDrawerTexts` | `{}` | Custom text labels |
|
|
276
|
+
|
|
277
|
+
## ๐ก Events
|
|
274
278
|
|
|
275
279
|
| Event | Payload | Description |
|
|
276
280
|
|-------|---------|-------------|
|
|
277
|
-
|
|
|
278
|
-
|
|
|
279
|
-
|
|
|
281
|
+
| `update:modelValue` | `boolean` | Drawer state changed |
|
|
282
|
+
| `close` | - | Drawer closed |
|
|
283
|
+
| `contact-support` | - | Support mode activated |
|
|
284
|
+
| `new-chat` | - | New chat started |
|
|
280
285
|
|
|
281
|
-
|
|
286
|
+
## ๐ฐ Slots
|
|
282
287
|
|
|
283
288
|
| Slot | Props | Description |
|
|
284
289
|
|------|-------|-------------|
|
|
285
|
-
|
|
|
286
|
-
|
|
|
287
|
-
|
|
|
288
|
-
|
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
290
|
+
| `header` | `{ quota, isFullscreen, onNewChat, onClose }` | Custom header |
|
|
291
|
+
| `empty-state` | `{ suggestions, onClick }` | Custom empty state |
|
|
292
|
+
| `message` | `{ message, isUser, isLoading }` | Custom message bubble |
|
|
293
|
+
| `input` | `{ modelValue, sending, onSubmit }` | Custom input area |
|
|
294
|
+
|
|
295
|
+
```vue
|
|
296
|
+
<AiChatDrawer v-model="isOpen">
|
|
297
|
+
<template #header="{ quota, isFullscreen, onNewChat, onClose }">
|
|
298
|
+
<MyCustomHeader />
|
|
299
|
+
</template>
|
|
300
|
+
<template #empty-state="{ suggestions, onClick }">
|
|
301
|
+
<MyCustomEmptyState />
|
|
302
|
+
</template>
|
|
303
|
+
<template #message="{ message, isUser, isLoading }">
|
|
304
|
+
<MyCustomMessage />
|
|
305
|
+
</template>
|
|
306
|
+
<template #input="{ modelValue, sending, onSubmit }">
|
|
307
|
+
<MyCustomInput />
|
|
308
|
+
</template>
|
|
309
|
+
</AiChatDrawer>
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
## ๐ช Store API
|
|
313
|
+
|
|
314
|
+
Access the Pinia store for advanced use cases:
|
|
315
|
+
|
|
316
|
+
```typescript
|
|
317
|
+
import { useRestifyAiStore } from "@doderasoftware/restify-ai"
|
|
306
318
|
|
|
307
|
-
---
|
|
308
|
-
|
|
309
|
-
## ๐จ UI Customization
|
|
310
|
-
|
|
311
|
-
Every component accepts a \`:ui\` prop for CSS class overrides:
|
|
312
|
-
|
|
313
|
-
\`\`\`vue
|
|
314
|
-
<AiChatDrawer
|
|
315
|
-
:ui="{
|
|
316
|
-
drawer: 'my-drawer-class',
|
|
317
|
-
header: 'my-header-class',
|
|
318
|
-
body: 'my-body-class',
|
|
319
|
-
}"
|
|
320
|
-
:texts="{
|
|
321
|
-
title: 'My AI',
|
|
322
|
-
placeholder: 'Ask anything...',
|
|
323
|
-
}"
|
|
324
|
-
/>
|
|
325
|
-
\`\`\`
|
|
326
|
-
|
|
327
|
-
### Available UI Props
|
|
328
|
-
|
|
329
|
-
- \`AiChatDrawerUI\` - backdrop, drawer, panel, header, body, etc.
|
|
330
|
-
- \`ChatInputUI\` - root, textarea, sendButton, attachButton, etc.
|
|
331
|
-
- \`ChatMessageUI\` - userBubble, assistantBubble, loadingDots, etc.
|
|
332
|
-
- \`AiEmptyStateUI\` - grid, suggestionCard, title, etc.
|
|
333
|
-
- \`MentionListUI\` - item, itemSelected, groupHeader, etc.
|
|
334
|
-
|
|
335
|
-
---
|
|
336
|
-
|
|
337
|
-
## ๐ Composables
|
|
338
|
-
|
|
339
|
-
\`\`\`typescript
|
|
340
|
-
import {
|
|
341
|
-
useRestifyAiStore, // Pinia store
|
|
342
|
-
useAiSuggestions, // Suggestions management
|
|
343
|
-
useAiContext, // Page context
|
|
344
|
-
usePageAiContext, // Route-based context
|
|
345
|
-
useKeyboardShortcut, // Custom shortcuts
|
|
346
|
-
useAiDrawerShortcut, // Drawer toggle shortcut
|
|
347
|
-
useMentionParsing, // Parse @mentions
|
|
348
|
-
useChatMarkdown, // Markdown rendering
|
|
349
|
-
useChatScroll, // Auto-scroll
|
|
350
|
-
useChatErrorHandling, // Error management
|
|
351
|
-
useLoadingText, // Dynamic loading messages
|
|
352
|
-
useHistoryLimit, // History limit dialogs
|
|
353
|
-
} from '@doderasoftware/restify-ai'
|
|
354
|
-
\`\`\`
|
|
355
|
-
|
|
356
|
-
### Store Actions
|
|
357
|
-
|
|
358
|
-
\`\`\`typescript
|
|
359
319
|
const store = useRestifyAiStore()
|
|
360
320
|
|
|
361
|
-
|
|
321
|
+
// State
|
|
322
|
+
store.chatHistory // ChatMessage[]
|
|
323
|
+
store.loading // boolean
|
|
324
|
+
store.sending // boolean
|
|
325
|
+
store.quota // { limit, used, remaining }
|
|
326
|
+
store.error // { message, failedQuestion, timestamp }
|
|
327
|
+
|
|
328
|
+
// Actions
|
|
329
|
+
store.toggleChat()
|
|
330
|
+
store.sendMessage(payload)
|
|
362
331
|
store.cancelRequest()
|
|
363
|
-
store.
|
|
332
|
+
store.retryLastMessage()
|
|
364
333
|
store.clearChatHistory()
|
|
365
|
-
store.clearError()
|
|
366
334
|
store.fetchQuota()
|
|
367
|
-
store.
|
|
368
|
-
|
|
335
|
+
store.uploadFile(file)
|
|
336
|
+
```
|
|
369
337
|
|
|
370
|
-
|
|
338
|
+
## ๐ช Composables
|
|
371
339
|
|
|
372
|
-
|
|
373
|
-
store.chatHistory // ChatMessage[]
|
|
374
|
-
store.sending // boolean
|
|
375
|
-
store.error // ChatError
|
|
376
|
-
store.quota // ChatQuota
|
|
377
|
-
store.showChat // boolean
|
|
378
|
-
store.isFullscreen // boolean
|
|
379
|
-
store.supportRequestMode // boolean
|
|
380
|
-
\`\`\`
|
|
340
|
+
### useAiDrawerShortcut
|
|
381
341
|
|
|
382
|
-
|
|
342
|
+
Enable keyboard shortcuts to toggle the chat drawer:
|
|
383
343
|
|
|
384
|
-
|
|
344
|
+
```typescript
|
|
345
|
+
import { useAiDrawerShortcut } from "@doderasoftware/restify-ai"
|
|
385
346
|
|
|
386
|
-
|
|
347
|
+
// Uses store directly (recommended)
|
|
348
|
+
useAiDrawerShortcut()
|
|
387
349
|
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
showWarningAt: 3,
|
|
393
|
-
warningMessage: 'Almost at limit!',
|
|
394
|
-
limitMessage: 'Start a new chat to continue.',
|
|
395
|
-
}"
|
|
396
|
-
/>
|
|
397
|
-
\`\`\`
|
|
350
|
+
// Or pass a ref
|
|
351
|
+
const drawerRef = ref(false)
|
|
352
|
+
useAiDrawerShortcut(drawerRef)
|
|
353
|
+
```
|
|
398
354
|
|
|
399
|
-
###
|
|
355
|
+
### usePageAiContext
|
|
400
356
|
|
|
401
|
-
|
|
402
|
-
<AiChatDrawer
|
|
403
|
-
:loading-text="{
|
|
404
|
-
messages: ['Thinking...', 'Analyzing...', 'Almost done...'],
|
|
405
|
-
intervals: [0, 2000, 4000],
|
|
406
|
-
}"
|
|
407
|
-
/>
|
|
408
|
-
\`\`\`
|
|
357
|
+
Provide page context to the AI for smarter responses:
|
|
409
358
|
|
|
410
|
-
|
|
359
|
+
```typescript
|
|
360
|
+
import { usePageAiContext } from "@doderasoftware/restify-ai"
|
|
411
361
|
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
362
|
+
usePageAiContext({
|
|
363
|
+
pageType: "invoice-detail",
|
|
364
|
+
entityId: route.params.id,
|
|
365
|
+
entityType: "invoice",
|
|
366
|
+
metadata: { customerName: invoice.value?.customer?.name },
|
|
367
|
+
})
|
|
368
|
+
```
|
|
415
369
|
|
|
416
|
-
|
|
417
|
-
const { toggle } = useAiDrawerShortcut()
|
|
418
|
-
\`\`\`
|
|
370
|
+
## ๐ Backend Integration
|
|
419
371
|
|
|
420
|
-
|
|
372
|
+
This package is designed to work with [Laravel Restify](https://laravel-restify.com):
|
|
421
373
|
|
|
422
|
-
|
|
374
|
+
```php
|
|
375
|
+
// routes/api.php
|
|
376
|
+
Route::middleware("auth:sanctum")->group(function () {
|
|
377
|
+
Route::post("/ai/ask", [AiController::class, "ask"]);
|
|
378
|
+
Route::post("/ai/upload", [AiController::class, "upload"]);
|
|
379
|
+
Route::get("/ai/quota", [AiController::class, "quota"]);
|
|
380
|
+
});
|
|
381
|
+
```
|
|
423
382
|
|
|
424
|
-
Expected
|
|
383
|
+
### Expected Request/Response Formats
|
|
425
384
|
|
|
426
|
-
|
|
427
|
-
// routes/api.php
|
|
428
|
-
Route::post('/ai/ask', [AiController::class, 'ask']);
|
|
429
|
-
Route::get('/ai/quota', [AiController::class, 'quota']);
|
|
430
|
-
\`\`\`
|
|
385
|
+
**Ask Endpoint (SSE Stream):**
|
|
431
386
|
|
|
432
|
-
|
|
433
|
-
//
|
|
434
|
-
public function ask(Request $request)
|
|
387
|
+
```typescript
|
|
388
|
+
// Request
|
|
435
389
|
{
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
ob_flush();
|
|
442
|
-
flush();
|
|
443
|
-
|
|
444
|
-
echo "data: [DONE]\n\n";
|
|
445
|
-
}, 200, [
|
|
446
|
-
'Content-Type' => 'text/event-stream',
|
|
447
|
-
'Cache-Control' => 'no-cache',
|
|
448
|
-
'X-Accel-Buffering' => 'no',
|
|
449
|
-
]);
|
|
390
|
+
question: string
|
|
391
|
+
history: Array<{ role: string, message: string }>
|
|
392
|
+
stream: true
|
|
393
|
+
files?: Array<{ id: string, name: string }>
|
|
394
|
+
mentions?: Array<{ id: string, type: string, name: string }>
|
|
450
395
|
}
|
|
451
|
-
\`\`\`
|
|
452
396
|
|
|
453
|
-
|
|
397
|
+
// Response: Server-Sent Events (OpenAI format)
|
|
398
|
+
data: {"choices":[{"delta":{"content":"Hello"}}]}
|
|
399
|
+
data: {"choices":[{"delta":{"content":" world"}}]}
|
|
400
|
+
data: [DONE]
|
|
401
|
+
```
|
|
402
|
+
|
|
403
|
+
**Upload Endpoint:**
|
|
454
404
|
|
|
455
|
-
|
|
405
|
+
```typescript
|
|
406
|
+
// Response
|
|
407
|
+
{ id: string, name: string, url: string, type: string, size: number }
|
|
408
|
+
```
|
|
456
409
|
|
|
457
|
-
|
|
410
|
+
**Quota Endpoint:**
|
|
458
411
|
|
|
459
|
-
|
|
412
|
+
```typescript
|
|
413
|
+
// Response
|
|
414
|
+
{ limit: number, used: number, remaining: number }
|
|
415
|
+
```
|
|
416
|
+
|
|
417
|
+
## ๐ TypeScript
|
|
418
|
+
|
|
419
|
+
Full TypeScript support with exported types:
|
|
420
|
+
|
|
421
|
+
```typescript
|
|
460
422
|
import type {
|
|
461
423
|
ChatMessage,
|
|
462
424
|
ChatAttachment,
|
|
463
425
|
Mention,
|
|
426
|
+
ChatQuota,
|
|
427
|
+
RestifyAiConfig,
|
|
464
428
|
MentionProvider,
|
|
465
429
|
SuggestionProvider,
|
|
466
430
|
AISuggestion,
|
|
467
|
-
RestifyAiConfig,
|
|
468
|
-
ChatQuota,
|
|
469
|
-
ChatError,
|
|
470
|
-
// UI types
|
|
471
431
|
AiChatDrawerUI,
|
|
472
|
-
ChatInputUI,
|
|
473
|
-
ChatMessageUI,
|
|
474
|
-
// Text types
|
|
475
432
|
AiChatDrawerTexts,
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
HistoryLimitConfig,
|
|
479
|
-
LoadingTextConfig,
|
|
480
|
-
RetryConfig,
|
|
481
|
-
// Hook types
|
|
482
|
-
BeforeSendHook,
|
|
483
|
-
AfterResponseHook,
|
|
484
|
-
StreamParserFunction,
|
|
485
|
-
} from '@doderasoftware/restify-ai'
|
|
486
|
-
\`\`\`
|
|
433
|
+
} from "@doderasoftware/restify-ai"
|
|
434
|
+
```
|
|
487
435
|
|
|
488
|
-
|
|
436
|
+
## ๐ Browser Support
|
|
489
437
|
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
| SSE Streaming | Built-in, automatic |
|
|
495
|
-
| Keyboard Shortcut | \`keyboardShortcut: 'cmd+g'\` |
|
|
496
|
-
| @Mentions | \`mentionProviders: [...]\` |
|
|
497
|
-
| Route Suggestions | \`suggestionProviders: [...]\` |
|
|
498
|
-
| File Attachments | \`endpoints.uploadFile\` + \`maxAttachments\` |
|
|
499
|
-
| Support Mode | \`enableSupportMode: true\` |
|
|
500
|
-
| Custom Headers | \`getCustomHeaders: () => ({...})\` |
|
|
501
|
-
| Auth Token | \`getAuthToken: () => token\` |
|
|
502
|
-
| Base URL | \`baseUrl: 'https://api.example.com'\` |
|
|
503
|
-
| Error Handling | \`onError: (err) => {...}\` |
|
|
504
|
-
| Retry Logic | \`retry: { maxRetries: 3 }\` |
|
|
505
|
-
| UI Customization | \`:ui\` prop on all components |
|
|
506
|
-
| Text/i18n | \`:texts\` prop + \`labels\` config |
|
|
507
|
-
| History Limit | \`:history-limit\` prop |
|
|
508
|
-
| Loading Messages | \`:loading-text\` prop |
|
|
438
|
+
- Chrome 80+
|
|
439
|
+
- Firefox 75+
|
|
440
|
+
- Safari 13+
|
|
441
|
+
- Edge 80+
|
|
509
442
|
|
|
510
|
-
|
|
443
|
+
## ๐ Links
|
|
444
|
+
|
|
445
|
+
- [๐ Laravel Restify Documentation](https://laravel-restify.com)
|
|
446
|
+
- [๐ฆ npm Package](https://www.npmjs.com/package/@doderasoftware/restify-ai)
|
|
447
|
+
- [๐ข BinarCode](https://binarcode.com)
|
|
511
448
|
|
|
512
449
|
## ๐ License
|
|
513
450
|
|
|
514
|
-
MIT ยฉ [
|
|
451
|
+
MIT ยฉ [BinarCode](https://binarcode.com)
|
|
452
|
+
|
|
453
|
+
---
|
|
454
|
+
|
|
455
|
+
Built with โค๏ธ by [BinarCode](https://binarcode.com) ยท Published by [Dodera Software](https://doderasoft.com)
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@doderasoftware/restify-ai",
|
|
3
|
-
"version": "0.1.0-beta.
|
|
4
|
-
"description": "Professional AI Chatbot Vue 3 component for Laravel Restify backends.
|
|
3
|
+
"version": "0.1.0-beta.6",
|
|
4
|
+
"description": "Professional AI Chatbot Vue 3 component for Laravel Restify backends. SSE streaming, file uploads, @mentions, context-aware suggestions, and full TypeScript support.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/restify-ai.umd.cjs",
|
|
7
7
|
"module": "./dist/restify-ai.js",
|
|
@@ -83,37 +83,49 @@
|
|
|
83
83
|
"chatbot",
|
|
84
84
|
"chat",
|
|
85
85
|
"assistant",
|
|
86
|
+
"ai-assistant",
|
|
86
87
|
"laravel",
|
|
87
88
|
"restify",
|
|
88
89
|
"laravel-restify",
|
|
90
|
+
"binarcode",
|
|
89
91
|
"sse",
|
|
90
92
|
"streaming",
|
|
91
93
|
"component",
|
|
92
94
|
"openai",
|
|
93
95
|
"anthropic",
|
|
94
96
|
"claude",
|
|
97
|
+
"gpt",
|
|
98
|
+
"llm",
|
|
95
99
|
"tailwindcss",
|
|
96
|
-
"typescript"
|
|
100
|
+
"typescript",
|
|
101
|
+
"pinia",
|
|
102
|
+
"vue-component",
|
|
103
|
+
"chatgpt",
|
|
104
|
+
"ai-chat"
|
|
97
105
|
],
|
|
98
106
|
"author": {
|
|
99
|
-
"name": "
|
|
100
|
-
"email": "
|
|
101
|
-
"url": "https://
|
|
107
|
+
"name": "BinarCode",
|
|
108
|
+
"email": "hello@binarcode.com",
|
|
109
|
+
"url": "https://binarcode.com"
|
|
102
110
|
},
|
|
103
111
|
"license": "MIT",
|
|
104
112
|
"repository": {
|
|
105
113
|
"type": "git",
|
|
106
|
-
"url": "git+https://github.com/
|
|
114
|
+
"url": "git+https://github.com/BinarCode/restify-ai.git"
|
|
107
115
|
},
|
|
108
116
|
"bugs": {
|
|
109
|
-
"url": "https://github.com/
|
|
117
|
+
"url": "https://github.com/BinarCode/restify-ai/issues"
|
|
110
118
|
},
|
|
111
|
-
"homepage": "https://
|
|
119
|
+
"homepage": "https://laravel-restify.com",
|
|
112
120
|
"publishConfig": {
|
|
113
121
|
"access": "public",
|
|
114
122
|
"registry": "https://registry.npmjs.org/"
|
|
115
123
|
},
|
|
116
124
|
"engines": {
|
|
117
125
|
"node": ">=18.0.0"
|
|
126
|
+
},
|
|
127
|
+
"funding": {
|
|
128
|
+
"type": "github",
|
|
129
|
+
"url": "https://github.com/sponsors/binarcode"
|
|
118
130
|
}
|
|
119
131
|
}
|