mirror of
https://github.com/open-jarvis/OpenJarvis.git
synced 2026-07-29 18:40:38 +00:00
fix: use server's model for agent ticks, add logging (#138)
fix: use server's model for agent ticks, add logging
This commit is contained in:
@@ -1280,7 +1280,7 @@ function InteractTab({ agentId, agentStatus }: { agentId: string; agentStatus: s
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === 'Enter' && !e.shiftKey) {
|
||||
e.preventDefault();
|
||||
handleSend('queued');
|
||||
handleSend('immediate');
|
||||
}
|
||||
}}
|
||||
placeholder="Send a message to this agent..."
|
||||
@@ -1293,23 +1293,8 @@ function InteractTab({ agentId, agentStatus }: { agentId: string; agentStatus: s
|
||||
disabled={sending || !input.trim()}
|
||||
className="flex items-center gap-1.5 px-3 py-1.5 rounded-lg text-sm cursor-pointer font-medium"
|
||||
style={{ background: 'var(--color-accent)', color: '#fff', opacity: sending || !input.trim() ? 0.5 : 1 }}
|
||||
title="Send immediately (interrupts agent)"
|
||||
>
|
||||
<Zap size={13} /> Immediate
|
||||
</button>
|
||||
<button
|
||||
onClick={() => handleSend('queued')}
|
||||
disabled={sending || !input.trim()}
|
||||
className="flex items-center gap-1.5 px-3 py-1.5 rounded-lg text-sm cursor-pointer"
|
||||
style={{
|
||||
background: 'var(--color-bg-secondary)',
|
||||
color: 'var(--color-text)',
|
||||
border: '1px solid var(--color-border)',
|
||||
opacity: sending || !input.trim() ? 0.5 : 1,
|
||||
}}
|
||||
title="Queue message for next run"
|
||||
>
|
||||
<Send size={13} /> Queue
|
||||
<Send size={13} /> Send
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -203,6 +203,12 @@ class AgentExecutor:
|
||||
if not model:
|
||||
raise FatalError("No model configured for agent")
|
||||
|
||||
logger.info(
|
||||
"Agent %s [%s]: using model=%s, engine=%s",
|
||||
agent["name"], agent["id"],
|
||||
model, type(engine).__name__,
|
||||
)
|
||||
|
||||
# Optionally override model via router policy
|
||||
router_policy_key = config.get("router_policy")
|
||||
if router_policy_key and self._system:
|
||||
@@ -247,6 +253,16 @@ class AgentExecutor:
|
||||
input_text = f"{input_text}\n\nNew instructions:\n{user_msgs}"
|
||||
for m in pending:
|
||||
self._manager.mark_message_delivered(m["id"])
|
||||
logger.info(
|
||||
"Agent %s: delivering %d pending message(s)",
|
||||
agent["name"], len(pending),
|
||||
)
|
||||
else:
|
||||
logger.info(
|
||||
"Agent %s: no pending messages, running with "
|
||||
"instruction only",
|
||||
agent["name"],
|
||||
)
|
||||
|
||||
# Build AgentContext with memory results from FTS5 backend
|
||||
from openjarvis.agents._stubs import AgentContext
|
||||
@@ -298,7 +314,22 @@ class AgentExecutor:
|
||||
pass # Don't break agent tick if memory retrieval fails
|
||||
|
||||
agent_ctx.memory_results = memory_results
|
||||
return agent_instance.run(input_text, context=agent_ctx)
|
||||
logger.info(
|
||||
"Agent %s: calling agent.run() with %d chars input",
|
||||
agent["name"], len(input_text),
|
||||
)
|
||||
_t0 = time.time()
|
||||
result = agent_instance.run(input_text, context=agent_ctx)
|
||||
_elapsed = time.time() - _t0
|
||||
logger.info(
|
||||
"Agent %s: agent.run() completed in %.1fs, "
|
||||
"content_len=%d, turns=%d, tokens=%s",
|
||||
agent["name"], _elapsed,
|
||||
len(result.content or ""),
|
||||
result.turns,
|
||||
result.metadata.get("total_tokens", "?"),
|
||||
)
|
||||
return result
|
||||
|
||||
def _build_error_detail(self, error: AgentTickError) -> dict[str, Any]:
|
||||
"""Build structured error detail for trace metadata."""
|
||||
@@ -336,6 +367,12 @@ class AgentExecutor:
|
||||
"""Update agent state after tick completion or failure."""
|
||||
if error is None:
|
||||
# Success
|
||||
logger.info(
|
||||
"Tick succeeded for agent %s in %.1fs, "
|
||||
"response_len=%d",
|
||||
agent_id, duration,
|
||||
len(result.content or "") if result else 0,
|
||||
)
|
||||
self._manager.end_tick(agent_id)
|
||||
self._manager.update_agent(agent_id, total_runs_increment=1)
|
||||
|
||||
@@ -381,6 +418,10 @@ class AgentExecutor:
|
||||
"status": "ok",
|
||||
})
|
||||
elif isinstance(error, EscalateError):
|
||||
logger.warning(
|
||||
"Tick escalated for agent %s after %.1fs: %s",
|
||||
agent_id, duration, error,
|
||||
)
|
||||
self._manager.end_tick(agent_id)
|
||||
self._manager.update_agent(agent_id, status="needs_attention")
|
||||
self._bus.publish(EventType.AGENT_TICK_ERROR, {
|
||||
@@ -390,6 +431,10 @@ class AgentExecutor:
|
||||
"duration": duration,
|
||||
})
|
||||
else:
|
||||
logger.error(
|
||||
"Tick failed for agent %s after %.1fs: %s",
|
||||
agent_id, duration, error, exc_info=error,
|
||||
)
|
||||
self._manager.end_tick(agent_id)
|
||||
self._manager.update_agent(agent_id, status="error")
|
||||
# Write error detail to summary_memory so frontend can display it
|
||||
|
||||
@@ -64,6 +64,24 @@ _BROWSER_SUB_TOOLS = {
|
||||
}
|
||||
|
||||
|
||||
class _LightweightSystem:
|
||||
"""Minimal system facade for the executor — avoids rebuilding the
|
||||
full JarvisSystem (which picks a random model from Ollama)."""
|
||||
|
||||
def __init__(self, engine: Any, model: str, config: Any = None):
|
||||
self.engine = engine
|
||||
self.model = model
|
||||
self.config = config
|
||||
self.memory_backend = None
|
||||
|
||||
|
||||
def _make_lightweight_system(
|
||||
engine: Any, model: str, config: Any = None,
|
||||
) -> _LightweightSystem:
|
||||
"""Build a minimal system from the server's existing state."""
|
||||
return _LightweightSystem(engine, model, config)
|
||||
|
||||
|
||||
def _ensure_registries_populated() -> None:
|
||||
"""Ensure ToolRegistry and ChannelRegistry are populated.
|
||||
|
||||
@@ -452,7 +470,7 @@ def create_agent_manager_router(
|
||||
return {"status": "idle"}
|
||||
|
||||
@agents_router.post("/{agent_id}/run")
|
||||
async def run_agent(agent_id: str):
|
||||
async def run_agent(agent_id: str, request: Request):
|
||||
import threading
|
||||
|
||||
agent = manager.get_agent(agent_id)
|
||||
@@ -473,28 +491,30 @@ def create_agent_manager_router(
|
||||
status_code=409, detail="Agent is already running"
|
||||
)
|
||||
|
||||
# Re-use the server's engine + model so we don't pick a
|
||||
# random model from Ollama's list.
|
||||
server_engine = getattr(request.app.state, "engine", None)
|
||||
server_model = getattr(request.app.state, "model", "")
|
||||
server_config = getattr(request.app.state, "config", None)
|
||||
|
||||
def _run_tick():
|
||||
try:
|
||||
from openjarvis.agents.executor import AgentExecutor
|
||||
from openjarvis.core.events import get_event_bus
|
||||
from openjarvis.system import SystemBuilder
|
||||
|
||||
executor = AgentExecutor(
|
||||
manager=manager, event_bus=get_event_bus(),
|
||||
)
|
||||
try:
|
||||
system = SystemBuilder().build()
|
||||
executor.set_system(system)
|
||||
except Exception as build_err:
|
||||
manager.end_tick(agent_id)
|
||||
manager.update_agent(agent_id, status="error")
|
||||
manager.update_summary_memory(
|
||||
agent_id,
|
||||
f"ERROR: Failed to build system: {build_err}",
|
||||
)
|
||||
return
|
||||
system = _make_lightweight_system(
|
||||
server_engine, server_model, server_config,
|
||||
)
|
||||
executor.set_system(system)
|
||||
executor.execute_tick(agent_id)
|
||||
except Exception as exc:
|
||||
logger.error(
|
||||
"Run-tick failed for agent %s: %s",
|
||||
agent_id, exc, exc_info=True,
|
||||
)
|
||||
try:
|
||||
manager.end_tick(agent_id)
|
||||
except Exception:
|
||||
@@ -603,25 +623,56 @@ def create_agent_manager_router(
|
||||
if not req.stream and req.mode == "immediate":
|
||||
# Non-streaming immediate: trigger a background tick so the
|
||||
# agent processes the message, then return the stored msg.
|
||||
# Re-use the server's existing system (correct model/engine).
|
||||
import threading
|
||||
import time as _time
|
||||
|
||||
from openjarvis.agents.executor import AgentExecutor
|
||||
from openjarvis.core.events import get_event_bus
|
||||
from openjarvis.system import SystemBuilder
|
||||
|
||||
_srv_engine = getattr(request.app.state, "engine", None)
|
||||
_srv_model = getattr(request.app.state, "model", "")
|
||||
_srv_config = getattr(request.app.state, "config", None)
|
||||
|
||||
def _immediate_tick():
|
||||
_start = _time.time()
|
||||
logger.info(
|
||||
"Immediate tick starting for agent %s "
|
||||
"(model=%s)",
|
||||
agent_id, _srv_model,
|
||||
)
|
||||
try:
|
||||
executor = AgentExecutor(
|
||||
manager=manager, event_bus=get_event_bus(),
|
||||
)
|
||||
try:
|
||||
system = SystemBuilder().build()
|
||||
executor.set_system(system)
|
||||
except Exception:
|
||||
return
|
||||
system = _make_lightweight_system(
|
||||
_srv_engine, _srv_model, _srv_config,
|
||||
)
|
||||
executor.set_system(system)
|
||||
logger.info(
|
||||
"Immediate tick: system ready in %.1fs, "
|
||||
"executing tick for agent %s",
|
||||
_time.time() - _start, agent_id,
|
||||
)
|
||||
executor.execute_tick(agent_id)
|
||||
except Exception:
|
||||
pass
|
||||
logger.info(
|
||||
"Immediate tick completed for agent %s "
|
||||
"in %.1fs",
|
||||
agent_id, _time.time() - _start,
|
||||
)
|
||||
except Exception as exc:
|
||||
logger.error(
|
||||
"Immediate tick failed for agent %s: %s",
|
||||
agent_id, exc, exc_info=True,
|
||||
)
|
||||
try:
|
||||
manager.end_tick(agent_id)
|
||||
except Exception:
|
||||
pass
|
||||
manager.update_agent(agent_id, status="error")
|
||||
manager.update_summary_memory(
|
||||
agent_id, f"ERROR: {exc}",
|
||||
)
|
||||
|
||||
threading.Thread(
|
||||
target=_immediate_tick, daemon=True,
|
||||
|
||||
Reference in New Issue
Block a user