728x90

출처

소스 다운로드

orangepi@orangepi5pro:~/Llama$ source .venv/bin/activate
(.venv) orangepi@orangepi5pro:~/Llama$ git clone https://github.com/NotPunchnox/web-client-rkllm.git

실행

(.venv) orangepi@orangepi5pro:~/Llama$ cd web-client-rkllm/
orangepi@orangepi5pro:~/Llama/web-client-rkllm$ ./start.sh
Node.js is already installed (version: v24.11.1).
Starting rkllama Web Server on port 3000...
 ERROR  Cannot copy server address to clipboard: Couldn't find the `xsel` binary and fallback didn't work. On Debian/Ubuntu you can install xsel with: sudo apt install xsel.
 
   ┌───────────────────────────────────────────┐
   │                                           │
   │   Serving!                                │
   │                                           │
   │   - Local:    http://localhost:3000       │
   │   - Network:  http://192.168.0.218:3000   │
   │                                           │
   └───────────────────────────────────────────┘
 
rkllama Web Server is now running at http://localhost:3000
Port used: 3000
Press Ctrl+C to stop the server.

script.js 수정

(.venv) orangepi@orangepi5pro:~/Llama/web-client-rkllm$ vi src/js/script.js
// const API_URL = 'http://localhost:8080/';
const API_URL = 'http://192.168.0.218:8080/';


// sendBtn.addEventListener('click', sendMessage);
sendBtn.addEventListener('click', sendChat);
userInput.addEventListener('keypress', (e) => {
    if (e.key === 'Enter') sendChat(); // sendMessage();
});


async function sendChat() {
    const modelName = modelSelect.value;
    if (!modelName) return;
    
    // Prevent double sending
    if (requestInProgress) return;

    const message = userInput.value.trim();
    const selectedModel = modelSelect.value;

    if (!message) return;
    if (!selectedModel) {
        addMessage('error', 'Please select a model first');
        return;
    }

    // Add user message to chat
    addMessage('user', message);
    HISTORY.push({ role: 'user', content: message });
    userInput.value = '';

    // Prepare for sending
    requestInProgress = true;
    chatLoading.style.display = 'flex';

    try {
        const response = await fetch(API_URL + '/v1/chat/completions', {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify({ model: modelName, messages: HISTORY, stream: true })
        });

        if (!response.ok) {
            const errorData = await response.json();
            throw new Error(errorData.error || 'Failed to generate response');
        }

        // Create message container
        const messageDiv = document.createElement('div');
        messageDiv.className = 'message';

        // Add author
        const authorSpan = document.createElement('strong');
        authorSpan.textContent = `RKLLAMA (${selectedModel}): `;
        messageDiv.appendChild(authorSpan);

        // Add content container for streaming
        const contentContainer = document.createElement('div');
        contentContainer.className = 'message-content';
        messageDiv.appendChild(contentContainer);

        chatBox.appendChild(messageDiv);

        // Stream response - définir reader ici
        const reader = response.body.getReader();
        const decoder = new TextDecoder('utf-8');
        let assistantMessage = '';

        // Fonction pour lire le stream
        async function readStream() {
            let done = false;

            while (!done) {
                const { value, done: doneReading } = await reader.read();
                done = doneReading;

                if (done) break;

                const chunk = decoder.decode(value, { stream: true });
                try {
                    const lines = chunk.split('\n');

                    for (const line of lines) {
                        if (!line.trim()) continue;
                        
                        jsonChunk = '';
                        if (line.startsWith("data: ")) {
                          const jsonString = line.replace(/^data:\s*/, "");
                          jsonChunk = JSON.parse(jsonString);
                        } else {
                          jsonChunk = JSON.parse(line);
                        }

                        // const jsonChunk = JSON.parse(line);
                        console.log(jsonChunk);

                        if (jsonChunk.choices && jsonChunk.choices[0].delta && jsonChunk.choices[0].delta.content) {
                            assistantMessage += jsonChunk.choices[0].delta.content;
                            processContent(contentContainer, assistantMessage);
                            chatBox.scrollTop = chatBox.scrollHeight;
                        }
                    }
                } catch (e) {
                    console.error('Error parsing chunk:', e);
                }
            }
        }

        await readStream();

        // Add to history and save conversation
        HISTORY.push({ role: 'assistant', content: assistantMessage });
        saveConversation();
    } catch (error) {
        addMessage('error', error.message);
    } finally {
        requestInProgress = false;
        chatLoading.style.display = 'none';
    }
}

async function showModelInfo() {
    const modelName = modelSelect.value;
    if (!modelName) return;

    try {
        // Show loading state
        // modelSelect.disabled = true;

        const res = await fetch(API_URL + 'api/show', {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify({ name: modelName })
        });
        
        if (!res.ok) throw new Error('Failed to fetch models');

        const data = await res.json();
        console.log('showApi.data = ' + data);
        
        /*
        modelSelect.innerHTML = '<option value="">Select a model</option>';

        if (data.models && data.models.length > 0) {
            data.models.forEach(model => {
                modelSelect.innerHTML += `<option value="${model}">${model}</option>`;
            });
            addMessage('system', `${data.models.length} models available. Select one to start chatting.`);
        } else {
            addMessage('system', 'No models available. Download a model to get started.');
        }
        */
    } catch (err) {
        console.error('Error loading models:', err);
        addMessage('error', `Could not load models: ${err.message}`);
    }
}

브라우저 실행

728x90

+ Recent posts