Consume the api locally

This commit is contained in:
2025-01-13 21:32:20 +01:00
parent 754bc1f7d8
commit fc07f39796
4 changed files with 42 additions and 16 deletions
+2 -2
View File
@@ -1,5 +1,4 @@
.container { .container {
max-width: 500px;
margin: auto; margin: auto;
padding: 1rem; padding: 1rem;
letter-spacing: 0.25rem; letter-spacing: 0.25rem;
@@ -23,12 +22,13 @@
.enter:before { .enter:before {
content: '\21b5'; content: '\21b5';
display: inline-block;
} }
.enter:after { .enter:after {
content: ''; content: '';
display: block; display: block;
clear: both; clear: right;
} }
.tab:before { .tab:before {
+37 -9
View File
@@ -2,35 +2,57 @@ import { useEffect, useState } from 'react';
import './App.css'; import './App.css';
const KEYS_TO_DISABLE = ['Backspace', 'Shift', 'Alt', 'Control', 'F1', 'F2', 'F3', 'F4', 'F5', 'F6', 'F7', 'F8', 'F9', 'F10', 'F11', 'F12', 'Escape', 'Delete', 'PageDown', 'PageUp', 'Home', 'End', 'Insert', 'WakeUp', 'Pause', 'ScrollLock', 'ContextMenu', 'BrowserForward', 'BrowserBack', 'CapsLock', 'ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight']; const KEYS_TO_DISABLE = ['Backspace', 'Shift', 'Alt', 'Control', 'F1', 'F2', 'F3', 'F4', 'F5', 'F6', 'F7', 'F8', 'F9', 'F10', 'F11', 'F12', 'Escape', 'Delete', 'PageDown', 'PageUp', 'Home', 'End', 'Insert', 'WakeUp', 'Pause', 'ScrollLock', 'ContextMenu', 'BrowserForward', 'BrowserBack', 'CapsLock', 'ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight'];
const SNIPPET = 'ctx := context.Background();\nconsole.log("hello");\nfunction() {\n\talert(123);\n}';
function App() { function App() {
const [code, setCode] = useState('');
const [loaded, setLoaded] = useState(false);
const [characters, setCharacters] = useState([]); const [characters, setCharacters] = useState([]);
useEffect(() => {
(async function() {
const response = await fetch('http://localhost:5000/generate?lang=golang&lines=10');
const reader = response.body.getReader();
const decoder = new TextDecoder();
while(true) {
const {value, done} = await reader.read();
setCode(prev => prev + decoder.decode(value));
if (done) {
setLoaded(true);
break;
}
}
})();
}, []);
useEffect(() => { useEffect(() => {
function handleKeyPress(event) { function handleKeyPress(event) {
event.preventDefault(); event.preventDefault();
if (characters.length === SNIPPET.length) return; if (!loaded) return;
if (characters.length === code.length) return;
if (KEYS_TO_DISABLE.includes(event.key)) return; if (KEYS_TO_DISABLE.includes(event.key)) return;
if (SNIPPET[characters.length] === '\n') if (code[characters.length] === '\n')
return setCharacters( return setCharacters(
characters.concat(event.key === 'Enter') characters.concat(event.key === 'Enter')
); );
if (SNIPPET[characters.length] === '\t') if (code[characters.length] === '\t')
return setCharacters( return setCharacters(
characters.concat(event.key === 'Tab') characters.concat(event.key === 'Tab')
); );
setCharacters( setCharacters(
characters.concat(event.key === SNIPPET[characters.length]) characters.concat(event.key === code[characters.length])
); );
} }
window.addEventListener('keydown', handleKeyPress); window.addEventListener('keydown', handleKeyPress);
return () => window.removeEventListener('keydown', handleKeyPress); return () => window.removeEventListener('keydown', handleKeyPress);
}, [characters]); // eslint-disable-next-line react-hooks/exhaustive-deps
}, [loaded, characters]);
function renderCharacterClassName(index) { function renderCharacterClassName(index) {
if (index === characters.length) return 'highlight' if (index === characters.length) return 'highlight'
@@ -40,16 +62,22 @@ function App() {
return 'correct' return 'correct'
} }
// TODO: Remove rendering of spacing characters and render a cursor with the highlight instead.
// TODO: Or look into pretty printing libraries.
function renderSpacingCharacter(char) { function renderSpacingCharacter(char) {
if (/^\t$/.test(char)) return 'tab'
if (/^\n$/.test(char)) return 'enter' if (/^\n$/.test(char)) return 'enter'
if (/^\t$/.test(char)) return 'tab'
if (/^\s$/.test(char)) return 'space' if (/^\s$/.test(char)) return 'space'
return '' return ''
} }
return <div className='container'>{SNIPPET.split('').map((char, index) => ( function renderClassName(index, char) {
<span className={`${renderCharacterClassName(index)} ${renderSpacingCharacter(char)}`} key={char + index}>{char}</span> return `${renderCharacterClassName(index)} ${renderSpacingCharacter(char)}`.trim();
}
return <div className='container'>{code.split('').map((char, index) => (
<span className={renderClassName(index, char)} key={char + index}>{char}</span>
))}</div>; ))}</div>;
} }
+1 -3
View File
@@ -1,10 +1,8 @@
import { StrictMode } from 'react';
import { createRoot } from 'react-dom/client'; import { createRoot } from 'react-dom/client';
import './index.css'; import './index.css';
import App from './App.jsx'; import App from './App.jsx';
// TODO: Bring back string mode when building and deploying
createRoot(document.getElementById('root')).render( createRoot(document.getElementById('root')).render(
<StrictMode>
<App /> <App />
</StrictMode>,
); );
+2 -2
View File
@@ -41,9 +41,9 @@ func main() {
ollamaCtx := context.Background() ollamaCtx := context.Background()
content := []llms.MessageContent{ content := []llms.MessageContent{
llms.TextParts(llms.ChatMessageTypeSystem, `You are only a code generator. You must not respond with anything else but code and do not format with code fences.`), llms.TextParts(llms.ChatMessageTypeSystem, `You are only a code generator. You must not respond with anything else but code and do not format with code fences. Always use tabs instead of spaces.`),
llms.TextParts(llms.ChatMessageTypeHuman, fmt.Sprintf(` llms.TextParts(llms.ChatMessageTypeHuman, fmt.Sprintf(`
Generate max %d lines of code without any unncessary formatting from a well known open source project in the %s programming language. First line should always be a code comment in this format: Language/Project`, lines, lang)), Generate max %d lines of code without any unncessary formatting from a well known open source project in the %s programming language. First line should always be a code comment in the used language in this format: "// Language/Project"`, lines, lang)),
} }
if _, err := llm.GenerateContent(ollamaCtx, content, llms.WithStreamingFunc(func(streamCtx context.Context, chunk []byte) error { if _, err := llm.GenerateContent(ollamaCtx, content, llms.WithStreamingFunc(func(streamCtx context.Context, chunk []byte) error {