Implemented vibecoding functionality for llmchat

This commit is contained in:
Luxferre
2025-12-26 14:10:10 +02:00
parent ab5c859cee
commit e66ebd50a9
4 changed files with 138 additions and 16 deletions
+9 -1
View File
@@ -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
View File
@@ -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
View File
@@ -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.
+1
View File
@@ -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()