Added iteration loading

This commit is contained in:
2024-11-24 20:35:28 +01:00
parent b6c1a95abd
commit a9e70f0a22
3 changed files with 196 additions and 5 deletions

146
sia/iteration_parser.py Normal file
View File

@@ -0,0 +1,146 @@
from datetime import datetime
import xml.etree.ElementTree as ET
from typing import List
from .entry import Entry
from .entry.background_entry import BackgroundEntry
from .entry.parse_error_entry import ParseErrorEntry
from .entry.read_entry import ReadEntry
from .entry.reasoning_entry import ReasoningEntry
from .entry.repeat_entry import RepeatEntry
from .entry.single_entry import SingleEntry
from .entry.write_entry import WriteEntry
class IterationParser:
"""Parses iteration XML files into entries"""
@staticmethod
def parse_iteration(content: str, io_buffer) -> tuple[str, str, List[Entry]]:
"""
Parse iteration XML content into context, response and entries
Args:
content: Raw XML content as string
io_buffer: Buffer to use for IO operations
Returns:
Tuple of (context_str, response_str, entry_list)
"""
root = ET.fromstring(content)
context_elem = root.find("context")
if context_elem is None:
raise ValueError("Invalid iteration file - missing context")
context = ET.fromstring(context_elem.text)
entries = []
for elem in context:
if elem.tag == "background":
entries.append(IterationParser._parse_background(elem))
elif elem.tag == "parse_error":
entries.append(IterationParser._parse_parse_error(elem))
elif elem.tag == "read_stdin":
entries.append(IterationParser._parse_read(elem, io_buffer))
elif elem.tag == "reasoning":
entries.append(IterationParser._parse_reasoning(elem))
elif elem.tag == "repeat":
entries.append(IterationParser._parse_repeat(elem))
elif elem.tag == "single":
entries.append(IterationParser._parse_single(elem))
elif elem.tag == "write_stdout":
entries.append(IterationParser._parse_write(elem, io_buffer))
return entries
@staticmethod
def _parse_background(elem: ET.Element) -> BackgroundEntry:
entry = BackgroundEntry(
id=elem.get("id"),
script=elem.text
)
if elem.get("exit_code"):
entry.exit_code = int(elem.get("exit_code"))
stdout = elem.find("stdout")
if stdout is not None:
entry.stdout = stdout.text or ""
stderr = elem.find("stderr")
if stderr is not None:
entry.stderr = stderr.text or ""
return entry
@staticmethod
def _parse_parse_error(elem: ET.Element) -> ParseErrorEntry:
error = elem.find("error")
content = elem.find("content")
return ParseErrorEntry(
id=elem.get("id"),
content=content.text if content is not None else "",
error=error.text if error is not None else ""
)
@staticmethod
def _parse_read(elem: ET.Element, io_buffer) -> ReadEntry:
entry = ReadEntry(
id=elem.get("id"),
io_buffer=io_buffer
)
entry.content = elem.text or ""
entry.read = True
return entry
@staticmethod
def _parse_reasoning(elem: ET.Element) -> ReasoningEntry:
return ReasoningEntry(
id=elem.get("id"),
content=elem.text or ""
)
@staticmethod
def _parse_repeat(elem: ET.Element) -> RepeatEntry:
entry = RepeatEntry(
id=elem.get("id"),
script=elem.text,
timeout=float(elem.get("timeout")) if elem.get("timeout") else None,
limit=int(elem.get("limit")) if elem.get("limit") else None
)
if elem.get("exit_code"):
entry.exit_code = int(elem.get("exit_code"))
entry.timed_out = elem.get("timed_out") == "true"
stdout = elem.find("stdout")
if stdout is not None:
entry.stdout = stdout.text or ""
stderr = elem.find("stderr")
if stderr is not None:
entry.stderr = stderr.text or ""
return entry
@staticmethod
def _parse_single(elem: ET.Element) -> SingleEntry:
entry = SingleEntry(
id=elem.get("id"),
script=elem.text,
timeout=float(elem.get("timeout")) if elem.get("timeout") else None,
limit=int(elem.get("limit")) if elem.get("limit") else None
)
if elem.get("exit_code"):
entry.exit_code = int(elem.get("exit_code"))
entry.executed = True
entry.timed_out = elem.get("timed_out") == "true"
stdout = elem.find("stdout")
if stdout is not None:
entry.stdout = stdout.text or ""
stderr = elem.find("stderr")
if stderr is not None:
entry.stderr = stderr.text or ""
return entry
@staticmethod
def _parse_write(elem: ET.Element, io_buffer) -> WriteEntry:
entry = WriteEntry(
id=elem.get("id"),
content=elem.text or "",
io_buffer=io_buffer
)
entry.written = True
return entry

View File

@@ -2,11 +2,13 @@ from aiohttp import web
import json
import asyncio
from ..auto_approver import AutoApprover
from ..entry.entry_factory import EntryFactory
from ..iteration_parser import IterationParser
from ..web_agent import WebAgent
from ..web_io_buffer import WebIOBuffer
from ..working_memory import WorkingMemory
from ..entry.entry_factory import EntryFactory
class Api:
def __init__(
@@ -47,6 +49,7 @@ class Api:
self._app.router.add_delete("/api/memory/entry/{id}", self._delete_entry)
self._app.router.add_post("/api/memory/entry/{id}/reset", self._reset_entry)
self._app.router.add_post("/api/memory/entry/{id}/update", self._update_entry)
self._app.router.add_post("/api/memory/load_iteration", self._load_iteration)
async def _run_inference(self, request: web.Request) -> web.Response:
"""Start inference on specified LLM."""
@@ -253,4 +256,18 @@ class Api:
entry.notify_change()
return web.Response(status=200)
except ValueError as e:
return web.Response(status=400, text=str(e))
return web.Response(status=400, text=str(e))
async def _load_iteration(self, request: web.Request) -> web.Response:
"""Load entries from iteration XML content into working memory"""
data = await request.json()
content = data.get("content")
if not content:
return web.Response(status=400, text="Missing content in request body")
entries = IterationParser.parse_iteration(content, self._io_buffer)
for entry in entries:
self._working_memory.add_entry(entry)
return web.Response(status=200)

View File

@@ -43,6 +43,28 @@ export const MemoryEditor = ({
setShowCreateDialog(false);
};
const handleLoadIteration = async (event) => {
const file = event.target.files[0];
if (file) {
const content = await file.text();
const response = await fetch('/api/memory/load_iteration', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ content: content })
});
if (!response.ok) throw new Error('Failed to load iteration');
}
};
const handleLoadIterationClick = () => {
const input = document.createElement('input');
input.type = 'file';
input.accept = '.xml';
input.onchange = handleLoadIteration;
input.click();
};
const generateId = () => {
const now = new Date();
return now.getFullYear() +
@@ -160,10 +182,16 @@ export const MemoryEditor = ({
return (
<div className="p-4">
<div className="flex justify-end mb-4">
<div className="flex justify-end mb-4 gap-2">
<button
onClick={() => setShowCreateDialog(true)}
className="px-4 py-2 text-sm font-medium text-white bg-blue-600 rounded-md hover:bg-blue-700"
onClick={handleLoadIterationClick}
className="px-4 py-2 text-sm font-medium bg-white hover:bg-gray-100 border border-gray-200 rounded-md"
>
Load Iteration
</button>
<button
onClick={() => setShowCreateDialog(true)}
className="px-4 py-2 text-sm font-medium bg-white hover:bg-gray-100 border border-gray-200 rounded-md"
>
Add Entry
</button>