Implemented vibecoding functionality for llmchat
This commit is contained in:
@@ -206,6 +206,9 @@ The instance exposes the following methods (all returning a `(status, result)` t
|
|||||||
- `reset_context()`: clear the conversation history and re-add the system prompt
|
- `reset_context()`: clear the conversation history and re-add the system prompt
|
||||||
- `get_completion(prompt)`: prompt the LLM and return the response message
|
- `get_completion(prompt)`: prompt the LLM and return the response message
|
||||||
- `list_models()`: list available model IDs from the current active provider
|
- `list_models()`: list available model IDs from the current active provider
|
||||||
|
- `get_last_response()`: get the most recent assistant's response as a plain string
|
||||||
|
- `add_file(fname)`: read a text file and add it to the context (within the message limit)
|
||||||
|
- `export()`: export the entire current conversation (within the message limit) as a plain string
|
||||||
|
|
||||||
## Applets (`app.*`)
|
## Applets (`app.*`)
|
||||||
|
|
||||||
@@ -241,7 +244,7 @@ The editor mode supports the following subset of POSIX ed commands in the standa
|
|||||||
|
|
||||||
### `app.llmchat`
|
### `app.llmchat`
|
||||||
|
|
||||||
A simple interactive chat with remote LLMs. Configured via `llmcfg.json` (see the `deck.llm` module reference) placed into the storage root.
|
A simple interactive chat with remote LLMs. Configured via `llmcfg.json` (see the `deck.llm` module reference) placed into the storage root. Run with the single `llmchat()` method it exports.
|
||||||
|
|
||||||
Supported chat commands:
|
Supported chat commands:
|
||||||
|
|
||||||
@@ -253,6 +256,11 @@ Supported chat commands:
|
|||||||
- `/model`: display or set the current model selection
|
- `/model`: display or set the current model selection
|
||||||
- `/provlist`: list provider IDs in the config
|
- `/provlist`: list provider IDs in the config
|
||||||
- `/modellist`: list model IDs available for current provider
|
- `/modellist`: list model IDs available for current provider
|
||||||
|
- `/add`: add a (text) file to the context
|
||||||
|
- `/saveconv`: save the entire conversation log (within the current limits) into a file
|
||||||
|
- `/savelast`: save the most recent message into a file
|
||||||
|
- `/savecode`: extract code blocks from the most recent message and save them into a file
|
||||||
|
- `/edcode`: extract code blocks from the most recent message and open `app.ed.edbuf()` on the contents
|
||||||
- `/exit` or `/quit`: exit the chat applet
|
- `/exit` or `/quit`: exit the chat applet
|
||||||
|
|
||||||
### `app.blog`
|
### `app.blog`
|
||||||
|
|||||||
+94
-15
@@ -2,10 +2,12 @@
|
|||||||
# (also can be run on MicroPython and usual CPython)
|
# (also can be run on MicroPython and usual CPython)
|
||||||
# On the T-Deck, requires to be run from tdeck_repl for the
|
# On the T-Deck, requires to be run from tdeck_repl for the
|
||||||
# keyboard to work correctly
|
# keyboard to work correctly
|
||||||
|
# Usage: from llmchat import llmchat
|
||||||
# Created by Luxferre in 2025, released into public domain
|
# Created by Luxferre in 2025, released into public domain
|
||||||
|
|
||||||
from deck.chat import DeckChat
|
from deck.chat import DeckChat
|
||||||
from deck.llm import LLMChat
|
from deck.llm import LLMChat
|
||||||
|
from app.ed import edbuf
|
||||||
|
|
||||||
CONFIG_FILE = 'llmcfg.json'
|
CONFIG_FILE = 'llmcfg.json'
|
||||||
|
|
||||||
@@ -67,8 +69,79 @@ def list_providers(stub, state):
|
|||||||
resp += f'\n - {p}'
|
resp += f'\n - {p}'
|
||||||
return resp, state
|
return resp, state
|
||||||
|
|
||||||
|
def save_conversation(fname, state):
|
||||||
|
resp = ''
|
||||||
|
status, cnv = state.export()
|
||||||
|
try:
|
||||||
|
with open(fname, 'w') as f:
|
||||||
|
f.write(cnv)
|
||||||
|
resp = 'Conversation saved'
|
||||||
|
except:
|
||||||
|
resp = 'Error saving conversation'
|
||||||
|
return resp, state
|
||||||
|
|
||||||
|
def save_last_msg(fname, state):
|
||||||
|
resp = ''
|
||||||
|
status, cnv = state.get_last_response()
|
||||||
|
try:
|
||||||
|
with open(fname, 'w') as f:
|
||||||
|
f.write(cnv)
|
||||||
|
resp = 'Response saved'
|
||||||
|
except:
|
||||||
|
resp = 'Error saving response'
|
||||||
|
return resp, state
|
||||||
|
|
||||||
|
def extract_code_block(text, start_marker="```", end_marker="```"):
|
||||||
|
extracted_parts = []
|
||||||
|
search_index = 0
|
||||||
|
while True:
|
||||||
|
block_start = text.find(start_marker, search_index)
|
||||||
|
if block_start == -1:
|
||||||
|
break
|
||||||
|
content_start = block_start + len(start_marker)
|
||||||
|
if start_marker == "```":
|
||||||
|
newline_after_start = text.find("\n", content_start)
|
||||||
|
if newline_after_start != -1:
|
||||||
|
potential_end = text.find(end_marker, content_start)
|
||||||
|
if potential_end == -1 or newline_after_start < potential_end:
|
||||||
|
content_start = newline_after_start + 1
|
||||||
|
block_end = text.find(end_marker, content_start)
|
||||||
|
if block_end == -1:
|
||||||
|
break
|
||||||
|
extracted_block = text[content_start:block_end].strip()
|
||||||
|
if extracted_block:
|
||||||
|
extracted_parts.append(extracted_block)
|
||||||
|
search_index = block_end + len(end_marker)
|
||||||
|
return "\n\n".join(extracted_parts)
|
||||||
|
|
||||||
|
def save_last_msg_code(fname, state):
|
||||||
|
resp = ''
|
||||||
|
status, cnv = state.get_last_response()
|
||||||
|
cnv = extract_code_block(cnv)
|
||||||
|
try:
|
||||||
|
with open(fname, 'w') as f:
|
||||||
|
f.write(cnv)
|
||||||
|
resp = 'Code saved'
|
||||||
|
except:
|
||||||
|
resp = 'Error saving code'
|
||||||
|
return resp, state
|
||||||
|
|
||||||
|
def edit_last_msg_code(fname, state):
|
||||||
|
resp = ''
|
||||||
|
status, cnv = state.get_last_response()
|
||||||
|
cnv = extract_code_block(cnv)
|
||||||
|
try:
|
||||||
|
edbuf(cnv)
|
||||||
|
except:
|
||||||
|
resp = 'Error extracting code'
|
||||||
|
return resp, state
|
||||||
|
|
||||||
|
def add_file(fname, state):
|
||||||
|
status, resp = state.add_file(fname)
|
||||||
|
return resp, state
|
||||||
|
|
||||||
def display_help(stub, state):
|
def display_help(stub, state):
|
||||||
resp = "/clear, /system, /temp, /prov, /model, /modellist, /provlist, /exit"
|
resp = "/clear, /system, /temp, /prov, /model, /modellist, /provlist, /add, /saveconv, /savelast, /savecode, /edcode, /exit, /quit"
|
||||||
return resp, state
|
return resp, state
|
||||||
|
|
||||||
def clear_ctx(stub, state):
|
def clear_ctx(stub, state):
|
||||||
@@ -76,17 +149,23 @@ def clear_ctx(stub, state):
|
|||||||
return 'Context reset', state
|
return 'Context reset', state
|
||||||
|
|
||||||
# main initialization
|
# main initialization
|
||||||
llm = LLMChat(config_file=CONFIG_FILE, message_limit=20)
|
def llmchat():
|
||||||
chat = DeckChat(start_state=llm, chat_prefix='> ')
|
llm = LLMChat(config_file=CONFIG_FILE, message_limit=20)
|
||||||
chat.command('default', send_msg)
|
chat = DeckChat(start_state=llm, chat_prefix='> ')
|
||||||
chat.command('clear', clear_ctx)
|
chat.command('default', send_msg)
|
||||||
chat.command('system', set_sys_prompt)
|
chat.command('clear', clear_ctx)
|
||||||
chat.command('temp', set_temp)
|
chat.command('system', set_sys_prompt)
|
||||||
chat.command('prov', set_prov)
|
chat.command('temp', set_temp)
|
||||||
chat.command('model', set_model)
|
chat.command('prov', set_prov)
|
||||||
chat.command('modellist', list_models)
|
chat.command('model', set_model)
|
||||||
chat.command('provlist', list_providers)
|
chat.command('modellist', list_models)
|
||||||
chat.command('help', display_help)
|
chat.command('provlist', list_providers)
|
||||||
chat.command('exit', lambda m,s: ('Exiting...', None))
|
chat.command('add', add_file)
|
||||||
chat.command('quit', lambda m,s: ('Exiting...', None))
|
chat.command('saveconv', save_conversation)
|
||||||
chat.start()
|
chat.command('savelast', save_last_msg)
|
||||||
|
chat.command('savecode', save_last_msg_code)
|
||||||
|
chat.command('edcode', edit_last_msg_code)
|
||||||
|
chat.command('help', display_help)
|
||||||
|
chat.command('exit', lambda m,s: ('Exiting...', None))
|
||||||
|
chat.command('quit', lambda m,s: ('Exiting...', None))
|
||||||
|
chat.start()
|
||||||
|
|||||||
+34
@@ -111,6 +111,40 @@ class LLMChat:
|
|||||||
if len(self.messages) > 0: self.messages.pop()
|
if len(self.messages) > 0: self.messages.pop()
|
||||||
return (False, f"Error: {e}")
|
return (False, f"Error: {e}")
|
||||||
|
|
||||||
|
def get_last_response(self):
|
||||||
|
"""
|
||||||
|
Get the most recent assistant's response.
|
||||||
|
"""
|
||||||
|
idx = len(self.messages) - 1
|
||||||
|
while idx > 0 and self.messages[idx]['role'] != 'assistant':
|
||||||
|
idx -= 1
|
||||||
|
if idx == 0:
|
||||||
|
return (False, 'No assistant messages detected')
|
||||||
|
else:
|
||||||
|
return (True, self.messages[idx]['content'])
|
||||||
|
|
||||||
|
def export(self):
|
||||||
|
"""
|
||||||
|
Export the entire conversation as a string.
|
||||||
|
"""
|
||||||
|
out = ''
|
||||||
|
for msg in self.messages:
|
||||||
|
out += f'{msg['role']}: {msg['content'].strip()}\n'
|
||||||
|
return (True, out.strip())
|
||||||
|
|
||||||
|
def add_file(self, fname):
|
||||||
|
"""
|
||||||
|
Add a (text) file to the context.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
with open(fname, 'r') as f:
|
||||||
|
text = f.read()
|
||||||
|
prompt = f'Contents of the file {fname}:\n\n{text}'
|
||||||
|
self.messages.append({'role': 'user', 'content': prompt})
|
||||||
|
return (True, 'File content added')
|
||||||
|
except:
|
||||||
|
return (False, 'Could not read the file')
|
||||||
|
|
||||||
def list_models(self):
|
def list_models(self):
|
||||||
"""
|
"""
|
||||||
Lists available models for the active provider.
|
Lists available models for the active provider.
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ from deck.http import auth_header, url_escape, form_encode
|
|||||||
from deck import xlat
|
from deck import xlat
|
||||||
from app.ed import edit, view, edbuf, viewbuf, edurl, viewurl
|
from app.ed import edit, view, edbuf, viewbuf, edurl, viewurl
|
||||||
from app.blog import blogpost, blogpost_str, bloged
|
from app.blog import blogpost, blogpost_str, bloged
|
||||||
|
from app.llmchat import llmchat
|
||||||
try:
|
try:
|
||||||
from deck import net
|
from deck import net
|
||||||
requests = net.init_requests()
|
requests = net.init_requests()
|
||||||
|
|||||||
Reference in New Issue
Block a user