research-agent-ui 0.1.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.
Files changed (125) hide show
  1. package/README.md +177 -0
  2. package/dist/api/handlers/agents.d.ts +4 -0
  3. package/dist/api/handlers/article-followups.d.ts +2 -0
  4. package/dist/api/handlers/article-qa.d.ts +2 -0
  5. package/dist/api/handlers/autocomplete.d.ts +8 -0
  6. package/dist/api/handlers/chats.d.ts +23 -0
  7. package/dist/api/handlers/mcpservers.d.ts +24 -0
  8. package/dist/api/handlers/messages.d.ts +4 -0
  9. package/dist/api/handlers/providers.d.ts +29 -0
  10. package/dist/api/handlers/rewrite.d.ts +4 -0
  11. package/dist/api/handlers/search.d.ts +7 -0
  12. package/dist/api/handlers/suggestions.d.ts +3 -0
  13. package/dist/api/handlers/test-models.d.ts +3 -0
  14. package/dist/api/handlers/transcript.d.ts +4 -0
  15. package/dist/api/handlers/validate-openrouter.d.ts +5 -0
  16. package/dist/api/handlers/voice.d.ts +4 -0
  17. package/dist/api/index.d.ts +16 -0
  18. package/dist/api/types.d.ts +86 -0
  19. package/dist/api.cjs +22 -0
  20. package/dist/api.mjs +768 -0
  21. package/dist/components/ArticleReader/ArticleAIResponse.d.ts +11 -0
  22. package/dist/components/ArticleReader/ArticleActionButtons.d.ts +18 -0
  23. package/dist/components/ArticleReader/ArticleContent.d.ts +8 -0
  24. package/dist/components/ArticleReader/ArticleExtractPanel.d.ts +4 -0
  25. package/dist/components/ArticleReader/ArticleFollowupQuestions.d.ts +12 -0
  26. package/dist/components/ArticleReader/ArticlePanelHeader.d.ts +9 -0
  27. package/dist/components/ArticleReader/ArticlePromptInput.d.ts +11 -0
  28. package/dist/components/ArticleReader/ExtractPanelContext.d.ts +18 -0
  29. package/dist/components/ArticleReader/LexicalArticleViewer.d.ts +15 -0
  30. package/dist/components/ArticleReader/index.d.ts +12 -0
  31. package/dist/components/ArticleReader/unified-markdown.d.ts +19 -0
  32. package/dist/components/ChatConversation/ChatConversationThread.d.ts +10 -0
  33. package/dist/components/ChatConversation/ChatHeaderNavigationBar.d.ts +2 -0
  34. package/dist/components/ChatConversation/ChatHomepage.d.ts +6 -0
  35. package/dist/components/ChatConversation/ChatMessageBubble.d.ts +19 -0
  36. package/dist/components/ChatConversation/ChatMessageInputBar.d.ts +10 -0
  37. package/dist/components/ChatConversation/ChatWindow.d.ts +75 -0
  38. package/dist/components/ChatConversation/EmptyStateChatMessageInput.d.ts +9 -0
  39. package/dist/components/ChatConversation/MessageBubble/AssistantMessageActions.d.ts +15 -0
  40. package/dist/components/ChatConversation/MessageBubble/FollowUpSuggestions.d.ts +15 -0
  41. package/dist/components/ChatConversation/MessageBubble/ThinkTagProcessor.d.ts +13 -0
  42. package/dist/components/ChatConversation/MessageBubble/UserMessageHeader.d.ts +20 -0
  43. package/dist/components/ChatConversation/MessageReasoningPanel.d.ts +14 -0
  44. package/dist/components/ChatConversation/RecentHistoryChips.d.ts +1 -0
  45. package/dist/components/ChatConversation/SearchModeSpotlightPanel.d.ts +10 -0
  46. package/dist/components/ChatConversation/background-art.d.ts +11 -0
  47. package/dist/components/ChatHistoryDropdown/HistoryChatItem.d.ts +9 -0
  48. package/dist/components/ChatHistoryDropdown/HistoryDialogs.d.ts +14 -0
  49. package/dist/components/ChatHistoryDropdown/HistoryDropdown.d.ts +6 -0
  50. package/dist/components/ChatHistoryDropdown/HistoryHeader.d.ts +10 -0
  51. package/dist/components/ChatHistoryDropdown/index.d.ts +4 -0
  52. package/dist/components/ChatHistoryDropdown/useHistoryState.d.ts +27 -0
  53. package/dist/components/ConfigError.d.ts +1 -0
  54. package/dist/components/FileUpload/CompactFileAttachmentButton.d.ts +2 -0
  55. package/dist/components/FileUpload/FileAttachmentButton.d.ts +2 -0
  56. package/dist/components/FileUpload/FilePreviewCard.d.ts +13 -0
  57. package/dist/components/FileUpload/FileUploadDropdown.d.ts +7 -0
  58. package/dist/components/FileUpload/GoogleDrivePicker.d.ts +4 -0
  59. package/dist/components/FileUpload/useFileHandling.d.ts +26 -0
  60. package/dist/components/Footer.d.ts +19 -0
  61. package/dist/components/MessageActions/CopyMessageButton.d.ts +6 -0
  62. package/dist/components/MessageActions/DeleteChatSessionButton.d.ts +8 -0
  63. package/dist/components/MessageActions/RewriteMessageButton.d.ts +5 -0
  64. package/dist/components/MessageComposer/ActionBar.d.ts +17 -0
  65. package/dist/components/MessageComposer/ChatInputBox.d.ts +6 -0
  66. package/dist/components/MessageComposer/MessageInputIconSet.d.ts +24 -0
  67. package/dist/components/MessageComposer/PastedContentCard.d.ts +12 -0
  68. package/dist/components/SearchConfig/CategoriesMenu.d.ts +2 -0
  69. package/dist/components/SearchConfig/ChatModelSelector.d.ts +2 -0
  70. package/dist/components/SearchConfig/ModelSelector.d.ts +8 -0
  71. package/dist/components/SearchConfig/ModelSelectorSubmenu.d.ts +2 -0
  72. package/dist/components/SearchConfig/ResearchFocusToggleButton.d.ts +2 -0
  73. package/dist/components/SearchConfig/SearchOptimizationSelector.d.ts +2 -0
  74. package/dist/components/SearchConfig/categories.d.ts +10 -0
  75. package/dist/components/SearchResults/MessageSources.d.ts +11 -0
  76. package/dist/components/SearchResults/NewsArticleWidget.d.ts +2 -0
  77. package/dist/components/SearchResults/SearchProgressIndicator.d.ts +8 -0
  78. package/dist/components/SearchResults/WebCitationBadge.d.ts +7 -0
  79. package/dist/config.cjs +1 -0
  80. package/dist/config.d.ts +54 -0
  81. package/dist/config.mjs +16 -0
  82. package/dist/hooks/useChat/ChatContext.d.ts +147 -0
  83. package/dist/hooks/useChat/ChatProvider.d.ts +35 -0
  84. package/dist/hooks/useChat/buildSections.d.ts +39 -0
  85. package/dist/hooks/useChat/chatConfig.d.ts +31 -0
  86. package/dist/hooks/useChat/chatMessages.d.ts +48 -0
  87. package/dist/hooks/useChat/index.d.ts +58 -0
  88. package/dist/hooks/useChat/sendMessage.d.ts +91 -0
  89. package/dist/hooks/useChat/useChat.d.ts +3 -0
  90. package/dist/hooks/useChat/useChatState.d.ts +127 -0
  91. package/dist/hooks/useSession.d.ts +21 -0
  92. package/dist/hooks/voice/useInteruptionDetection.d.ts +16 -0
  93. package/dist/hooks/voice/useSpeechToTranscript.d.ts +5 -0
  94. package/dist/hooks/voice/useTextToVoice.d.ts +13 -0
  95. package/dist/icons/IconSearchAcademic.d.ts +1 -0
  96. package/dist/icons/IconSearchFiles.d.ts +1 -0
  97. package/dist/icons/IconSearchImages.d.ts +1 -0
  98. package/dist/icons/IconSearchNews.d.ts +1 -0
  99. package/dist/icons/IconSearchTech.d.ts +1 -0
  100. package/dist/icons/IconSearchVideos.d.ts +1 -0
  101. package/dist/icons/IconSearchWeb.d.ts +1 -0
  102. package/dist/index.cjs +52 -0
  103. package/dist/index.d.ts +14 -0
  104. package/dist/index.mjs +5847 -0
  105. package/dist/lib/cdn-loader.d.ts +2 -0
  106. package/dist/lib/export.d.ts +17 -0
  107. package/dist/lib/guest.d.ts +19 -0
  108. package/dist/lib/suggestions.d.ts +2 -0
  109. package/dist/lib/url-autolink.d.ts +21 -0
  110. package/dist/lib/utils.d.ts +3 -0
  111. package/dist/rolldown-runtime-CMqjfN_6.cjs +1 -0
  112. package/dist/types/chat.d.ts +132 -0
  113. package/dist/types/research.d.ts +81 -0
  114. package/dist/ui/Loader.d.ts +2 -0
  115. package/dist/ui/badge.d.ts +9 -0
  116. package/dist/ui/button.d.ts +11 -0
  117. package/dist/ui/dialog.d.ts +15 -0
  118. package/dist/ui/dropdown-menu.d.ts +25 -0
  119. package/dist/ui/glowing-effect.d.ts +14 -0
  120. package/dist/ui/gradient-blur.d.ts +9 -0
  121. package/dist/ui/live-waveform.d.ts +23 -0
  122. package/dist/ui/popover.d.ts +9 -0
  123. package/dist/ui/tooltip.d.ts +7 -0
  124. package/dist/ui/visually-hidden.d.ts +2 -0
  125. package/package.json +96 -0
