Added iteration loading
This commit is contained in:
146
sia/iteration_parser.py
Normal file
146
sia/iteration_parser.py
Normal 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
|
||||||
@@ -2,11 +2,13 @@ from aiohttp import web
|
|||||||
import json
|
import json
|
||||||
import asyncio
|
import asyncio
|
||||||
|
|
||||||
|
|
||||||
from ..auto_approver import AutoApprover
|
from ..auto_approver import AutoApprover
|
||||||
|
from ..entry.entry_factory import EntryFactory
|
||||||
|
from ..iteration_parser import IterationParser
|
||||||
from ..web_agent import WebAgent
|
from ..web_agent import WebAgent
|
||||||
from ..web_io_buffer import WebIOBuffer
|
from ..web_io_buffer import WebIOBuffer
|
||||||
from ..working_memory import WorkingMemory
|
from ..working_memory import WorkingMemory
|
||||||
from ..entry.entry_factory import EntryFactory
|
|
||||||
|
|
||||||
class Api:
|
class Api:
|
||||||
def __init__(
|
def __init__(
|
||||||
@@ -47,6 +49,7 @@ class Api:
|
|||||||
self._app.router.add_delete("/api/memory/entry/{id}", self._delete_entry)
|
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}/reset", self._reset_entry)
|
||||||
self._app.router.add_post("/api/memory/entry/{id}/update", self._update_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:
|
async def _run_inference(self, request: web.Request) -> web.Response:
|
||||||
"""Start inference on specified LLM."""
|
"""Start inference on specified LLM."""
|
||||||
@@ -253,4 +256,18 @@ class Api:
|
|||||||
entry.notify_change()
|
entry.notify_change()
|
||||||
return web.Response(status=200)
|
return web.Response(status=200)
|
||||||
except ValueError as e:
|
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)
|
||||||
|
|||||||
@@ -43,6 +43,28 @@ export const MemoryEditor = ({
|
|||||||
setShowCreateDialog(false);
|
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 generateId = () => {
|
||||||
const now = new Date();
|
const now = new Date();
|
||||||
return now.getFullYear() +
|
return now.getFullYear() +
|
||||||
@@ -160,10 +182,16 @@ export const MemoryEditor = ({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="p-4">
|
<div className="p-4">
|
||||||
<div className="flex justify-end mb-4">
|
<div className="flex justify-end mb-4 gap-2">
|
||||||
<button
|
<button
|
||||||
onClick={() => setShowCreateDialog(true)}
|
onClick={handleLoadIterationClick}
|
||||||
className="px-4 py-2 text-sm font-medium text-white bg-blue-600 rounded-md hover:bg-blue-700"
|
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
|
Add Entry
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
Reference in New Issue
Block a user