Spaces:
Build error
Build error
| import { COOKIE_NAME } from "@shared/const"; | |
| import { getSessionCookieOptions } from "./_core/cookies"; | |
| import { systemRouter } from "./_core/systemRouter"; | |
| import { publicProcedure, protectedProcedure, router } from "./_core/trpc"; | |
| import { z } from "zod"; | |
| import { generateResponseWithReasoning, generateImage } from "./llm"; | |
| import { searchOnline, formatSearchResults, sanitizeSearchQuery } from "./search"; | |
| import { checkRateLimit } from "./rateLimit"; | |
| export const appRouter = router({ | |
| // if you need to use socket.io, read and register route in server/_core/index.ts, all api should start with '/api/' so that the gateway can route correctly | |
| system: systemRouter, | |
| auth: router({ | |
| me: publicProcedure.query(opts => opts.ctx.user), | |
| logout: publicProcedure.mutation(({ ctx }) => { | |
| const cookieOptions = getSessionCookieOptions(ctx.req); | |
| ctx.res.clearCookie(COOKIE_NAME, { ...cookieOptions, maxAge: -1 }); | |
| return { | |
| success: true, | |
| } as const; | |
| }), | |
| }), | |
| // Section 1: Chat procedures (Ask mode) | |
| chat: router({ | |
| send: protectedProcedure | |
| .input( | |
| z.object({ | |
| prompt: z.string().min(1), | |
| conversationId: z.number().optional(), | |
| enableSearch: z.boolean().default(false), | |
| enableThinking: z.boolean().default(false), | |
| history: z | |
| .array( | |
| z.object({ | |
| role: z.enum(["user", "assistant"]), | |
| content: z.string(), | |
| }) | |
| ) | |
| .default([]), | |
| }) | |
| ) | |
| .mutation(async ({ ctx, input }) => { | |
| // Check rate limit | |
| const userId = ctx.user?.id?.toString() || "anonymous"; | |
| const rateLimitCheck = checkRateLimit(userId, "chat"); | |
| if (!rateLimitCheck.allowed) { | |
| throw new Error( | |
| `Rate limit exceeded. Try again in ${Math.ceil(rateLimitCheck.resetIn / 1000)} seconds.` | |
| ); | |
| } | |
| try { | |
| let searchResults = ""; | |
| // Search online if enabled | |
| if (input.enableSearch) { | |
| const sanitizedQuery = sanitizeSearchQuery(input.prompt); | |
| const results = await searchOnline(sanitizedQuery, 5); | |
| searchResults = formatSearchResults(results); | |
| } | |
| // Generate response with optional reasoning | |
| const response = await generateResponseWithReasoning( | |
| input.prompt, | |
| searchResults, | |
| input.enableThinking, | |
| input.history | |
| ); | |
| return { | |
| success: true, | |
| response: response.response, | |
| reasoning: response.reasoning, | |
| model: response.model, | |
| tokensUsed: response.tokensUsed, | |
| searchResults: searchResults || undefined, | |
| }; | |
| } catch (error) { | |
| console.error("Chat error:", error); | |
| throw new Error( | |
| error instanceof Error ? error.message : "Failed to generate response" | |
| ); | |
| } | |
| }), | |
| }), | |
| // Section 1: Image generation procedures (Imagine mode) | |
| imagine: router({ | |
| generate: protectedProcedure | |
| .input( | |
| z.object({ | |
| prompt: z.string().min(1), | |
| }) | |
| ) | |
| .mutation(async ({ ctx, input }) => { | |
| // Check rate limit | |
| const userId = ctx.user?.id?.toString() || "anonymous"; | |
| const rateLimitCheck = checkRateLimit(userId, "imagine"); | |
| if (!rateLimitCheck.allowed) { | |
| throw new Error( | |
| `Rate limit exceeded. Try again in ${Math.ceil(rateLimitCheck.resetIn / 1000)} seconds.` | |
| ); | |
| } | |
| try { | |
| const imageUrl = await generateImage(input.prompt); | |
| return { | |
| success: true, | |
| imageUrl, | |
| prompt: input.prompt, | |
| }; | |
| } catch (error) { | |
| console.error("Image generation error:", error); | |
| throw new Error( | |
| error instanceof Error ? error.message : "Failed to generate image" | |
| ); | |
| } | |
| }), | |
| }), | |
| // Section 1: Search procedure | |
| search: router({ | |
| online: publicProcedure | |
| .input( | |
| z.object({ | |
| query: z.string().min(1), | |
| maxResults: z.number().default(5), | |
| }) | |
| ) | |
| .query(async ({ input }) => { | |
| try { | |
| const sanitizedQuery = sanitizeSearchQuery(input.query); | |
| const results = await searchOnline(sanitizedQuery, input.maxResults); | |
| return { | |
| success: true, | |
| results, | |
| query: input.query, | |
| }; | |
| } catch (error) { | |
| console.error("Search error:", error); | |
| throw new Error( | |
| error instanceof Error ? error.message : "Search failed" | |
| ); | |
| } | |
| }), | |
| }), | |
| }); | |
| export type AppRouter = typeof appRouter; | |