Skip to content

Commit

Permalink
fix: stream chat & copying functionality & trim down context when loo…
Browse files Browse the repository at this point in the history
…m is too long
  • Loading branch information
tribhuwan-kumar committed Feb 24, 2025
1 parent f88d5f4 commit 32e0953
Show file tree
Hide file tree
Showing 9 changed files with 246 additions and 181 deletions.
43 changes: 30 additions & 13 deletions pipes/loom/app/api/copy/route.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
import os from 'os'
import { constants } from 'fs';
import { exec } from 'child_process';
import { access } from 'fs/promises';
import { NextRequest, NextResponse } from 'next/server';

export async function GET(req: NextRequest) {
Expand All @@ -7,19 +10,33 @@ export async function GET(req: NextRequest) {
return NextResponse.json({ error: 'path is required' }, { status: 400 });
}

const command = `powershell.exe -NoProfile -WindowStyle hidden -File copyToClipboard.ps1 -FilePath "${path}"`;
try {
await access(path, constants.F_OK);
} catch (error) {
return NextResponse.json({ error: 'File does not exist' }, { status: 404 });
}

exec(command, (error, stdout, stderr) => {
if (error) {
console.error(`Error: ${error.message}`);
return NextResponse.json({error: `${error.message}`}, { status: 400 });
}
if (stderr) {
console.error(`Stderr: ${stderr}`);
return NextResponse.json({error: `${stderr}`}, { status: 500 });
}
return NextResponse.json({message: 'sucessfully copied to clipboard', stdout: stdout}, {status: 200});
});
let command: string;
if (os.platform() === 'win32') {
command = `powershell.exe -NoProfile -WindowStyle hidden -File copyToClipboard.ps1 -FilePath "${path}"`;
} else if (os.platform() === 'darwin') {
command = `osascript -e 'set the clipboard to (read (POSIX file "${path}") as JPEG picture)'`;
} else {
return NextResponse.json({ error: 'Unsupported operating system' }, { status: 400 });
}

return NextResponse.json({message: 'sucessfully copied to clipboard'}, {status: 200});

return new Promise((resolve) => {
exec(command, (error, stdout, stderr) => {
if(error) {
console.error(`Error: ${error.message}`);
resolve(NextResponse.json({ error: `${error.message}` }, { status: 400 }));
} else if(stderr) {
console.error(`Stderr: ${stderr}`);
resolve(NextResponse.json({ error: `${stderr}` }, { status: 500 }));
} else {
resolve(NextResponse.json({ message: 'successfully copied to clipboard', stdout: stdout }, { status: 200 }));
}
})
})
}
86 changes: 86 additions & 0 deletions pipes/loom/app/api/stream/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import { Message } from 'ai';
import { OpenAI } from 'openai';
import { ContentItem } from "@screenpipe/js";
import { NextRequest, NextResponse } from 'next/server';

export async function POST(req: NextRequest) {
const { settings, chatMessages, floatingInput, selectedAgent, data } = await req.json();
const MAX_CONTENT_LENGTH = settings.aiMaxContextChars || 6000;

// had to trim down the context when the loom video is tooooo long :(
const removeDuplicateLines = (textContent: string[]) => {
const uniqueLines = Array.from(new Set(textContent));
const rmdups = uniqueLines.map((i) => i.replace(/(\S+)(\s+\1)+/g, "$1"))
let context = rmdups.join('\n');

if (context.length > MAX_CONTENT_LENGTH) {
context = context.slice(0, 6000);
}

return context;
};

const ocrTexts = data.map((item: ContentItem) => {
if(item.type === "OCR"){
return item.content.text;
}
});

const context = removeDuplicateLines(ocrTexts);

try {
const openai = new OpenAI({
apiKey: settings.aiProviderType === 'screenpipe-cloud'
? settings.user.token : settings.openaiApiKey,
baseURL: settings.aiUrl,
});

const model = settings.aiModel;
const customPrompt = settings.customPrompt || '';
const customRules = settings.loom?.customRules || '';

const messages = [
{
role: 'user' as const,
content: `You are a helpful assistant specialized as a "${selectedAgent.name}". ${selectedAgent.systemPrompt}
Rules:
- Current time (JavaScript Date.prototype.toString): ${new Date().toString()}
- User timezone: ${Intl.DateTimeFormat().resolvedOptions().timeZone}
- User timezone offset: ${new Date().getTimezoneOffset()}
- ${customPrompt ? `Prompt: ${customPrompt}` : ''}
- Rules: ${customRules} `,
},
...chatMessages.map((msg: Message) => ({
role: msg.role as 'user' | 'assistant' | 'system',
content: msg.content,
})),
{
role: 'user' as const,
content: `Context data: ${context}
User query: ${floatingInput}`,
},
];

const stream = await openai.chat.completions.create(
{
model: model,
messages: messages,
stream: true,
},
);

let fullResponse = '';
for await (const chunk of stream) {
const content = chunk.choices[0]?.delta?.content || '';
fullResponse += content;
}

return NextResponse.json({ response: fullResponse }, { status: 200 });
} catch (error: any) {
console.error('Error generating AI response:', error);
return NextResponse.json(
{ message: "Failed to generate AI response" },
{ status: 500, statusText: `${error}`}
);
}
}
2 changes: 1 addition & 1 deletion pipes/loom/app/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import Pipe from "@/components/pipe"

