File size: 4,001 Bytes
8dc575c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
"""
database.py — SQLite Hardware Abstraction Layer.
Persists all system component states across steps within a session.
"""

from __future__ import annotations
import sqlite3
import json
import threading
import tempfile
from pathlib import Path
from typing import Any, Dict, Optional

import tempfile
DB_PATH = Path(tempfile.gettempdir()) / "quest_openenv.db"

_local = threading.local()


def get_conn() -> sqlite3.Connection:
    """Return a thread-local SQLite connection."""
    if not hasattr(_local, "conn") or _local.conn is None:
        _local.conn = sqlite3.connect(str(DB_PATH), check_same_thread=False)
        _local.conn.row_factory = sqlite3.Row
        _local.conn.execute("PRAGMA journal_mode=WAL")
        _local.conn.execute("PRAGMA synchronous=NORMAL")
    return _local.conn


def init_db() -> None:
    """Initialize all required tables."""
    conn = get_conn()
    conn.executescript("""
        CREATE TABLE IF NOT EXISTS env_state (
            key   TEXT PRIMARY KEY,
            value TEXT NOT NULL
        );

        CREATE TABLE IF NOT EXISTS action_log (
            id         INTEGER PRIMARY KEY AUTOINCREMENT,
            step       INTEGER NOT NULL,
            task_id    TEXT NOT NULL,
            action     TEXT NOT NULL,
            reward     REAL NOT NULL,
            success    INTEGER NOT NULL,
            message    TEXT NOT NULL,
            timestamp  REAL NOT NULL DEFAULT (unixepoch('now', 'subsec'))
        );

        CREATE TABLE IF NOT EXISTS session_backup (
            key   TEXT PRIMARY KEY,
            value TEXT NOT NULL
        );
    """)
    conn.commit()


def set_state(key: str, value: Any) -> None:
    conn = get_conn()
    conn.execute(
        "INSERT OR REPLACE INTO env_state (key, value) VALUES (?, ?)",
        (key, json.dumps(value))
    )
    conn.commit()


def get_state(key: str, default: Any = None) -> Any:
    conn = get_conn()
    row = conn.execute(
        "SELECT value FROM env_state WHERE key = ?", (key,)
    ).fetchone()
    if row is None:
        return default
    return json.loads(row["value"])


def set_state_bulk(data: Dict[str, Any]) -> None:
    conn = get_conn()
    conn.executemany(
        "INSERT OR REPLACE INTO env_state (key, value) VALUES (?, ?)",
        [(k, json.dumps(v)) for k, v in data.items()]
    )
    conn.commit()


def get_all_state() -> Dict[str, Any]:
    conn = get_conn()
    rows = conn.execute("SELECT key, value FROM env_state").fetchall()
    return {row["key"]: json.loads(row["value"]) for row in rows}


def log_action(step: int, task_id: str, action: str, reward: float,
               success: bool, message: str) -> None:
    conn = get_conn()
    conn.execute(
        """INSERT INTO action_log (step, task_id, action, reward, success, message)
           VALUES (?, ?, ?, ?, ?, ?)""",
        (step, task_id, action, reward, int(success), message)
    )
    conn.commit()


def backup_session(data: Dict[str, Any]) -> None:
    """Save session data snapshot (simulates persistent workspace)."""
    conn = get_conn()
    conn.executemany(
        "INSERT OR REPLACE INTO session_backup (key, value) VALUES (?, ?)",
        [(k, json.dumps(v)) for k, v in data.items()]
    )
    conn.commit()


def restore_session() -> Dict[str, Any]:
    """Restore session data from backup."""
    conn = get_conn()
    rows = conn.execute("SELECT key, value FROM session_backup").fetchall()
    return {row["key"]: json.loads(row["value"]) for row in rows}


def clear_all() -> None:
    """Wipe env_state (does NOT touch session_backup or action_log)."""
    conn = get_conn()
    conn.execute("DELETE FROM env_state")
    conn.commit()


def get_action_history(task_id: str, limit: int = 20) -> list:
    conn = get_conn()
    rows = conn.execute(
        """SELECT step, action, reward, success, message
           FROM action_log WHERE task_id = ?
           ORDER BY id DESC LIMIT ?""",
        (task_id, limit)
    ).fetchall()
    return [dict(r) for r in rows]