Skip to content

Commit

Permalink
Improved API Enhancer
Browse files Browse the repository at this point in the history
  • Loading branch information
ali00209 committed Oct 27, 2024
1 parent 8e7220e commit 0fd6899
Show file tree
Hide file tree
Showing 2 changed files with 192 additions and 73 deletions.
212 changes: 161 additions & 51 deletions app/lib/hooks/usePromptEnhancer.ts
Original file line number Diff line number Diff line change
@@ -1,71 +1,181 @@
import { useState } from 'react';
import { useState, useCallback } from 'react';
import { createScopedLogger } from '~/utils/logger';

const logger = createScopedLogger('usePromptEnhancement');

export function usePromptEnhancer() {
const [enhancingPrompt, setEnhancingPrompt] = useState(false);
const [promptEnhanced, setPromptEnhanced] = useState(false);

const resetEnhancer = () => {
setEnhancingPrompt(false);
setPromptEnhanced(false);
};

const enhancePrompt = async (input: string, setInput: (value: string) => void) => {
setEnhancingPrompt(true);
setPromptEnhanced(false);
interface EnhancerState {
isEnhancing: boolean;
isEnhanced: boolean;
error: string | null;
originalPrompt: string | null;
}

const response = await fetch('/api/enhancer', {
method: 'POST',
body: JSON.stringify({
message: input,
}),
export function usePromptEnhancer() {
const [state, setState] = useState<EnhancerState>({
isEnhancing: false,
isEnhanced: false,
error: null,
originalPrompt: null,
});

const resetEnhancer = useCallback(() => {
setState({
isEnhancing: false,
isEnhanced: false,
error: null,
originalPrompt: null,
});
}, []);

const reader = response.body?.getReader();

const originalInput = input;

if (reader) {
const decoder = new TextDecoder();

let _input = '';
let _error;
const revertToOriginal = useCallback((setInput: (value: string) => void) => {
if (state.originalPrompt) {
setInput(state.originalPrompt);
resetEnhancer();
}
}, [state.originalPrompt, resetEnhancer]);

const enhancePrompt = useCallback(async (
input: string,
setInput: (value: string) => void,
options?: {
onSuccess?: () => void;
onError?: (error: Error) => void;
}
) => {
if (!input.trim()) {
setState(prev => ({ ...prev, error: 'Please enter a prompt to enhance' }));
return;
}

try {
setInput('');
setState(prev => ({
...prev,
isEnhancing: true,
error: null,
originalPrompt: input
}));

try {
const response = await fetch('/api/enhancer', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
message: input,
}),
});

if (!response.ok) {
throw new Error(`Enhancement failed: ${response.statusText}`);
}

while (true) {
const { value, done } = await reader.read();
const reader = response.body?.getReader();

if (done) {
break;
}
if (!reader) {
throw new Error('Failed to get response reader');
}

_input += decoder.decode(value);
setInput(''); // Clear existing input before streaming
let enhancedPrompt = '';

logger.trace('Set input', _input);
while (true) {
const { value, done } = await reader.read();

setInput(_input);
}
} catch (error) {
_error = error;
setInput(originalInput);
} finally {
if (_error) {
logger.error(_error);
}
if (done) break;

setEnhancingPrompt(false);
setPromptEnhanced(true);
const chunk = new TextDecoder().decode(value);
enhancedPrompt += chunk;
setInput(enhancedPrompt);

logger.trace('Streaming chunk', { chunk, enhancedPrompt });
}

setTimeout(() => {
setInput(_input);
});
setState(prev => ({
...prev,
isEnhancing: false,
isEnhanced: true,
error: null,
}));

options?.onSuccess?.();

} catch (error) {
logger.error('Enhancement error:', error);

const errorMessage = error instanceof Error ? error.message : 'Failed to enhance prompt';

setState(prev => ({
...prev,
isEnhancing: false,
isEnhanced: false,
error: errorMessage,
}));

// Revert to original input on error
if (state.originalPrompt) {
setInput(state.originalPrompt);
}

options?.onError?.(error instanceof Error ? error : new Error(errorMessage));
}
}, [state.originalPrompt]);

return {
enhancePrompt,
revertToOriginal,
resetEnhancer,
isEnhancing: state.isEnhancing,
isEnhanced: state.isEnhanced,
error: state.error,
hasOriginal: !!state.originalPrompt,
};
}

return { enhancingPrompt, promptEnhanced, enhancePrompt, resetEnhancer };
// Example usage:
/*
function PromptInput() {
const [input, setInput] = useState('');
const {
enhancePrompt,
revertToOriginal,
isEnhancing,
isEnhanced,
error,
hasOriginal
} = usePromptEnhancer();
return (
<div>
<textarea
value={input}
onChange={(e) => setInput(e.target.value)}
disabled={isEnhancing}
/>
<div>
<button
onClick={() => enhancePrompt(input, setInput, {
onSuccess: () => console.log('Enhancement successful!'),
onError: (error) => console.error('Enhancement failed:', error)
})}
disabled={isEnhancing || !input.trim()}
>
{isEnhancing ? 'Enhancing...' : 'Enhance Prompt'}
</button>
{hasOriginal && (
<button
onClick={() => revertToOriginal(setInput)}
disabled={isEnhancing}
>
Revert to Original
</button>
)}
</div>
{error && <div className="error">{error}</div>}
{isEnhanced && <div className="success">Prompt enhanced successfully!</div>}
</div>
);
}
*/
53 changes: 31 additions & 22 deletions app/routes/api.enhancer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,42 +19,51 @@ async function enhancerAction({ context, request }: ActionFunctionArgs) {
{
role: 'user',
content: stripIndents`
I want you to improve the user prompt that is wrapped in \`<original_prompt>\` tags.
IMPORTANT: Only respond with the improved prompt and nothing else!
<original_prompt>
${message}
</original_prompt>
`,
Enhance and expand this prompt to be more specific and detailed. Add requirements, features, and technical specifications that would help create a better application.
Consider:
- UI/UX requirements
- Data management
- Features and functionality
- Technical stack specifics
- Error handling
Original prompt:
"${message}"
Respond only with the enhanced prompt, no explanations or additional text.
`,
},
],
context.cloudflare.env,
);

const transformStream = new TransformStream({
transform(chunk, controller) {
const processedChunk = decoder
.decode(chunk)
.split('\n')
.filter((line) => line !== '')
.map(parseStreamPart)
.map((part) => part.value)
.join('');

controller.enqueue(encoder.encode(processedChunk));
},
const text = decoder.decode(chunk);
const lines = text.split('\n').filter(line => line.trim());

for (const line of lines) {
try {
const parsed = parseStreamPart(line);
if (parsed && typeof parsed.value === 'string') {
controller.enqueue(encoder.encode(parsed.value));
}
} catch {
// If parsing fails, just send the line directly
controller.enqueue(encoder.encode(line));
}
}
}
});

const transformedStream = result.toAIStream().pipeThrough(transformStream);

return new StreamingTextResponse(transformedStream);
} catch (error) {
console.log(error);

console.error('Enhancer action error:', error);
throw new Response(null, {
status: 500,
statusText: 'Internal Server Error',
});
}
}
}

0 comments on commit 0fd6899

Please sign in to comment.