export default function Home() {
return (
<main className="min-h-[1500px]">
<main className="max-h-fit mb-[200px]">
<Header />
<Pipe />
</main>
Expand Down
51 changes: 39 additions & 12 deletions pipes/loom/components/dialog-setting.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import React, { useState, useEffect } from "react";
import { useSettings } from "@/lib/hooks/use-settings";
import { Button } from "@/components/ui/button";
import { Label } from "@/components/ui/label";
import { Input } from "@/components/ui/input";
import { FileCheck, Laptop } from "lucide-react";
import { useToast } from "@/lib/use-toast";
import { SqlAutocompleteInput } from "./sql-autocomplete-input";
Expand Down Expand Up @@ -35,11 +36,18 @@ const DialogSettings: React.FC<SettingsDialogProps> = ({
const [windowName, setWindowName] = useState("");
const [contentType, setContentType] = useState("");

const defaultCustomRules = `- Craft engaging and community-friendly posts based on given screen data.
- Focus on generating specific and thoughtful questions that encourage discussion or helpful responses from the Reddit community.
- Use casual and approachable language, keeping the posts concise and easy to read.
- Include context when it adds value to the question but avoid overly personal details.
- Ensure posts are well-structured, starting with a clear title, followed by a detailed body, and end with relevant subreddit recommendations.
const defaultCustomRules = `- Create a concise summary of the given context
- Ensure the summary is precise and captures the essence of the content.
- Start with an engaging opening sentence.
- Follow with a detailed description of the main topics.
- Conclude with a call-to-action or additional information.
- Ensure the description is written in a natural and readable manner.
- Avoid technical jargon unless necessary.
- Keep the tone and style consistent throughout the description.
- Use proper grammar and punctuation.
- Add relevant tags or keywords to improve searchability.
- Don't ask any type of question from user.
- Ensure tags are related to the main topics identified.
`;

useEffect(() => {
Expand All @@ -57,14 +65,15 @@ const defaultCustomRules = `- Craft engaging and community-friendly posts based
e.preventDefault();
const formData = new FormData(e.target as HTMLFormElement);

const newLoomSettings = {
const loomSettings = {
customRules: formData.get("customRules") as string,
maxLength: formData.get("maxLength") as string,
windowName: (formData.get("windowName") as string) || windowName,
contentType: contentType as string,
};

try {
await updateSettings(newLoomSettings, "loom");
await updateSettings(loomSettings, "loom");
toast({
title: "settings saved",
description: "your loom pipe settings have been updated",
Expand All @@ -91,8 +100,8 @@ const defaultCustomRules = `- Craft engaging and community-friendly posts based
</DialogDescription>
</DialogHeader>

<form onSubmit={handleSave} className="space-y-8 w-full">
<div className="space-y-4">
<form onSubmit={handleSave} className="space-y-4 w-full">
<div className="space-y-2">
<Label htmlFor="contentType">
<span>content type </span>
<span className="text-[13px] text-muted-foreground !font-normal">
Expand Down Expand Up @@ -120,7 +129,25 @@ const defaultCustomRules = `- Craft engaging and community-friendly posts based
</SelectContent>
</Select>
</div>
<div className="space-y-4">
<div className="space-y-2">
<Label htmlFor="maxLength">max content length</Label>
<span className="text-[13px] text-muted-foreground">
&nbsp;&nbsp; max content length of characters getting from screenpipe api
</span>
<div className="flex gap-2">
<Input
id="maxLength"
name="maxLength"
type="number"
defaultValue={
settings.customSettings?.loom?.maxLength || 100
}
placeholder="max value for contents"
className="flex-1"
/>
</div>
</div>
<div className="space-y-2">
<Label htmlFor="windowName">window name</Label>
<span className="text-[13px] text-muted-foreground">
&nbsp;&nbsp;specific window name to include in loom video, for
Expand All @@ -132,14 +159,14 @@ const defaultCustomRules = `- Craft engaging and community-friendly posts based
type="window"
icon={<Laptop className="h-4 w-4" />}
defaultValue={
settings.customSettings?.["reddit-auto-posts"]?.windowName
settings.customSettings?.loom?.windowName
}
onChange={(v) => setWindowName(v)}
placeholder="window name to filter the screen data"
className="flex-grow"
/>
</div>
<div className="space-y-4">
<div className="space-y-2">
<Label htmlFor="customRules">custom rules</Label>
<textarea
id="customRules"
Expand Down
2 changes: 1 addition & 1 deletion pipes/loom/components/header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export default function Header() {
<Button
variant="outline"
onClick={() => setIsDialogSettingOpen(true)}
className="absolute right-10 top-[-25px]"
className="absolute right-10 top-[-25px] !border-none"
>
<SettingsIcon className="h-4 w-4" />
</Button>
Expand Down
Loading

0 comments on commit 32e0953

Please sign in to comment.