package/README.md ADDED
@@ -0,0 +1,177 @@
1
+ # research-agent-ui
2
+
3
+ Chat research agent UI: conversation window, article reader, search config,
4
+ file uploads, and chat history for QwkSearch-style apps. Includes the shadcn
5
+ primitives and icons the components depend on, so it can be dropped into a
6
+ Next.js app with a single dependency.
7
+
8
+ ![img1](https://i.imgur.com/UxNJOKy.png)
9
+
10
+ ## Usage
11
+
12
+ ```tsx
13
+ import {
14
+ ChatProvider,
15
+ SessionProvider,
16
+ ExtractPanelProvider,
17
+ ChatWindow,
18
+ configureResearchAgentUI,
19
+ } from 'research-agent-ui';
20
+
21
+ configureResearchAgentUI({
22
+ appName: 'MyApp',
23
+ getAutoMediaSearch: () => true,
24
+ // ...see ResearchAgentUIConfig for the full list of overridable values
25
+ });
26
+
27
+ function App() {
28
+ return (
29
+ <SessionProvider authClient={myAuthClient}>
30
+ <ExtractPanelProvider>
31
+ <ChatProvider>
32
+ <ChatWindow />
33
+ </ChatProvider>
34
+ </ExtractPanelProvider>
35
+ </SessionProvider>
36
+ );
37
+ }
38
+ ```
39
+
40
+ ## API Routes (`research-agent-ui/api`)
41
+
42
+ All 25 Next.js route handlers are exported from the `research-agent-ui/api`
43
+ subpath as factory functions. Each factory accepts a **deps** object that
44
+ injects your app's database, auth helpers, and other services, so the same
45
+ handler logic works in any Next.js project without hard-coding any imports.
46
+
47
+ ### How it works
48
+
49
+ The route logic lives in `packages/research-agent-ui/src/api/handlers/`.
50
+ Your app's `app/api/agent/*/route.ts` files become thin wrappers that call
51
+ the factory and re-export the HTTP method handlers.
52
+
53
+ ### Step 1 — install / workspace link
54
+
55
+ If you are in this monorepo, `research-agent-ui` is already linked via the
56
+ `workspace:*` protocol. For an external project, install the published
57
+ package:
58
+
59
+ ```bash
60
+ npm install research-agent-ui
61
+ # or
62
+ bun add research-agent-ui
63
+ ```
64
+
65
+ ### Step 2 — create your route files
66
+
67
+ For each API path, create a `route.ts` that calls the matching factory and
68
+ passes in your app's dependencies. Every factory is named
69
+ `create<RouteName>Handler` and is exported from `research-agent-ui/api`.
70
+
71
+ #### Example: `app/api/agent/chats/route.ts`
72
+
73
+ ```ts
74
+ import { createChatsHandler } from "research-agent-ui/api";
75
+ import { getDB } from "@/lib/database";
76
+ import { chats, messages } from "@/lib/database/schema";
77
+ import { requireUserId } from "@/lib/auth/session";
78
+
79
+ const handler = createChatsHandler({
80
+ getDB,
81
+ requireUserId,
82
+ schema: { chats, messages },
83
+ });
84
+ export const { GET, DELETE } = handler;
85
+ ```
86
+
87
+ #### Example: `app/api/agent/article-followups/route.ts`
88
+
89
+ ```ts
90
+ import { createArticleFollowupsHandler } from "research-agent-ui/api";
91
+ import { getUserId } from "@/lib/auth/session";
92
+ import { getDB } from "@/lib/database";
93
+ import { user as userSchema } from "@/lib/database/schema";
94
+ import { getEnv } from "@/lib/env";
95
+
96
+ export const POST = createArticleFollowupsHandler({
97
+ getUserId,
98
+ requireUserId: async () => {
99
+ const id = await getUserId();
100
+ if (!id) throw new Error("Unauthorized");
101
+ return id;
102
+ },
103
+ getDB,
104
+ userSchema,
105
+ getEnv,
106
+ });
107
+ ```
108
+
109
+ ### All available factories and their dep shapes
110
+
111
+ | Factory | File | Required deps |
112
+ |---|---|---|
113
+ | `createArticleFollowupsHandler` | `article-followups` | `getUserId`, `requireUserId`, `getDB`, `userSchema`, `getEnv` |
114
+ | `createArticleQAHandler` | `article-qa` | `getUserId`, `requireUserId`, `getDB`, `userSchema`, `getEnv` |
115
+ | `createChatsHandler` | `chats` | `getDB`, `requireUserId`, `schema.chats`, `schema.messages` |
116
+ | `createChatByIdHandler` | `chats/[id]` | `getDB`, `requireUserId`, `schema.chats`, `schema.messages` |
117
+ | `createChatsSearchHandler` | `chats/search` | `getDB`, `requireUserId`, `schema.chats`, `schema.messages` |
118
+ | `createChatsShareHandler` | `chats/share` | `getDB`, `requireUserId`, `schema.chats`, `schema.messages` |
119
+ | `createMessagesHandler` | `messages` | `getDB`, `requireUserId`, `messagesSchema` |
120
+ | `createProvidersHandler` | `providers` | `getSession` |
121
+ | `createProviderByIdHandler` | `providers/[id]` | _(none)_ |
122
+ | `createProviderModelsHandler` | `providers/[id]/models` | _(none)_ |
123
+ | `createMCPServersHandler` | `mcpservers` | `configManager`, `getConfiguredMCPServers` |
124
+ | `createMCPServerByIdHandler` | `mcpservers/[id]` | `configManager`, `getConfiguredMCPServers` |
125
+ | `createMCPServerToggleHandler` | `mcpservers/[id]/toggle` | `configManager`, `getConfiguredMCPServers` |
126
+ | `createSearchHandler` | `search` | `searxngDomain?` (default: `https://search.qwksearch.com`) |
127
+ | `createDiscoverHandler` | `discover` | _(none)_ |
128
+ | `createAutocompleteHandler` | `autocomplete` | _(none)_ |
129
+ | `createSuggestionsHandler` | `suggestions` | _(none)_ |
130
+ | `createAgentsHandler` | `agents` | `getUserId`, `requireUserId`, `getDB`, `userSchema`, `getEnv` |
131
+ | `createRewriteHandler` | `rewrite` | `getEnv`, `generateText`, `createGroq` |
132
+ | `createVoiceHandler` | `voice` | `getUserId`, `checkTTSRateLimit`, `generateSpeech` |
133
+ | `createTranscriptHandler` | `transcript` | `getCloudflareContext` |
134
+ | `createTestModelsHandler` | `test-models` | _(none)_ |
135
+ | `createValidateOpenRouterHandler` | `validate-openrouter` | `validateOpenRouterModels` |
136
+
137
+ ### Dep type definitions
138
+
139
+ All dep interfaces are exported from `research-agent-ui/api`:
140
+
141
+ ```ts
142
+ import type {
143
+ ArticleDeps,
144
+ ChatsDeps,
145
+ MessagesDeps,
146
+ ProvidersDeps,
147
+ MCPServersDeps,
148
+ SearchDeps,
149
+ VoiceDeps,
150
+ TranscriptDeps,
151
+ RewriteDeps,
152
+ ValidateOpenRouterDeps,
153
+ AgentsDeps,
154
+ } from "research-agent-ui/api";
155
+ ```
156
+
157
+ ### The chat route
158
+
159
+ `POST /api/agent/chat` is not migrated into this package because it delegates
160
+ to a full `handleChatRequest` orchestrator that is app-specific (streaming,
161
+ search integration, database writes). Keep it directly in your app:
162
+
163
+ ```ts
164
+ // app/api/agent/chat/route.ts
165
+ import { handleChatRequest } from "@/lib/chat";
166
+
167
+ export const runtime = "nodejs";
168
+ export const dynamic = "force-dynamic";
169
+ export const POST = handleChatRequest;
170
+ ```
171
+
172
+ ## Configuration
173
+
174
+ `configureResearchAgentUI` overrides app-specific values (branding strings,
175
+ the Google API key used by the Drive picker, and the auto-media-search
176
+ toggle) that would otherwise couple this package to a specific app. See
177
+ `ResearchAgentUIConfig` in `src/config.ts` for the full list.
@@ -0,0 +1,4 @@
1
+ import type { AgentsDeps } from "../types";
2
+ export declare function createAgentsHandler(deps: AgentsDeps): {
3
+ POST: (req: Request) => Promise<Response>;
4
+ };
@@ -0,0 +1,2 @@
1
+ import type { ArticleDeps } from "../types";
2
+ export declare function createArticleFollowupsHandler(deps: ArticleDeps): (req: Request) => Promise<Response>;
@@ -0,0 +1,2 @@
1
+ import type { ArticleDeps } from "../types";
2
+ export declare function createArticleQAHandler(deps: ArticleDeps): (req: Request) => Promise<Response>;
@@ -0,0 +1,8 @@
1
+ export interface DomainSuggestion {
2
+ domain: string;
3
+ name: string;
4
+ favicon: string;
5
+ }
6
+ export declare function createAutocompleteHandler(): {
7
+ GET: (req: Request) => Promise<Response>;
8
+ };
@@ -0,0 +1,23 @@
1
+ import type { ChatsDeps } from "../types";
2
+ export declare function createChatsHandler(deps: ChatsDeps): {
3
+ GET: (_req: Request) => Promise<Response>;
4
+ DELETE: (_req: Request) => Promise<Response>;
5
+ };
6
+ export declare function createChatByIdHandler(deps: ChatsDeps): {
7
+ GET: (_req: Request, { params }: {
8
+ params: Promise<{
9
+ id: string;
10
+ }>;
11
+ }) => Promise<Response>;
12
+ DELETE: (_req: Request, { params }: {
13
+ params: Promise<{
14
+ id: string;
15
+ }>;
16
+ }) => Promise<Response>;
17
+ };
18
+ export declare function createChatsSearchHandler(deps: ChatsDeps): {
19
+ GET: (req: Request) => Promise<Response>;
20
+ };
21
+ export declare function createChatsShareHandler(deps: ChatsDeps): {
22
+ POST: (req: Request) => Promise<Response>;
23
+ };
@@ -0,0 +1,24 @@
1
+ import type { MCPServersDeps } from "../types";
2
+ export declare function createMCPServersHandler(deps: MCPServersDeps): {
3
+ GET: (_req: Request) => Promise<Response>;
4
+ POST: (req: Request) => Promise<Response>;
5
+ };
6
+ export declare function createMCPServerByIdHandler(deps: MCPServersDeps): {
7
+ DELETE: (_req: Request, { params }: {
8
+ params: Promise<{
9
+ id: string;
10
+ }>;
11
+ }) => Promise<Response>;
12
+ PATCH: (req: Request, { params }: {
13
+ params: Promise<{
14
+ id: string;
15
+ }>;
16
+ }) => Promise<Response>;
17
+ };
18
+ export declare function createMCPServerToggleHandler(deps: MCPServersDeps): {
19
+ POST: (req: Request, { params }: {
20
+ params: Promise<{
21
+ id: string;
22
+ }>;
23
+ }) => Promise<Response>;
24
+ };
@@ -0,0 +1,4 @@
1
+ import type { MessagesDeps } from "../types";
2
+ export declare function createMessagesHandler(deps: MessagesDeps): {
3
+ POST: (req: Request) => Promise<Response>;
4
+ };
@@ -0,0 +1,29 @@
1
+ import type { ProvidersDeps } from "../types";
2
+ export declare function createProvidersHandler(deps: ProvidersDeps): {
3
+ GET: (req: Request) => Promise<Response>;
4
+ POST: (req: Request) => Promise<Response>;
5
+ };
6
+ export declare function createProviderByIdHandler(): {
7
+ DELETE: (_req: Request, { params }: {
8
+ params: Promise<{
9
+ id: string;
10
+ }>;
11
+ }) => Promise<Response>;
12
+ PATCH: (req: Request, { params }: {
13
+ params: Promise<{
14
+ id: string;
15
+ }>;
16
+ }) => Promise<Response>;
17
+ };
18
+ export declare function createProviderModelsHandler(): {
19
+ POST: (req: Request, { params }: {
20
+ params: Promise<{
21
+ id: string;
22
+ }>;
23
+ }) => Promise<Response>;
24
+ DELETE: (req: Request, { params }: {
25
+ params: Promise<{
26
+ id: string;
27
+ }>;
28
+ }) => Promise<Response>;
29
+ };
@@ -0,0 +1,4 @@
1
+ import type { RewriteDeps } from "../types";
2
+ export declare function createRewriteHandler(deps: RewriteDeps): {
3
+ POST: (req: Request) => Promise<Response>;
4
+ };
@@ -0,0 +1,7 @@
1
+ import type { SearchDeps } from "../types";
2
+ export declare function createSearchHandler(deps?: SearchDeps): {
3
+ GET: (req: Request) => Promise<Response>;
4
+ };
5
+ export declare function createDiscoverHandler(): {
6
+ GET: (req: Request) => Promise<Response>;
7
+ };
@@ -0,0 +1,3 @@
1
+ export declare function createSuggestionsHandler(): {
2
+ POST: (req: Request) => Promise<Response>;
3
+ };
@@ -0,0 +1,3 @@
1
+ export declare function createTestModelsHandler(): {
2
+ POST: (req: Request) => Promise<Response>;
3
+ };
@@ -0,0 +1,4 @@
1
+ import type { TranscriptDeps } from "../types";
2
+ export declare function createTranscriptHandler(deps: TranscriptDeps): {
3
+ POST: (req: Request) => Promise<Response>;
4
+ };
@@ -0,0 +1,5 @@
1
+ import type { ValidateOpenRouterDeps } from "../types";
2
+ export declare function createValidateOpenRouterHandler(deps: ValidateOpenRouterDeps): {
3
+ GET: (_req: Request) => Promise<Response>;
4
+ POST: (req: Request) => Promise<Response>;
5
+ };
@@ -0,0 +1,4 @@
1
+ import type { VoiceDeps } from "../types";
2
+ export declare function createVoiceHandler(deps: VoiceDeps): {
3
+ POST: (req: Request) => Promise<Response>;
4
+ };
@@ -0,0 +1,16 @@
1
+ export * from "./types";
2
+ export * from "./handlers/article-followups";
3
+ export * from "./handlers/article-qa";
4
+ export * from "./handlers/chats";
5
+ export * from "./handlers/messages";
6
+ export * from "./handlers/providers";
7
+ export * from "./handlers/mcpservers";
8
+ export * from "./handlers/search";
9
+ export * from "./handlers/autocomplete";
10
+ export * from "./handlers/suggestions";
11
+ export * from "./handlers/agents";
12
+ export * from "./handlers/rewrite";
13
+ export * from "./handlers/voice";
14
+ export * from "./handlers/transcript";
15
+ export * from "./handlers/test-models";
16
+ export * from "./handlers/validate-openrouter";
@@ -0,0 +1,86 @@
1
+ /**
2
+ * Dependency injection types for research-agent-ui API route handlers.
3
+ *
4
+ * Each factory function accepts a narrow `deps` object containing only the
5
+ * app-specific implementations it needs (db, auth helpers, env accessor, etc.)
6
+ * so the same handler logic can run in any Next.js app.
7
+ */
8
+ export type DrizzleDB = any;
9
+ export interface AuthDeps {
10
+ getUserId: () => Promise<string | null>;
11
+ requireUserId: () => Promise<string>;
12
+ }
13
+ export interface SessionDeps {
14
+ getSession: () => Promise<any>;
15
+ }
16
+ export interface EnvDeps {
17
+ getEnv: (key: string) => string | undefined | null;
18
+ }
19
+ export interface ArticleDeps extends AuthDeps, EnvDeps {
20
+ getDB: () => DrizzleDB;
21
+ userSchema: any;
22
+ }
23
+ export interface ChatsDeps {
24
+ getDB: () => DrizzleDB;
25
+ requireUserId: () => Promise<string>;
26
+ getUserId?: () => Promise<string | null>;
27
+ schema: {
28
+ chats: any;
29
+ messages: any;
30
+ };
31
+ }
32
+ export interface MessagesDeps {
33
+ getDB: () => DrizzleDB;
34
+ requireUserId: () => Promise<string>;
35
+ messagesSchema: any;
36
+ }
37
+ export interface ProvidersDeps extends SessionDeps {
38
+ }
39
+ export interface MCPServersDeps {
40
+ configManager: {
41
+ addMCPServer(type: string, name: string, config: any): any;
42
+ removeMCPServer(id: string): void;
43
+ updateMCPServer(id: string, name: string, config: any): Promise<any>;
44
+ toggleMCPServer(id: string, enabled: boolean): any;
45
+ };
46
+ getConfiguredMCPServers: () => any[];
47
+ }
48
+ export interface SearchDeps {
49
+ searxngDomain?: string;
50
+ }
51
+ export interface VoiceDeps extends AuthDeps {
52
+ checkTTSRateLimit: (key: string) => {
53
+ allowed: boolean;
54
+ };
55
+ generateSpeech: (opts: {
56
+ text: string;
57
+ provider: string;
58
+ voice: string;
59
+ }) => Promise<{
60
+ audio: ArrayBuffer | Uint8Array | Buffer<ArrayBuffer>;
61
+ contentType: string;
62
+ }>;
63
+ }
64
+ export interface TranscriptDeps {
65
+ getCloudflareContext: () => {
66
+ env: any;
67
+ };
68
+ }
69
+ export interface RewriteDeps extends EnvDeps {
70
+ generateText: (opts: any) => Promise<{
71
+ text: string;
72
+ }>;
73
+ createGroq: (opts: {
74
+ apiKey: string;
75
+ }) => (modelId: string) => any;
76
+ }
77
+ export interface ValidateOpenRouterDeps {
78
+ validateOpenRouterModels: (concurrency?: number, timeout?: number) => Promise<any>;
79
+ }
80
+ export interface AgentsDeps extends AuthDeps, EnvDeps {
81
+ getDB: () => DrizzleDB;
82
+ userSchema: any;
83
+ }
84
+ export interface ChatHandlerDeps {
85
+ handleChatRequest: (req: Request) => Promise<Response>;
86
+ }
package/dist/api.cjs ADDED
@@ -0,0 +1,22 @@
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./rolldown-runtime-CMqjfN_6.cjs");let t=require("chat-agent-toolkit/models/registry");t=e.t(t,1);let n=require("drizzle-orm"),r=require("extract-webpage/search/public-searxng"),i=require("extract-webpage/suggest-next-words/autocomplete-search-engines"),a=require("fuse.js");a=e.t(a,1);let o=require("domain-rank/data/domain-rank-merged.json");o=e.t(o,1);let s=require("chat-agent-toolkit/tools/search/suggestionGeneratorAgent");s=e.t(s,1);let c=require("chat-agent-toolkit/config/model-tester"),l=require("chat-agent-toolkit/config/language-models-database");function u(e){return async r=>{try{let{article:i,chatHistory:a=[],maxQuestions:o=5,provider:s=`groq`,chatModel:c}=await r.json();if(!i)return Response.json({error:`Article is required`},{status:400});let l=e.getDB(),u=await e.getUserId(),d=null,f=null;if(u&&(d=await l.query.user.findFirst({where:(0,n.eq)(e.userSchema.id,u)})),d&&(f=d.settings?.providerApiKeys?.find(e=>e.provider==s)?.key),f||=s==`groq`?e.getEnv(`GROQ_API_KEY`):null,!f)return Response.json({error:`API key is required`},{status:500});let p=new t.default,m;if(m=c?await p.loadChatModel(c.providerId,c.key):await p.loadChatModel(`groq`,`llama-3.3-70b-versatile`),!m)return Response.json({error:`Failed to load language model`},{status:500});let h=a.length>0?`\n\nPrevious questions asked:\n${a.filter(e=>e.role===`user`).map(e=>`- ${e.content}`).join(`
2
+ `)}`:``,g=`You are a helpful AI that generates insightful follow-up questions about articles.
3
+ Generate ${o} thought-provoking questions that would help readers understand the article better.
4
+ Return ONLY the questions, one per line, without numbering or bullet points.`,_=`Article content:
5
+ ${i.slice(0,15e3)}
6
+
7
+ ${h}
8
+
9
+ Generate ${o} follow-up questions that would help readers dive deeper into this article.`,v=await m.stream([{role:`system`,content:g},{role:`user`,content:_}]),y=``;for await(let e of v)y+=e.content;let b=y.split(`
10
+ `).map(e=>e.trim()).filter(e=>e.length>0).map(e=>e.replace(/^[\d\-\*\.\)]+\s*/,``).trim()).filter(e=>e.length>10).slice(0,o);return Response.json({extract:b,success:!0})}catch(e){return console.error(`Error generating follow-up questions:`,e),Response.json({error:`An error occurred while generating follow-up questions`,details:e instanceof Error?e.message:String(e)},{status:500})}}}function d(e){return async r=>{try{let{article:i,question:a,chatHistory:o=[],provider:s=`groq`,chatModel:c}=await r.json();if(!i||!a)return Response.json({error:`Article and question are required`},{status:400});let l=e.getDB(),u=await e.getUserId(),d=null,f=null;if(u&&(d=await l.query.user.findFirst({where:(0,n.eq)(e.userSchema.id,u)})),d&&(f=d.settings?.providerApiKeys?.find(e=>e.provider==s)?.key),f||=s==`groq`?e.getEnv(`GROQ_API_KEY`):null,!f)return Response.json({error:`API key is required`},{status:500});let p=new t.default,m;if(m=c?await p.loadChatModel(c.providerId,c.key):await p.loadChatModel(`groq`,`llama-3.3-70b-versatile`),!m)return Response.json({error:`Failed to load language model`},{status:500});let h=o.length>0?`\n\nPrevious conversation:\n${o.map(e=>`${e.role===`user`?`User`:`Assistant`}: ${e.content}`).join(`
11
+ `)}`:``,g=`Article content:
12
+ ${i.slice(0,15e3)}
13
+
14
+ ${h}
15
+
16
+ User question: ${a}
17
+
18
+ Please provide a helpful answer based on the article content above.`,_=await m.stream([{role:`system`,content:`You are a helpful AI assistant that answers questions about articles.
19
+ Provide clear, concise, and accurate answers based on the article content provided.
20
+ If the answer is not in the article, say so.`},{role:`user`,content:g}]),v=``;for await(let e of _)v+=e.content;return Response.json({content:v,success:!0})}catch(e){return console.error(`Error in article Q&A:`,e),Response.json({error:`An error occurred while generating the answer`,details:e instanceof Error?e.message:String(e)},{status:500})}}}function f(e){let{chats:t,messages:r}=e.schema;return{GET:async i=>{try{let i=e.getDB(),a=await e.requireUserId(),o=await i.select({id:t.id,title:t.title,createdAt:t.createdAt,focusMode:t.focusMode,userId:t.userId,files:t.files,messageCount:(0,n.count)(r.id)}).from(t).leftJoin(r,(0,n.and)((0,n.eq)(r.chatId,t.id),(0,n.eq)(r.role,`user`))).where((0,n.eq)(t.userId,a)).groupBy(t.id,t.title,t.createdAt,t.focusMode,t.userId,t.files).orderBy(n.sql`${t.createdAt} DESC`);return Response.json({chats:o},{status:200})}catch(e){return e instanceof Error&&e.message===`Unauthorized`?Response.json({message:`Authentication required`},{status:401}):(console.error(`Error in getting chats: `,e),Response.json({message:`An error has occurred.`},{status:500}))}},DELETE:async i=>{try{let i=e.getDB(),a=await e.requireUserId(),o=await i.query.chats.findMany({where:(0,n.eq)(t.userId,a),columns:{id:!0}});if(o.length>0){let e=o.map(e=>e.id);await i.delete(r).where((0,n.inArray)(r.chatId,e)),await i.delete(t).where((0,n.eq)(t.userId,a))}return Response.json({message:`All chats deleted`},{status:200})}catch(e){return e instanceof Error&&e.message===`Unauthorized`?Response.json({message:`Authentication required`},{status:401}):(console.error(`Error in deleting all chats: `,e),Response.json({message:`An error has occurred.`},{status:500}))}}}}function p(e){let{chats:t,messages:r}=e.schema;return{GET:async(i,{params:a})=>{try{let i=e.getDB(),{id:o}=await a,s;try{s=await e.requireUserId()}catch{s=void 0}let c=await i.query.chats.findFirst({where:(0,n.eq)(t.id,o)});if(!c)return Response.json({message:`Chat not found`},{status:404});if(!(c.userId===s||c.isPublic))return Response.json({message:`Unauthorized - this chat is private`},{status:403});let l=await i.query.messages.findMany({where:(0,n.eq)(r.chatId,o)});return Response.json({chat:c,messages:l},{status:200})}catch(e){return console.error(`Error in getting chat by id: `,e),Response.json({message:`An error has occurred.`},{status:500})}},DELETE:async(i,{params:a})=>{try{let i=e.getDB(),{id:o}=await a,s=await e.requireUserId();return await i.query.chats.findFirst({where:(0,n.and)((0,n.eq)(t.id,o),(0,n.eq)(t.userId,s))})?(await i.delete(t).where((0,n.eq)(t.id,o)).execute(),await i.delete(r).where((0,n.eq)(r.chatId,o)).execute(),Response.json({message:`Chat deleted successfully`},{status:200})):Response.json({message:`Chat not found`},{status:404})}catch(e){return e instanceof Error&&e.message===`Unauthorized`?Response.json({message:`Authentication required`},{status:401}):(console.error(`Error in deleting chat by id: `,e),Response.json({message:`An error has occurred.`},{status:500}))}}}}function m(e){let{chats:t,messages:r}=e.schema;return{GET:async i=>{try{let a=e.getDB(),o=await e.requireUserId(),{searchParams:s}=new URL(i.url),c=s.get(`q`);if(!c||c.trim().length===0)return Response.json({chats:[]},{status:200});let l=`%${c.trim()}%`,u=await a.select({id:t.id,title:t.title,createdAt:t.createdAt,focusMode:t.focusMode,userId:t.userId,files:t.files}).from(t).where((0,n.and)((0,n.eq)(t.userId,o),(0,n.like)(t.title,l))).orderBy(n.sql`${t.createdAt} DESC`),d=await a.selectDistinct({id:t.id,title:t.title,createdAt:t.createdAt,focusMode:t.focusMode,userId:t.userId,files:t.files}).from(r).innerJoin(t,(0,n.eq)(r.chatId,t.id)).where((0,n.and)((0,n.eq)(t.userId,o),(0,n.or)((0,n.like)(r.content,l)))).orderBy(n.sql`${t.createdAt} DESC`),f=[...u,...d],p=Array.from(new Map(f.map(e=>[e.id,e])).values()),m=p.map(e=>e.id),h=m.length?await a.select({chatId:r.chatId,count:n.sql`count(*)`}).from(r).where((0,n.and)(n.sql`${r.chatId} IN (${n.sql.join(m.map(e=>n.sql`${e}`),n.sql`, `)})`,(0,n.eq)(r.role,`user`))).groupBy(r.chatId):[],g=new Map(h.map(e=>[e.chatId,Number(e.count)])),_=p.map(e=>({...e,messageCount:g.get(e.id)||0}));return Response.json({chats:_},{status:200})}catch(e){return e instanceof Error&&e.message===`Unauthorized`?Response.json({message:`Authentication required`},{status:401}):(console.error(`Error searching chats:`,e),Response.json({message:`An error has occurred.`},{status:500}))}}}}function h(e){let{chats:t}=e.schema;return{POST:async r=>{try{let i=e.getDB(),{chatId:a}=await r.json();if(!a)return Response.json({success:!1,error:`Chat ID is required`},{status:400});let o=await e.requireUserId(),s=await i.query.chats.findFirst({where:(0,n.eq)(t.id,a)});if(!s)return Response.json({success:!1,error:`Chat not found`},{status:404});if(s.userId!==o)return Response.json({success:!1,error:`Unauthorized`},{status:403});await i.update(t).set({isPublic:1}).where((0,n.eq)(t.id,a)).execute();let c=`${new URL(r.url).origin}/c/${a}`;return Response.json({success:!0,data:{chatId:a,shareUrl:c}})}catch(e){return e instanceof Error&&e.message===`Unauthorized`?Response.json({success:!1,error:`Authentication required`},{status:401}):(console.error(`Error in making chat public: `,e),Response.json({success:!1,error:`An error has occurred.`},{status:500}))}}}}function g(e){return{POST:async t=>{try{let n=e.getDB(),r=await e.requireUserId(),{chatId:i,messageId:a,role:o,suggestions:s,content:c,sources:l}=await t.json();return!i||!a||!o?Response.json({message:`Missing required fields`},{status:400}):(await n.insert(e.messagesSchema).values({chatId:i,userId:r,messageId:a,role:o,content:c||``,suggestions:s||[],sources:l||[],createdAt:new Date().toISOString()}),Response.json({message:`Message saved successfully`},{status:200}))}catch(e){return console.error(`Error saving message:`,e),Response.json({message:`Failed to save message`},{status:500})}}}}function _(e){return{GET:async n=>{try{let r=new t.default,i=new URL(n.url).searchParams.get(`guest`),a=i===`true`;i===null&&(a=!await e.getSession());let o=(await r.getActiveProviders(a)).filter(e=>!e.chatModels.some(e=>e.key===`error`));return o.length===0?Response.json({providers:[],error:a?`No guest-safe AI providers available. Please sign in for more options.`:`No AI providers configured. Please add OPENROUTER_API_KEY to your environment variables or configure your own API keys in Settings.`},{status:200}):Response.json({providers:o,isGuest:a},{status:200})}catch(e){return console.error(`An error occurred while fetching providers`,e),Response.json({message:`An error has occurred.`},{status:500})}},POST:async e=>{try{let{type:n,config:r}=await e.json();if(!n||!r)return Response.json({message:`Missing required fields.`},{status:400});let i=await new t.default().addProvider(n,r);return Response.json({provider:i},{status:200})}catch(e){return console.error(`An error occurred while creating provider`,e),Response.json({message:`An error has occurred.`},{status:500})}}}}function v(){return{DELETE:async(e,{params:n})=>{try{let{id:e}=await n;return e?(await new t.default().removeProvider(e),Response.json({message:`Provider deleted successfully.`},{status:200})):Response.json({message:`Provider ID is required.`},{status:400})}catch(e){return console.error(`An error occurred while deleting provider`,e.message),Response.json({message:`An error has occurred.`},{status:500})}},PATCH:async(e,{params:n})=>{try{let{config:r}=await e.json(),{id:i}=await n;if(!i||!r)return Response.json({message:`Missing required fields.`},{status:400});let a=await new t.default().updateProvider(i,r);return Response.json({provider:a},{status:200})}catch(e){return console.error(`An error occurred while updating provider`,e.message),Response.json({message:`An error has occurred.`},{status:500})}}}}function y(){return{POST:async(e,{params:n})=>{try{let{id:r}=await n,i=await e.json();return!i.key||!i.name?Response.json({message:`Key and name must be provided`},{status:400}):(await new t.default().addProviderModel(r,i.type,i),Response.json({message:`Model added successfully`},{status:200}))}catch(e){return console.error(`An error occurred while adding provider model`,e),Response.json({message:`An error has occurred.`},{status:500})}},DELETE:async(e,{params:n})=>{try{let{id:r}=await n,i=await e.json();return i.key?(await new t.default().removeProviderModel(r,i.type,i.key),Response.json({message:`Model added successfully`},{status:200})):Response.json({message:`Key and name must be provided`},{status:400})}catch(e){return console.error(`An error occurred while deleting provider model`,e),Response.json({message:`An error has occurred.`},{status:500})}}}}function b(e){return{GET:async t=>{try{let t=e.getConfiguredMCPServers();return Response.json({servers:t},{status:200})}catch(e){return console.error(`An error occurred while fetching MCP servers`,e),Response.json({message:`An error has occurred.`},{status:500})}},POST:async t=>{try{let{type:n,name:r,config:i}=await t.json();if(!n||!r||!i)return Response.json({message:`Missing required fields.`},{status:400});let a=e.configManager.addMCPServer(n,r,i);return Response.json({server:a},{status:200})}catch(e){return console.error(`An error occurred while creating MCP server`,e),Response.json({message:`An error has occurred.`},{status:500})}}}}function x(e){return{DELETE:async(t,{params:n})=>{try{let{id:t}=await n;return t?(e.configManager.removeMCPServer(t),Response.json({message:`MCP server deleted successfully.`},{status:200})):Response.json({message:`MCP Server ID is required.`},{status:400})}catch(e){return console.error(`An error occurred while deleting MCP server`,e.message),Response.json({message:`An error has occurred.`},{status:500})}},PATCH:async(t,{params:n})=>{try{let{name:r,config:i}=await t.json(),{id:a}=await n;if(!a||!r||!i)return Response.json({message:`Missing required fields.`},{status:400});let o=await e.configManager.updateMCPServer(a,r,i);return Response.json({server:o},{status:200})}catch(e){return console.error(`An error occurred while updating MCP server`,e.message),Response.json({message:`An error has occurred.`},{status:500})}}}}function S(e){return{POST:async(t,{params:n})=>{try{let{enabled:r}=await t.json(),{id:i}=await n;if(!i||typeof r!=`boolean`)return Response.json({message:`Missing required fields.`},{status:400});let a=e.configManager.toggleMCPServer(i,r);return Response.json({server:a},{status:200})}catch(e){return console.error(`An error occurred while toggling MCP server`,e.message),Response.json({message:`An error has occurred.`},{status:500})}}}}var C={tech:{query:[`technology news`,`latest tech`,`AI`,`science and innovation`],links:[`techcrunch.com`,`wired.com`,`theverge.com`]},finance:{query:[`finance news`,`economy`,`stock market`,`investing`],links:[`bloomberg.com`,`cnbc.com`,`marketwatch.com`]},art:{query:[`art news`,`culture`,`modern art`,`cultural events`],links:[`artnews.com`,`hyperallergic.com`,`theartnewspaper.com`]},sports:{query:[`sports news`,`latest sports`,`cricket football tennis`],links:[`espn.com`,`bbc.com/sport`,`skysports.com`]},entertainment:{query:[`entertainment news`,`movies`,`TV shows`,`celebrities`],links:[`hollywoodreporter.com`,`variety.com`,`deadline.com`]}};function w(e={}){let t=e.searxngDomain??`https://search.qwksearch.com`;return{GET:async e=>{let n=new URL(e.url).searchParams,i=n.get(`q`),a=n.get(`cat`)||`general`,o=parseInt(n.get(`page`)||`1`,10),s=n.get(`lang`)||`en-US`,c=n.get(`safesearch`)===`true`,l=n.get(`recency`)||void 0,u=n.get(`publicInstances`)===`true`;if(!i)return Response.json({error:`Query parameter is required`},{status:400});let d=Date.now();try{let e=await(0,r.searchWeb)(i,{category:a,recency:l,safesearch:c,maxRetries:6,privateSearxng:!u&&t,proxy:``,lang:s,page:o});e&&(Array.isArray(e)?e.length>0:e.results&&e.results.length>0)||(e=await(0,r.searchWeb)(i,{category:a,recency:l,safesearch:c,maxRetries:6,privateSearxng:!1,proxy:``,lang:s,page:o}));let n=Date.now()-d;return e?Array.isArray(e)?Response.json({results:e,elapsedTime:n}):Response.json({...e,elapsedTime:n}):Response.json({results:[],suggestions:[],elapsedTime:n})}catch(e){return console.error(`Search error:`,e),Response.json({error:`Search failed`,results:[]},{status:500})}}}}function T(){return{GET:async e=>{try{let t=new URL(e.url).searchParams,n=t.get(`mode`)||`normal`,i=C[t.get(`topic`)||`tech`],a=[];if(n===`normal`){let e=new Set;a=(await Promise.all(i.links.flatMap(e=>i.query.map(async t=>(await(0,r.searchSearxng)(`site:${e} ${t}`,{engines:[`bing news`],pageno:1,language:`en`})).results)))).flat().filter(t=>{let n=t.url?.toLowerCase().trim();return e.has(n)?!1:(e.add(n),!0)}).sort(()=>Math.random()-.5)}else a=(await(0,r.searchSearxng)(`site:${i.links[Math.floor(Math.random()*i.links.length)]} ${i.query[Math.floor(Math.random()*i.query.length)]}`,{engines:[`bing news`],pageno:1,language:`en`})).results;return Response.json({blogs:a},{status:200})}catch(e){return console.error(`An error occurred in discover route: ${e}`),Response.json({message:`An error has occurred`},{status:500})}}}}var E=[`google`,`duckduckgo`,`wikipedia`],D=3,O=3,k=Object.entries(o.default).map(([e,t])=>({domain:e,name:typeof t?.[0]==`string`?t[0]:``,rank:typeof t?.[1]==`number`?t[1]:2**53-1})),A=null;function j(){return A||=new a.default(k,{keys:[{name:`name`,weight:.6},{name:`domain`,weight:.4}],threshold:.1,ignoreLocation:!0,includeScore:!0,minMatchCharLength:3}),A}function M(e){let t=e.split(/\s+/).filter(Boolean),n=t[t.length-1]||``;return n.length<3?[]:j().search(n,{limit:12}).sort((e,t)=>Math.round((e.score??1)*10)-Math.round((t.score??1)*10)||e.item.rank-t.item.rank).slice(0,O).map(({item:e})=>({domain:e.domain,name:e.name,favicon:`https://www.google.com/s2/favicons?domain=${encodeURIComponent(e.domain)}&sz=64`}))}async function N(e,t,n){let r=await(0,i.searchAutocompleteMulti)(e,t,n);if(r.length>0)return r;let a=t.split(/\s+/).filter(Boolean),o=Math.min(D,a.length-1);for(let r=o;r>=1;r--){let o=a.slice(-r).join(` `),s=a.slice(0,-r).join(` `),c=await(0,i.searchAutocompleteMulti)(e,o,n);if(c.length>0){let e=new Set;for(let n of c){let r=s?`${s} ${n}`:n;r.toLowerCase()!==t.toLowerCase()&&e.add(r)}if(e.size>0)return Array.from(e)}}return[]}function P(){return{GET:async e=>{let{searchParams:t}=new URL(e.url),n=t.get(`q`)?.trim(),r=t.get(`locale`)||`en-US`,i=t.get(`backends`),a=parseInt(t.get(`limit`)||`8`,10);if(!n)return Response.json({suggestions:[],domains:[]});let o=i?i.split(`,`).map(e=>e.trim()).filter(Boolean):E;try{let e=await N(o,n,r),t=M(n);return Response.json({suggestions:e.slice(0,a),domains:t})}catch(e){return console.error(`Autocomplete error:`,e),Response.json({suggestions:[],domains:[]},{status:500})}}}}function F(){return{POST:async e=>{let n=await e.json(),r=n.chatHistory.filter(e=>e.role===`user`||e.role===`assistant`).map(e=>({role:e.role,content:String(e.content??``)})),i=await new t.default().loadChatModel(n.chatModel.providerId,n.chatModel.key),a=(await(0,s.default)({chat_history:r,maxQuestions:n.maxQuestions},i)).flatMap(e=>(e.match(/\?/g)||[]).length>1?e.split(/\?/).map(e=>e.trim()).filter(e=>e.length>0).map(e=>e+`?`):[e]);return Response.json({suggestions:a},{status:200})}}}function I(e){return{POST:async t=>{let r;try{r=await t.json()}catch{return Response.json({error:`Invalid JSON input`},{status:500})}r.ip=t.headers?.get?.(`x-forwarded-for`)||t.headers?.get?.(`x-real-ip`)||`unknown`;let i=e.getDB(),a=await e.getUserId(),o=null;return a&&(o=await i.query.user.findFirst({where:(0,n.eq)(e.userSchema.id,a)})),o&&(r.apiKey=o.settings?.providerApiKeys?.find(e=>e.provider==r.provider)?.key),r.apiKey||=r.provider==`groq`&&e.getEnv(`GROQ_API_KEY`),r.apiKey?Response.json({error:`Language generation endpoint is deprecated. Please use /api/agent/chat instead.`},{status:501}):Response.json({error:`API key is required`},{status:500})}}}function L(e){return{POST:async t=>{try{let{text:n,prompt:r}=await t.json();if(!n||typeof n!=`string`)return Response.json({error:`Text is required and must be a string`},{status:400});let i=e.getEnv(`GROQ_API_KEY`);if(!i)return console.error(`GROQ_API_KEY is not configured`),Response.json({error:`AI service is not configured. Please contact the administrator.`},{status:500});let a=e.createGroq({apiKey:i})(`llama-3.3-70b-versatile`),o=r||`Rewrite the following text to improve clarity, grammar, and style while maintaining the original meaning and tone. Only return the rewritten text without any explanation or additional commentary:
21
+
22
+ ${n}`,s=(await e.generateText({model:a,prompt:o,temperature:.7})).text.trim();return Response.json({rewrittenText:s})}catch(e){return console.error(`AI rewrite error:`,e),Response.json({error:`Failed to process AI request. Please try again.`},{status:500})}}}}function R(e){return{POST:async t=>{let n,r,i;try{let e=await t.json();n=e.text,r=e.voice||e.speaker||`af_heart`,i=e.provider||`kokoro`}catch{return Response.json({error:`Invalid request body`},{status:400})}if(!n||typeof n!=`string`||n.trim().length===0)return Response.json({error:`text is required`},{status:400});let a=await e.getUserId()??t.headers?.get?.(`x-forwarded-for`)?.split(`,`)[0]?.trim()??t.headers?.get?.(`x-real-ip`)??`unknown`,{allowed:o}=e.checkTTSRateLimit(a);if(!o)return Response.json({error:`Daily TTS limit reached (10/day)`,rateLimited:!0},{status:429});try{let t=await e.generateSpeech({text:n.slice(0,5e3),provider:i,voice:r});return new Response(t.audio,{headers:{"Content-Type":t.contentType,"Cache-Control":`public, max-age=86400`,"Content-Disposition":`inline; filename="speech.${t.contentType.includes(`wav`)?`wav`:`mp3`}"`}})}catch(e){console.error(`[TTS] Error:`,e);let t=e instanceof Error?e.message:`TTS generation failed`;return t.includes(`Cloudflare AI binding`)?Response.json({error:`Deepgram provider requires Cloudflare AI binding. Use 'kokoro' provider instead.`},{status:503}):Response.json({error:t},{status:500})}}}}function z(e){return e===`small`||e===`fast`?`@cf/openai/whisper-tiny-en`:e===`medium`||e===`turbo`?`@cf/openai/whisper-large-v3-turbo`:e===`large`?`@cf/openai/whisper-large-v3`:`@cf/openai/whisper-large-v3-turbo`}function B(e){return{POST:async t=>{try{let n=await t.formData(),r=n.get(`file`);if(!r||!(r instanceof File))return Response.json({error:`file is required`},{status:400});let i=z(n.get(`model`)),a;try{a=e.getCloudflareContext().env?.AI}catch{}if(!a)return Response.json({error:`Cloudflare AI binding not available`},{status:503});let o=await r.arrayBuffer(),{text:s}=await a.run(i,{audio:[...new Uint8Array(o)]});return Response.json({text:s,model:i})}catch(e){return console.error(`Transcript error:`,e),Response.json({error:`Failed to transcribe audio`},{status:500})}}}}function V(){return{POST:async e=>{try{let{providerType:t,apiKey:n,onlyFree:r=!0}=await e.json();if(!t)return Response.json({error:`Provider type is required`},{status:400});if(!n)return Response.json({error:`API key is required`},{status:400});let i=l.LANGUAGE_MODELS.find(e=>e.provider.toLowerCase()===t.toLowerCase());if(!i)return Response.json({error:`Provider ${t} not found`},{status:404});let a=await(0,c.testProviderModels)(t,n,i.models,{onlyFree:r,concurrency:3,timeout:15e3});return Response.json(a)}catch(e){return console.error(`[test-models] Error:`,e),Response.json({error:e.message||`Failed to test models`},{status:500})}}}}var H=1440*60*1e3,U=null;function W(e){return{GET:async t=>{try{let t=Date.now();if(U&&t-U.timestamp<H)return Response.json({...U.result,cached:!0,cacheAge:t-U.timestamp});let n=await e.validateOpenRouterModels();return U={result:n,timestamp:t},Response.json({...n,cached:!1})}catch(e){return console.error(`[validate-openrouter] GET error:`,e),Response.json({error:e.message||`Validation failed`},{status:500})}},POST:async t=>{try{let{concurrency:n=3,timeout:r=15e3}=await t.json().catch(()=>({})),i=await e.validateOpenRouterModels(n,r);return U={result:i,timestamp:Date.now()},Response.json({...i,cached:!1})}catch(e){return console.error(`[validate-openrouter] POST error:`,e),Response.json({error:e.message||`Validation failed`},{status:500})}}}}exports.createAgentsHandler=I,exports.createArticleFollowupsHandler=u,exports.createArticleQAHandler=d,exports.createAutocompleteHandler=P,exports.createChatByIdHandler=p,exports.createChatsHandler=f,exports.createChatsSearchHandler=m,exports.createChatsShareHandler=h,exports.createDiscoverHandler=T,exports.createMCPServerByIdHandler=x,exports.createMCPServerToggleHandler=S,exports.createMCPServersHandler=b,exports.createMessagesHandler=g,exports.createProviderByIdHandler=v,exports.createProviderModelsHandler=y,exports.createProvidersHandler=_,exports.createRewriteHandler=L,exports.createSearchHandler=w,exports.createSuggestionsHandler=F,exports.createTestModelsHandler=V,exports.createTranscriptHandler=B,exports.createValidateOpenRouterHandler=W,exports.createVoiceHandler=R;