diff --git a/apps/webapp/app/components/conversation/conversation-item.client.tsx b/apps/webapp/app/components/conversation/conversation-item.client.tsx index 02a2200..e7796c9 100644 --- a/apps/webapp/app/components/conversation/conversation-item.client.tsx +++ b/apps/webapp/app/components/conversation/conversation-item.client.tsx @@ -10,6 +10,15 @@ interface AIConversationItemProps { message: UIMessage; } +function getMessage(message: string) { + let finalMessage = message.replace("", ""); + finalMessage = finalMessage.replace("", ""); + finalMessage = finalMessage.replace("", ""); + finalMessage = finalMessage.replace("", ""); + + return finalMessage; +} + const ConversationItemComponent = ({ message }: AIConversationItemProps) => { const isUser = message.role === "user" || false; const textPart = message.parts.find((part) => part.type === "text"); @@ -17,12 +26,12 @@ const ConversationItemComponent = ({ message }: AIConversationItemProps) => { const editor = useEditor({ extensions: [...extensionsForConversation, skillExtension], editable: false, - content: textPart ? textPart.text : "", + content: textPart ? getMessage(textPart.text) : "", }); useEffect(() => { if (textPart) { - editor?.commands.setContent(textPart.text); + editor?.commands.setContent(getMessage(textPart.text)); } // eslint-disable-next-line react-hooks/exhaustive-deps }, [message]); diff --git a/apps/webapp/app/lib/prompt.server.ts b/apps/webapp/app/lib/prompt.server.ts index dbc1d12..b25cd31 100644 --- a/apps/webapp/app/lib/prompt.server.ts +++ b/apps/webapp/app/lib/prompt.server.ts @@ -1,5 +1,4 @@ -import { StopCondition, tool } from "ai"; -import z from "zod"; +import { type StopCondition } from "ai"; export const hasAnswer: StopCondition = ({ steps }) => { return ( @@ -9,8 +8,7 @@ export const hasAnswer: StopCondition = ({ steps }) => { export const hasQuestion: StopCondition = ({ steps }) => { return ( - steps.some((step) => step.text?.includes("")) ?? - false + steps.some((step) => step.text?.includes("")) ?? false ); }; @@ -163,19 +161,6 @@ CRITICAL: `; -export const fixedTools = { - progressUpdate: tool({ - description: - "Send a progress update to the user about what has been discovered or will be done next in a crisp and user friendly way no technical terms", - inputSchema: z.object({ - message: z.string(), - }), - execute: async ({ message }: { message: string }) => ({ - message, - }), - }), -}; - export function getReActPrompt( metadata?: { source?: string; url?: string; pageTitle?: string }, intentOverride?: string, diff --git a/apps/webapp/app/lib/queue-adapter.server.ts b/apps/webapp/app/lib/queue-adapter.server.ts index 64ce24c..af9281d 100644 --- a/apps/webapp/app/lib/queue-adapter.server.ts +++ b/apps/webapp/app/lib/queue-adapter.server.ts @@ -15,6 +15,7 @@ import type { z } from "zod"; import type { IngestBodyRequest } from "~/jobs/ingest/ingest-episode.logic"; import type { CreateConversationTitlePayload } from "~/jobs/conversation/create-title.logic"; import type { SessionCompactionPayload } from "~/jobs/session/session-compaction.logic"; +import { type SpaceAssignmentPayload } from "~/trigger/spaces/space-assignment"; type QueueProvider = "trigger" | "bullmq"; @@ -145,12 +146,9 @@ export async function enqueueSessionCompaction( * Enqueue space assignment job * (Helper for common job logic to call) */ -export async function enqueueSpaceAssignment(payload: { - userId: string; - workspaceId: string; - mode: "episode"; - episodeIds: string[]; -}): Promise { +export async function enqueueSpaceAssignment( + payload: SpaceAssignmentPayload, +): Promise { const provider = env.QUEUE_PROVIDER as QueueProvider; if (provider === "trigger") { diff --git a/apps/webapp/app/routes/api.v1.conversation._index.tsx b/apps/webapp/app/routes/api.v1.conversation._index.tsx index 5a316fc..8e92a4c 100644 --- a/apps/webapp/app/routes/api.v1.conversation._index.tsx +++ b/apps/webapp/app/routes/api.v1.conversation._index.tsx @@ -6,7 +6,6 @@ import { experimental_createMCPClient as createMCPClient, generateId, stepCountIs, - StopCondition, } from "ai"; import { z } from "zod"; import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js"; @@ -21,7 +20,11 @@ import { getModel } from "~/lib/model.server"; import { UserTypeEnum } from "@core/types"; import { nanoid } from "nanoid"; import { getOrCreatePersonalAccessToken } from "~/services/personalAccessToken.server"; -import { hasAnswer, hasQuestion, REACT_SYSTEM_PROMPT } from "~/lib/prompt.server"; +import { + hasAnswer, + hasQuestion, + REACT_SYSTEM_PROMPT, +} from "~/lib/prompt.server"; import { enqueueCreateConversationTitle } from "~/lib/queue-adapter.server"; import { env } from "~/env.server"; @@ -109,8 +112,6 @@ const { loader, action } = createHybridActionApiRoute( messages: finalMessages, }); - - const result = streamText({ model: getModel() as LanguageModel, messages: [ @@ -121,7 +122,7 @@ const { loader, action } = createHybridActionApiRoute( ...convertToModelMessages(validatedMessages), ], tools, - stopWhen: [stepCountIs(10), hasAnswer,hasQuestion], + stopWhen: [stepCountIs(10), hasAnswer, hasQuestion], }); result.consumeStream(); // no await @@ -129,7 +130,6 @@ const { loader, action } = createHybridActionApiRoute( return result.toUIMessageStreamResponse({ originalMessages: validatedMessages, onFinish: async ({ messages }) => { - console.log(JSON.stringify(messages)); const lastMessage = messages.pop(); let message = ""; lastMessage?.parts.forEach((part) => { diff --git a/apps/webapp/app/routes/api.v1.deep-search.tsx b/apps/webapp/app/routes/api.v1.deep-search.tsx index a7bfe1e..b0cba95 100644 --- a/apps/webapp/app/routes/api.v1.deep-search.tsx +++ b/apps/webapp/app/routes/api.v1.deep-search.tsx @@ -10,7 +10,6 @@ import { import { convertToModelMessages, - type CoreMessage, generateId, generateText, type LanguageModel, @@ -109,11 +108,17 @@ const { action, loader } = createActionApiRoute( const tools = { searchMemory: searchTool, }; + // Build initial messages with ReAct prompt const initialMessages = [ { role: "user", - parts: [{ type: "text", text: `CONTENT TO ANALYZE:\n${body.content}\n\nPlease search my memory for relevant context and synthesize what you find.` }], + parts: [ + { + type: "text", + text: `CONTENT TO ANALYZE:\n${body.content}\n\nPlease search my memory for relevant context and synthesize what you find.`, + }, + ], id: generateId(), }, ]; @@ -134,7 +139,7 @@ const { action, loader } = createActionApiRoute( ...convertToModelMessages(validatedMessages), ], tools, - stopWhen: [stepCountIs(10), hasAnswer], + stopWhen: [hasAnswer, stepCountIs(10)], }); return result.toUIMessageStreamResponse({ @@ -151,7 +156,7 @@ const { action, loader } = createActionApiRoute( ...convertToModelMessages(validatedMessages), ], tools, - stopWhen: [stepCountIs(10), hasAnswer], + stopWhen: [hasAnswer, stepCountIs(10)], }); await deletePersonalAccessToken(pat?.id); diff --git a/apps/webapp/app/routes/api.v1.spaces.$spaceId.reset.ts b/apps/webapp/app/routes/api.v1.spaces.$spaceId.reset.ts index ba3d93c..8c16319 100644 --- a/apps/webapp/app/routes/api.v1.spaces.$spaceId.reset.ts +++ b/apps/webapp/app/routes/api.v1.spaces.$spaceId.reset.ts @@ -3,7 +3,7 @@ import { createHybridActionApiRoute } from "~/services/routeBuilders/apiBuilder. import { SpaceService } from "~/services/space.server"; import { json } from "@remix-run/node"; import { logger } from "~/services/logger.service"; -import { triggerSpaceAssignment } from "~/trigger/spaces/space-assignment"; +import { enqueueSpaceAssignment } from "~/lib/queue-adapter.server"; // Schema for space ID parameter const SpaceParamsSchema = z.object({ @@ -31,7 +31,7 @@ const { loader, action } = createHybridActionApiRoute( // Trigger automatic episode assignment for the reset space try { - await triggerSpaceAssignment({ + await enqueueSpaceAssignment({ userId: userId, workspaceId: space.workspaceId, mode: "new_space", diff --git a/apps/webapp/app/routes/api.v1.spaces.assignments.ts b/apps/webapp/app/routes/api.v1.spaces.assignments.ts index 75a5561..4c0e888 100644 --- a/apps/webapp/app/routes/api.v1.spaces.assignments.ts +++ b/apps/webapp/app/routes/api.v1.spaces.assignments.ts @@ -1,8 +1,8 @@ import { z } from "zod"; import { createActionApiRoute } from "~/services/routeBuilders/apiBuilder.server"; import { json } from "@remix-run/node"; -import { triggerSpaceAssignment } from "~/trigger/spaces/space-assignment"; import { prisma } from "~/db.server"; +import { enqueueSpaceAssignment } from "~/lib/queue-adapter.server"; // Schema for manual assignment trigger const ManualAssignmentSchema = z.object({ @@ -38,7 +38,7 @@ const { action } = createActionApiRoute( let taskRun; // Direct LLM assignment trigger - taskRun = await triggerSpaceAssignment({ + taskRun = await enqueueSpaceAssignment({ userId, workspaceId: user?.Workspace?.id as string, mode: body.mode, @@ -49,7 +49,7 @@ const { action } = createActionApiRoute( return json({ success: true, message: `${body.mode} assignment task triggered successfully`, - taskId: taskRun.id, + payload: { userId, mode: body.mode, diff --git a/apps/webapp/app/routes/home.conversation.$conversationId.tsx b/apps/webapp/app/routes/home.conversation.$conversationId.tsx index 0179877..684466f 100644 --- a/apps/webapp/app/routes/home.conversation.$conversationId.tsx +++ b/apps/webapp/app/routes/home.conversation.$conversationId.tsx @@ -55,9 +55,10 @@ export default function SingleConversation() { }, }), }); + console.log("new", messages); React.useEffect(() => { - if (conversation.ConversationHistory.length === 1) { + if (messages.length === 1) { regenerate(); } }, []); @@ -96,7 +97,7 @@ export default function SingleConversation() {
{ if (message) { sendMessage({ text: message }); diff --git a/apps/webapp/app/services/space.server.ts b/apps/webapp/app/services/space.server.ts index 9a72714..dcf213b 100644 --- a/apps/webapp/app/services/space.server.ts +++ b/apps/webapp/app/services/space.server.ts @@ -6,7 +6,6 @@ import { } from "@core/types"; import { type Space } from "@prisma/client"; -import { triggerSpaceAssignment } from "~/trigger/spaces/space-assignment"; import { assignEpisodesToSpace, createSpace, @@ -18,6 +17,7 @@ import { } from "./graphModels/space"; import { prisma } from "~/trigger/utils/prisma"; import { trackFeatureUsage } from "./telemetry.server"; +import { enqueueSpaceAssignment } from "~/lib/queue-adapter.server"; export class SpaceService { /** @@ -69,7 +69,7 @@ export class SpaceService { // Trigger automatic LLM assignment for the new space try { - await triggerSpaceAssignment({ + await enqueueSpaceAssignment({ userId: params.userId, workspaceId: params.workspaceId, mode: "new_space", diff --git a/apps/webapp/app/services/telemetry.server.ts b/apps/webapp/app/services/telemetry.server.ts index 98f42b5..2accf62 100644 --- a/apps/webapp/app/services/telemetry.server.ts +++ b/apps/webapp/app/services/telemetry.server.ts @@ -128,6 +128,7 @@ export async function trackEvent( modelProvider: getModelProvider(), embeddingModel: env.EMBEDDING_MODEL, appEnv: env.APP_ENV, + appOrigin: env.APP_ORIGIN, timestamp: new Date().toISOString(), }; @@ -175,6 +176,7 @@ export async function trackFeatureUsage( event: feature, properties: { ...properties, + appOrigin: env.APP_ORIGIN, timestamp: new Date().toISOString(), }, }); @@ -204,6 +206,7 @@ export async function trackConfig(): Promise { appEnv: env.APP_ENV, nodeEnv: env.NODE_ENV, timestamp: new Date().toISOString(), + appOrigin: env.APP_ORIGIN, }, }); } catch (error) { @@ -231,6 +234,7 @@ export async function trackError( properties: { errorType: error.name, errorMessage: error.message, + appOrigin: env.APP_ORIGIN, stackTrace: error.stack, ...context, timestamp: new Date().toISOString(), diff --git a/apps/webapp/app/trigger/spaces/space-assignment.ts b/apps/webapp/app/trigger/spaces/space-assignment.ts index d43a7cb..23359d7 100644 --- a/apps/webapp/app/trigger/spaces/space-assignment.ts +++ b/apps/webapp/app/trigger/spaces/space-assignment.ts @@ -18,7 +18,7 @@ import type { CoreMessage } from "ai"; import { z } from "zod"; import { type Space } from "@prisma/client"; -interface SpaceAssignmentPayload { +export interface SpaceAssignmentPayload { userId: string; workspaceId: string; mode: "new_space" | "episode"; diff --git a/docker/Dockerfile.neo4j b/docker/Dockerfile.neo4j index 2e85560..408adec 100644 --- a/docker/Dockerfile.neo4j +++ b/docker/Dockerfile.neo4j @@ -1,22 +1,16 @@ -FROM neo4j:5 +FROM neo4j:5.26.0 -# Set environment variables for plugin versions -# GDS 2.13 is compatible with Neo4j 5.26 -# APOC 5.26.14 is the latest for Neo4j 5.x -ENV GDS_VERSION=2.13.0 -ENV APOC_VERSION=5.26.0 - -# Install GDS and APOC plugins +# Manual installation of plugins with correct download URLs +# GDS 2.13.2 is compatible with Neo4j 5.26 +# APOC 5.26.0 matches Neo4j 5.26 RUN apt-get update && apt-get install -y curl && \ - curl -L https://github.com/neo4j/graph-data-science/releases/download/${GDS_VERSION}/neo4j-graph-data-science-${GDS_VERSION}.jar \ - -o /var/lib/neo4j/plugins/neo4j-graph-data-science-${GDS_VERSION}.jar && \ - curl -L https://github.com/neo4j/apoc/releases/download/${APOC_VERSION}/apoc-${APOC_VERSION}-core.jar \ - -o /var/lib/neo4j/plugins/apoc-${APOC_VERSION}-core.jar && \ + curl -L https://github.com/neo4j/graph-data-science/releases/download/2.13.2/neo4j-graph-data-science-2.13.2.jar \ + -o /var/lib/neo4j/plugins/neo4j-graph-data-science.jar && \ + curl -L https://github.com/neo4j/apoc/releases/download/5.26.0/apoc-5.26.0-core.jar \ + -o /var/lib/neo4j/plugins/apoc-core.jar && \ apt-get clean && \ - rm -rf /var/lib/apt/lists/* - -# Set proper permissions -RUN chown -R neo4j:neo4j /var/lib/neo4j/plugins + rm -rf /var/lib/apt/lists/* && \ + chown -R neo4j:neo4j /var/lib/neo4j/plugins # Default configuration for GDS and APOC ENV NEO4J_dbms_security_procedures_unrestricted=gds.*,apoc.* diff --git a/hosting/docker/docker-compose.yaml b/hosting/docker/docker-compose.yaml index 5c934a6..d0b70e7 100644 --- a/hosting/docker/docker-compose.yaml +++ b/hosting/docker/docker-compose.yaml @@ -42,6 +42,9 @@ services: - FROM_EMAIL=${FROM_EMAIL} - RESEND_API_KEY=${RESEND_API_KEY} - COHERE_API_KEY=${COHERE_API_KEY} + - QUEUE_PROVIDER=${QUEUE_PROVIDER} + - TELEMETRY_ENABLED=${TELEMETRY_ENABLED} + - TELEMETRY_ANONYMOUS=${TELEMETRY_ANONYMOUS} ports: - "3033:3000" depends_on: @@ -84,7 +87,7 @@ services: neo4j: container_name: core-neo4j - image: neo4j:5 + image: redplanethq/neo4j:0.1.0 environment: - NEO4J_AUTH=${NEO4J_AUTH} - NEO4J_dbms_security_procedures_unrestricted=gds.*,apoc.* @@ -94,7 +97,6 @@ services: - NEO4J_apoc_import_file_use_neo4j_config=true - NEO4J_server_memory_heap_initial__size=2G - NEO4J_server_memory_heap_max__size=4G - - NEO4JLABS_PLUGINS=apoc,graph-data-science ports: - "7474:7474" - "7687:7687"