mirror of
https://github.com/zvx-echo6/meshai.git
synced 2026-05-21 23:24:44 +02:00
Fix bugs: MeshMessage position field, summary loading, Google system prompt, import placement, cleanup timer
- 1a: Declare _position as proper dataclass field with field(default=None, init=False) so hasattr() check isn't needed and the attribute always exists - 1b: Load persisted conversation summaries from DB into memory cache on startup via new _load_summaries() method called after backend creation - 1c: Use Gemini's system_instruction parameter on GenerativeModel instead of only prepending to first message, so system prompt persists across all turns - 1d: Move 'import os' from line 198 to top of main.py with other imports - 1e: Replace unreliable modulo-based cleanup timer with _last_cleanup timestamp comparison that won't miss hours due to async sleep jitter Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
bc3e85a6fb
commit
c1f2c48494
3 changed files with 53 additions and 13 deletions
|
|
@ -162,6 +162,12 @@ class GoogleBackend(LLMBackend):
|
|||
)
|
||||
|
||||
try:
|
||||
# Create model with system instruction for persistent system prompt
|
||||
model = genai.GenerativeModel(
|
||||
self.config.model,
|
||||
system_instruction=enhanced_system if enhanced_system else None,
|
||||
)
|
||||
|
||||
# Convert messages to Gemini format
|
||||
# Gemini uses "user" and "model" roles
|
||||
history = []
|
||||
|
|
@ -170,15 +176,11 @@ class GoogleBackend(LLMBackend):
|
|||
history.append({"role": role, "parts": [msg["content"]]})
|
||||
|
||||
# Start chat with history
|
||||
chat = self._model.start_chat(history=history)
|
||||
chat = model.start_chat(history=history)
|
||||
|
||||
# Get the last user message
|
||||
last_message = final_messages[-1]["content"] if final_messages else ""
|
||||
|
||||
# Prepend system prompt to first message if needed
|
||||
if enhanced_system and not history:
|
||||
last_message = f"{enhanced_system}\n\n{last_message}"
|
||||
|
||||
# Generate response
|
||||
response = await chat.send_message_async(
|
||||
last_message,
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
import asyncio
|
||||
import logging
|
||||
from dataclasses import dataclass
|
||||
from dataclasses import dataclass, field
|
||||
from typing import Callable, Optional
|
||||
|
||||
import meshtastic
|
||||
|
|
@ -26,13 +26,12 @@ class MeshMessage:
|
|||
channel: int # Channel index
|
||||
is_dm: bool # True if direct message to us
|
||||
packet: dict # Raw packet for additional data
|
||||
_position: Optional[tuple[float, float]] = field(default=None, repr=False, init=False)
|
||||
|
||||
@property
|
||||
def sender_position(self) -> Optional[tuple[float, float]]:
|
||||
"""Get sender's GPS position if available (lat, lon)."""
|
||||
# Position comes from node info, not the message itself
|
||||
# This will be populated by the connector if available
|
||||
return self._position if hasattr(self, "_position") else None
|
||||
return self._position
|
||||
|
||||
|
||||
class MeshConnector:
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
import argparse
|
||||
import asyncio
|
||||
import logging
|
||||
import os
|
||||
import signal
|
||||
import sys
|
||||
import time
|
||||
|
|
@ -18,6 +19,7 @@ from .commands.status import set_start_time
|
|||
from .config import Config, load_config
|
||||
from .connector import MeshConnector, MeshMessage
|
||||
from .history import ConversationHistory
|
||||
from .memory import ConversationSummary
|
||||
from .responder import Responder
|
||||
from .router import MessageRouter, RouteType
|
||||
|
||||
|
|
@ -37,6 +39,7 @@ class MeshAI:
|
|||
self.responder: Optional[Responder] = None
|
||||
self._running = False
|
||||
self._loop: Optional[asyncio.AbstractEventLoop] = None
|
||||
self._last_cleanup: float = 0.0
|
||||
|
||||
async def start(self) -> None:
|
||||
"""Start the bot."""
|
||||
|
|
@ -52,6 +55,7 @@ class MeshAI:
|
|||
|
||||
self._running = True
|
||||
self._loop = asyncio.get_event_loop()
|
||||
self._last_cleanup = time.time()
|
||||
|
||||
# Write PID file
|
||||
self._write_pid()
|
||||
|
|
@ -63,8 +67,9 @@ class MeshAI:
|
|||
await asyncio.sleep(1)
|
||||
|
||||
# Periodic cleanup
|
||||
if int(time.time()) % 3600 == 0: # Every hour
|
||||
if time.time() - self._last_cleanup >= 3600:
|
||||
await self.history.cleanup_expired()
|
||||
self._last_cleanup = time.time()
|
||||
|
||||
async def stop(self) -> None:
|
||||
"""Stop the bot."""
|
||||
|
|
@ -121,6 +126,9 @@ class MeshAI:
|
|||
self.config.llm, api_key, window_size, summarize_threshold
|
||||
)
|
||||
|
||||
# Load persisted summaries into memory cache
|
||||
await self._load_summaries()
|
||||
|
||||
# Meshtastic connector
|
||||
self.connector = MeshConnector(self.config.connection)
|
||||
|
||||
|
|
@ -183,6 +191,40 @@ class MeshAI:
|
|||
except Exception as e:
|
||||
logger.error(f"Error handling message: {e}", exc_info=True)
|
||||
|
||||
async def _load_summaries(self) -> None:
|
||||
"""Load persisted summaries from database into memory cache."""
|
||||
memory = self.llm.get_memory()
|
||||
if not memory:
|
||||
return
|
||||
|
||||
if not self.history or not self.history._db:
|
||||
return
|
||||
|
||||
try:
|
||||
async with self.history._lock:
|
||||
cursor = await self.history._db.execute(
|
||||
"SELECT user_id, summary, message_count, updated_at "
|
||||
"FROM conversation_summaries"
|
||||
)
|
||||
rows = await cursor.fetchall()
|
||||
|
||||
loaded = 0
|
||||
for row in rows:
|
||||
user_id, summary_text, message_count, updated_at = row
|
||||
summary = ConversationSummary(
|
||||
summary=summary_text,
|
||||
last_updated=updated_at,
|
||||
message_count=message_count,
|
||||
)
|
||||
memory.load_summary(user_id, summary)
|
||||
loaded += 1
|
||||
|
||||
if loaded:
|
||||
logger.info(f"Loaded {loaded} conversation summaries from database")
|
||||
|
||||
except Exception as e:
|
||||
logger.warning(f"Failed to load summaries from database: {e}")
|
||||
|
||||
def _write_pid(self) -> None:
|
||||
"""Write PID file."""
|
||||
pid_file = Path("/tmp/meshai.pid")
|
||||
|
|
@ -195,9 +237,6 @@ class MeshAI:
|
|||
pid_file.unlink()
|
||||
|
||||
|
||||
import os
|
||||
|
||||
|
||||
def setup_logging(verbose: bool = False) -> None:
|
||||
"""Configure logging."""
|
||||
level = logging.DEBUG if verbose else logging.INFO
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue